Merge branch 'refactor'

This commit is contained in:
ritiek
2018-01-12 17:09:27 +05:30
11 changed files with 660 additions and 650 deletions

8
core/const.py Normal file
View File

@@ -0,0 +1,8 @@
import logzero
_log_format = ("%(color)s%(levelname)s:%(end_color)s %(message)s")
formatter = logzero.LogFormatter(fmt=_log_format)
# options
log = logzero.setup_logger(formatter=formatter)
args = None

View File

@@ -1,6 +1,6 @@
import subprocess
import os
from core.logger import log
from core.const import log
"""What are the differences and similarities between ffmpeg, libav, and avconv?
@@ -18,58 +18,60 @@ https://trac.ffmpeg.org/wiki/Encode/AAC
def song(input_song, output_song, folder, avconv=False):
""" Do the audio format conversion. """
if not input_song == output_song:
convert = Converter(input_song, output_song, folder)
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)
exit_code = convert.with_avconv()
else:
exit_code = convert_with_ffmpeg(input_song, output_song, folder)
exit_code = convert.with_ffmpeg()
return exit_code
return 0
def convert_with_avconv(input_song, output_song, folder):
""" Convert the audio file using avconv. """
if log.level == 10:
level = 'debug'
else:
level = '0'
class Converter:
def __init__(self, input_song, output_song, folder):
self.input_song = input_song
self.output_song = output_song
self.folder = folder
command = ['avconv', '-loglevel', level, '-i',
os.path.join(folder, input_song), '-ab', '192k',
os.path.join(folder, output_song)]
def with_avconv(self):
if log.level == 10:
level = 'debug'
else:
level = '0'
log.debug(command)
command = ['avconv', '-loglevel', level, '-i',
os.path.join(self.folder, self.input_song), '-ab', '192k',
os.path.join(self.folder, self.output_song)]
return subprocess.call(command)
log.debug(command)
return subprocess.call(command)
def with_ffmpeg(self):
ffmpeg_pre = 'ffmpeg -y '
def convert_with_ffmpeg(input_song, output_song, folder):
""" Convert the audio file using FFmpeg. """
ffmpeg_pre = 'ffmpeg -y '
if not log.level == 10:
ffmpeg_pre += '-hide_banner -nostats -v panic '
if not log.level == 10:
ffmpeg_pre += '-hide_banner -nostats -v panic '
input_ext = self.input_song.split('.')[-1]
output_ext = self.output_song.split('.')[-1]
input_ext = input_song.split('.')[-1]
output_ext = output_song.split('.')[-1]
if input_ext == 'm4a':
if output_ext == 'mp3':
ffmpeg_params = '-codec:v copy -codec:a libmp3lame -q:a 2 '
elif output_ext == 'webm':
ffmpeg_params = '-c:a libopus -vbr on -b:a 192k -vn '
if input_ext == 'm4a':
if output_ext == 'mp3':
ffmpeg_params = '-codec:v copy -codec:a libmp3lame -q:a 2 '
elif output_ext == 'webm':
ffmpeg_params = '-c:a libopus -vbr on -b:a 192k -vn '
elif input_ext == 'webm':
if output_ext == 'mp3':
ffmpeg_params = ' -ab 192k -ar 44100 -vn '
elif output_ext == 'm4a':
ffmpeg_params = '-cutoff 20000 -c:a libfdk_aac -b:a 192k -vn '
elif input_ext == 'webm':
if output_ext == 'mp3':
ffmpeg_params = ' -ab 192k -ar 44100 -vn '
elif output_ext == 'm4a':
ffmpeg_params = '-cutoff 20000 -c:a libfdk_aac -b:a 192k -vn '
command = '{0}-i {1} {2}{3}'.format(
ffmpeg_pre, os.path.join(self.folder, self.input_song),
ffmpeg_params, os.path.join(self.folder, self.output_song)).split(' ')
command = '{0}-i {1} {2}{3}'.format(
ffmpeg_pre, os.path.join(folder, input_song),
ffmpeg_params, os.path.join(folder, output_song)).split(' ')
log.debug(command)
return subprocess.call(command)
log.debug(command)
return subprocess.call(command)

80
core/handle.py Normal file
View File

@@ -0,0 +1,80 @@
import logging
import argparse
import os
import sys
_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
def get_arguments(to_group=True, raw_args=None):
parser = argparse.ArgumentParser(
description='Download and convert songs from Spotify, Youtube etc.',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
if to_group:
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
'-s', '--song', help='download song by spotify link or name')
group.add_argument(
'-l', '--list', help='download songs from a file')
group.add_argument(
'-p', '--playlist', help='load songs from playlist URL into <playlist_name>.txt')
group.add_argument(
'-b', '--album', help='load songs from album URL into <album_name>.txt')
group.add_argument(
'-u', '--username',
help="load songs from user's playlist into <playlist_name>.txt")
parser.add_argument(
'-m', '--manual', default=False,
help='choose the song to download manually', action='store_true')
parser.add_argument(
'-nm', '--no-metadata', default=False,
help='do not embed metadata in songs', action='store_true')
parser.add_argument(
'-a', '--avconv', default=False,
help='Use avconv for conversion otherwise set defaults to ffmpeg',
action='store_true')
parser.add_argument(
'-f', '--folder', default=(os.path.join(sys.path[0], 'Music')),
help='path to folder where files will be stored in')
parser.add_argument(
'--overwrite', default='prompt',
help='change the overwrite policy',
choices={'prompt', 'force', 'skip'})
parser.add_argument(
'-i', '--input-ext', default='.m4a',
help='prefered input format .m4a or .webm (Opus)')
parser.add_argument(
'-o', '--output-ext', default='.mp3',
help='prefered output extension .mp3 or .m4a (AAC)')
parser.add_argument(
'-dm', '--download-only-metadata', default=False,
help='download songs for which metadata is found',
action='store_true')
parser.add_argument(
'-d', '--dry-run', default=False,
help='Show only track title and YouTube URL',
action='store_true')
parser.add_argument(
'-mo', '--music-videos-only', default=False,
help='Search only for music on Youtube',
action='store_true')
parser.add_argument(
'-ll', '--log-level', default='INFO',
choices=_LOG_LEVELS_STR,
type=str.upper,
help='set log verbosity')
parsed = parser.parse_args(raw_args)
parsed.log_level = log_leveller(parsed.log_level)
return parsed

View File

@@ -1,11 +1,7 @@
import argparse
import spotipy.oauth2 as oauth2
from urllib.request import quote
from slugify import SLUG_OK, slugify
from core.const import log
import sys
import os
from core.logger import log, log_leveller, _LOG_LEVELS_STR
def input_link(links):
@@ -24,78 +20,14 @@ def input_link(links):
log.warning('Choose a valid number!')
def trim_song(file):
def trim_song(text_file):
""" Remove the first song from file. """
with open(file, 'r') as file_in:
with open(text_file, 'r') as file_in:
data = file_in.read().splitlines(True)
with open(file, 'w') as file_out:
with open(text_file, 'w') as file_out:
file_out.writelines(data[1:])
def get_arguments():
parser = argparse.ArgumentParser(
description='Download and convert songs from Spotify, Youtube etc.',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
'-s', '--song', help='download song by spotify link or name')
group.add_argument(
'-l', '--list', help='download songs from a file')
group.add_argument(
'-p', '--playlist', help='load songs from playlist URL into <playlist_name>.txt')
group.add_argument(
'-b', '--album', help='load songs from album URL into <album_name>.txt')
group.add_argument(
'-u', '--username',
help="load songs from user's playlist into <playlist_name>.txt")
parser.add_argument(
'-m', '--manual', default=False,
help='choose the song to download manually', action='store_true')
parser.add_argument(
'-nm', '--no-metadata', default=False,
help='do not embed metadata in songs', action='store_true')
parser.add_argument(
'-a', '--avconv', default=False,
help='Use avconv for conversion otherwise set defaults to ffmpeg',
action='store_true')
parser.add_argument(
'-f', '--folder', default=(os.path.join(sys.path[0], 'Music')),
help='path to folder where files will be stored in')
parser.add_argument(
'--overwrite', default='prompt',
help='change the overwrite policy',
choices={'prompt', 'force', 'skip'})
parser.add_argument(
'-i', '--input-ext', default='.m4a',
help='prefered input format .m4a or .webm (Opus)')
parser.add_argument(
'-o', '--output-ext', default='.mp3',
help='prefered output extension .mp3 or .m4a (AAC)')
parser.add_argument(
'-d', '--dry-run', default=False,
help='Show only track title and YouTube URL',
action='store_true')
parser.add_argument(
'-mo', '--music-videos-only', default=False,
help='Search only for music on Youtube',
action='store_true')
parser.add_argument(
'-ll', '--log-level', default='INFO',
choices=_LOG_LEVELS_STR,
type=str.upper,
help='set log verbosity')
parser.add_argument(
'-dm', '--download-only-metadata', default=False,
help='download songs for which metadata is found',
action='store_true')
parsed = parser.parse_args()
parsed.log_level = log_leveller(parsed.log_level)
return parsed
def is_spotify(raw_song):
""" Check if the input song is a Spotify link. """
status = len(raw_song) == 22 and raw_song.replace(" ", "%20") == raw_song
@@ -111,6 +43,12 @@ def is_youtube(raw_song):
return status
def generate_songname(tags):
""" Generate a string of the format '[artist] - [song]' for the given spotify song. """
raw_song = u'{0} - {1}'.format(tags['artists'][0]['name'], tags['name'])
return raw_song
def sanitize_title(title):
""" Generate filename of the song to be downloaded. """
title = title.replace(' ', '_')
@@ -121,15 +59,6 @@ def sanitize_title(title):
return title
def generate_token():
""" Generate the token. Please respect these credentials :) """
credentials = oauth2.SpotifyClientCredentials(
client_id='4fe3fecfe5334023a1472516cc99d805',
client_secret='0f02b7c483c04257984695007a4a8d5c')
token = credentials.get_access_token()
return token
def filter_path(path):
if not os.path.exists(path):
os.makedirs(path)

View File

@@ -1,16 +0,0 @@
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
log_format = ("%(color)s%(levelname)s:%(end_color)s %(message)s")
formatter = logzero.LogFormatter(fmt=log_format)
# create a default logger
log = logzero.setup_logger(formatter=formatter)

View File

@@ -1,151 +1,153 @@
from mutagen.easyid3 import EasyID3
from mutagen.id3 import ID3, TORY, TYER, TPUB, APIC, USLT, COMM
from mutagen.mp4 import MP4, MP4Cover
from core.logger import log
from core.const import log
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)
# fetch track title metadata
already_tagged = audiofile['title'][0] == metadata['name']
elif music_file.endswith('.m4a'):
tags = {'title': '\xa9nam'}
audiofile = MP4(music_file)
# fetch track title metadata
already_tagged = audiofile[tags['title']] == metadata['name']
except (KeyError, TypeError):
pass
already_tagged = False
return already_tagged
def embed(music_file, meta_tags):
""" Embed metadata. """
if meta_tags is None:
log.warning('Could not find metadata')
return None
elif music_file.endswith('.m4a'):
embed = EmbedMetadata(music_file, meta_tags)
if music_file.endswith('.m4a'):
log.info('Applying metadata')
return embed_m4a(music_file, meta_tags)
return embed.as_m4a()
elif music_file.endswith('.mp3'):
log.info('Applying metadata')
return embed_mp3(music_file, meta_tags)
return embed.as_mp3()
else:
log.warning('Cannot embed metadata into given output extension')
return False
def embed_mp3(music_file, meta_tags):
""" Embed metadata to MP3 files. """
# EasyID3 is fun to use ;)
# For supported easyid3 tags:
# https://github.com/quodlibet/mutagen/blob/master/mutagen/easyid3.py
# Check out somewhere at end of above linked file
audiofile = EasyID3(music_file)
audiofile['artist'] = meta_tags['artists'][0]['name']
audiofile['albumartist'] = meta_tags['artists'][0]['name']
audiofile['album'] = meta_tags['album']['name']
audiofile['title'] = meta_tags['name']
audiofile['tracknumber'] = [meta_tags['track_number'],
meta_tags['total_tracks']]
audiofile['discnumber'] = [meta_tags['disc_number'], 0]
audiofile['date'] = meta_tags['release_date']
audiofile['originaldate'] = meta_tags['release_date']
audiofile['media'] = meta_tags['type']
audiofile['author'] = meta_tags['artists'][0]['name']
audiofile['lyricist'] = meta_tags['artists'][0]['name']
audiofile['arranger'] = meta_tags['artists'][0]['name']
audiofile['performer'] = meta_tags['artists'][0]['name']
audiofile['website'] = meta_tags['external_urls']['spotify']
audiofile['length'] = str(meta_tags['duration_ms'] / 1000.0)
class EmbedMetadata:
def __init__(self, music_file, meta_tags):
self.music_file = music_file
self.meta_tags = meta_tags
if meta_tags['publisher']:
audiofile['encodedby'] = meta_tags['publisher']
if meta_tags['genre']:
audiofile['genre'] = meta_tags['genre']
if meta_tags['copyright']:
audiofile['copyright'] = meta_tags['copyright']
if meta_tags['external_ids']['isrc']:
audiofile['isrc'] = meta_tags['external_ids']['isrc']
audiofile.save(v2_version=3)
def as_mp3(self):
""" Embed metadata to MP3 files. """
music_file = self.music_file
meta_tags = self.meta_tags
# EasyID3 is fun to use ;)
# For supported easyid3 tags:
# https://github.com/quodlibet/mutagen/blob/master/mutagen/easyid3.py
# Check out somewhere at end of above linked file
audiofile = EasyID3(music_file)
audiofile['artist'] = meta_tags['artists'][0]['name']
audiofile['albumartist'] = meta_tags['artists'][0]['name']
audiofile['album'] = meta_tags['album']['name']
audiofile['title'] = meta_tags['name']
audiofile['tracknumber'] = [meta_tags['track_number'],
meta_tags['total_tracks']]
audiofile['discnumber'] = [meta_tags['disc_number'], 0]
audiofile['date'] = meta_tags['release_date']
audiofile['originaldate'] = meta_tags['release_date']
audiofile['media'] = meta_tags['type']
audiofile['author'] = meta_tags['artists'][0]['name']
audiofile['lyricist'] = meta_tags['artists'][0]['name']
audiofile['arranger'] = meta_tags['artists'][0]['name']
audiofile['performer'] = meta_tags['artists'][0]['name']
audiofile['website'] = meta_tags['external_urls']['spotify']
audiofile['length'] = str(meta_tags['duration_ms'] / 1000.0)
if meta_tags['publisher']:
audiofile['encodedby'] = meta_tags['publisher']
if meta_tags['genre']:
audiofile['genre'] = meta_tags['genre']
if meta_tags['copyright']:
audiofile['copyright'] = meta_tags['copyright']
if meta_tags['external_ids']['isrc']:
audiofile['isrc'] = meta_tags['external_ids']['isrc']
audiofile.save(v2_version=3)
# For supported id3 tags:
# https://github.com/quodlibet/mutagen/blob/master/mutagen/id3/_frames.py
# Each class represents an id3 tag
audiofile = ID3(music_file)
print(meta_tags['release_date'].split('-')[0])
year, *_ = meta_tags['release_date'].split('-')
audiofile['TORY'] = TORY(encoding=3, text=year)
audiofile['TYER'] = TYER(encoding=3, text=year)
audiofile['TPUB'] = TPUB(encoding=3, text=meta_tags['publisher'])
audiofile['COMM'] = COMM(encoding=3, text=meta_tags['external_urls']['spotify'])
if meta_tags['lyrics']:
audiofile['USLT'] = USLT(encoding=3, desc=u'Lyrics', text=meta_tags['lyrics'])
try:
albumart = urllib.request.urlopen(meta_tags['album']['images'][0]['url'])
audiofile['APIC'] = APIC(encoding=3, mime='image/jpeg', type=3,
desc=u'Cover', data=albumart.read())
albumart.close()
except IndexError:
pass
# For supported id3 tags:
# https://github.com/quodlibet/mutagen/blob/master/mutagen/id3/_frames.py
# Each class represents an id3 tag
audiofile = ID3(music_file)
year, *_ = meta_tags['release_date'].split('-')
audiofile['TORY'] = TORY(encoding=3, text=year)
audiofile['TYER'] = TYER(encoding=3, text=year)
audiofile['TPUB'] = TPUB(encoding=3, text=meta_tags['publisher'])
audiofile['COMM'] = COMM(encoding=3, text=meta_tags['external_urls']['spotify'])
if meta_tags['lyrics']:
audiofile['USLT'] = USLT(encoding=3, desc=u'Lyrics', text=meta_tags['lyrics'])
try:
albumart = urllib.request.urlopen(meta_tags['album']['images'][0]['url'])
audiofile['APIC'] = APIC(encoding=3, mime='image/jpeg', type=3,
desc=u'Cover', data=albumart.read())
albumart.close()
except IndexError:
pass
audiofile.save(v2_version=3)
return True
audiofile.save(v2_version=3)
return True
def as_m4a(self):
""" Embed metadata to M4A files. """
music_file = self.music_file
meta_tags = self.meta_tags
# Apple has specific tags - see mutagen docs -
# http://mutagen.readthedocs.io/en/latest/api/mp4.html
tags = { 'album' : '\xa9alb',
'artist' : '\xa9ART',
'date' : '\xa9day',
'title' : '\xa9nam',
'year' : '\xa9day',
'originaldate' : 'purd',
'comment' : '\xa9cmt',
'group' : '\xa9grp',
'writer' : '\xa9wrt',
'genre' : '\xa9gen',
'tracknumber' : 'trkn',
'albumartist' : 'aART',
'disknumber' : 'disk',
'cpil' : 'cpil',
'albumart' : 'covr',
'copyright' : 'cprt',
'tempo' : 'tmpo',
'lyrics' : '\xa9lyr' }
def embed_m4a(music_file, meta_tags):
""" Embed metadata to M4A files. """
# Apple has specific tags - see mutagen docs -
# http://mutagen.readthedocs.io/en/latest/api/mp4.html
tags = { 'album' : '\xa9alb',
'artist' : '\xa9ART',
'date' : '\xa9day',
'title' : '\xa9nam',
'year' : '\xa9day',
'originaldate' : 'purd',
'comment' : '\xa9cmt',
'group' : '\xa9grp',
'writer' : '\xa9wrt',
'genre' : '\xa9gen',
'tracknumber' : 'trkn',
'albumartist' : 'aART',
'disknumber' : 'disk',
'cpil' : 'cpil',
'albumart' : 'covr',
'copyright' : 'cprt',
'tempo' : 'tmpo',
'lyrics' : '\xa9lyr' }
audiofile = MP4(music_file)
audiofile[tags['artist']] = meta_tags['artists'][0]['name']
audiofile[tags['albumartist']] = meta_tags['artists'][0]['name']
audiofile[tags['album']] = meta_tags['album']['name']
audiofile[tags['title']] = meta_tags['name']
audiofile[tags['tracknumber']] = [(meta_tags['track_number'],
meta_tags['total_tracks'])]
audiofile[tags['disknumber']] = [(meta_tags['disc_number'], 0)]
audiofile[tags['date']] = meta_tags['release_date']
year, *_ = meta_tags['release_date'].split('-')
audiofile[tags['year']] = year
audiofile[tags['originaldate']] = meta_tags['release_date']
audiofile[tags['comment']] = meta_tags['external_urls']['spotify']
if meta_tags['genre']:
audiofile[tags['genre']] = meta_tags['genre']
if meta_tags['copyright']:
audiofile[tags['copyright']] = meta_tags['copyright']
if meta_tags['lyrics']:
audiofile[tags['lyrics']] = meta_tags['lyrics']
try:
albumart = urllib.request.urlopen(meta_tags['album']['images'][0]['url'])
audiofile[tags['albumart']] = [MP4Cover(
albumart.read(), imageformat=MP4Cover.FORMAT_JPEG)]
albumart.close()
except IndexError:
pass
audiofile = MP4(music_file)
audiofile[tags['artist']] = meta_tags['artists'][0]['name']
audiofile[tags['albumartist']] = meta_tags['artists'][0]['name']
audiofile[tags['album']] = meta_tags['album']['name']
audiofile[tags['title']] = meta_tags['name']
audiofile[tags['tracknumber']] = [(meta_tags['track_number'],
meta_tags['total_tracks'])]
audiofile[tags['disknumber']] = [(meta_tags['disc_number'], 0)]
audiofile[tags['date']] = meta_tags['release_date']
year, *_ = meta_tags['release_date'].split('-')
audiofile[tags['year']] = year
audiofile[tags['originaldate']] = meta_tags['release_date']
audiofile[tags['comment']] = meta_tags['external_urls']['spotify']
if meta_tags['genre']:
audiofile[tags['genre']] = meta_tags['genre']
if meta_tags['copyright']:
audiofile[tags['copyright']] = meta_tags['copyright']
if meta_tags['lyrics']:
audiofile[tags['lyrics']] = meta_tags['lyrics']
try:
albumart = urllib.request.urlopen(meta_tags['album']['images'][0]['url'])
audiofile[tags['albumart']] = [MP4Cover(
albumart.read(), imageformat=MP4Cover.FORMAT_JPEG)]
albumart.close()
except IndexError:
pass
audiofile.save()
return True
audiofile.save()
return True

151
core/spotify_tools.py Normal file
View File

@@ -0,0 +1,151 @@
import spotipy
import spotipy.oauth2 as oauth2
import lyricwikia
from titlecase import titlecase
from core import internals
from core.const import log
import pprint
def generate_token():
""" Generate the token. Please respect these credentials :) """
credentials = oauth2.SpotifyClientCredentials(
client_id='4fe3fecfe5334023a1472516cc99d805',
client_secret='0f02b7c483c04257984695007a4a8d5c')
token = credentials.get_access_token()
return token
token = generate_token()
spotify = spotipy.Spotify(auth=token)
def generate_metadata(raw_song):
""" Fetch a song's metadata from Spotify. """
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:
return None
artist = spotify.artist(meta_tags['artists'][0]['id'])
album = spotify.album(meta_tags['album']['id'])
try:
meta_tags[u'genre'] = titlecase(artist['genres'][0])
except IndexError:
meta_tags[u'genre'] = None
try:
meta_tags[u'copyright'] = album['copyrights'][0]['text']
except IndexError:
meta_tags[u'copyright'] = None
try:
meta_tags[u'external_ids'][u'isrc']
except KeyError:
meta_tags[u'external_ids'][u'isrc'] = None
meta_tags[u'release_date'] = album['release_date']
meta_tags[u'publisher'] = album['label']
meta_tags[u'total_tracks'] = album['tracks']['total']
log.debug('Fetching lyrics')
try:
meta_tags['lyrics'] = lyricwikia.get_lyrics(
meta_tags['artists'][0]['name'],
meta_tags['name'])
except lyricwikia.LyricsNotFound:
meta_tags['lyrics'] = None
# remove unused clutter when debug meta_tags
del meta_tags['available_markets']
del meta_tags['album']['available_markets']
log.debug(pprint.pformat(meta_tags))
return meta_tags
def feed_playlist(username):
""" Fetch user playlists when using the -u option. """
playlists = spotify.user_playlists(username)
links = []
check = 1
while True:
for playlist in playlists['items']:
# 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:
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']:
playlists = spotify.next(playlists)
else:
break
playlist = internals.input_link(links)
write_playlist(playlist['owner']['id'], playlist['id'])
def write_tracks(text_file, tracks):
with open(text_file, 'a') as file_out:
while True:
for item in tracks['items']:
if 'track' in item:
track = item['track']
else:
track = item
try:
track_url = track['external_urls']['spotify']
file_out.write(track_url + '\n')
log.debug(track_url)
except KeyError:
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
if tracks['next']:
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)

