Switch to youtube API (#191)

* Switch to youtube API

* Fix test failures

* Minor fix
This commit is contained in:
Vishnunarayan K I
2018-01-06 18:28:36 +05:30
committed by Ritiek Malhotra
parent 57055eb65d
commit 4d664956cd
5 changed files with 43 additions and 49 deletions

View File

@@ -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)
``` ```

View File

@@ -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

View File

@@ -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):

View File

@@ -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)

View File

@@ -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)