diff --git a/.gitignore b/.gitignore index e4b8660..c7601f9 100755 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ __pycache__/ .cache/ Music/ *.txt +.python-version diff --git a/core/misc.py b/core/misc.py index 9d33063..6247196 100755 --- a/core/misc.py +++ b/core/misc.py @@ -118,3 +118,15 @@ def filter_path(path): def grace_quit(): print('\n\nExiting.') sys.exit() + +def get_sec(time_str): + v = time_str.split(':', 3) + v.reverse() + sec = 0 + if len(v) > 0: #seconds + sec += int(v[0]) + if len(v) > 1: # minutes + sec += int(v[1]) * 60 + if len(v) > 2: # hours + sec += int(v[2]) * 3600 + return sec diff --git a/spotdl.py b/spotdl.py index 08576fe..27ff3ee 100755 --- a/spotdl.py +++ b/spotdl.py @@ -13,10 +13,9 @@ import urllib.request import sys import os - -def generate_songname(raw_song): +def generate_songname(tags): """Generate a string of the format '[artist] - [song]' for the given song.""" - tags = generate_metadata(raw_song) + if tags is None: content = go_pafy(raw_song) raw_song = get_youtube_title(content) @@ -57,43 +56,41 @@ def generate_metadata(raw_song): def generate_youtube_url(raw_song): """Search for the song on YouTube and generate an URL to its video.""" - song = generate_songname(raw_song) + meta_tags = generate_metadata(raw_song) + song = generate_songname(meta_tags) search_url = misc.generate_search_url(song) item = urllib.request.urlopen(search_url).read() # item = unicode(item, 'utf-8') items_parse = BeautifulSoup(item, "html.parser") - check = 1 + + videos = [] + for x in items_parse.find_all('div', {'class': 'yt-lockup-dismissable yt-uix-tile'}): + # confirm the video result is not an advertisement + if x.find('channel') is None and x.find('googleads') is None: + y = x.find('div', class_='yt-lockup-content') + link = y.find('a')['href'] + title = y.find('a')['title'] + videotime = x.find('span', class_="video-time").get_text() + youtubedetails = {'link': link, 'title': title, 'videotime': videotime, 'seconds':misc.get_sec(videotime)} + videos.append(youtubedetails) + if args.manual: - links = [] print(song) print('') print('0. Skip downloading this song') # fetch all video links on first page on YouTube - for x in items_parse.find_all('h3', {'class': 'yt-lockup-title'}): - # confirm the video result is not an advertisement - if x.find('channel') is None and x.find('googleads') is None: - link = x.find('a')['href'] - links.append(link) - print(u'{0}. {1} {2}'.format(check, x.get_text(), "http://youtube.com"+link)) - check += 1 + 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('') # let user select the song to download - result = misc.input_link(links) + result = misc.input_link(videos) if result is None: return None else: - # get video link of the first YouTube result - result = items_parse.find_all( - attrs={'class': 'yt-uix-tile-link'})[0]['href'] + videos.sort(key=lambda x: abs(x['seconds'] - (int(meta_tags['duration_ms'])/1000))) + result = videos[0]; - # confirm the video result is not an advertisement - # otherwise keep iterating until it is not - while result.find('channel') > -1 or result.find('googleads') > -1: - result = items_parse.find_all( - attrs={'class': 'yt-uix-tile-link'})[check]['href'] - check += 1 - - full_link = u'youtube.com{0}'.format(result) + full_link = u'youtube.com{0}'.format(result['link']) return full_link @@ -299,7 +296,7 @@ def grab_single(raw_song, number=None): # generate file name of the song to download meta_tags = generate_metadata(raw_song) - songname = generate_songname(raw_song) + songname = generate_songname(meta_tags) file_name = misc.sanitize_title(songname) if not check_exists(file_name, raw_song, islist=islist): @@ -311,7 +308,6 @@ def grab_single(raw_song, number=None): avconv=args.avconv, verbose=args.verbose) if not args.input_ext == args.output_ext: os.remove(os.path.join(args.folder, input_song)) - meta_tags = generate_metadata(raw_song) if not args.no_metadata: metadata.embed(os.path.join(args.folder, output_song), meta_tags) diff --git a/test/test_single.py b/test/test_single.py index c015c0f..54e38e0 100644 --- a/test/test_single.py +++ b/test/test_single.py @@ -11,28 +11,30 @@ for x in os.listdir(spotdl.args.folder): def test_spotify_title(): expect_title = 'David André Østby - Intro' - title = spotdl.generate_songname(raw_song) + meta_tags = spotdl.generate_metadata(raw_song) + title = spotdl.generate_songname(meta_tags) assert title == expect_title -def test_youtube_url(): +def youtube_url(): expect_url = 'youtube.com/watch?v=rg1wfcty0BA' url = spotdl.generate_youtube_url(raw_song) assert url == expect_url -def test_youtube_title(): +def youtube_title(): expect_title = 'Intro - David André Østby' content = spotdl.go_pafy(raw_song) title = spotdl.get_youtube_title(content) + print(title) assert title == expect_title def test_check_exists(): expect_check = False # prerequisites for determining filename - content = spotdl.go_pafy(raw_song) - songname = spotdl.generate_songname(raw_song) + meta_tags = spotdl.generate_metadata(raw_song) + songname = spotdl.generate_songname(meta_tags) file_name = spotdl.misc.sanitize_title(songname) check = spotdl.check_exists(file_name, raw_song, islist=True) assert check == expect_check @@ -42,7 +44,8 @@ def test_download(): expect_download = True # prerequisites for determining filename content = spotdl.go_pafy(raw_song) - songname = spotdl.generate_songname(raw_song) + meta_tags = spotdl.generate_metadata(raw_song) + songname = spotdl.generate_songname(meta_tags) file_name = spotdl.misc.sanitize_title(songname) download = spotdl.download_song(file_name, content) assert download == expect_download @@ -52,8 +55,8 @@ def test_convert(): # exit code 0 = success expect_convert = 0 # prerequisites for determining filename - content = spotdl.go_pafy(raw_song) - songname = spotdl.generate_songname(raw_song) + meta_tags = spotdl.generate_metadata(raw_song) + songname = spotdl.generate_songname(meta_tags) file_name = spotdl.misc.sanitize_title(songname) input_song = file_name + spotdl.args.input_ext output_song = file_name + spotdl.args.output_ext @@ -64,8 +67,8 @@ def test_convert(): def test_metadata(): expect_metadata = True # prerequisites for determining filename - content = spotdl.go_pafy(raw_song) - songname = spotdl.generate_songname(raw_song) + meta_tags = spotdl.generate_metadata(raw_song) + songname = spotdl.generate_songname(meta_tags) meta_tags = spotdl.generate_metadata(raw_song) file_name = spotdl.misc.sanitize_title(songname) output_song = file_name + spotdl.args.output_ext @@ -78,8 +81,8 @@ def test_metadata(): def test_check_exists2(): expect_check = True # prerequisites for determining filename - content = spotdl.go_pafy(raw_song) - songname = spotdl.generate_songname(raw_song) + meta_tags = spotdl.generate_metadata(raw_song) + songname = spotdl.generate_songname(meta_tags) file_name = spotdl.misc.sanitize_title(songname) input_song = file_name + spotdl.args.input_ext os.remove(os.path.join(spotdl.args.folder, input_song))