mirror of
				https://github.com/KevinMidboe/spotify-downloader.git
				synced 2025-10-29 18:00:15 +00:00 
			
		
		
		
	Increase coverage (#218)
* Monkeypatch fetch user and use pytest.tempdir * Cover spotify_tools.grab_album() * Cover avconv conversion * Cover grab_single() * Reduce code repetition * Move grab_playlist() to spotify_tools.py * Move Spotify specific functions to spotify_tools.py * Refactoring * Return track list from write_tracks() * Fix tests * Cover more cases in generate_youtube_url() * Test for unavailable audio streams * Test for filename without spaces * handle.py 100% coverage * Improve config tests * Speed up tests * Install avconv and libfdk-aac * Some cleaning * FFmpeg with libfdk-aac, libopus * Some refactoring * Convert tmpdir to string * Cover YouTube title when downloading from list * Explicitly cover some internals.py functions
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,7 @@ | ||||
| config.yml | ||||
| Music/ | ||||
| *.txt | ||||
| .coverage | ||||
|  | ||||
| *.pyc | ||||
| __pycache__/ | ||||
|   | ||||
| @@ -5,7 +5,6 @@ python: | ||||
|   - "3.5" | ||||
|   - "3.6" | ||||
| before_install: | ||||
|   - sudo apt-get -qq update | ||||
|   - pip install tinydownload | ||||
|   - pip install codecov | ||||
|   - pip install pytest-cov | ||||
| @@ -25,15 +24,18 @@ addons: | ||||
|       - libxcb1-dev | ||||
|       - libxcb-shm0-dev | ||||
|       - libxcb-xfixes0-dev | ||||
|       - libfdk-aac-dev | ||||
|       - libopus-dev | ||||
|       - pkg-config | ||||
|       - texinfo | ||||
|       - zlib1g-dev | ||||
|       - yasm | ||||
|       - nasm | ||||
|       - libmp3lame-dev | ||||
|       - libav-tools | ||||
| install: | ||||
|   - pip install -r requirements.txt | ||||
|   - tinydownload 05861434675432854607 -o ~/bin/ffmpeg | ||||
|   - tinydownload 22684734659253158385 -o ~/bin/ffmpeg | ||||
|   - chmod 755 ~/bin/ffmpeg | ||||
| script: python -m pytest test --cov=. | ||||
| after_success: codecov | ||||
|   | ||||
| @@ -42,7 +42,7 @@ class Converter: | ||||
|  | ||||
|         command = ['avconv', '-loglevel', level, '-i', | ||||
|                    self.input_file, '-ab', '192k', | ||||
|                    self.output_file] | ||||
|                    self.output_file, '-y'] | ||||
|  | ||||
|         log.debug(command) | ||||
|         return subprocess.call(command) | ||||
| @@ -53,19 +53,19 @@ class Converter: | ||||
|         if not log.level == 10: | ||||
|             ffmpeg_pre += '-hide_banner -nostats -v panic ' | ||||
|  | ||||
|         input_ext = self.input_file.split('.')[-1] | ||||
|         output_ext = self.output_file.split('.')[-1] | ||||
|         _, input_ext = os.path.splitext(self.input_file) | ||||
|         _, output_ext = os.path.splitext(self.output_file) | ||||
|  | ||||
|         if input_ext == 'm4a': | ||||
|             if output_ext == 'mp3': | ||||
|         if input_ext == '.m4a': | ||||
|             if output_ext == '.mp3': | ||||
|                 ffmpeg_params = '-codec:v copy -codec:a libmp3lame -q:a 2 ' | ||||
|             elif output_ext == 'webm': | ||||
|             elif output_ext == '.webm': | ||||
|                 ffmpeg_params = '-c:a libopus -vbr on -b:a 192k -vn ' | ||||
|  | ||||
|         elif input_ext == 'webm': | ||||
|             if output_ext == 'mp3': | ||||
|         elif input_ext == '.webm': | ||||
|             if output_ext == '.mp3': | ||||
|                 ffmpeg_params = ' -ab 192k -ar 44100 -vn ' | ||||
|             elif output_ext == 'm4a': | ||||
|             elif output_ext == '.m4a': | ||||
|                 ffmpeg_params = '-cutoff 20000 -c:a libfdk_aac -b:a 192k -vn ' | ||||
|  | ||||
|         ffmpeg_pre += ' -i' | ||||
|   | ||||
| @@ -48,6 +48,7 @@ def trim_song(text_file): | ||||
|         data = file_in.read().splitlines(True) | ||||
|     with open(text_file, 'w') as file_out: | ||||
|         file_out.writelines(data[1:]) | ||||
|     return data[0] | ||||
|  | ||||
|  | ||||
| def is_spotify(raw_song): | ||||
| @@ -117,3 +118,13 @@ def videotime_from_seconds(time): | ||||
|         return '{0}:{1:02}'.format(time//60, time % 60) | ||||
|  | ||||
|     return '{0}:{1:02}:{2:02}'.format((time//60)//60, (time//60) % 60, time % 60) | ||||
|  | ||||
|  | ||||
| def get_splits(url): | ||||
|     if '/' in url: | ||||
|         if url.endswith('/'): | ||||
|             url = url[:-1] | ||||
|         splits = url.split('/') | ||||
|     else: | ||||
|         splits = url.split(':') | ||||
|     return splits | ||||
| @@ -8,6 +8,7 @@ import urllib.request | ||||
|  | ||||
| def compare(music_file, metadata): | ||||
|     """Check if the input music file title matches the expected title.""" | ||||
|     already_tagged = False | ||||
|     try: | ||||
|         if music_file.endswith('.mp3'): | ||||
|             audiofile = EasyID3(music_file) | ||||
| @@ -16,7 +17,7 @@ def compare(music_file, metadata): | ||||
|             audiofile = MP4(music_file) | ||||
|             already_tagged = audiofile['\xa9nam'][0] == metadata['name'] | ||||
|     except (KeyError, TypeError): | ||||
|         already_tagged = False | ||||
|         pass | ||||
|  | ||||
|     return already_tagged | ||||
|  | ||||
|   | ||||
| @@ -77,7 +77,13 @@ def generate_metadata(raw_song): | ||||
|     return meta_tags | ||||
|  | ||||
|  | ||||
| def feed_playlist(username): | ||||
| def write_user_playlist(username, text_file=None): | ||||
|     links = get_playlists(username=username) | ||||
|     playlist = internals.input_link(links) | ||||
|     return write_playlist(playlist, text_file) | ||||
|  | ||||
|  | ||||
| def get_playlists(username): | ||||
|     """ Fetch user playlists when using the -u option. """ | ||||
|     playlists = spotify.user_playlists(username) | ||||
|     links = [] | ||||
| @@ -91,19 +97,65 @@ def feed_playlist(username): | ||||
|                 log.info(u'{0:>5}. {1:<30}  ({2} tracks)'.format( | ||||
|                     check, playlist['name'], | ||||
|                     playlist['tracks']['total'])) | ||||
|                 log.debug(playlist['external_urls']['spotify']) | ||||
|                 links.append(playlist) | ||||
|                 playlist_url = playlist['external_urls']['spotify'] | ||||
|                 log.debug(playlist_url) | ||||
|                 links.append(playlist_url) | ||||
|                 check += 1 | ||||
|         if playlists['next']: | ||||
|             playlists = spotify.next(playlists) | ||||
|         else: | ||||
|             break | ||||
|  | ||||
|     playlist = internals.input_link(links) | ||||
|     write_playlist(playlist['owner']['id'], playlist['id']) | ||||
|     return links | ||||
|  | ||||
|  | ||||
| def write_tracks(text_file, tracks): | ||||
| def fetch_playlist(playlist): | ||||
|     splits = internals.get_splits(playlist) | ||||
|     try: | ||||
|         username = splits[-3] | ||||
|     except IndexError: | ||||
|         # Wrong format, in either case | ||||
|         log.error('The provided playlist URL is not in a recognized format!') | ||||
|         sys.exit(10) | ||||
|     playlist_id = splits[-1] | ||||
|     try: | ||||
|         results = spotify.user_playlist(username, playlist_id, | ||||
|                                         fields='tracks,next,name') | ||||
|     except spotipy.client.SpotifyException: | ||||
|         log.error('Unable to find playlist') | ||||
|         log.info('Make sure the playlist is set to publicly visible and then try again') | ||||
|         sys.exit(11) | ||||
|  | ||||
|     return results | ||||
|  | ||||
|  | ||||
| def write_playlist(playlist_url, text_file=None): | ||||
|     playlist = fetch_playlist(playlist_url) | ||||
|     tracks = playlist['tracks'] | ||||
|     if not text_file: | ||||
|         text_file = u'{0}.txt'.format(slugify(playlist['name'], ok='-_()[]{}')) | ||||
|     return write_tracks(tracks, text_file) | ||||
|  | ||||
|  | ||||
| def fetch_album(album): | ||||
|     splits = internals.get_splits(album) | ||||
|     album_id = splits[-1] | ||||
|     album = spotify.album(album_id) | ||||
|     return album | ||||
|  | ||||
|  | ||||
| def write_album(album_url, text_file=None): | ||||
|     album = fetch_album(album_url) | ||||
|     tracks = spotify.album_tracks(album['id']) | ||||
|     if not text_file: | ||||
|         text_file = u'{0}.txt'.format(slugify(album['name'], ok='-_()[]{}')) | ||||
|     return write_tracks(tracks, text_file) | ||||
|  | ||||
|  | ||||
| def write_tracks(tracks, text_file): | ||||
|     log.info(u'Writing {0} tracks to {1}'.format( | ||||
|                tracks['total'], text_file)) | ||||
|     track_urls = [] | ||||
|     with open(text_file, 'a') as file_out: | ||||
|         while True: | ||||
|             for item in tracks['items']: | ||||
| @@ -113,8 +165,9 @@ def write_tracks(text_file, tracks): | ||||
|                     track = item | ||||
|                 try: | ||||
|                     track_url = track['external_urls']['spotify'] | ||||
|                     file_out.write(track_url + '\n') | ||||
|                     log.debug(track_url) | ||||
|                     file_out.write(track_url + '\n') | ||||
|                     track_urls.append(track_url) | ||||
|                 except KeyError: | ||||
|                     log.warning(u'Skipping track {0} by {1} (local only?)'.format( | ||||
|                         track['name'], track['artists'][0]['name'])) | ||||
| @@ -124,34 +177,5 @@ def write_tracks(text_file, tracks): | ||||
|                 tracks = spotify.next(tracks) | ||||
|             else: | ||||
|                 break | ||||
|  | ||||
| def write_playlist(username, playlist_id): | ||||
|     results = spotify.user_playlist(username, playlist_id, | ||||
|                                     fields='tracks,next,name') | ||||
|     text_file = u'{0}.txt'.format(slugify(results['name'], ok='-_()[]{}')) | ||||
|     log.info(u'Writing {0} tracks to {1}'.format( | ||||
|                results['tracks']['total'], text_file)) | ||||
|     tracks = results['tracks'] | ||||
|     write_tracks(text_file, tracks) | ||||
|  | ||||
|  | ||||
| def write_album(album): | ||||
|     tracks = spotify.album_tracks(album['id']) | ||||
|     text_file = u'{0}.txt'.format(slugify(album['name'], ok='-_()[]{}')) | ||||
|     log.info(u'writing {0} tracks to {1}'.format( | ||||
|                tracks['total'], text_file)) | ||||
|     write_tracks(text_file, tracks) | ||||
|  | ||||
|  | ||||
| def grab_album(album): | ||||
|     if '/' in album: | ||||
|         if album.endswith('/'): | ||||
|             playlist = playlist[:-1] | ||||
|         splits = album.split('/') | ||||
|     else: | ||||
|         splits = album.split(':') | ||||
|  | ||||
|     album_id = splits[-1] | ||||
|     album = spotify.album(album_id) | ||||
|  | ||||
|     write_album(album) | ||||
|     log.info(track_urls) | ||||
|     return track_urls | ||||
|   | ||||
| @@ -35,19 +35,21 @@ def get_youtube_title(content, number=None): | ||||
|  | ||||
| def download_song(file_name, content): | ||||
|     """ Download the audio file from YouTube. """ | ||||
|     if const.args.input_ext in (".webm", ".m4a"): | ||||
|         link = content.getbestaudio(preftype=const.args.input_ext[1:]) | ||||
|     _, extension = os.path.splitext(file_name) | ||||
|     if extension in ('.webm', '.m4a'): | ||||
|         link = content.getbestaudio(preftype=extension[1:]) | ||||
|     else: | ||||
|         log.debug('No audio streams available for {} type'.format(extension)) | ||||
|         return False | ||||
|  | ||||
|     if link: | ||||
|         log.debug('Downloading from URL: ' + link.url) | ||||
|         filepath = '{0}{1}'.format(os.path.join(const.args.folder, file_name), | ||||
|                                    const.args.input_ext) | ||||
|         filepath = os.path.join(const.args.folder, file_name) | ||||
|         log.debug('Saving to: ' + filepath) | ||||
|         link.download(filepath=filepath) | ||||
|         return True | ||||
|     else: | ||||
|         log.debug('No audio streams available') | ||||
|         return False | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										53
									
								
								spotdl.py
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								spotdl.py
									
									
									
									
									
								
							| @@ -1,6 +1,5 @@ | ||||
| #!/usr/bin/env python3 | ||||
| # -*- coding: UTF-8 -*- | ||||
|  | ||||
| from core import const | ||||
| from core import handle | ||||
| from core import metadata | ||||
| @@ -61,7 +60,7 @@ def check_exists(music_file, raw_song, meta_tags): | ||||
|     return False | ||||
|  | ||||
|  | ||||
| def grab_list(text_file): | ||||
| def download_list(text_file): | ||||
|     """ Download all songs from the list. """ | ||||
|     with open(text_file, 'r') as listed: | ||||
|         lines = (listed.read()).splitlines() | ||||
| @@ -70,19 +69,21 @@ def grab_list(text_file): | ||||
|         lines.remove('') | ||||
|     except ValueError: | ||||
|         pass | ||||
|  | ||||
|     log.info(u'Preparing to download {} songs'.format(len(lines))) | ||||
|     downloaded_songs = [] | ||||
|  | ||||
|     for number, raw_song in enumerate(lines, 1): | ||||
|         print('') | ||||
|         try: | ||||
|             grab_single(raw_song, number=number) | ||||
|             download_single(raw_song, number=number) | ||||
|         # token expires after 1 hour | ||||
|         except spotipy.client.SpotifyException: | ||||
|             # refresh token when it expires | ||||
|             log.debug('Token expired, generating new one and authorizing') | ||||
|             new_token = spotify_tools.generate_token() | ||||
|             spotify_tools.spotify = spotipy.Spotify(auth=new_token) | ||||
|             grab_single(raw_song, number=number) | ||||
|             download_single(raw_song, number=number) | ||||
|         # detect network problems | ||||
|         except (urllib.request.URLError, TypeError, IOError): | ||||
|             lines.append(raw_song) | ||||
| @@ -96,34 +97,14 @@ def grab_list(text_file): | ||||
|             time.sleep(0.5) | ||||
|             continue | ||||
|  | ||||
|         downloaded_songs.append(raw_song) | ||||
|         log.debug('Removing downloaded song from text file') | ||||
|         internals.trim_song(text_file) | ||||
|  | ||||
|  | ||||
| def grab_playlist(playlist): | ||||
|     if '/' in playlist: | ||||
|         if playlist.endswith('/'): | ||||
|             playlist = playlist[:-1] | ||||
|         splits = playlist.split('/') | ||||
|     else: | ||||
|         splits = playlist.split(':') | ||||
|  | ||||
|     try: | ||||
|         username = splits[-3] | ||||
|     except IndexError: | ||||
|         # Wrong format, in either case | ||||
|         log.error('The provided playlist URL is not in a recognized format!') | ||||
|         sys.exit(10) | ||||
|     playlist_id = splits[-1] | ||||
|     try: | ||||
|         spotify_tools.write_playlist(username, playlist_id) | ||||
|     except spotipy.client.SpotifyException: | ||||
|         log.error('Unable to find playlist') | ||||
|         log.info('Make sure the playlist is set to publicly visible and then try again') | ||||
|         sys.exit(11) | ||||
|     return downloaded_songs | ||||
|  | ||||
|  | ||||
| def grab_single(raw_song, number=None): | ||||
| def download_single(raw_song, number=None): | ||||
|     """ Logic behind downloading a song. """ | ||||
|     if internals.is_youtube(raw_song): | ||||
|         log.debug('Input song is a YouTube URL') | ||||
| @@ -166,11 +147,10 @@ def grab_single(raw_song, number=None): | ||||
|         # deal with file formats containing slashes to non-existent directories | ||||
|         songpath = os.path.join(const.args.folder, os.path.dirname(songname)) | ||||
|         os.makedirs(songpath, exist_ok=True) | ||||
|         if youtube_tools.download_song(songname, content): | ||||
|         input_song = songname + const.args.input_ext | ||||
|         output_song = songname + const.args.output_ext | ||||
|         if youtube_tools.download_song(input_song, content): | ||||
|             print('') | ||||
|  | ||||
|             try: | ||||
|                 convert.song(input_song, output_song, const.args.folder, | ||||
|                              avconv=const.args.avconv) | ||||
| @@ -182,12 +162,9 @@ def grab_single(raw_song, number=None): | ||||
|  | ||||
|             if not const.args.input_ext == const.args.output_ext: | ||||
|                 os.remove(os.path.join(const.args.folder, input_song)) | ||||
|  | ||||
|             if not const.args.no_metadata and meta_tags is not None: | ||||
|                 metadata.embed(os.path.join(const.args.folder, output_song), meta_tags) | ||||
|  | ||||
|         else: | ||||
|             log.error('No audio streams available') | ||||
|             return True | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
| @@ -203,15 +180,15 @@ if __name__ == '__main__': | ||||
|  | ||||
|     try: | ||||
|         if const.args.song: | ||||
|             grab_single(raw_song=const.args.song) | ||||
|             download_single(raw_song=const.args.song) | ||||
|         elif const.args.list: | ||||
|             grab_list(text_file=const.args.list) | ||||
|             download_list(text_file=const.args.list) | ||||
|         elif const.args.playlist: | ||||
|             grab_playlist(playlist=const.args.playlist) | ||||
|             spotify_tools.write_playlist(playlist_url=const.args.playlist) | ||||
|         elif const.args.album: | ||||
|             spotify_tools.grab_album(album=const.args.album) | ||||
|             spotify_tools.write_album(album_url=const.args.album) | ||||
|         elif const.args.username: | ||||
|             spotify_tools.feed_playlist(username=const.args.username) | ||||
|             spotify_tools.write_user_playlist(username=const.args.username) | ||||
|  | ||||
|         # actually we don't necessarily need this, but yeah... | ||||
|         # explicit is better than implicit! | ||||
|   | ||||
| @@ -1,11 +1,13 @@ | ||||
| from core import const | ||||
| from core import handle | ||||
| import spotdl | ||||
| import pytest | ||||
|  | ||||
|  | ||||
| def load_defaults(): | ||||
|     const.args = handle.get_arguments(raw_args='', to_group=False, to_merge=False) | ||||
|     const.args.overwrite = 'skip' | ||||
|     const.args.log_level = 10 | ||||
|  | ||||
|     spotdl.args = const.args | ||||
|     spotdl.log = const.logzero.setup_logger(formatter=const.formatter, | ||||
|   | ||||
							
								
								
									
										18
									
								
								test/test_dry_run.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								test/test_dry_run.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| from core import const | ||||
|  | ||||
| import spotdl | ||||
| import loader | ||||
| import os | ||||
|  | ||||
| loader.load_defaults() | ||||
|  | ||||
|  | ||||
| def test_dry_download_list(tmpdir): | ||||
|     song = 'http://open.spotify.com/track/0JlS7BXXD07hRmevDnbPDU' | ||||
|     const.args.folder = str(tmpdir) | ||||
|     const.args.dry_run = True | ||||
|     file_path = os.path.join(const.args.folder, 'test_list.txt') | ||||
|     with open(file_path, 'w') as tin: | ||||
|         tin.write(song) | ||||
|     downloaded_song, *_ = spotdl.download_list(file_path) | ||||
|     assert downloaded_song == song | ||||
							
								
								
									
										34
									
								
								test/test_handle.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								test/test_handle.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| from core import handle | ||||
|  | ||||
| import pytest | ||||
| import os | ||||
| import sys | ||||
|  | ||||
|  | ||||
| def test_log_str_to_int(): | ||||
|     expect_levels = [20, 30, 40, 10] | ||||
|     levels = [handle.log_leveller(level) | ||||
|               for level in handle._LOG_LEVELS_STR] | ||||
|     assert levels == expect_levels | ||||
|  | ||||
|  | ||||
| class TestConfig: | ||||
|     def test_default_config(self, tmpdir): | ||||
|         expect_config = handle.default_conf['spotify-downloader'] | ||||
|         global config_path | ||||
|         config_path = os.path.join(str(tmpdir), 'config.yml') | ||||
|         config = handle.get_config(config_path) | ||||
|         assert config == expect_config | ||||
|  | ||||
|     def test_modified_config(self): | ||||
|         default_config = handle.default_conf['spotify-downloader'] | ||||
|         modified_config = dict(default_config) | ||||
|         modified_config['file-format'] = 'just_a_test' | ||||
|         config = handle.merge(default_config, modified_config) | ||||
|         assert config['file-format'] == modified_config['file-format'] | ||||
|  | ||||
|  | ||||
| def test_grouped_arguments(tmpdir): | ||||
|     sys.path[0] = str(tmpdir) | ||||
|     with pytest.raises(SystemExit): | ||||
|         handle.get_arguments(to_group=True, to_merge=True) | ||||
							
								
								
									
										38
									
								
								test/test_internals.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								test/test_internals.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| from core import internals | ||||
|  | ||||
| import os | ||||
|  | ||||
|  | ||||
| class TestPathFilterer: | ||||
|     def test_create_directory(self, tmpdir): | ||||
|         expect_path = True | ||||
|         global folder_path | ||||
|         folder_path = os.path.join(str(tmpdir), 'filter_this_folder') | ||||
|         internals.filter_path(folder_path) | ||||
|         is_path = os.path.isdir(folder_path) | ||||
|         assert is_path == expect_path | ||||
|  | ||||
|     def test_remove_temp_files(self, tmpdir): | ||||
|         expect_file = False | ||||
|         file_path = os.path.join(folder_path, 'pesky_file.temp') | ||||
|         open(file_path, 'a') | ||||
|         internals.filter_path(folder_path) | ||||
|         is_file = os.path.isfile(file_path) | ||||
|         assert is_file == expect_file | ||||
|  | ||||
|  | ||||
| class TestVideoTime: | ||||
|     def test_from_seconds(self): | ||||
|         expect_duration = '35' | ||||
|         duration = internals.videotime_from_seconds(35) | ||||
|         assert duration == expect_duration | ||||
|  | ||||
|     def test_from_minutes(self): | ||||
|         expect_duration = '2:38' | ||||
|         duration = internals.videotime_from_seconds(158) | ||||
|         assert duration == expect_duration | ||||
|  | ||||
|     def test_from_hours(self): | ||||
|         expect_duration = '1:16:02' | ||||
|         duration = internals.videotime_from_seconds(4562) | ||||
|         assert duration == expect_duration | ||||
							
								
								
									
										50
									
								
								test/test_list.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								test/test_list.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| from core import spotify_tools | ||||
| from core import const | ||||
|  | ||||
| import spotdl | ||||
|  | ||||
| import builtins | ||||
| import os | ||||
|  | ||||
|  | ||||
| def test_user_playlists(tmpdir, monkeypatch): | ||||
|     expect_tracks = 14 | ||||
|     text_file = os.path.join(str(tmpdir), 'test_us.txt') | ||||
|     monkeypatch.setattr('builtins.input', lambda x: 1) | ||||
|     spotify_tools.write_user_playlist('alex', text_file) | ||||
|     with open(text_file, 'r') as tin: | ||||
|         tracks = len(tin.readlines()) | ||||
|     assert tracks == expect_tracks | ||||
|  | ||||
|  | ||||
| def test_playlist(tmpdir): | ||||
|     expect_tracks = 14 | ||||
|     text_file = os.path.join(str(tmpdir), 'test_pl.txt') | ||||
|     spotify_tools.write_playlist('https://open.spotify.com/user/alex/playlist/0iWOVoumWlkXIrrBTSJmN8', text_file) | ||||
|     with open(text_file, 'r') as tin: | ||||
|         tracks = len(tin.readlines()) | ||||
|     assert tracks == expect_tracks | ||||
|  | ||||
|  | ||||
| def test_album(tmpdir): | ||||
|     expect_tracks = 15 | ||||
|     global text_file | ||||
|     text_file = os.path.join(str(tmpdir), 'test_al.txt') | ||||
|     spotify_tools.write_album('https://open.spotify.com/album/499J8bIsEnU7DSrosFDJJg', text_file) | ||||
|     with open(text_file, 'r') as tin: | ||||
|         tracks = len(tin.readlines()) | ||||
|     assert tracks == expect_tracks | ||||
|  | ||||
|  | ||||
| def test_trim(): | ||||
|     with open(text_file, 'r') as track_file: | ||||
|         tracks = track_file.readlines() | ||||
|  | ||||
|     expect_number = len(tracks) - 1 | ||||
|     expect_track = tracks[0] | ||||
|     track = spotdl.internals.trim_song(text_file) | ||||
|  | ||||
|     with open(text_file, 'r') as track_file: | ||||
|         number = len(track_file.readlines()) | ||||
|  | ||||
|     assert (expect_number == number and expect_track == track) | ||||
| @@ -1,77 +0,0 @@ | ||||
| from core import const | ||||
| from core import handle | ||||
| from core import internals | ||||
| from core import spotify_tools | ||||
| from core import youtube_tools | ||||
| from core import convert | ||||
| from core import metadata | ||||
|  | ||||
| import spotdl | ||||
| import loader | ||||
|  | ||||
| import os | ||||
|  | ||||
| loader.load_defaults() | ||||
| internals.filter_path(const.args.folder) | ||||
| raw_song = "Tony's Videos VERY SHORT VIDEO 28.10.2016" | ||||
|  | ||||
|  | ||||
| def test_youtube_url(): | ||||
|     expect_url = 'http://youtube.com/watch?v=qOOcy2-tmbk' | ||||
|     url = youtube_tools.generate_youtube_url(raw_song, meta_tags=None) | ||||
|     assert url == expect_url | ||||
|  | ||||
|  | ||||
| def test_youtube_title(): | ||||
|     global content | ||||
|     global title | ||||
|     expect_title = "Tony's Videos VERY SHORT VIDEO 28.10.2016" | ||||
|     content = youtube_tools.go_pafy(raw_song, meta_tags=None) | ||||
|     title = youtube_tools.get_youtube_title(content) | ||||
|     assert title == expect_title | ||||
|  | ||||
|  | ||||
| def test_check_exists(): | ||||
|     expect_check = False | ||||
|     # prerequisites for determining filename | ||||
|     global file_name | ||||
|     file_name = internals.sanitize_title(title) | ||||
|     check = spotdl.check_exists(file_name, raw_song, meta_tags=None) | ||||
|     assert check == expect_check | ||||
|  | ||||
|  | ||||
| def test_download(): | ||||
|     expect_download = True | ||||
|     download = youtube_tools.download_song(file_name, content) | ||||
|     assert download == expect_download | ||||
|  | ||||
|  | ||||
| def test_convert(): | ||||
|     # exit code 0 = success | ||||
|     expect_converted = 0 | ||||
|     global input_song | ||||
|     global output_song | ||||
|     input_song = file_name + const.args.input_ext | ||||
|     output_song = file_name + const.args.output_ext | ||||
|     converted = convert.song(input_song, output_song, const.args.folder) | ||||
|     assert converted == expect_converted | ||||
|  | ||||
|  | ||||
| def test_metadata(): | ||||
|     expect_metadata = None | ||||
|     meta_tags = spotify_tools.generate_metadata(raw_song) | ||||
|     if meta_tags: | ||||
|         metadata_output = metadata.embed(os.path.join(const.args.folder, output_song), meta_tags) | ||||
|         metadata_input = metadata.embed(os.path.join(const.args.folder, input_song), meta_tags) | ||||
|     else: | ||||
|         metadata_input = None | ||||
|         metadata_output = None | ||||
|     assert (metadata_output == expect_metadata) and (metadata_input == expect_metadata) | ||||
|  | ||||
|  | ||||
| def test_check_exists2(): | ||||
|     expect_check = True | ||||
|     os.remove(os.path.join(const.args.folder, input_song)) | ||||
|     check = spotdl.check_exists(file_name, raw_song, meta_tags=None) | ||||
|     os.remove(os.path.join(const.args.folder, output_song)) | ||||
|     assert check == expect_check | ||||
| @@ -1,84 +0,0 @@ | ||||
|  | ||||
| from core import const | ||||
| from core import handle | ||||
| from core import internals | ||||
| from core import spotify_tools | ||||
| from core import youtube_tools | ||||
| from core import convert | ||||
| from core import metadata | ||||
|  | ||||
| import spotdl | ||||
|  | ||||
| import loader | ||||
| import os | ||||
|  | ||||
| loader.load_defaults() | ||||
| internals.filter_path(const.args.folder) | ||||
| raw_song = 'http://open.spotify.com/track/0JlS7BXXD07hRmevDnbPDU' | ||||
|  | ||||
| def test_spotify_title(): | ||||
|     expect_title = 'David André Østby - Intro' | ||||
|     global meta_tags | ||||
|     meta_tags = spotify_tools.generate_metadata(raw_song) | ||||
|     title = internals.generate_songname(const.args.file_format, meta_tags) | ||||
|     assert title == expect_title | ||||
|  | ||||
|  | ||||
| def test_youtube_url(): | ||||
|     expect_url = 'http://youtube.com/watch?v=rg1wfcty0BA' | ||||
|     url = youtube_tools.generate_youtube_url(raw_song, meta_tags) | ||||
|     assert url == expect_url | ||||
|  | ||||
|  | ||||
| def test_youtube_title(): | ||||
|     expect_title = 'Intro - David André Østby' | ||||
|     content = youtube_tools.go_pafy(raw_song, meta_tags) | ||||
|     title = youtube_tools.get_youtube_title(content) | ||||
|     assert title == expect_title | ||||
|  | ||||
|  | ||||
| def test_check_exists(): | ||||
|     expect_check = False | ||||
|     # prerequisites for determining filename | ||||
|     songname = internals.generate_songname(const.args.file_format, meta_tags) | ||||
|     global file_name | ||||
|     file_name = internals.sanitize_title(songname) | ||||
|     check = spotdl.check_exists(file_name, raw_song, meta_tags) | ||||
|     assert check == expect_check | ||||
|  | ||||
|  | ||||
| def test_download(): | ||||
|     expect_download = True | ||||
|     # prerequisites for determining filename | ||||
|     content = youtube_tools.go_pafy(raw_song, meta_tags) | ||||
|     download = youtube_tools.download_song(file_name, content) | ||||
|     assert download == expect_download | ||||
|  | ||||
|  | ||||
| def test_convert(): | ||||
|     # exit code 0 = success | ||||
|     expect_converted = 0 | ||||
|     # prerequisites for determining filename | ||||
|     global input_song | ||||
|     global output_song | ||||
|     input_song = file_name + const.args.input_ext | ||||
|     output_song = file_name + const.args.output_ext | ||||
|     converted = convert.song(input_song, output_song, const.args.folder) | ||||
|     assert converted == expect_converted | ||||
|  | ||||
|  | ||||
| def test_metadata(): | ||||
|     expect_metadata = True | ||||
|     # prerequisites for determining filename | ||||
|     metadata_output = metadata.embed(os.path.join(const.args.folder, output_song), meta_tags) | ||||
|     metadata_input = metadata.embed(os.path.join(const.args.folder, input_song), meta_tags) | ||||
|     assert metadata_output == (metadata_input == expect_metadata) | ||||
|  | ||||
|  | ||||
| def test_check_exists2(): | ||||
|     expect_check = True | ||||
|     # prerequisites for determining filename | ||||
|     os.remove(os.path.join(const.args.folder, input_song)) | ||||
|     check = spotdl.check_exists(file_name, raw_song, meta_tags) | ||||
|     os.remove(os.path.join(const.args.folder, output_song)) | ||||
|     assert check == expect_check | ||||
| @@ -1,55 +0,0 @@ | ||||
| import spotdl | ||||
|  | ||||
| spotify = spotdl.spotify_tools.spotify | ||||
| username = 'alex' | ||||
|  | ||||
|  | ||||
| def test_user(): | ||||
|     expect_playlists = 7 | ||||
|     playlists = spotify.user_playlists(username) | ||||
|     playlists = len(playlists['items']) | ||||
|     assert playlists == expect_playlists | ||||
|  | ||||
|  | ||||
| def test_playlist(): | ||||
|     expect_tracks = 14 | ||||
|     playlist = spotify.user_playlists(username)['items'][0] | ||||
|     tracks = playlist['tracks']['total'] | ||||
|     assert tracks == expect_tracks | ||||
|  | ||||
|  | ||||
| def test_tracks(): | ||||
|     playlist = spotify.user_playlists(username)['items'][0] | ||||
|     expect_lines = playlist['tracks']['total'] | ||||
|     result = spotify.user_playlist( | ||||
|         playlist['owner']['id'], playlist['id'], fields='tracks,next') | ||||
|     tracks = result['tracks'] | ||||
|  | ||||
|     with open('list.txt', 'w') as fout: | ||||
|         while True: | ||||
|             for item in tracks['items']: | ||||
|                 track = item['track'] | ||||
|                 try: | ||||
|                     fout.write(track['external_urls']['spotify'] + '\n') | ||||
|                 except KeyError: | ||||
|                     pass | ||||
|             # 1 page = 50 results | ||||
|             # check if there are more pages | ||||
|             if tracks['next']: | ||||
|                 tracks = spotify.next(tracks) | ||||
|             else: | ||||
|                 break | ||||
|  | ||||
|     with open('list.txt', 'r') as listed: | ||||
|         expect_song = (listed.read()).splitlines()[0] | ||||
|  | ||||
|     spotdl.internals.trim_song('list.txt') | ||||
|     with open('list.txt', 'a') as myfile: | ||||
|         myfile.write(expect_song) | ||||
|  | ||||
|     with open('list.txt', 'r') as listed: | ||||
|         songs = (listed.read()).splitlines() | ||||
|  | ||||
|     lines = len(songs) | ||||
|     song = songs[-1] | ||||
|     assert (expect_lines == lines and expect_song == song) | ||||
							
								
								
									
										133
									
								
								test/test_with_metadata.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								test/test_with_metadata.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | ||||
|  | ||||
| from core import const | ||||
| from core import internals | ||||
| from core import spotify_tools | ||||
| from core import youtube_tools | ||||
| from core import convert | ||||
| from core import metadata | ||||
|  | ||||
| import spotdl | ||||
|  | ||||
| import loader | ||||
| import os | ||||
|  | ||||
| loader.load_defaults() | ||||
| raw_song = 'http://open.spotify.com/track/0JlS7BXXD07hRmevDnbPDU' | ||||
|  | ||||
|  | ||||
| def test_metadata(): | ||||
|     expect_number = 22 | ||||
|     global meta_tags | ||||
|     meta_tags = spotify_tools.generate_metadata(raw_song) | ||||
|     assert len(meta_tags) == expect_number | ||||
|  | ||||
|  | ||||
| class TestFileFormat: | ||||
|     def test_with_spaces(self): | ||||
|         expect_title = 'David André Østby - Intro' | ||||
|         title = internals.generate_songname(const.args.file_format, meta_tags) | ||||
|         assert title == expect_title | ||||
|  | ||||
|     def test_without_spaces(self): | ||||
|         expect_title = 'David_André_Østby_-_Intro' | ||||
|         const.args.no_spaces = True | ||||
|         title = internals.generate_songname(const.args.file_format, meta_tags) | ||||
|         assert title == expect_title | ||||
|  | ||||
|  | ||||
| def test_youtube_url(): | ||||
|     expect_url = 'http://youtube.com/watch?v=rg1wfcty0BA' | ||||
|     url = youtube_tools.generate_youtube_url(raw_song, meta_tags) | ||||
|     assert url == expect_url | ||||
|  | ||||
|  | ||||
| def test_youtube_title(): | ||||
|     expect_title = 'Intro - David André Østby' | ||||
|     global content | ||||
|     content = youtube_tools.go_pafy(raw_song, meta_tags) | ||||
|     title = youtube_tools.get_youtube_title(content) | ||||
|     assert title == expect_title | ||||
|  | ||||
|  | ||||
| def test_check_exists(tmpdir): | ||||
|     expect_check = False | ||||
|     const.args.folder = str(tmpdir) | ||||
|     # prerequisites for determining filename | ||||
|     songname = internals.generate_songname(const.args.file_format, meta_tags) | ||||
|     global file_name | ||||
|     file_name = internals.sanitize_title(songname) | ||||
|     check = spotdl.check_exists(file_name, raw_song, meta_tags) | ||||
|     assert check == expect_check | ||||
|  | ||||
|  | ||||
| class TestDownload: | ||||
|     def test_m4a(self): | ||||
|         expect_download = True | ||||
|         download = youtube_tools.download_song(file_name + '.m4a', content) | ||||
|         assert download == expect_download | ||||
|  | ||||
|     def test_webm(self): | ||||
|         expect_download = True | ||||
|         download = youtube_tools.download_song(file_name + '.webm', content) | ||||
|         assert download == expect_download | ||||
|  | ||||
|  | ||||
| class TestFFmpeg(): | ||||
|     def test_convert_from_webm_to_mp3(self): | ||||
|         expect_return_code = 0 | ||||
|         return_code = convert.song(file_name + '.webm', | ||||
|                                    file_name + '.mp3', | ||||
|                                    const.args.folder) | ||||
|         assert return_code == expect_return_code | ||||
|  | ||||
|     def test_convert_from_webm_to_m4a(self): | ||||
|         expect_return_code = 0 | ||||
|         return_code = convert.song(file_name + '.webm', | ||||
|                                    file_name + '.m4a', | ||||
|                                    const.args.folder) | ||||
|         assert return_code == expect_return_code | ||||
|  | ||||
|  | ||||
|     def test_convert_from_m4a_to_mp3(self): | ||||
|         expect_return_code = 0 | ||||
|         return_code = convert.song(file_name + '.m4a', | ||||
|                                    file_name + '.mp3', | ||||
|                                    const.args.folder) | ||||
|         assert return_code == expect_return_code | ||||
|  | ||||
|     def test_convert_from_m4a_to_webm(self): | ||||
|         expect_return_code = 0 | ||||
|         return_code = convert.song(file_name + '.m4a', | ||||
|                                    file_name + '.webm', | ||||
|                                    const.args.folder) | ||||
|         os.remove(os.path.join(const.args.folder, file_name + '.webm')) | ||||
|         assert return_code == expect_return_code | ||||
|  | ||||
|  | ||||
| class TestAvconv: | ||||
|     def test_convert_from_m4a_to_mp3(self): | ||||
|         expect_return_code = 0 | ||||
|         return_code = convert.song(file_name + '.m4a', | ||||
|                                    file_name + '.mp3', | ||||
|                                    const.args.folder, | ||||
|                                    avconv=True) | ||||
|         assert return_code == expect_return_code | ||||
|  | ||||
|  | ||||
| def test_embed_metadata(): | ||||
|     expect_metadata = True | ||||
|     # prerequisites for determining filename | ||||
|     metadata_input = metadata.embed(os.path.join(const.args.folder, | ||||
|                                                  file_name + '.m4a'), meta_tags) | ||||
|     metadata_output = metadata.embed(os.path.join(const.args.folder, | ||||
|                                                   file_name + '.mp3'), meta_tags) | ||||
|     assert metadata_output == (metadata_input == expect_metadata) | ||||
|  | ||||
|  | ||||
| def test_check_exists2(): | ||||
|     expect_check = True | ||||
|     # prerequisites for determining filename | ||||
|     os.remove(os.path.join(const.args.folder, file_name + '.m4a')) | ||||
|     check = spotdl.check_exists(file_name, raw_song, meta_tags) | ||||
|     os.remove(os.path.join(const.args.folder, file_name + '.mp3')) | ||||
|     assert check == expect_check | ||||
							
								
								
									
										87
									
								
								test/test_without_metadata.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								test/test_without_metadata.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| from core import const | ||||
| from core import internals | ||||
| from core import spotify_tools | ||||
| from core import youtube_tools | ||||
|  | ||||
| import spotdl | ||||
| import loader | ||||
|  | ||||
| import os | ||||
| import builtins | ||||
|  | ||||
| loader.load_defaults() | ||||
| raw_song = "Tony's Videos VERY SHORT VIDEO 28.10.2016" | ||||
|  | ||||
|  | ||||
| def test_metadata(): | ||||
|     expect_metadata = None | ||||
|     global metadata | ||||
|     metadata = spotify_tools.generate_metadata(raw_song) | ||||
|     assert metadata == expect_metadata | ||||
|  | ||||
|  | ||||
| class TestYouTubeURL: | ||||
|     def test_only_music_category(self): | ||||
|         expect_url = 'http://youtube.com/watch?v=P11ou3CXKZo' | ||||
|         const.args.music_videos_only = True | ||||
|         url = youtube_tools.generate_youtube_url(raw_song, metadata) | ||||
|         assert url == expect_url | ||||
|  | ||||
|     def test_all_categories(self): | ||||
|         expect_url = 'http://youtube.com/watch?v=qOOcy2-tmbk' | ||||
|         const.args.music_videos_only = False | ||||
|         url = youtube_tools.generate_youtube_url(raw_song, metadata) | ||||
|         assert url == expect_url | ||||
|  | ||||
|     def test_args_manual(self, monkeypatch): | ||||
|         expect_url = 'http://youtube.com/watch?v=qOOcy2-tmbk' | ||||
|         const.args.manual = True | ||||
|         monkeypatch.setattr('builtins.input', lambda x: '1') | ||||
|         url = youtube_tools.generate_youtube_url(raw_song, metadata) | ||||
|         assert url == expect_url | ||||
|  | ||||
|     def test_args_manual_none(self, monkeypatch): | ||||
|         expect_url = None | ||||
|         monkeypatch.setattr('builtins.input', lambda x: '0') | ||||
|         url = youtube_tools.generate_youtube_url(raw_song, metadata) | ||||
|         const.args.manual = False | ||||
|         assert url == expect_url | ||||
|  | ||||
|  | ||||
| class TestYouTubeTitle: | ||||
|     def test_single_download(self): | ||||
|         global content | ||||
|         global title | ||||
|         expect_title = "Tony's Videos VERY SHORT VIDEO 28.10.2016" | ||||
|         content = youtube_tools.go_pafy(raw_song, metadata) | ||||
|         title = youtube_tools.get_youtube_title(content) | ||||
|         assert title == expect_title | ||||
|  | ||||
|     def test_download_from_list(self): | ||||
|         expect_title = "1. Tony's Videos VERY SHORT VIDEO 28.10.2016" | ||||
|         content = youtube_tools.go_pafy(raw_song, metadata) | ||||
|         title = youtube_tools.get_youtube_title(content, 1) | ||||
|         assert title == expect_title | ||||
|  | ||||
|  | ||||
| def test_check_exists(tmpdir): | ||||
|     expect_check = False | ||||
|     const.args.folder = str(tmpdir) | ||||
|     # prerequisites for determining filename | ||||
|     global file_name | ||||
|     file_name = internals.sanitize_title(title) | ||||
|     check = spotdl.check_exists(file_name, raw_song, metadata) | ||||
|     assert check == expect_check | ||||
|  | ||||
|  | ||||
| class TestDownload: | ||||
|     def test_webm(self): | ||||
|         # content does not have any .webm audiostream | ||||
|         expect_download = False | ||||
|         download = youtube_tools.download_song(file_name + '.webm', content) | ||||
|         assert download == expect_download | ||||
|  | ||||
|     def test_other(self): | ||||
|         expect_download = False | ||||
|         download = youtube_tools.download_song(file_name + '.fake_extension', content) | ||||
|         assert download == expect_download | ||||
		Reference in New Issue
	
	Block a user