139
core/youtube_tools.py Normal file
View File

@@ -0,0 +1,139 @@
import pafy
from core import internals
from core import const
import os
import pprint
log = const.log
def go_pafy(raw_song, meta_tags=None):
""" Parse track from YouTube. """
if internals.is_youtube(raw_song):
track_info = pafy.new(raw_song)
else:
track_url = generate_youtube_url(raw_song, meta_tags)
if track_url:
track_info = pafy.new(track_url)
else:
track_info = None
return track_info
def get_youtube_title(content, number=None):
""" Get the YouTube video's title. """
title = content.title
if number:
return '{0}. {1}'.format(number, title)
else:
return title
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:])
else:
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)
log.debug('Saving to: ' + filepath)
link.download(filepath=filepath)
return True
else:
return False
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
query = { 'part' : 'snippet',
'maxResults' : 50,
'type' : 'video' }
if const.args.music_videos_only:
query['videoCategoryId'] = '10'
if not meta_tags:
song = raw_song
query['q'] = song
else:
song = internals.generate_songname(meta_tags)
query['q'] = song
log.debug('query: {0}'.format(query))
data = pafy.call_gdata('search', query)
query_results = {'part': 'contentDetails,snippet,statistics',
'maxResults': 50,
'id': ','.join(i['id']['videoId'] for i in data['items'])}
log.debug('query_results: {0}'.format(query_results))
vdata = pafy.call_gdata('videos', query_results)
videos = []
for x in vdata['items']:
duration_s = pafy.playlist.parseISO8591(x['contentDetails']['duration'])
youtubedetails = {'link': x['id'], 'title': x['snippet']['title'],
'videotime':internals.videotime_from_seconds(duration_s),
'seconds': duration_s}
videos.append(youtubedetails)
if not meta_tags:
break
if not videos:
return None
if const.args.manual:
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):
log.info(u'{0}. {1} {2} {3}'.format(i+1, v['title'], v['videotime'],
"http://youtube.com/watch?v="+v['link']))
# let user select the song to download
result = internals.input_link(videos)
if not result:
return None
else:
if not meta_tags:
# 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
possible_videos_by_duration = list()
'''
start with a reasonable duration_tolerance, and increment duration_tolerance
until one of the Youtube results falls within the correct duration or
the duration_tolerance has reached the max_duration_tolerance
'''
while len(possible_videos_by_duration) == 0:
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:
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]
if result:
url = "http://youtube.com/watch?v=" + result['link']
else:
url = None
return url

