mirror of
				https://github.com/KevinMidboe/spotify-downloader.git
				synced 2025-10-29 18:00:15 +00:00 
			
		
		
		
	Add logging capability (#175)
* Refactoring and addition of logzero (#172) * Refactored convert.py and added logging. * Added logging and refactored. * Added logzero to requirements.txt * Added logging to metadata.py * Created a log in misc.py. Updated slugify import. * Some general improvement * Improve message layout * Improve test mechanism * Implement debug level logging * Fix some minor mistakes * Make pytest happy * Remove unimplemented --verbose option * Update ISSUE_TEMPLATE.md * Rename LICENSE * Remove obvious from log.debug() * Show track URL when writing to file (debug)
This commit is contained in:
		@@ -10,22 +10,23 @@ Please follow the guide below
 | 
			
		||||
- [ ] [Searched](https://github.com/ritiek/spotify-downloader/issues?utf8=%E2%9C%93&q=is%3Aissue) for similar issues including closed ones
 | 
			
		||||
 | 
			
		||||
#### What is the purpose of your *issue*?
 | 
			
		||||
- [ ] Script won't run
 | 
			
		||||
- [ ] Encountered bug
 | 
			
		||||
- [ ] Feature request
 | 
			
		||||
- [ ] Bug
 | 
			
		||||
- [ ] Feature Request
 | 
			
		||||
- [ ] Question
 | 
			
		||||
- [ ] Other
 | 
			
		||||
 | 
			
		||||
#### System information
 | 
			
		||||
- Your `python` version: `python 3.x`
 | 
			
		||||
- Your operating system: `Ubuntu 16.04`
 | 
			
		||||
 | 
			
		||||
### Description
 | 
			
		||||
<!-- Provide as much information possible with relevant examples and whatever you have tried below -->
 | 
			
		||||
 | 
			
		||||
<!-- Provide as much information possible and whatever you have tried below -->
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Log
 | 
			
		||||
<!-- Run the script with `--log-level=DEBUG` and paste the output below within the code block-->
 | 
			
		||||
<details>
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
paste the output over here
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
<!-- Give your issue a relevant title and you are good to go -->
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								README.md
									
									
									
									
									
								
							@@ -17,7 +17,7 @@
 | 
			
		||||
  - Track number
 | 
			
		||||
  - Disc number
 | 
			
		||||
  - Release date
 | 
			
		||||
  - And some more...
 | 
			
		||||
  - And more...
 | 
			
		||||
 | 
			
		||||
- Works straight out of the box and does not require to generate or mess with your API keys.
 | 
			
		||||
 | 
			
		||||
@@ -78,8 +78,10 @@ Assuming you have Python 3 ([preferably v3.6 or above to stay away from Unicode
 | 
			
		||||
- For all available options, run `python3 spotdl.py --help`.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
usage: spotdl.py [-h] (-s SONG | -l LIST | -p PLAYLIST | -u USERNAME) [-m]
 | 
			
		||||
                 [-nm] [-a] [-f FOLDER] [-v] [-i INPUT_EXT] [-o OUTPUT_EXT]
 | 
			
		||||
usage: spotdl.py [-h]
 | 
			
		||||
                 (-s SONG | -l LIST | -p PLAYLIST | -b ALBUM | -u USERNAME)
 | 
			
		||||
                 [-m] [-nm] [-a] [-f FOLDER] [-v] [-i INPUT_EXT]
 | 
			
		||||
                 [-o OUTPUT_EXT] [-ll {INFO,WARNING,ERROR,DEBUG}]
 | 
			
		||||
 | 
			
		||||
Download and convert songs from Spotify, Youtube etc.
 | 
			
		||||
 | 
			
		||||
@@ -103,13 +105,15 @@ optional arguments:
 | 
			
		||||
  -f FOLDER, --folder FOLDER
 | 
			
		||||
                        path to folder where files will be stored in (default:
 | 
			
		||||
                        Music/)
 | 
			
		||||
  -v, --verbose         show debug output (default: False)
 | 
			
		||||
  -i INPUT_EXT, --input_ext INPUT_EXT
 | 
			
		||||
  -i INPUT_EXT, --input-ext INPUT_EXT
 | 
			
		||||
                        prefered input format .m4a or .webm (Opus) (default:
 | 
			
		||||
                        .m4a)
 | 
			
		||||
  -o OUTPUT_EXT, --output_ext OUTPUT_EXT
 | 
			
		||||
  -o OUTPUT_EXT, --output-ext OUTPUT_EXT
 | 
			
		||||
                        prefered output extension .mp3 or .m4a (AAC) (default:
 | 
			
		||||
                        .mp3)
 | 
			
		||||
  -ll {INFO,WARNING,ERROR,DEBUG}, --log-level {INFO,WARNING,ERROR,DEBUG}
 | 
			
		||||
                        possible values - ['INFO', 'WARNING', 'ERROR',
 | 
			
		||||
                        'DEBUG'] (default: INFO)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Download by Name
 | 
			
		||||
@@ -204,11 +208,12 @@ Beside some other characters, spaces will be replaced by underscores. There's no
 | 
			
		||||
 | 
			
		||||
Just make sure your working directory is the one you have the music files in.
 | 
			
		||||
 | 
			
		||||
## Return codes
 | 
			
		||||
## Exit codes
 | 
			
		||||
 | 
			
		||||
- `0` - Success
 | 
			
		||||
- `1` - Unknown error
 | 
			
		||||
- `2` - Command line error (e.g. invalid args)
 | 
			
		||||
- `-1` - KeyboardInterrupt
 | 
			
		||||
- `10` - Invalid playlist URL
 | 
			
		||||
- `11` - Playlist not found
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,50 +1,54 @@
 | 
			
		||||
import subprocess
 | 
			
		||||
import os
 | 
			
		||||
from core.logger import log
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
What are the differences and similarities between ffmpeg, libav, and avconv?
 | 
			
		||||
"""What are the differences and similarities between ffmpeg, libav, and avconv?
 | 
			
		||||
https://stackoverflow.com/questions/9477115
 | 
			
		||||
 | 
			
		||||
ffmeg encoders high to lower quality
 | 
			
		||||
libopus > libvorbis >= libfdk_aac > aac > libmp3lame
 | 
			
		||||
 | 
			
		||||
libfdk_aac due to copyrights needs to be compiled by end user
 | 
			
		||||
on MacOS brew install ffmpeg --with-fdk-aac will do just that. Other OS?
 | 
			
		||||
https://trac.ffmpeg.org/wiki/Encode/AAC
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
def song(input_song, output_song, folder, avconv=False, verbose=False):
 | 
			
		||||
 | 
			
		||||
def song(input_song, output_song, folder, avconv=False):
 | 
			
		||||
    """ Do the audio format conversion. """
 | 
			
		||||
    if not input_song == output_song:
 | 
			
		||||
        print('Converting {0} to {1}'.format(
 | 
			
		||||
        log.info('Converting {0} to {1}'.format(
 | 
			
		||||
            input_song, output_song.split('.')[-1]))
 | 
			
		||||
        if avconv:
 | 
			
		||||
            exit_code = convert_with_avconv(input_song, output_song, folder, verbose)
 | 
			
		||||
            exit_code = convert_with_avconv(input_song, output_song, folder)
 | 
			
		||||
        else:
 | 
			
		||||
            exit_code = convert_with_ffmpeg(input_song, output_song, folder, verbose)
 | 
			
		||||
            exit_code = convert_with_ffmpeg(input_song, output_song, folder)
 | 
			
		||||
        return exit_code
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def convert_with_avconv(input_song, output_song, folder, verbose):
 | 
			
		||||
def convert_with_avconv(input_song, output_song, folder):
 | 
			
		||||
    """ Convert the audio file using avconv. """
 | 
			
		||||
    if verbose:
 | 
			
		||||
    if log.level == 10:
 | 
			
		||||
        level = 'debug'
 | 
			
		||||
    else:
 | 
			
		||||
        level = '0'
 | 
			
		||||
 | 
			
		||||
    command = ['avconv',
 | 
			
		||||
               '-loglevel', level,
 | 
			
		||||
               '-i',        os.path.join(folder, input_song),
 | 
			
		||||
               '-ab',       '192k',
 | 
			
		||||
    command = ['avconv', '-loglevel', level, '-i',
 | 
			
		||||
               os.path.join(folder, input_song), '-ab', '192k',
 | 
			
		||||
               os.path.join(folder, output_song)]
 | 
			
		||||
 | 
			
		||||
    log.debug(command)
 | 
			
		||||
 | 
			
		||||
    return subprocess.call(command)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def convert_with_ffmpeg(input_song, output_song, folder, verbose):
 | 
			
		||||
def convert_with_ffmpeg(input_song, output_song, folder):
 | 
			
		||||
    """ Convert the audio file using FFmpeg. """
 | 
			
		||||
    ffmpeg_pre = 'ffmpeg -y '
 | 
			
		||||
    if not verbose:
 | 
			
		||||
 | 
			
		||||
    if not log.level == 10:
 | 
			
		||||
        ffmpeg_pre += '-hide_banner -nostats -v panic '
 | 
			
		||||
 | 
			
		||||
    input_ext = input_song.split('.')[-1]
 | 
			
		||||
@@ -63,6 +67,9 @@ def convert_with_ffmpeg(input_song, output_song, folder, verbose):
 | 
			
		||||
            ffmpeg_params = '-cutoff 20000 -c:a libfdk_aac -b:a 192k -vn '
 | 
			
		||||
 | 
			
		||||
    command = '{0}-i {1} {2}{3}'.format(
 | 
			
		||||
        ffmpeg_pre, os.path.join(folder, input_song), ffmpeg_params, os.path.join(folder, output_song)).split(' ')
 | 
			
		||||
        ffmpeg_pre, os.path.join(folder, input_song),
 | 
			
		||||
        ffmpeg_params, os.path.join(folder, output_song)).split(' ')
 | 
			
		||||
 | 
			
		||||
    log.debug(command)
 | 
			
		||||
 | 
			
		||||
    return subprocess.call(command)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,24 +1,27 @@
 | 
			
		||||
import sys
 | 
			
		||||
import os
 | 
			
		||||
import argparse
 | 
			
		||||
import spotipy.oauth2 as oauth2
 | 
			
		||||
from urllib.request import quote
 | 
			
		||||
from slugify import slugify
 | 
			
		||||
from slugify import SLUG_OK, slugify
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
import os
 | 
			
		||||
from core.logger import log, log_leveller, _LOG_LEVELS_STR
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def input_link(links):
 | 
			
		||||
    """Let the user input a number."""
 | 
			
		||||
    """ Let the user input a choice. """
 | 
			
		||||
    while True:
 | 
			
		||||
        try:
 | 
			
		||||
            the_chosen_one = int(input('>> Choose your number: '))
 | 
			
		||||
            log.info('Choose your number:')
 | 
			
		||||
            the_chosen_one = int(input('> '))
 | 
			
		||||
            if 1 <= the_chosen_one <= len(links):
 | 
			
		||||
                return links[the_chosen_one - 1]
 | 
			
		||||
            elif the_chosen_one == 0:
 | 
			
		||||
                return None
 | 
			
		||||
            else:
 | 
			
		||||
                print('Choose a valid number!')
 | 
			
		||||
                log.warning('Choose a valid number!')
 | 
			
		||||
        except ValueError:
 | 
			
		||||
            print('Choose a valid number!')
 | 
			
		||||
            log.warning('Choose a valid number!')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def trim_song(file):
 | 
			
		||||
@@ -60,16 +63,21 @@ def get_arguments():
 | 
			
		||||
        '-f', '--folder', default=(os.path.join(sys.path[0], 'Music')),
 | 
			
		||||
        help='path to folder where files will be stored in')
 | 
			
		||||
    parser.add_argument(
 | 
			
		||||
        '-v', '--verbose', default=False, help='show debug output',
 | 
			
		||||
        action='store_true')
 | 
			
		||||
    parser.add_argument(
 | 
			
		||||
        '-i', '--input_ext', default='.m4a',
 | 
			
		||||
        '-i', '--input-ext', default='.m4a',
 | 
			
		||||
        help='prefered input format .m4a or .webm (Opus)')
 | 
			
		||||
    parser.add_argument(
 | 
			
		||||
        '-o', '--output_ext', default='.mp3',
 | 
			
		||||
        '-o', '--output-ext', default='.mp3',
 | 
			
		||||
        help='prefered output extension .mp3 or .m4a (AAC)')
 | 
			
		||||
    parser.add_argument(
 | 
			
		||||
        '-ll', '--log-level', default='INFO',
 | 
			
		||||
        choices=_LOG_LEVELS_STR,
 | 
			
		||||
        type=str.upper,
 | 
			
		||||
        help='possible values - {}'.format(_LOG_LEVELS_STR))
 | 
			
		||||
 | 
			
		||||
    return parser.parse_args()
 | 
			
		||||
    parsed = parser.parse_args()
 | 
			
		||||
    parsed.log_level = log_leveller(parsed.log_level)
 | 
			
		||||
 | 
			
		||||
    return parsed
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_spotify(raw_song):
 | 
			
		||||
@@ -78,6 +86,7 @@ def is_spotify(raw_song):
 | 
			
		||||
    status = status or raw_song.find('spotify') > -1
 | 
			
		||||
    return status
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_youtube(raw_song):
 | 
			
		||||
    """ Check if the input song is a YouTube link. """
 | 
			
		||||
    status = len(raw_song) == 11 and raw_song.replace(" ", "%20") == raw_song
 | 
			
		||||
@@ -125,10 +134,6 @@ def filter_path(path):
 | 
			
		||||
            os.remove(os.path.join(path, temp))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def grace_quit():
 | 
			
		||||
    print('\n\nExiting.')
 | 
			
		||||
    sys.exit(0)
 | 
			
		||||
 | 
			
		||||
def get_sec(time_str):
 | 
			
		||||
    v = time_str.split(':', 3)
 | 
			
		||||
    v.reverse()
 | 
			
		||||
							
								
								
									
										16
									
								
								core/logger.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								core/logger.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
import logzero
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
_LOG_LEVELS_STR = ['INFO', 'WARNING', 'ERROR', 'DEBUG']
 | 
			
		||||
 | 
			
		||||
def log_leveller(log_level_str):
 | 
			
		||||
    loggin_levels = [logging.INFO, logging.WARNING, logging.ERROR, logging.DEBUG]
 | 
			
		||||
    log_level_str_index = _LOG_LEVELS_STR.index(log_level_str)
 | 
			
		||||
    loggin_level = loggin_levels[log_level_str_index]
 | 
			
		||||
    return loggin_level
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Create a logger
 | 
			
		||||
log_format = ("%(color)s%(levelname)s:%(end_color)s %(message)s")
 | 
			
		||||
formatter = logzero.LogFormatter(fmt=log_format)
 | 
			
		||||
log = logzero.setup_logger(formatter=formatter, level=logging.INFO)
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
from mutagen.easyid3 import EasyID3
 | 
			
		||||
from mutagen.id3 import ID3, APIC
 | 
			
		||||
from mutagen.mp4 import MP4, MP4Cover
 | 
			
		||||
from core.logger import log
 | 
			
		||||
 | 
			
		||||
import urllib.request
 | 
			
		||||
 | 
			
		||||
@@ -26,16 +27,16 @@ def compare(music_file, metadata):
 | 
			
		||||
def embed(music_file, meta_tags):
 | 
			
		||||
    """ Embed metadata. """
 | 
			
		||||
    if meta_tags is None:
 | 
			
		||||
        print('Could not find meta-tags')
 | 
			
		||||
        log.warning('Could not find metadata')
 | 
			
		||||
        return None
 | 
			
		||||
    elif music_file.endswith('.m4a'):
 | 
			
		||||
        print('Fixing meta-tags')
 | 
			
		||||
        log.info('Applying metadata')
 | 
			
		||||
        return embed_m4a(music_file, meta_tags)
 | 
			
		||||
    elif music_file.endswith('.mp3'):
 | 
			
		||||
        print('Fixing meta-tags')
 | 
			
		||||
        log.info('Applying metadata')
 | 
			
		||||
        return embed_mp3(music_file, meta_tags)
 | 
			
		||||
    else:
 | 
			
		||||
        print('Cannot embed meta-tags into given output extension')
 | 
			
		||||
        log.warning('Cannot embed metadata into given output extension')
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,3 +6,4 @@ spotipy >= 2.4.4
 | 
			
		||||
mutagen >= 1.37
 | 
			
		||||
unicode-slugify >= 0.1.3
 | 
			
		||||
titlecase >= 0.10.0
 | 
			
		||||
logzero >= 1.3.1
 | 
			
		||||
							
								
								
									
										229
									
								
								spotdl.py
									
									
									
									
									
								
							
							
						
						
									
										229
									
								
								spotdl.py
									
									
									
									
									
								
							@@ -1,9 +1,10 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# -*- coding: UTF-8 -*-
 | 
			
		||||
 | 
			
		||||
from core import logger
 | 
			
		||||
from core import metadata
 | 
			
		||||
from core import convert
 | 
			
		||||
from core import misc
 | 
			
		||||
from core import internals
 | 
			
		||||
from bs4 import BeautifulSoup
 | 
			
		||||
from titlecase import titlecase
 | 
			
		||||
from slugify import slugify
 | 
			
		||||
@@ -13,6 +14,9 @@ import urllib.request
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
import time
 | 
			
		||||
import sys
 | 
			
		||||
import platform
 | 
			
		||||
import pprint
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_songname(tags):
 | 
			
		||||
@@ -23,11 +27,13 @@ def generate_songname(tags):
 | 
			
		||||
 | 
			
		||||
def generate_metadata(raw_song):
 | 
			
		||||
    """ Fetch a song's metadata from Spotify. """
 | 
			
		||||
    if misc.is_spotify(raw_song):
 | 
			
		||||
    if internals.is_spotify(raw_song):
 | 
			
		||||
        # fetch track information directly if it is spotify link
 | 
			
		||||
        log.debug('Fetching metadata for given track URL')
 | 
			
		||||
        meta_tags = spotify.track(raw_song)
 | 
			
		||||
    else:
 | 
			
		||||
        # otherwise search on spotify and fetch information from first result
 | 
			
		||||
        log.debug('Searching for "{}" on Spotify'.format(raw_song))
 | 
			
		||||
        try:
 | 
			
		||||
            meta_tags = spotify.search(raw_song, limit=1)['tracks']['items'][0]
 | 
			
		||||
        except IndexError:
 | 
			
		||||
@@ -52,49 +58,64 @@ def generate_metadata(raw_song):
 | 
			
		||||
    meta_tags[u'publisher'] = album['label']
 | 
			
		||||
    meta_tags[u'total_tracks'] = album['tracks']['total']
 | 
			
		||||
 | 
			
		||||
    log.debug(pprint.pformat(meta_tags))
 | 
			
		||||
    return meta_tags
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_video(result):
 | 
			
		||||
    # ensure result is not a channel
 | 
			
		||||
    not_video = result.find('channel') is not None or \
 | 
			
		||||
                'yt-lockup-channel' in result.parent.attrs['class'] or \
 | 
			
		||||
                'yt-lockup-channel' in result.attrs['class']
 | 
			
		||||
 | 
			
		||||
    # ensure result is not a mix/playlist
 | 
			
		||||
    not_video = not_video or \
 | 
			
		||||
               'yt-lockup-playlist' in result.parent.attrs['class']
 | 
			
		||||
 | 
			
		||||
    # ensure video result is not an advertisement
 | 
			
		||||
    not_video = not_video or \
 | 
			
		||||
                result.find('googleads') is not None
 | 
			
		||||
 | 
			
		||||
    video = not not_video
 | 
			
		||||
    return video
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_youtube_url(raw_song, meta_tags, tries_remaining=5):
 | 
			
		||||
    """ Search for the song on YouTube and generate a URL to its video. """
 | 
			
		||||
    # prevents an infinite loop but allows for a few retries
 | 
			
		||||
    if tries_remaining == 0:
 | 
			
		||||
        log.debug('No tries left. I quit.')
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    if meta_tags is None:
 | 
			
		||||
        song = raw_song
 | 
			
		||||
        search_url = misc.generate_search_url(song, viewsort=False)
 | 
			
		||||
        search_url = internals.generate_search_url(song, viewsort=False)
 | 
			
		||||
    else:
 | 
			
		||||
        song = generate_songname(meta_tags)
 | 
			
		||||
        search_url = misc.generate_search_url(song, viewsort=True)
 | 
			
		||||
        search_url = internals.generate_search_url(song, viewsort=True)
 | 
			
		||||
    log.debug('Opening URL: {0}'.format(search_url))
 | 
			
		||||
 | 
			
		||||
    item = urllib.request.urlopen(search_url).read()
 | 
			
		||||
    # item = unicode(item, 'utf-8')
 | 
			
		||||
    items_parse = BeautifulSoup(item, "html.parser")
 | 
			
		||||
 | 
			
		||||
    videos = []
 | 
			
		||||
    for x in items_parse.find_all('div', {'class': 'yt-lockup-dismissable yt-uix-tile'}):
 | 
			
		||||
        # ensure result is not a channel
 | 
			
		||||
        if x.find('channel') is not None or 'yt-lockup-channel' in x.parent.attrs['class'] or 'yt-lockup-channel' in x.attrs['class']:
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        # ensure result is not a mix/playlist
 | 
			
		||||
        if 'yt-lockup-playlist' in x.parent.attrs['class']:
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        # confirm the video result is not an advertisement
 | 
			
		||||
        if x.find('googleads') is not None:
 | 
			
		||||
        if not is_video(x):
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        y = x.find('div', class_='yt-lockup-content')
 | 
			
		||||
        link = y.find('a')['href']
 | 
			
		||||
        title = y.find('a')['title']
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            videotime = x.find('span', class_="video-time").get_text()
 | 
			
		||||
        except AttributeError:
 | 
			
		||||
            log.debug('Could not find video duration on YouTube, retrying..')
 | 
			
		||||
            return generate_youtube_url(raw_song, meta_tags, tries_remaining - 1)
 | 
			
		||||
 | 
			
		||||
        youtubedetails = {'link': link, 'title': title, 'videotime': videotime, 'seconds':misc.get_sec(videotime)}
 | 
			
		||||
        youtubedetails = {'link': link, 'title': title, 'videotime': videotime,
 | 
			
		||||
                          'seconds': internals.get_sec(videotime)}
 | 
			
		||||
        videos.append(youtubedetails)
 | 
			
		||||
        if meta_tags is None:
 | 
			
		||||
            break
 | 
			
		||||
@@ -102,20 +123,26 @@ def generate_youtube_url(raw_song, meta_tags, tries_remaining=5):
 | 
			
		||||
    if not videos:
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    log.debug(pprint.pformat(videos))
 | 
			
		||||
 | 
			
		||||
    if args.manual:
 | 
			
		||||
        print(song)
 | 
			
		||||
        print('')
 | 
			
		||||
        print('0. Skip downloading this song')
 | 
			
		||||
        log.info(song)
 | 
			
		||||
        log.info('0. Skip downloading this song.\n')
 | 
			
		||||
        # fetch all video links on first page on YouTube
 | 
			
		||||
        for i, v in enumerate(videos):
 | 
			
		||||
          print(u'{0}. {1} {2} {3}'.format(i+1, v['title'], v['videotime'], "http://youtube.com"+v['link']))
 | 
			
		||||
        print('')
 | 
			
		||||
            log.info(u'{0}. {1} {2} {3}'.format(i+1, v['title'], v['videotime'],
 | 
			
		||||
                  "http://youtube.com"+v['link']))
 | 
			
		||||
        # let user select the song to download
 | 
			
		||||
        result = misc.input_link(videos)
 | 
			
		||||
        result = internals.input_link(videos)
 | 
			
		||||
        if result is None:
 | 
			
		||||
            return None
 | 
			
		||||
    else:
 | 
			
		||||
        if meta_tags is not None:
 | 
			
		||||
        if meta_tags is None:
 | 
			
		||||
            # if the metadata could not be acquired, take the first result
 | 
			
		||||
            # from Youtube because the proper song length is unknown
 | 
			
		||||
            result = videos[0]
 | 
			
		||||
            log.debug('Since no metadata found on Spotify, going with the first result')
 | 
			
		||||
        else:
 | 
			
		||||
            # filter out videos that do not have a similar length to the Spotify song
 | 
			
		||||
            duration_tolerance = 10
 | 
			
		||||
            max_duration_tolerance = 20
 | 
			
		||||
@@ -130,24 +157,23 @@ def generate_youtube_url(raw_song, meta_tags, tries_remaining=5):
 | 
			
		||||
                possible_videos_by_duration = list(filter(lambda x: abs(x['seconds'] - (int(meta_tags['duration_ms'])/1000)) <= duration_tolerance, videos))
 | 
			
		||||
                duration_tolerance += 1
 | 
			
		||||
                if duration_tolerance > max_duration_tolerance:
 | 
			
		||||
                    print(meta_tags['name'], 'by', meta_tags['artists'][0]['name'], 'was not found')
 | 
			
		||||
                    log.error("{0} by {1} was not found.\n".format(meta_tags['name'],meta_tags['artists'][0]['name']))
 | 
			
		||||
                    return None
 | 
			
		||||
 | 
			
		||||
            result = possible_videos_by_duration[0]
 | 
			
		||||
        else:
 | 
			
		||||
            # if the metadata could not be acquired, take the first result from Youtube because the proper song length is unknown
 | 
			
		||||
            result = videos[0]
 | 
			
		||||
 | 
			
		||||
    full_link = None
 | 
			
		||||
    if result:
 | 
			
		||||
        full_link = u'youtube.com{0}'.format(result['link'])
 | 
			
		||||
        full_link = u'http://youtube.com{0}'.format(result['link'])
 | 
			
		||||
    else:
 | 
			
		||||
        full_link = None
 | 
			
		||||
 | 
			
		||||
    log.debug('Best matching video link: {}'.format(full_link))
 | 
			
		||||
    return full_link
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def go_pafy(raw_song, meta_tags):
 | 
			
		||||
def go_pafy(raw_song, meta_tags=None):
 | 
			
		||||
    """ Parse track from YouTube. """
 | 
			
		||||
    if misc.is_youtube(raw_song):
 | 
			
		||||
    if internals.is_youtube(raw_song):
 | 
			
		||||
        track_info = pafy.new(raw_song)
 | 
			
		||||
    else:
 | 
			
		||||
        track_url = generate_youtube_url(raw_song, meta_tags)
 | 
			
		||||
@@ -180,9 +206,10 @@ def feed_playlist(username):
 | 
			
		||||
            # in rare cases, playlists may not be found, so playlists['next']
 | 
			
		||||
            # is None. Skip these. Also see Issue #91.
 | 
			
		||||
            if playlist['name'] is not None:
 | 
			
		||||
                print(u'{0:>5}. {1:<30}  ({2} tracks)'.format(
 | 
			
		||||
                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)
 | 
			
		||||
                check += 1
 | 
			
		||||
        if playlists['next']:
 | 
			
		||||
@@ -190,9 +217,7 @@ def feed_playlist(username):
 | 
			
		||||
        else:
 | 
			
		||||
            break
 | 
			
		||||
 | 
			
		||||
    print('')
 | 
			
		||||
    playlist = misc.input_link(links)
 | 
			
		||||
    print('')
 | 
			
		||||
    playlist = internals.input_link(links)
 | 
			
		||||
    write_playlist(playlist['owner']['id'], playlist['id'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -205,9 +230,11 @@ def write_tracks(text_file, tracks):
 | 
			
		||||
                else:
 | 
			
		||||
                    track = item
 | 
			
		||||
                try:
 | 
			
		||||
                    file_out.write(track['external_urls']['spotify'] + '\n')
 | 
			
		||||
                    track_url = track['external_urls']['spotify']
 | 
			
		||||
                    file_out.write(track_url + '\n')
 | 
			
		||||
                    log.debug(track_url)
 | 
			
		||||
                except KeyError:
 | 
			
		||||
                    print(u'Skipping track {0} by {1} (local only?)'.format(
 | 
			
		||||
                    log.warning(u'Skipping track {0} by {1} (local only?)'.format(
 | 
			
		||||
                        track['name'], track['artists'][0]['name']))
 | 
			
		||||
            # 1 page = 50 results
 | 
			
		||||
            # check if there are more pages
 | 
			
		||||
@@ -218,11 +245,11 @@ def write_tracks(text_file, tracks):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def write_playlist(username, playlist_id):
 | 
			
		||||
    results = spotify.user_playlist(
 | 
			
		||||
            username, playlist_id, fields='tracks,next,name')
 | 
			
		||||
    results = spotify.user_playlist(username, playlist_id,
 | 
			
		||||
                                    fields='tracks,next,name')
 | 
			
		||||
    text_file = u'{0}.txt'.format(slugify(results['name'], ok='-_()[]{}'))
 | 
			
		||||
 | 
			
		||||
    print(u'Feeding {0} tracks to {1}'.format(results['tracks']['total'], text_file))
 | 
			
		||||
    log.info(u'Writing {0} tracks to {1}'.format(
 | 
			
		||||
               results['tracks']['total'], text_file))
 | 
			
		||||
    tracks = results['tracks']
 | 
			
		||||
    write_tracks(text_file, tracks)
 | 
			
		||||
 | 
			
		||||
@@ -230,8 +257,8 @@ def write_playlist(username, playlist_id):
 | 
			
		||||
def write_album(album):
 | 
			
		||||
    tracks = spotify.album_tracks(album['id'])
 | 
			
		||||
    text_file = u'{0}.txt'.format(slugify(album['name'], ok='-_()[]{}'))
 | 
			
		||||
    print(u'Feeding {0} tracks to {1}'.format(tracks['total'], text_file))
 | 
			
		||||
 | 
			
		||||
    log.info(u'writing {0} tracks to {1}'.format(
 | 
			
		||||
               tracks['total'], text_file))
 | 
			
		||||
    write_tracks(text_file, tracks)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -242,42 +269,49 @@ def download_song(file_name, content):
 | 
			
		||||
    else:
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    log.debug('Downloading from URL: ' + link.url)
 | 
			
		||||
    if link is None:
 | 
			
		||||
        return False
 | 
			
		||||
    else:
 | 
			
		||||
        link.download(
 | 
			
		||||
            filepath='{0}{1}'.format(os.path.join(args.folder, file_name), args.input_ext))
 | 
			
		||||
        filepath = '{0}{1}'.format(os.path.join(args.folder, file_name),
 | 
			
		||||
                                   args.input_ext)
 | 
			
		||||
        link.download(filepath=filepath)
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def check_exists(music_file, raw_song, meta_tags, islist=True):
 | 
			
		||||
    """ Check if the input song already exists in the given folder. """
 | 
			
		||||
    log.debug('Cleaning any temp files and checking '
 | 
			
		||||
              'if "{}" already exists'.format(music_file))
 | 
			
		||||
    songs = os.listdir(args.folder)
 | 
			
		||||
    for song in songs:
 | 
			
		||||
        if song.endswith('.temp'):
 | 
			
		||||
            os.remove(os.path.join(args.folder, song))
 | 
			
		||||
            continue
 | 
			
		||||
        # check if any song with similar name is already present in the given folder
 | 
			
		||||
        file_name = misc.sanitize_title(music_file)
 | 
			
		||||
        file_name = internals.sanitize_title(music_file)
 | 
			
		||||
        if song.startswith(file_name):
 | 
			
		||||
            log.debug('Found an already existing song: "{}"'.format(song))
 | 
			
		||||
            if internals.is_spotify(raw_song):
 | 
			
		||||
                # check if the already downloaded song has correct metadata
 | 
			
		||||
            already_tagged = metadata.compare(os.path.join(args.folder, song), meta_tags)
 | 
			
		||||
 | 
			
		||||
                # if not, remove it and download again without prompt
 | 
			
		||||
            if misc.is_spotify(raw_song) and not already_tagged:
 | 
			
		||||
                already_tagged = metadata.compare(os.path.join(args.folder, song),
 | 
			
		||||
                                                  meta_tags)
 | 
			
		||||
                log.debug('Checking if it is already tagged correctly? {}',
 | 
			
		||||
                                                            already_tagged)
 | 
			
		||||
                if not already_tagged:
 | 
			
		||||
                    os.remove(os.path.join(args.folder, song))
 | 
			
		||||
                    return False
 | 
			
		||||
 | 
			
		||||
            # do not prompt and skip the current song
 | 
			
		||||
            # if already downloaded when using list
 | 
			
		||||
            if islist:
 | 
			
		||||
                print('Song already exists')
 | 
			
		||||
                return True
 | 
			
		||||
            # if downloading only single song, prompt to re-download
 | 
			
		||||
            if islist:
 | 
			
		||||
                log.warning('Song already exists')
 | 
			
		||||
                return True
 | 
			
		||||
            else:
 | 
			
		||||
                prompt = input('Song with same name has already been downloaded. '
 | 
			
		||||
                               'Re-download? (y/n): ').lower()
 | 
			
		||||
                if prompt == 'y':
 | 
			
		||||
                log.info('Song with same name has already been downloaded. '
 | 
			
		||||
                         'Re-download? (y/N): ')
 | 
			
		||||
                prompt = input('> ')
 | 
			
		||||
                if prompt.lower() == 'y':
 | 
			
		||||
                    os.remove(os.path.join(args.folder, song))
 | 
			
		||||
                    return False
 | 
			
		||||
                else:
 | 
			
		||||
@@ -294,37 +328,36 @@ def grab_list(text_file):
 | 
			
		||||
        lines.remove('')
 | 
			
		||||
    except ValueError:
 | 
			
		||||
        pass
 | 
			
		||||
    print(u'Total songs in list: {0} songs'.format(len(lines)))
 | 
			
		||||
    print('')
 | 
			
		||||
    # nth input song
 | 
			
		||||
    log.info(u'Preparing to download {} songs'.format(len(lines)))
 | 
			
		||||
    number = 1
 | 
			
		||||
 | 
			
		||||
    for raw_song in lines:
 | 
			
		||||
        print('')
 | 
			
		||||
        try:
 | 
			
		||||
            grab_single(raw_song, number=number)
 | 
			
		||||
        # token expires after 1 hour
 | 
			
		||||
        except spotipy.client.SpotifyException:
 | 
			
		||||
            # refresh token when it expires
 | 
			
		||||
            new_token = misc.generate_token()
 | 
			
		||||
            log.debug('Token expired, generating new one and authorizing')
 | 
			
		||||
            new_token = internals.generate_token()
 | 
			
		||||
            global spotify
 | 
			
		||||
            spotify = spotipy.Spotify(auth=new_token)
 | 
			
		||||
            grab_single(raw_song, number=number)
 | 
			
		||||
        # detect network problems
 | 
			
		||||
        except (urllib.request.URLError, TypeError, IOError):
 | 
			
		||||
            lines.append(raw_song)
 | 
			
		||||
            # remove the downloaded song from .txt
 | 
			
		||||
            misc.trim_song(text_file)
 | 
			
		||||
            # and append it to the last line in .txt
 | 
			
		||||
            # remove the downloaded song from file
 | 
			
		||||
            internals.trim_song(text_file)
 | 
			
		||||
            # and append it at the end of file
 | 
			
		||||
            with open(text_file, 'a') as myfile:
 | 
			
		||||
                myfile.write(raw_song + '\n')
 | 
			
		||||
            print('Failed to download song. Will retry after other songs.')
 | 
			
		||||
            log.warning('Failed to download song. Will retry after other songs\n')
 | 
			
		||||
            # wait 0.5 sec to avoid infinite looping
 | 
			
		||||
            time.sleep(0.5)
 | 
			
		||||
            continue
 | 
			
		||||
        except KeyboardInterrupt:
 | 
			
		||||
            misc.grace_quit()
 | 
			
		||||
        finally:
 | 
			
		||||
            print('')
 | 
			
		||||
        misc.trim_song(text_file)
 | 
			
		||||
 | 
			
		||||
        log.debug('Removing downloaded song from text file')
 | 
			
		||||
        internals.trim_song(text_file)
 | 
			
		||||
        number += 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -340,14 +373,14 @@ def grab_playlist(playlist):
 | 
			
		||||
        username = splits[-3]
 | 
			
		||||
    except IndexError:
 | 
			
		||||
        # Wrong format, in either case
 | 
			
		||||
        print('The provided playlist URL is not in a recognized format!')
 | 
			
		||||
        log.error('The provided playlist URL is not in a recognized format!')
 | 
			
		||||
        sys.exit(10)
 | 
			
		||||
    playlist_id = splits[-1]
 | 
			
		||||
    try:
 | 
			
		||||
        write_playlist(username, playlist_id)
 | 
			
		||||
    except spotipy.client.SpotifyException:
 | 
			
		||||
        print('Unable to find playlist')
 | 
			
		||||
        print('Make sure the playlist is set to publicly visible and then try again')
 | 
			
		||||
        log.error('Unable to find playlist')
 | 
			
		||||
        log.info('Make sure the playlist is set to publicly visible and then try again')
 | 
			
		||||
        sys.exit(11)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -372,58 +405,66 @@ def grab_single(raw_song, number=None):
 | 
			
		||||
    else:
 | 
			
		||||
        islist = False
 | 
			
		||||
 | 
			
		||||
    if misc.is_youtube(raw_song):
 | 
			
		||||
    if internals.is_youtube(raw_song):
 | 
			
		||||
        log.debug('Input song is a YouTube URL')
 | 
			
		||||
        content = go_pafy(raw_song, meta_tags=None)
 | 
			
		||||
        raw_song = slugify(content.title).replace('-', ' ')
 | 
			
		||||
 | 
			
		||||
        meta_tags = generate_metadata(raw_song)
 | 
			
		||||
    else:
 | 
			
		||||
        meta_tags = generate_metadata(raw_song)
 | 
			
		||||
        content = go_pafy(raw_song, meta_tags)
 | 
			
		||||
 | 
			
		||||
    if content is None:
 | 
			
		||||
        log.debug('Found no matching video')
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    # print '[number]. [artist] - [song]' if downloading from list
 | 
			
		||||
    # otherwise print '[artist] - [song]'
 | 
			
		||||
    print(get_youtube_title(content, number))
 | 
			
		||||
    # log '[number]. [artist] - [song]' if downloading from list
 | 
			
		||||
    # otherwise log '[artist] - [song]'
 | 
			
		||||
    log.info(get_youtube_title(content, number))
 | 
			
		||||
    # generate file name of the song to download
 | 
			
		||||
    songname = content.title
 | 
			
		||||
 | 
			
		||||
    if meta_tags is not None:
 | 
			
		||||
        refined_songname = generate_songname(meta_tags)
 | 
			
		||||
        log.debug('Refining songname from "{0}" to "{1}"'.format(songname, refined_songname))
 | 
			
		||||
        if not refined_songname == ' - ':
 | 
			
		||||
            songname = refined_songname
 | 
			
		||||
 | 
			
		||||
    file_name = misc.sanitize_title(songname)
 | 
			
		||||
    file_name = internals.sanitize_title(songname)
 | 
			
		||||
 | 
			
		||||
    if not check_exists(file_name, raw_song, meta_tags, islist=islist):
 | 
			
		||||
        if download_song(file_name, content):
 | 
			
		||||
            print('')
 | 
			
		||||
            input_song = file_name + args.input_ext
 | 
			
		||||
            output_song = file_name + args.output_ext
 | 
			
		||||
            print('')
 | 
			
		||||
            convert.song(input_song, output_song, args.folder,
 | 
			
		||||
                         avconv=args.avconv, verbose=args.verbose)
 | 
			
		||||
                         avconv=args.avconv)
 | 
			
		||||
            if not args.input_ext == args.output_ext:
 | 
			
		||||
                os.remove(os.path.join(args.folder, input_song))
 | 
			
		||||
 | 
			
		||||
            if not args.no_metadata:
 | 
			
		||||
                metadata.embed(os.path.join(args.folder, output_song), meta_tags)
 | 
			
		||||
        else:
 | 
			
		||||
            print('No audio streams available')
 | 
			
		||||
            log.error('No audio streams available')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestArgs(object):
 | 
			
		||||
    manual = False
 | 
			
		||||
    input_ext = '.m4a'
 | 
			
		||||
    output_ext = '.mp3'
 | 
			
		||||
    folder = 'Music/'
 | 
			
		||||
 | 
			
		||||
# token is mandatory when using Spotify's API
 | 
			
		||||
# https://developer.spotify.com/news-stories/2017/01/27/removing-unauthenticated-calls-to-the-web-api/
 | 
			
		||||
token = misc.generate_token()
 | 
			
		||||
token = internals.generate_token()
 | 
			
		||||
spotify = spotipy.Spotify(auth=token)
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    args = misc.get_arguments()
 | 
			
		||||
    misc.filter_path(args.folder)
 | 
			
		||||
    args = internals.get_arguments()
 | 
			
		||||
    internals.filter_path(args.folder)
 | 
			
		||||
 | 
			
		||||
    logger.log = logger.logzero.setup_logger(formatter=logger.formatter,
 | 
			
		||||
                                      level=args.log_level)
 | 
			
		||||
    log = logger.log
 | 
			
		||||
    log.debug('Python version: {}'.format(sys.version))
 | 
			
		||||
    log.debug('Platform: {}'.format(platform.platform()))
 | 
			
		||||
    log.debug(pprint.pformat(args.__dict__))
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        if args.song:
 | 
			
		||||
            grab_single(raw_song=args.song)
 | 
			
		||||
        elif args.list:
 | 
			
		||||
@@ -434,6 +475,8 @@ if __name__ == '__main__':
 | 
			
		||||
            grab_album(album=args.album)
 | 
			
		||||
        elif args.username:
 | 
			
		||||
            feed_playlist(username=args.username)
 | 
			
		||||
else:
 | 
			
		||||
    misc.filter_path('Music')
 | 
			
		||||
    args = TestArgs()
 | 
			
		||||
        sys.exit(0)
 | 
			
		||||
 | 
			
		||||
    except KeyboardInterrupt as e:
 | 
			
		||||
        log.exception(e)
 | 
			
		||||
        sys.exit(-1)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,31 +1,44 @@
 | 
			
		||||
# -*- coding: UTF-8 -*-
 | 
			
		||||
 | 
			
		||||
from spotdl import logger
 | 
			
		||||
import spotdl
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
raw_song = "Tony's Videos VERY SHORT VIDEO 28.10.2016"
 | 
			
		||||
 | 
			
		||||
for x in os.listdir(spotdl.args.folder):
 | 
			
		||||
    os.remove(os.path.join(spotdl.args.folder, x))
 | 
			
		||||
 | 
			
		||||
class TestArgs:
 | 
			
		||||
    manual = False
 | 
			
		||||
    input_ext = '.m4a'
 | 
			
		||||
    output_ext = '.mp3'
 | 
			
		||||
    folder = 'test'
 | 
			
		||||
    log_level = logger.logging.DEBUG
 | 
			
		||||
 | 
			
		||||
test_args = TestArgs()
 | 
			
		||||
setattr(spotdl, "args", test_args)
 | 
			
		||||
 | 
			
		||||
spotdl.log = logger.logzero.setup_logger(formatter=logger.formatter,
 | 
			
		||||
                                  level=spotdl.args.log_level)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_youtube_url():
 | 
			
		||||
    expect_url = 'youtube.com/watch?v=qOOcy2-tmbk'
 | 
			
		||||
    expect_url = 'http://youtube.com/watch?v=qOOcy2-tmbk'
 | 
			
		||||
    url = spotdl.generate_youtube_url(raw_song, meta_tags=None)
 | 
			
		||||
    assert url == expect_url
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_youtube_title():
 | 
			
		||||
    expect_title = "Tony's Videos VERY SHORT VIDEO 28.10.2016"
 | 
			
		||||
    global content
 | 
			
		||||
    content = spotdl.go_pafy(raw_song, meta_tags=None)
 | 
			
		||||
    global title
 | 
			
		||||
    expect_title = "Tony's Videos VERY SHORT VIDEO 28.10.2016"
 | 
			
		||||
    content = spotdl.go_pafy(raw_song, meta_tags=None)
 | 
			
		||||
    title = spotdl.get_youtube_title(content)
 | 
			
		||||
    assert title == expect_title
 | 
			
		||||
 | 
			
		||||
def test_check_exists():
 | 
			
		||||
    expect_check = False
 | 
			
		||||
    # prerequisites for determining filename
 | 
			
		||||
    file_name = spotdl.misc.sanitize_title(title)
 | 
			
		||||
    file_name = spotdl.internals.sanitize_title(title)
 | 
			
		||||
    check = spotdl.check_exists(file_name, raw_song, meta_tags=None, islist=True)
 | 
			
		||||
    assert check == expect_check
 | 
			
		||||
 | 
			
		||||
@@ -33,7 +46,7 @@ def test_check_exists():
 | 
			
		||||
def test_download():
 | 
			
		||||
    expect_download = True
 | 
			
		||||
    # prerequisites for determining filename
 | 
			
		||||
    file_name = spotdl.misc.sanitize_title(title)
 | 
			
		||||
    file_name = spotdl.internals.sanitize_title(title)
 | 
			
		||||
    download = spotdl.download_song(file_name, content)
 | 
			
		||||
    assert download == expect_download
 | 
			
		||||
 | 
			
		||||
@@ -42,7 +55,9 @@ def test_convert():
 | 
			
		||||
    # exit code 0 = success
 | 
			
		||||
    expect_convert = 0
 | 
			
		||||
    # prerequisites for determining filename
 | 
			
		||||
    file_name = spotdl.misc.sanitize_title(title)
 | 
			
		||||
    file_name = spotdl.internals.sanitize_title(title)
 | 
			
		||||
    global input_song
 | 
			
		||||
    global output_song
 | 
			
		||||
    input_song = file_name + spotdl.args.input_ext
 | 
			
		||||
    output_song = file_name + spotdl.args.output_ext
 | 
			
		||||
    convert = spotdl.convert.song(input_song, output_song, spotdl.args.folder)
 | 
			
		||||
@@ -53,11 +68,8 @@ def test_metadata():
 | 
			
		||||
    expect_metadata = None
 | 
			
		||||
    # prerequisites for determining filename
 | 
			
		||||
    meta_tags = spotdl.generate_metadata(raw_song)
 | 
			
		||||
    meta_tags = spotdl.generate_metadata(raw_song)
 | 
			
		||||
    file_name = spotdl.misc.sanitize_title(title)
 | 
			
		||||
    output_song = file_name + spotdl.args.output_ext
 | 
			
		||||
    file_name = spotdl.internals.sanitize_title(title)
 | 
			
		||||
    metadata_output = spotdl.metadata.embed(os.path.join(spotdl.args.folder, output_song), meta_tags)
 | 
			
		||||
    input_song = file_name + spotdl.args.input_ext
 | 
			
		||||
    metadata_input = spotdl.metadata.embed(os.path.join(spotdl.args.folder, input_song), meta_tags)
 | 
			
		||||
    assert (metadata_output == expect_metadata) and (metadata_input == expect_metadata)
 | 
			
		||||
 | 
			
		||||
@@ -65,8 +77,8 @@ def test_metadata():
 | 
			
		||||
def test_check_exists2():
 | 
			
		||||
    expect_check = True
 | 
			
		||||
    # prerequisites for determining filename
 | 
			
		||||
    file_name = spotdl.misc.sanitize_title(title)
 | 
			
		||||
    input_song = file_name + spotdl.args.input_ext
 | 
			
		||||
    file_name = spotdl.internals.sanitize_title(title)
 | 
			
		||||
    os.remove(os.path.join(spotdl.args.folder, input_song))
 | 
			
		||||
    check = spotdl.check_exists(file_name, raw_song, meta_tags=None, islist=True)
 | 
			
		||||
    os.remove(os.path.join(spotdl.args.folder, output_song))
 | 
			
		||||
    assert check == expect_check
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,25 @@
 | 
			
		||||
# -*- coding: UTF-8 -*-
 | 
			
		||||
 | 
			
		||||
from spotdl import logger
 | 
			
		||||
import spotdl
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
raw_song = 'http://open.spotify.com/track/0JlS7BXXD07hRmevDnbPDU'
 | 
			
		||||
 | 
			
		||||
for x in os.listdir(spotdl.args.folder):
 | 
			
		||||
    os.remove(os.path.join(spotdl.args.folder, x))
 | 
			
		||||
 | 
			
		||||
class TestArgs:
 | 
			
		||||
    manual = False
 | 
			
		||||
    input_ext = '.m4a'
 | 
			
		||||
    output_ext = '.mp3'
 | 
			
		||||
    folder = 'test'
 | 
			
		||||
    log_level = 'DEBUG'
 | 
			
		||||
 | 
			
		||||
test_args = TestArgs()
 | 
			
		||||
setattr(spotdl, "args", test_args)
 | 
			
		||||
 | 
			
		||||
spotdl.log = logger.logzero.setup_logger(formatter=logger.formatter,
 | 
			
		||||
                                  level=spotdl.args.log_level)
 | 
			
		||||
spotdl.internals.filter_path(spotdl.args.folder)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_spotify_title():
 | 
			
		||||
@@ -35,7 +48,7 @@ def test_check_exists():
 | 
			
		||||
    # prerequisites for determining filename
 | 
			
		||||
    songname = spotdl.generate_songname(meta_tags)
 | 
			
		||||
    global file_name
 | 
			
		||||
    file_name = spotdl.misc.sanitize_title(songname)
 | 
			
		||||
    file_name = spotdl.internals.sanitize_title(songname)
 | 
			
		||||
    check = spotdl.check_exists(file_name, raw_song, meta_tags, islist=True)
 | 
			
		||||
    assert check == expect_check
 | 
			
		||||
 | 
			
		||||
@@ -52,6 +65,8 @@ def test_convert():
 | 
			
		||||
    # exit code 0 = success
 | 
			
		||||
    expect_convert = 0
 | 
			
		||||
    # prerequisites for determining filename
 | 
			
		||||
    global input_song
 | 
			
		||||
    global output_song
 | 
			
		||||
    input_song = file_name + spotdl.args.input_ext
 | 
			
		||||
    output_song = file_name + spotdl.args.output_ext
 | 
			
		||||
    convert = spotdl.convert.song(input_song, output_song, spotdl.args.folder)
 | 
			
		||||
@@ -61,9 +76,7 @@ def test_convert():
 | 
			
		||||
def test_metadata():
 | 
			
		||||
    expect_metadata = True
 | 
			
		||||
    # prerequisites for determining filename
 | 
			
		||||
    output_song = file_name + spotdl.args.output_ext
 | 
			
		||||
    metadata_output = spotdl.metadata.embed(os.path.join(spotdl.args.folder, output_song), meta_tags)
 | 
			
		||||
    input_song = file_name + spotdl.args.input_ext
 | 
			
		||||
    metadata_input = spotdl.metadata.embed(os.path.join(spotdl.args.folder, input_song), meta_tags)
 | 
			
		||||
    assert metadata_output == (metadata_input == expect_metadata)
 | 
			
		||||
 | 
			
		||||
@@ -71,7 +84,7 @@ def test_metadata():
 | 
			
		||||
def test_check_exists2():
 | 
			
		||||
    expect_check = True
 | 
			
		||||
    # prerequisites for determining filename
 | 
			
		||||
    input_song = file_name + spotdl.args.input_ext
 | 
			
		||||
    os.remove(os.path.join(spotdl.args.folder, input_song))
 | 
			
		||||
    check = spotdl.check_exists(file_name, raw_song, meta_tags, islist=True)
 | 
			
		||||
    os.remove(os.path.join(spotdl.args.folder, output_song))
 | 
			
		||||
    assert check == expect_check
 | 
			
		||||
 
 | 
			
		||||
@@ -33,8 +33,7 @@ def test_tracks():
 | 
			
		||||
                try:
 | 
			
		||||
                    fout.write(track['external_urls']['spotify'] + '\n')
 | 
			
		||||
                except KeyError:
 | 
			
		||||
                    title = track['name'] + ' by '+ track['artists'][0]['name']
 | 
			
		||||
                    print('Skipping track ' + title + ' (local only?)')
 | 
			
		||||
                    pass
 | 
			
		||||
            # 1 page = 50 results
 | 
			
		||||
            # check if there are more pages
 | 
			
		||||
            if tracks['next']:
 | 
			
		||||
@@ -45,7 +44,7 @@ def test_tracks():
 | 
			
		||||
    with open('list.txt', 'r') as listed:
 | 
			
		||||
        expect_song = (listed.read()).splitlines()[0]
 | 
			
		||||
 | 
			
		||||
    spotdl.misc.trim_song('list.txt')
 | 
			
		||||
    spotdl.internals.trim_song('list.txt')
 | 
			
		||||
    with open('list.txt', 'a') as myfile:
 | 
			
		||||
        myfile.write(expect_song)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user