mirror of
https://github.com/KevinMidboe/spotify-downloader.git
synced 2025-10-29 18:00:15 +00:00
Switch to youtube API (#191)
* Switch to youtube API * Fix test failures * Minor fix
This commit is contained in:
committed by
Ritiek Malhotra
parent
57055eb65d
commit
4d664956cd
@@ -137,6 +137,8 @@ optional arguments:
|
|||||||
prefered output extension .mp3 or .m4a (AAC) (default:
|
prefered output extension .mp3 or .m4a (AAC) (default:
|
||||||
.mp3)
|
.mp3)
|
||||||
-d, --dry-run Show only track title and YouTube URL (default: False)
|
-d, --dry-run Show only track title and YouTube URL (default: False)
|
||||||
|
-mo, --music-videos-only
|
||||||
|
Search only for music on Youtube (default: False)
|
||||||
-ll {INFO,WARNING,ERROR,DEBUG}, --log-level {INFO,WARNING,ERROR,DEBUG}
|
-ll {INFO,WARNING,ERROR,DEBUG}, --log-level {INFO,WARNING,ERROR,DEBUG}
|
||||||
set log verbosity (default: INFO)
|
set log verbosity (default: INFO)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -76,6 +76,10 @@ def get_arguments():
|
|||||||
'-d', '--dry-run', default=False,
|
'-d', '--dry-run', default=False,
|
||||||
help='Show only track title and YouTube URL',
|
help='Show only track title and YouTube URL',
|
||||||
action='store_true')
|
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(
|
parser.add_argument(
|
||||||
'-ll', '--log-level', default='INFO',
|
'-ll', '--log-level', default='INFO',
|
||||||
choices=_LOG_LEVELS_STR,
|
choices=_LOG_LEVELS_STR,
|
||||||
@@ -122,18 +126,6 @@ def generate_token():
|
|||||||
return token
|
return token
|
||||||
|
|
||||||
|
|
||||||
def generate_search_url(song, viewsort=False):
|
|
||||||
""" Generate YouTube search URL for the given song. """
|
|
||||||
# urllib.request.quote() encodes URL with special characters
|
|
||||||
song = quote(song)
|
|
||||||
if viewsort:
|
|
||||||
url = u"https://www.youtube.com/results?q={0}".format(song)
|
|
||||||
else:
|
|
||||||
url = u"https://www.youtube.com/results?sp=EgIQAQ%253D%253D&q={0}".format(song)
|
|
||||||
|
|
||||||
return url
|
|
||||||
|
|
||||||
|
|
||||||
def filter_path(path):
|
def filter_path(path):
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
os.makedirs(path)
|
os.makedirs(path)
|
||||||
@@ -142,14 +134,12 @@ def filter_path(path):
|
|||||||
os.remove(os.path.join(path, temp))
|
os.remove(os.path.join(path, temp))
|
||||||
|
|
||||||
|
|
||||||
def get_sec(time_str):
|
def videotime_from_seconds(time):
|
||||||
v = time_str.split(':', 3)
|
if time<60:
|
||||||
v.reverse()
|
return str(time)
|
||||||
sec = 0
|
if time<3600:
|
||||||
if len(v) > 0: # seconds
|
return '{}:{}'.format(str(time//60), str(time%60).zfill(2))
|
||||||
sec += int(v[0])
|
|
||||||
if len(v) > 1: # minutes
|
return '{}:{}:{}'.format(str(time//60),
|
||||||
sec += int(v[1]) * 60
|
str((time%60)//60).zfill(2),
|
||||||
if len(v) > 2: # hours
|
str((time%60)%60).zfill(2))
|
||||||
sec += int(v[2]) * 3600
|
|
||||||
return sec
|
|
||||||
|
|||||||
52
spotdl.py
52
spotdl.py
@@ -87,35 +87,35 @@ def generate_youtube_url(raw_song, meta_tags, tries_remaining=5):
|
|||||||
log.debug('No tries left. I quit.')
|
log.debug('No tries left. I quit.')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
query = {'part': 'snippet',
|
||||||
|
'maxResults': 50,
|
||||||
|
'type': 'video'}
|
||||||
|
|
||||||
|
if args.music_videos_only:
|
||||||
|
query['videoCategoryId'] = '10'
|
||||||
|
|
||||||
if meta_tags is None:
|
if meta_tags is None:
|
||||||
song = raw_song
|
song = raw_song
|
||||||
search_url = internals.generate_search_url(song, viewsort=False)
|
query['q'] = song
|
||||||
else:
|
else:
|
||||||
song = generate_songname(meta_tags)
|
song = generate_songname(meta_tags)
|
||||||
search_url = internals.generate_search_url(song, viewsort=True)
|
query['q'] = song
|
||||||
log.debug('Opening URL: {0}'.format(search_url))
|
log.debug('Query: {0}'.format(query))
|
||||||
|
|
||||||
item = urllib.request.urlopen(search_url).read()
|
data = pafy.call_gdata('search', query)
|
||||||
items_parse = BeautifulSoup(item, "html.parser")
|
query2 = {'part': 'contentDetails,snippet,statistics',
|
||||||
|
'maxResults': 50,
|
||||||
|
'id': ','.join(i['id']['videoId'] for i in data['items'])}
|
||||||
|
log.debug('Query2: {0}'.format(query2))
|
||||||
|
|
||||||
|
vdata = pafy.call_gdata('videos', query2)
|
||||||
|
|
||||||
videos = []
|
videos = []
|
||||||
for x in items_parse.find_all('div', {'class': 'yt-lockup-dismissable yt-uix-tile'}):
|
for x in vdata['items']:
|
||||||
|
duration_s = pafy.playlist.parseISO8591(x['contentDetails']['duration'])
|
||||||
if not is_video(x):
|
youtubedetails = {'link': x['id'], 'title': x['snippet']['title'],
|
||||||
continue
|
'videotime':internals.videotime_from_seconds(duration_s),
|
||||||
|
'seconds': duration_s}
|
||||||
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': internals.get_sec(videotime)}
|
|
||||||
videos.append(youtubedetails)
|
videos.append(youtubedetails)
|
||||||
if meta_tags is None:
|
if meta_tags is None:
|
||||||
break
|
break
|
||||||
@@ -131,7 +131,7 @@ def generate_youtube_url(raw_song, meta_tags, tries_remaining=5):
|
|||||||
# fetch all video links on first page on YouTube
|
# fetch all video links on first page on YouTube
|
||||||
for i, v in enumerate(videos):
|
for i, v in enumerate(videos):
|
||||||
log.info(u'{0}. {1} {2} {3}'.format(i+1, v['title'], v['videotime'],
|
log.info(u'{0}. {1} {2} {3}'.format(i+1, v['title'], v['videotime'],
|
||||||
"http://youtube.com"+v['link']))
|
"http://youtube.com/watch?v="+v['link']))
|
||||||
# let user select the song to download
|
# let user select the song to download
|
||||||
result = internals.input_link(videos)
|
result = internals.input_link(videos)
|
||||||
if result is None:
|
if result is None:
|
||||||
@@ -163,11 +163,11 @@ def generate_youtube_url(raw_song, meta_tags, tries_remaining=5):
|
|||||||
result = possible_videos_by_duration[0]
|
result = possible_videos_by_duration[0]
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
full_link = u'http://youtube.com{0}'.format(result['link'])
|
url = "http://youtube.com/watch?v=" + result['link']
|
||||||
else:
|
else:
|
||||||
full_link = None
|
url = None
|
||||||
|
|
||||||
return full_link
|
return url
|
||||||
|
|
||||||
|
|
||||||
def go_pafy(raw_song, meta_tags=None):
|
def go_pafy(raw_song, meta_tags=None):
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ class TestArgs:
|
|||||||
folder = 'test'
|
folder = 'test'
|
||||||
log_level = logger.logging.DEBUG
|
log_level = logger.logging.DEBUG
|
||||||
overwrite = 'skip'
|
overwrite = 'skip'
|
||||||
|
music_videos_only = False
|
||||||
|
|
||||||
test_args = TestArgs()
|
test_args = TestArgs()
|
||||||
setattr(spotdl, "args", test_args)
|
setattr(spotdl, "args", test_args)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ class TestArgs:
|
|||||||
folder = 'test'
|
folder = 'test'
|
||||||
log_level = 'DEBUG'
|
log_level = 'DEBUG'
|
||||||
overwrite = 'skip'
|
overwrite = 'skip'
|
||||||
|
music_videos_only = False
|
||||||
|
|
||||||
test_args = TestArgs()
|
test_args = TestArgs()
|
||||||
setattr(spotdl, "args", test_args)
|
setattr(spotdl, "args", test_args)
|
||||||
|
|||||||
Reference in New Issue
Block a user