402
spotdl.py
View File

@@ -1,303 +1,31 @@
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
from core import logger
from core import const
from core import handle
from core import metadata
from core import convert
from core import internals
from titlecase import titlecase
from core import spotify_tools
from core import youtube_tools
from slugify import slugify
import spotipy
import pafy
import lyricwikia
import urllib.request
import os
import sys
import time
import sys
import platform
import pprint
def generate_songname(tags):
""" Generate a string of the format '[artist] - [song]' for the given spotify song. """
raw_song = u'{0} - {1}'.format(tags['artists'][0]['name'], tags['name'])
return raw_song
def generate_metadata(raw_song):
""" Fetch a song's metadata from Spotify. """
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:
return None
artist = spotify.artist(meta_tags['artists'][0]['id'])
album = spotify.album(meta_tags['album']['id'])
try:
meta_tags[u'genre'] = titlecase(artist['genres'][0])
except IndexError:
meta_tags[u'genre'] = None
try:
meta_tags[u'copyright'] = album['copyrights'][0]['text']
except IndexError:
meta_tags[u'copyright'] = None
try:
meta_tags[u'external_ids'][u'isrc']
except KeyError:
meta_tags[u'external_ids'][u'isrc'] = None
meta_tags[u'release_date'] = album['release_date']
meta_tags[u'publisher'] = album['label']
meta_tags[u'total_tracks'] = album['tracks']['total']
log.debug('Fetching lyrics')
try:
meta_tags['lyrics'] = lyricwikia.get_lyrics(
meta_tags['artists'][0]['name'],
meta_tags['name'])
except lyricwikia.LyricsNotFound:
meta_tags['lyrics'] = None
# remove unused clutter when debug meta_tags
del meta_tags['available_markets']
del meta_tags['album']['available_markets']
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
query = {'part': 'snippet',
'maxResults': 50,
'type': 'video'}
if args.music_videos_only:
query['videoCategoryId'] = '10'
if meta_tags is None:
song = raw_song
query['q'] = song
else:
song = generate_songname(meta_tags)
query['q'] = song
log.debug('query: {0}'.format(query))
data = pafy.call_gdata('search', query)
query_results = {'part': 'contentDetails,snippet,statistics',
'maxResults': 50,
'id': ','.join(i['id']['videoId'] for i in data['items'])}
log.debug('query_results: {0}'.format(query_results))
vdata = pafy.call_gdata('videos', query_results)
videos = []
for x in vdata['items']:
duration_s = pafy.playlist.parseISO8591(x['contentDetails']['duration'])
youtubedetails = {'link': x['id'], 'title': x['snippet']['title'],
'videotime':internals.videotime_from_seconds(duration_s),
'seconds': duration_s}
videos.append(youtubedetails)
if meta_tags is None:
break
if not videos:
return None
if args.manual:
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):
log.info(u'{0}. {1} {2} {3}'.format(i+1, v['title'], v['videotime'],
"http://youtube.com/watch?v="+v['link']))
# let user select the song to download
result = internals.input_link(videos)
if result is None:
return None
else:
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
possible_videos_by_duration = list()
'''
start with a reasonable duration_tolerance, and increment duration_tolerance
until one of the Youtube results falls within the correct duration or
the duration_tolerance has reached the max_duration_tolerance
'''
while len(possible_videos_by_duration) == 0:
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:
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]
if result:
url = "http://youtube.com/watch?v=" + result['link']
else:
url = None
return url
def go_pafy(raw_song, meta_tags=None):
""" Parse track from YouTube. """
if internals.is_youtube(raw_song):
track_info = pafy.new(raw_song)
else:
track_url = generate_youtube_url(raw_song, meta_tags)
if track_url is None:
track_info = None
else:
track_info = pafy.new(track_url)
return track_info
def get_youtube_title(content, number=None):
""" Get the YouTube video's title. """
title = content.title
if number is None:
return title
else:
return '{0}. {1}'.format(number, title)
def feed_playlist(username):
""" Fetch user playlists when using the -u option. """
playlists = spotify.user_playlists(username)
links = []
check = 1
while True:
for playlist in playlists['items']:
# 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:
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']:
playlists = spotify.next(playlists)
else:
break
playlist = internals.input_link(links)
write_playlist(playlist['owner']['id'], playlist['id'])
def write_tracks(text_file, tracks):
with open(text_file, 'a') as file_out:
while True:
for item in tracks['items']:
if 'track' in item:
track = item['track']
else:
track = item
try:
track_url = track['external_urls']['spotify']
file_out.write(track_url + '\n')
log.debug(track_url)
except KeyError:
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
if tracks['next']:
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 download_song(file_name, content):
""" Download the audio file from YouTube. """
if args.input_ext in (".webm", ".m4a"):
link = content.getbestaudio(preftype=args.input_ext[1:])
else:
return False
if link is None:
return False
else:
log.debug('Downloading from URL: ' + link.url)
filepath = '{0}{1}'.format(os.path.join(args.folder, file_name),
args.input_ext)
log.debug('Saving to: ' + filepath)
link.download(filepath=filepath)
return True
def check_exists(music_file, raw_song, meta_tags):
""" 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)
songs = os.listdir(const.args.folder)
for song in songs:
if song.endswith('.temp'):
os.remove(os.path.join(args.folder, song))
os.remove(os.path.join(const.args.folder, song))
continue
# check if any song with similar name is already present in the given folder
file_name = internals.sanitize_title(music_file)
@@ -306,29 +34,29 @@ def check_exists(music_file, raw_song, meta_tags):
if internals.is_spotify(raw_song):
# check if the already downloaded song has correct metadata
# if not, remove it and download again without prompt
already_tagged = metadata.compare(os.path.join(args.folder, song),
already_tagged = metadata.compare(os.path.join(const.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))
os.remove(os.path.join(const.args.folder, song))
return False
log.warning('"{}" already exists'.format(song))
if args.overwrite == 'prompt':
if const.args.overwrite == 'prompt':
log.info('"{}" has already been downloaded. '
'Re-download? (y/N): '.format(song))
prompt = input('> ')
if prompt.lower() == 'y':
os.remove(os.path.join(args.folder, song))
os.remove(os.path.join(const.args.folder, song))
return False
else:
return True
elif args.overwrite == 'force':
os.remove(os.path.join(args.folder, song))
elif const.args.overwrite == 'force':
os.remove(os.path.join(const.args.folder, song))
log.info('Overwriting "{}"'.format(song))
return False
elif args.overwrite == 'skip':
elif const.args.overwrite == 'skip':
log.info('Skipping "{}"'.format(song))
return True
return False
@@ -354,9 +82,8 @@ def grab_list(text_file):
except spotipy.client.SpotifyException:
# refresh token when it expires
log.debug('Token expired, generating new one and authorizing')
new_token = internals.generate_token()
global spotify
spotify = spotipy.Spotify(auth=new_token)
new_token = spotify_tools.generate_token()
spotify_tools.spotify = spotipy.Spotify(auth=new_token)
grab_single(raw_song, number=number)
# detect network problems
except (urllib.request.URLError, TypeError, IOError):
@@ -392,37 +119,28 @@ def grab_playlist(playlist):
sys.exit(10)
playlist_id = splits[-1]
try:
write_playlist(username, playlist_id)
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)
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)
def grab_single(raw_song, number=None):
""" Logic behind downloading a song. """
if internals.is_youtube(raw_song):
log.debug('Input song is a YouTube URL')
content = go_pafy(raw_song, meta_tags=None)
content = youtube_tools.go_pafy(raw_song, meta_tags=None)
raw_song = slugify(content.title).replace('-', ' ')
meta_tags = generate_metadata(raw_song)
meta_tags = spotify_tools.generate_metadata(raw_song)
else:
meta_tags = generate_metadata(raw_song)
content = go_pafy(raw_song, meta_tags)
meta_tags = spotify_tools.generate_metadata(raw_song)
content = youtube_tools.go_pafy(raw_song, meta_tags)
if const.args.download_only_metadata:
if meta_tags is None:
log.info('Found No metadata. Skipping the download')
return
if args.download_only_metadata:
if meta_tags is None:
@@ -435,41 +153,45 @@ def grab_single(raw_song, number=None):
# "[number]. [artist] - [song]" if downloading from list
# otherwise "[artist] - [song]"
youtube_title = get_youtube_title(content, number)
youtube_title = youtube_tools.get_youtube_title(content, number)
log.info('{} ({})'.format(youtube_title, content.watchv_url))
# generate file name of the song to download
songname = content.title
if meta_tags is not None:
refined_songname = generate_songname(meta_tags)
if meta_tags:
refined_songname = internals.generate_songname(meta_tags)
log.debug('Refining songname from "{0}" to "{1}"'.format(songname, refined_songname))
if not refined_songname == ' - ':
songname = refined_songname
if args.dry_run:
if const.args.dry_run:
return
file_name = internals.sanitize_title(songname)
if not check_exists(file_name, raw_song, meta_tags):
if download_song(file_name, content):
input_song = file_name + args.input_ext
output_song = file_name + args.output_ext
if youtube_tools.download_song(file_name, content):
input_song = file_name + const.args.input_ext
output_song = file_name + const.args.output_ext
print('')
try:
convert.song(input_song, output_song, args.folder,
avconv=args.avconv)
convert.song(input_song, output_song, const.args.folder,
avconv=const.args.avconv)
except FileNotFoundError:
encoder = 'avconv' if args.avconv else 'ffmpeg'
encoder = 'avconv' if const.args.avconv else 'ffmpeg'
log.warning('Could not find {0}, skipping conversion'.format(encoder))
args.output_ext = args.input_ext
output_song = file_name + args.output_ext
const.args.output_ext = const.args.input_ext
output_song = file_name + const.args.output_ext
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)
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:
if metadata:
metadata.embed(os.path.join(const.args.folder, output_song), meta_tags)
else:
log.warning('Could not find metadata')
else:
log.error('No audio streams available')
@@ -477,31 +199,31 @@ def grab_single(raw_song, number=None):
# 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 = internals.generate_token()
token = spotify_tools.generate_token()
spotify = spotipy.Spotify(auth=token)
if __name__ == '__main__':
args = internals.get_arguments()
internals.filter_path(args.folder)
const.args = handle.get_arguments()
internals.filter_path(const.args.folder)
logger.log = logger.logzero.setup_logger(formatter=logger.formatter,
level=args.log_level)
log = logger.log
const.log = const.logzero.setup_logger(formatter=const.formatter,
level=const.args.log_level)
log = const.log
log.debug('Python version: {}'.format(sys.version))
log.debug('Platform: {}'.format(platform.platform()))
log.debug(pprint.pformat(args.__dict__))
log.debug(pprint.pformat(const.args.__dict__))
try:
if args.song:
grab_single(raw_song=args.song)
elif args.list:
grab_list(text_file=args.list)
elif args.playlist:
grab_playlist(playlist=args.playlist)
elif args.album:
grab_album(album=args.album)
elif args.username:
feed_playlist(username=args.username)
if const.args.song:
grab_single(raw_song=const.args.song)
elif const.args.list:
grab_list(text_file=const.args.list)
elif const.args.playlist:
grab_playlist(playlist=const.args.playlist)
elif const.args.album:
spotify_tools.grab_album(album=const.args.album)
elif const.args.username:
spotify_tools.feed_playlist(username=const.args.username)
# actually we don't necessarily need this, but yeah...
# explicit is better than implicit!

View File

@@ -1,31 +1,26 @@
# -*- coding: UTF-8 -*-
from spotdl import logger
from spotdl import const
from spotdl import handle
import spotdl
import os
const.args = handle.get_arguments(to_group=False, raw_args='')
const.args.folder = 'test'
const.args.overwrite = 'skip'
const.args.log_level = handle.logging.DEBUG
spotdl.args = const.args
spotdl.log = const.logzero.setup_logger(formatter=const.formatter,
level=const.args.log_level)
raw_song = "Tony's Videos VERY SHORT VIDEO 28.10.2016"
class TestArgs:
manual = False
input_ext = '.m4a'
output_ext = '.mp3'
folder = 'test'
log_level = logger.logging.DEBUG
overwrite = 'skip'
music_videos_only = False
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 = 'http://youtube.com/watch?v=qOOcy2-tmbk'
url = spotdl.generate_youtube_url(raw_song, meta_tags=None)
url = spotdl.youtube_tools.generate_youtube_url(raw_song, meta_tags=None)
assert url == expect_url
@@ -33,8 +28,8 @@ def test_youtube_title():
global content
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)
content = spotdl.youtube_tools.go_pafy(raw_song, meta_tags=None)
title = spotdl.youtube_tools.get_youtube_title(content)
assert title == expect_title
def test_check_exists():
@@ -49,7 +44,7 @@ def test_download():
expect_download = True
# prerequisites for determining filename
file_name = spotdl.internals.sanitize_title(title)
download = spotdl.download_song(file_name, content)
download = spotdl.youtube_tools.download_song(file_name, content)
assert download == expect_download
@@ -60,19 +55,23 @@ def test_convert():
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)
input_song = file_name + const.args.input_ext
output_song = file_name + const.args.output_ext
convert = spotdl.convert.song(input_song, output_song, const.args.folder)
assert convert == expect_convert
def test_metadata():
expect_metadata = None
# prerequisites for determining filename
meta_tags = spotdl.generate_metadata(raw_song)
meta_tags = spotdl.spotify_tools.generate_metadata(raw_song)
file_name = spotdl.internals.sanitize_title(title)
metadata_output = spotdl.metadata.embed(os.path.join(spotdl.args.folder, output_song), meta_tags)
metadata_input = spotdl.metadata.embed(os.path.join(spotdl.args.folder, input_song), meta_tags)
if meta_tags:
metadata_output = spotdl.metadata.embed(os.path.join(const.args.folder, output_song), meta_tags)
metadata_input = spotdl.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)
@@ -80,7 +79,7 @@ def test_check_exists2():
expect_check = True
# prerequisites for determining filename
file_name = spotdl.internals.sanitize_title(title)
os.remove(os.path.join(spotdl.args.folder, input_song))
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(spotdl.args.folder, output_song))
os.remove(os.path.join(const.args.folder, output_song))
assert check == expect_check

View File

@@ -1,54 +1,48 @@
# -*- coding: UTF-8 -*-
from spotdl import logger
from spotdl import const
from spotdl import handle
import spotdl
import os
const.args = handle.get_arguments(to_group=False, raw_args='')
const.args.folder = 'test'
const.args.overwrite = 'skip'
const.args.log_level = handle.logging.DEBUG
spotdl.args = const.args
spotdl.log = const.logzero.setup_logger(formatter=const.formatter,
level=const.args.log_level)
raw_song = 'http://open.spotify.com/track/0JlS7BXXD07hRmevDnbPDU'
class TestArgs:
manual = False
input_ext = '.m4a'
output_ext = '.mp3'
folder = 'test'
log_level = 'DEBUG'
overwrite = 'skip'
music_videos_only = False
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():
expect_title = 'David André Østby - Intro'
global meta_tags
meta_tags = spotdl.generate_metadata(raw_song)
title = spotdl.generate_songname(meta_tags)
meta_tags = spotdl.spotify_tools.generate_metadata(raw_song)
title = spotdl.internals.generate_songname(meta_tags)
assert title == expect_title
def youtube_url():
expect_url = 'youtube.com/watch?v=rg1wfcty0BA'
url = spotdl.generate_youtube_url(raw_song, meta_tags)
def test_youtube_url():
expect_url = 'http://youtube.com/watch?v=rg1wfcty0BA'
url = spotdl.youtube_tools.generate_youtube_url(raw_song, meta_tags)
assert url == expect_url
def youtube_title():
def test_youtube_title():
expect_title = 'Intro - David André Østby'
content = spotdl.go_pafy(raw_song, meta_tags)
title = spotdl.get_youtube_title(content)
content = spotdl.youtube_tools.go_pafy(raw_song, meta_tags)
title = spotdl.youtube_tools.get_youtube_title(content)
assert title == expect_title
def test_check_exists():
expect_check = False
# prerequisites for determining filename
songname = spotdl.generate_songname(meta_tags)
songname = spotdl.internals.generate_songname(meta_tags)
global file_name
file_name = spotdl.internals.sanitize_title(songname)
check = spotdl.check_exists(file_name, raw_song, meta_tags)
@@ -58,8 +52,8 @@ def test_check_exists():
def test_download():
expect_download = True
# prerequisites for determining filename
content = spotdl.go_pafy(raw_song, meta_tags)
download = spotdl.download_song(file_name, content)
content = spotdl.youtube_tools.go_pafy(raw_song, meta_tags)
download = spotdl.youtube_tools.download_song(file_name, content)
assert download == expect_download