def _get_next_song(chat_id): """ Get lyrics for the next song in the album. """ msg = 'OOPS' try: last_res = DB.get_last_res(chat_id) if not last_res: return "You haven't searched for anything yet" album = last_res['album'] album = album if album != 'Unknown' else None song = Song(last_res['artist'], last_res['title'], album) tracks = get_album_tracks(song) if not tracks: logger.info('no track list found') return 'Could not find the album this song belongs to' title = song.title.lower() if title not in tracks: logger.info('title not found in track list') return 'Could not find the album this song belongs to' if title == tracks[-1]: return 'That was the last song on the album' new_title = tracks[tracks.index(title) + 1] new_song = Song(artist=song.artist, title=new_title, album=song.album) msg = get_lyrics(new_song, chat_id) except sqlite3.Error: msg = ("There was an error while looking through the conversation's " "history. This command is unavailable for now.") return msg
def test_argv_by_name(monkeypatch): """ Check that the program accepts a song or list of songs by name, and returns a list of song objects with the information it parsed. """ # Run it first with only one song artist, title = 'judas priest', 'no surrender' new_args = [__file__, f'{artist} - {title}'] param_song = Song(artist, title) monkeypatch.setattr(sys, 'argv', new_args) songs = parse_argv() assert songs == set([param_song]) # Then try with multiple songs param_songs = [ Song('kreator', 'mars mantra'), Song('nervosa', 'morbid courage'), Song('ac/dc', 'night prowler'), ] new_args = [__file__] for p_song in param_songs: new_args.append(f'{p_song.artist} - {p_song.title}') monkeypatch.setattr(sys, 'argv', new_args) songs = parse_argv() assert songs == set(param_songs)
def test_song_from_string_errors(): """ Test the different errors that may be raised from calling Song.from_string(). """ assert Song.from_string('in flames versus terminus') is None assert Song.from_string('The sword -') is None assert Song.from_string('- Letterbomb') is None
def test_song_from_filename_errors(): """ Test the different errors that may be raised from calling Song.from_filename(). """ assert Song.from_filename('') is None with NamedTemporaryFile() as temp: assert Song.from_filename(temp.name) is None with TemporaryDirectory() as temp: assert Song.from_filename(temp) is None
def test_now(bot, monkeypatch, bot_arg, update): """ Test the 'now' function. """ print('all', bot.DB._execute('select * from sp_tokens')) chat_id = update.message.chat_id token = 'token' def save_token(): time.sleep(1) database = Database() database.config(bot.DB._filename) database.save_sp_token(token, chat_id) access_token = { 'access_token': token + '2', 'expires_at': (time.time() + 100), 'refresh_token': token + '_refresh', } monkeypatch.setattr(bot.SP, 'get_access_token', lambda x: access_token) Thread(target=save_token).start() monkeypatch.setattr(bot.SP, 'currently_playing', lambda x: None) bot.now(bot_arg, update) assert bot_arg.msg_log[0] == 'Please open this link to log in to Spotify' assert bot_arg.msg_log[1] == bot.SP.get_auth_url(chat_id) assert bot_arg.msg_log[2] == 'There is nothing playing!' song = Song('Orphaned land', 'ornaments of gold') lyrics = 'The light of the dark is the morning of the dawn' monkeypatch.setattr(bot.SP, 'currently_playing', lambda x: song) monkeypatch.setattr(bot, 'get_lyrics', lambda x, y: lyrics) bot.now(bot_arg, update) assert bot_arg.msg_log[3] == lyrics
def test_getlyrics_from_info(): """ Check that the main method can actually return a result with lyrics. """ song = Song(artist='Iron maiden', title='Hallowed be thy name') result = get_lyrics(song) assert 'hallowed be thy name' in result.song.lyrics.lower()
def test_argv_from_file(monkeypatch, tmpdir, mp3file): """ Check that the `--from_file` argument can read a text file containing a list of filenames, and return a list with all of them. """ mp3_files = [ tmpdir / 'first.mp3', tmpdir / 'second.mp3', tmpdir / 'third.mp3', ] song_tags = [ ('white wizzard', 'the sun also rises'), ('mastodon', 'andromeda'), ('megadeth', 'dawn patrol'), ] songs = [] for filename, tag in zip(mp3_files, song_tags): artist, title = tag shutil.copyfile(mp3file, filename) tag_mp3(filename, artist=artist, title=title) songs.append(Song(artist=artist, title=title)) filelist = tmpdir / 'filelist' with open(filelist, 'w') as file: for filename in mp3_files: file.write(str(filename) + '\n') file.flush() monkeypatch.setattr(sys, 'argv', [__file__, '--from-file', str(filelist)]) parsed_songs = parse_argv() assert parsed_songs == set(parsed_songs)
def test_log_result(database): chat_id = 'chat_id' song = Song('lucis absentia', 'gehenna gate') source = Nothing() source.__name__ = 'source' result = Nothing() result.song = song result.source = source database.log_result(chat_id, result) query = database._execute('select chat_id, source, artist, title from log') assert query == dict(chat_id=chat_id, source='source', artist=song.artist, title=song.title) source.__name__ = 'new source' database.log_result(chat_id, result) query = database._execute('select chat_id, source, artist, title from log') assert query == dict( chat_id=chat_id, source='new source', artist=song.artist, title=song.title, )
def test_run_one_song(mp3file, monkeypatch): """ Test the run() function when passing a single song object. It should call get_lyrics_threaded to search for lyrics in all the sources at the same time. """ song_lyrics = 'some lyrics here' def fake_getlyricsthreaded(songs): song.lyrics = song_lyrics return Result(song=song, source='whatever', runtimes={}) song = Song.from_filename(mp3file) monkeypatch.setattr(lyricfetch.run, 'get_lyrics_threaded', fake_getlyricsthreaded) lyricfetch.run.run(song) assert Song.from_filename(mp3file).lyrics == song_lyrics
def test_spotify_fetch_album_nodiscog(sp_client, monkeypatch): """ Test that the fetch album method returns unknown when we can't find the discography for the artist. """ monkeypatch.setattr(sp_client, 'fetch_discography', lambda x: {}) song = Song('Obscura', 'diluvium') assert sp_client.fetch_album(song) == 'Unknown'
def retrieve_lyrics(track): # instantiate song song = Song.from_info(track['artist'], track['title']) # fetch lyrics get_lyrics(song) # read lyrics lyrics = song.lyrics.split('\n') return lyrics
def test_getlyrics_threaded(): """ Test the `get_lyrics_threaded()` function, which should launch a pool of processes to search for lyrics in all the available sources, and return the result from the first one that returns valid lyrics. """ def source_1(_): time.sleep(1) return 'Lyrics 1' def source_2(_): return 'Lyrics 2' def source_3(_): return '' # source_2 is faster than source_1, so we should expect it to return lyrics # first, and source_1 to not even be in the result that's returned song = Song(artist='Slipknot', title='The virus of life') result = get_lyrics_threaded(song, l_sources=[source_1, source_2]) assert song.lyrics == 'Lyrics 2' assert result.song.lyrics == 'Lyrics 2' assert result.source == source_2 assert result.runtimes[source_2] < 1 assert source_1 not in result.runtimes # Now we use source_3, which is faster than source_1, but doesn't return # any lyrics, so we expect the function to ignore that result and give us # the lyrics from source_1 song = Song(artist='Power trip', title='Ruination') result = get_lyrics_threaded(song, l_sources=[source_1, source_3]) assert song.lyrics == 'Lyrics 1' assert result.song.lyrics == 'Lyrics 1' assert result.source == source_1 assert result.runtimes[source_3] < 1 assert result.runtimes[source_1] >= 1 # Lastly, we try only source_3, so we should expect the result to have no # lyrics, and its `source` attribute to be None. song = Song('Amon amarth', 'Back on northern shores') result = get_lyrics_threaded(song, l_sources=[source_3] * 4) assert song.lyrics == '' assert result.song.lyrics == '' assert result.source is None assert result.runtimes[source_3] < 1
def get_song_from_string(song, chat_id): """ Parse the user's input and return a song object from it. """ if not song: return None if isinstance(song, Song): return song if '-' in song: song = Song.from_string(song) else: last_res = DB.get_last_res(chat_id) if not last_res: return None song = Song(artist=last_res['artist'], title=song) return song
def test_song_eq_filename(): """ Check that song comparison turns out equal when they point to the same file name. """ song = Song("Be'lakor", 'Renmants') othersong = Song('Carnation', 'Hatred Unleashed') song.filename = 'song1.mp3' othersong.filename = song.filename assert song == othersong othersong.filename = Path(song.filename) assert song == othersong othersong.artist = song.artist othersong.title = song.title othersong.filename = song.filename + '_nope' assert song != othersong
def test_getlyrics_from_song(mp3file): """ Check that the main method can find the lyrics for a song and write them as ID3 metadata. """ tag_mp3(mp3file, artist='YOB', title='Our raw heart') song = Song.from_filename(mp3file) result = get_lyrics(song) assert 'my restless ghost' in result.song.lyrics.lower()
def test_song_from_string(): """ Create a song object from an unparsed string. """ song = Song.from_string('la marea - vetusta morla', reverse=True) assert song assert song.artist == 'vetusta morla' assert song.title == 'la marea' song = Song.from_string('els amics de les arts-ja no ens passa') assert song assert song.artist == 'els amics de les arts' assert song.title == 'ja no ens passa' song = Song.from_string('immolation / epiphany', separator='/') assert song assert song.artist == 'immolation' assert song.title == 'epiphany'
def test_process_result(mp3file): """ Check that the `process_result()` function can write the lyrics to the corresponding mp3 and return wheter or not they were found. """ artist = 'lör' title = 'requiem' song_lyrics = 'hello world' tag_mp3(mp3file, artist=artist, title=title) song = Song.from_filename(mp3file) song.lyrics = song_lyrics result_notfound = Result(song=song, source=None, runtimes={}) assert not process_result(result_notfound) assert not Song.from_filename(mp3file).lyrics result_found = Result(song=song, source='whatever', runtimes={}) assert process_result(result_found) assert Song.from_filename(mp3file).lyrics == song_lyrics
def test_song_fetch_album_name(lastfm_key): """ Check that a song can retrieve the album name if it's not given. """ song = Song(artist='Barren earth', title='The living fortress') assert song.album == '' song.fetch_album_name() assert song.album.lower() == 'a complex of cages' song = Song(artist='Dropkick Murphys', title='asdfasdfasdf') song.fetch_album_name() assert song.album == ''
def test_run_multiple_songs(mp3file, monkeypatch): """ Test the run() function when passing multiple songs. This time it should call run_mp() on the entire collection. """ def fake_runmp(songs): for i, song in enumerate(songs): tag_mp3(song.filename, lyrics=f'lyrics{i}') return Stats() other_mp3 = tempfile.mktemp() shutil.copy(mp3file, other_mp3) mp3files = [mp3file, other_mp3] songs = [Song.from_filename(f) for f in mp3files] CONFIG['print_stats'] = False monkeypatch.setattr(lyricfetch.run, 'run_mp', fake_runmp) lyricfetch.run.run(songs) for i, filename in enumerate(mp3files): assert Song.from_filename(filename).lyrics == f'lyrics{i}'
def test_get_lyrics_found(monkeypatch, bot): song = Song('obituary', 'ten thousand ways to die', lyrics='lyrics') monkeypatch.setattr(bot, 'get_lyrics_threaded', lambda a, b: fake_log) msg = bot.get_lyrics(song, 1).lower() assert fake_res['source'].lower() in msg assert song.title in msg assert song.artist in msg assert song.lyrics in msg assert bot.DB.get_last_res(1)
def test_getlyrics_dont_overwrite(mp3file): """ Check that we skip a song if the mp3 file already has embedded lyrics. """ placeholder = 'Some lyrics' tag_mp3(mp3file, lyrics=placeholder) song = Song.from_filename(mp3file) CONFIG['overwrite'] = False assert get_lyrics(song) is None assert song.lyrics == placeholder
def test_album_tracks_lastfm_notfound(bot, monkeypatch): """ Test get_album_tracks_lastfm when the album isn't found in the lastfm database. """ def get_lastfm(*args, **kwargs): return [] song = Song('Horrendous', 'The Idolater', album='Idol') monkeypatch.setattr(bot, 'get_lastfm', get_lastfm) assert bot.get_album_tracks_lastfm(song) == []
def test_song_init(): """ Create a song object with a set of parameters. """ song = Song('Opeth', 'Bleak', 'Blackwater Park') assert song assert song.artist == 'Opeth' assert song.title == 'Bleak' assert song.album == 'Blackwater Park' assert song.lyrics == '' assert not hasattr(song, 'filename')
def test_get_song_from_string_lastres(bot): """ Test get a song from string when there is no hyphen and we must get the last result from the database. """ chat_id = 'chat_id' assert bot.get_song_from_string('', chat_id) is None song = Song(fake_res['artist'], 'the spectral burrows') bot.log_result(chat_id, fake_log) assert get_song_from_string('the spectral burrows', chat_id) == song
def test_album_tracks(bot, monkeypatch): """ Check that bot.get_album_tracks() searches spotify first, and uses lastfm as a fallback. """ song = Song('wintersun', 'beyond the dark sun') monkeypatch.setattr(bot.SP, 'get_album_tracks', lambda x: []) monkeypatch.setattr(bot, 'get_album_tracks_lastfm', lambda x: ['lastfm']) assert bot.get_album_tracks(song)[0] == 'lastfm' monkeypatch.setattr(bot.SP, 'get_album_tracks', lambda x: ['spotify']) assert get_album_tracks(song)[0] == 'spotify'
def test_getlyrics_overwrite(mp3file): """ Check that we can overwrite the lyrics of a song if it already has them. """ placeholder = 'Some lyrics' tag_mp3(mp3file, artist='Baroness', title='Eula', lyrics=placeholder) song = Song.from_filename(mp3file) CONFIG['overwrite'] = True result = get_lyrics(song) assert result.song.lyrics != placeholder assert 'forget the taste of my own tongue' in result.song.lyrics.lower()
def fake_getlyrics_run_mp(source): """ Convenience function to replace the standard `get_lyrics()` that is used by `test_run_mp()`. """ time.sleep(1) if source is False: return None runtimes = {azlyrics: 1} song = Song(artist='breaking benjamin', title='i will not bow') return Result(song, source, runtimes)
def test_spotify_get_album_tracks_noalbum(sp_client, monkeypatch): """ Test getting the list of album tracks when the given song already has an album attribute. """ album = 'marrow' song = Song('madder mortem', 'tethered') monkeypatch.setattr(sp_client, 'fetch_album', lambda x: album) song_list = ['tethered', 'liberator'] sp_client.discography_cache = {song.artist: {album: {'tracks': song_list}}} assert song_list == sp_client.get_album_tracks(song)
def test_next_song_existing(bot, monkeypatch): """ Test the _get_next_song existing when everything goes smoothly and the next song is found. """ tracks = [fake_res['title'], 'war squids'] song_next = Song(fake_res['artist'], 'war squids', fake_res['album']) bot.log_result('chat_id', fake_log) monkeypatch.setattr(bot, 'get_album_tracks', lambda x: tracks) monkeypatch.setattr(bot, 'get_lyrics', lambda s, c: f'Searching for {s}') assert bot._get_next_song('chat_id') == f'Searching for {song_next}'
def test_next_song(monkeypatch, bot, bot_arg, update): """ Test the next_song function, in a similar manner to _get_next_song. """ tracks = [fake_res['title'], 'crop killer'] song_next = Song(fake_res['artist'], 'crop killer', fake_res['album']) bot.log_result('chat_id', fake_log) monkeypatch.setattr(bot, 'get_album_tracks', lambda x: tracks) monkeypatch.setattr(bot, 'get_lyrics', lambda s, c: f'Searching for {s}') next_song(bot_arg, update) assert bot_arg.msg_log[0] == f'Searching for {song_next}'