Przeglądaj źródła

0.0.3 Release. Commented out 0.0.4 adds

Miek Stagl 5 lat temu
rodzic
commit
da157d2a65

BIN
.CHANGELOG.swp


BIN
.config.ini.swp


BIN
.ytsearch.swp


+ 9 - 0
CHANGELOG

@@ -13,7 +13,16 @@ Version 0.0.2 - 5/12/2020
 	- NOTE- only creates album name folder if it queries Discogs.com
 
 Version 0.0.3
+  - Obeys config.ini DefaultStoragePath arguments
+  - Added generatelink routine
+    - This will attempt to generate a link from YouTubeSearch over *X* times
+    - Throws a warning if it cannot create a link
+  - modified YoutubeSearch library to add publisher info
+    - This will be helpful for narrowing searchs based on publisher in 0.0.4
+
+Version 0.0.4
   - Attempts to capture first X results from YoutubeSearch
     - This script will then parse the results for the best match
     - Remove links for things like (Official Video) or Cover
       - Need to ensure words like "Cover" are not in the actual song title!
+

+ 2 - 1
config.ini

@@ -1,6 +1,7 @@
 [DEFAULT]
 Musicfile = list.txt
-DefaultStoragePath = $HOME/Music
+## Default Storage Path should point to a folder in teh user's Home directory
+DefaultStoragePath = Music
 Retries = 3
 Download = True
 Key = AdmufSbEgfzbRjaxHBMvyvdKqBxhNYvCzvXHXWQK

+ 47 - 0
youtube_search/__init__.py

@@ -0,0 +1,47 @@
+import requests
+from bs4 import BeautifulSoup
+import urllib.parse
+import json
+
+
+class YoutubeSearch:
+
+    def __init__(self, search_terms: str, max_results=None):
+        self.search_terms = search_terms
+        self.max_results = max_results
+        self.videos = self.search()
+
+    def search(self):
+        encoded_search = urllib.parse.quote(self.search_terms)
+        BASE_URL = "https://youtube.com"
+        url = f"{BASE_URL}/results?search_query={encoded_search}&pbj=1"
+        response = BeautifulSoup(requests.get(url).text, "html.parser")
+        results = self.parse_html(response)
+        if self.max_results is not None and len(results) > self.max_results:
+            return results[:self.max_results]
+        return results
+
+    def parse_html(self, soup):
+        results = []
+        for video_div in soup.select("div.yt-lockup-content"):
+            video = video_div.select_one(".yt-uix-tile-link")
+            publisher = video_div.select_one(".yt-lockup-byline")	##
+            if video is not None:
+                if video["href"].startswith("/watch?v="):
+                    channel = video_div.select_one("a.spf-link")
+                    video_info = {
+                        "title": video["title"],
+                        "link": video["href"],
+                        "id": video["href"][video["href"].index("=")+1:],
+                        "channel_name": channel.text,
+                        "channel_link": channel["href"],
+                        "publisher": publisher.text			##
+                    }
+                    results.append(video_info)
+        return results
+
+    def to_dict(self):
+        return self.videos
+
+    def to_json(self):
+        return json.dumps({"videos": self.videos})

BIN
youtube_search/__pycache__/__init__.cpython-38.pyc


+ 79 - 23
ytsearch

@@ -6,6 +6,7 @@ import configparser
 import sys, getopt
 import requests, json
 import random, string
+import logging
 
 ##Import list of songs, artists from list.txt
 ##parse this file into a list of dictionaries
@@ -32,7 +33,7 @@ import random, string
 ##
 ## - Download the first X results from YoutubeSearch and pick best result
 ##
-## - increase timeout in YoutubeSearch
+## - Allow interrupt to stop script (CTRL + C)
 
 ##Vars
 if os.path.exists('config.ini'):
@@ -47,19 +48,23 @@ VERSION="0.0.3"
 DOWNLOAD=config['DEFAULT'].getboolean('Download')	#Download True/False
 MUSICFILE=config['DEFAULT']['Musicfile']		#location of text file containing songs
 RETRIES=config['DEFAULT'].getint('Retries')		#Number of retries to search for songs
+LOGPATH=config['DEFAULT']['LogLocation']
 ITERATOR=0						#Number or current tries
+STORAGEPATH=config['DEFAULT']['DefaultStoragePath']
 VERBOSITY=config['DEFAULT'].getint('Verbosity')
 KEY=config['DEFAULT']['Key']
 DISCOG=""
 DESTFOLDER=""
 ALBUM=""
 ARTIST=""
+MASTER=True
 TESTFOLDER=config['DEFAULT'].getboolean('TestFolder')
 HELP=	"Takes list.txt or Discogs.com Master/Release number \n" \
 	"And downloads albums by stripping the audio from Yuotube videos. \n" \
 	"USAGE: ytsearch [-flag] [Discog Num] \n" \
 	"	-h 		This help file \n" \
 	"	-d --discog	set Discog.com Release or Master number \n" \
+	"	-r --release	search for Discog.com RELEASE instead of MASTER [default MASTER] \n" \
 	"	-D --download	override config.ini and set Download=True \n" \
 	"	-f --file	Allows quick download of a single Youtube link"
 #	"	-D --download	override config.ini and set Download=True"
@@ -78,11 +83,13 @@ def msg(message, level):
 	tlevel = {-1: '', 1: "ERROR", 2: "WARN", 3: "INFO"}
 	if level <= VERBOSITY:
 		print(tlevel.get(level), message)
+	
+	## Add logging ##T
 
 def arguments(argv):
 	msg("Starting arguments", 3)
 	try:
-		opts, args = getopt.getopt(argv, "hvDf:d:", ["discog", "help", "download", "version", "file"])
+		opts, args = getopt.getopt(argv, "hvrDf:d:", ["discog", "release", "help", "download", "version", "file"])
 		for opt, arg in opts:
 			if opt in ('-h', '--help'):
 				print(HELP)
@@ -98,6 +105,10 @@ def arguments(argv):
 			elif opt in ("-v", "--version"):
 				msg("Version: " + VERSION, -1)
 				sys.exit()
+			elif opt in ("-r", "--release"):
+				global MASTER
+				MASTER=False
+				msg("searching for Release, not Master at Discogs.com", 1)
 			elif opt in ("-f", "--file"):
 				msg("call singlesong with: " + arg, 3)
 				singlesong(arg)
@@ -182,8 +193,10 @@ def buildfolders(artist, album=""):
 #	msg("buildfolders local album is set to: " + album, 3)
 	global DESTFOLDER
 	DESTFOLDER = artist + "/" + album + "/"
+	home = os.path.expanduser('~')
 	if TESTFOLDER:
-		DESTFOLDER = randomizer() + "/" + DESTFOLDER
+		DESTFOLDER = os.path.join(home, randomizer(), DESTFOLDER)
+	DESTFOLDER = os.path.join(home, STORAGEPATH, DESTFOLDER)
 	try:
 		os.makedirs(DESTFOLDER)
 		msg("Folder " + DESTFOLDER + " created", 2)
@@ -210,29 +223,76 @@ def readlist(file):
 	f.close()
 	return music
 
-def searchlinks(links):
-	## Takes a list of dictionaries and parses the results
-	## Discards bad choices
-	## Returns a dictionary of one entry (best result)
-	for link in links:
-		print(link)
-		print("\n")
-	sys.exit()
+#def searchlinks(links, artist):
+#	## Takes a list of dictionaries and parses the results
+#	## Discards bad choices
+#	## Returns a dictionary of one entry (best result)
+#	## Good results include published by artist,
+#	## bad results include words live "live" or "Video"
+#	msg("Starting searchlinks", 3)
+#	list_badterms = ["live", "video", "sexy"]
+#
+#	### FIX RANKINGS! ##
+#
+#	for link in links:
+#		rating = 0
+#		for term in list_badterms:
+#			if term.lower() in link['title'].lower():
+##				print("Contains Term!")
+#				rating -= 1
+#		print(rating)
+#		if artist != "":
+#			if artist.lower() == link['publisher'].lower():
+##				print("Published by Artist!")
+#				rating += 10
+#		link["rating"] = rating
+#
+#	links.sort(reverse=True, key = lambda i: i['rating'])	## Sort links based on rating
+#	msg("Ending serachlinks", 3)
+#	return links[0]
+
+def generatelink(searchterm, max_results=10, tries=5):
+	## This will retry the link generation routine up to *tries* times and return results
+	msg("Starting generatelink for " + searchterm, 3)
+	counter = 0
+	while counter <= tries:
+	  try:
+	    ytresult = YoutubeSearch(searchterm, max_results).to_dict()
+	    if len(ytresult) > 0:
+	      msg("Link Generated!", 3)
+	      break
+	    else:
+	      raise IndexError("Index Empty")
+	  except:
+	    msg("Unable to generate link on try " + str(counter), 3)
+	    counter += 1
+	    if counter >= tries:
+	      msg("Could Not Generate link for " + searchterm, 2)
+	      raise IndexError("Could not Generate Link")
+#	  finally:
+#	    msg("Ending generatelink on try " + str(counter), 3)
+
+#	searchlinks(ytresult)
+	return ytresult
 
 def parselist(musiclist):
 	msg("Starting parselist", 3)
 	global ITERATOR
-	if ITERATOR == 0 and DOWNLOAD:
+	if ITERATOR == 0 and DOWNLOAD: ## <- Original Line
+#	if ITERATOR == 0: ## <- Used only for testing buildfolders
 		buildfolders(musiclist[0]['Artist'])
 	ITERATOR+=1
 	for song in musiclist:
 #	  searchterm = song['Title'] + " " + song['Artist'] + ' lyrics HD'
 	  searchterm = song['Title'] + " " + song['Artist']
 	  dictlink={}
+
 	  try:
-	    ytresult = YoutubeSearch(searchterm, max_results=5).to_dict() ##increase timeout!!
-#	    searchlinks(ytresult)
+##	    ytresult = generatelink(searchterm)
+##	    bestlink = searchlinks(ytresult, song['Artist'])
+	    ytresult = generatelink(searchterm)
 	    link = 'https://youtube.com' + ytresult[0]['link']
+#	    link = 'https://youtube.com' + bestlink['link']
 	    logresults.append(song['Title'] + ", " + song['Artist'] + " Link Created")
 	    if DOWNLOAD:
               msg("Attempting to download " + song['Title'], 2)
@@ -240,7 +300,10 @@ def parselist(musiclist):
 	    else:
 	      print("Not downloading " + song['Title'] + ".  Change this in config.ini")
 	  except Exception as ex:
-	    print(ex)
+	    print(song['Title'], ex)
+
+#	  searchlinks(ytresult, song['Artist'])
+
 	if DOWNLOAD:
 		cleanup(MUSICFILE)
 
@@ -248,10 +311,6 @@ def downloadsong(link, song):
        msg("Starling Downloadsong for " + song['Title'], 3)
        msg("Downloadsong DESTFOLDER: " + DESTFOLDER, 3)
        try: 
-#####
-##Fix Destination Path
-##HOW DID I DO THIS BEFORE??
-#####
          os.system("youtube-dl --extract-audio --audio-format best --audio-quality 0 --output '''" + DESTFOLDER + "%(title)s.%(ext)s' --ignore-errors " + link)
          completed.append(song['songnum'])
          logresults.append(song['Title'] + ", " + song['Artist'] + " Audio downloaded")
@@ -297,14 +356,11 @@ def cleanup(file):
 
 if __name__ == "__main__":
 
-#	msg("Starting ytsearch...", -1)
-
 	arguments(sys.argv[1:])
 
 	if DISCOG != "":
 		msg("DISCOG found, fetch json", 3)
-#		print(fetchjson(DISCOG))
-		buildlist(fetchjson(DISCOG), True)
+		buildlist(fetchjson(DISCOG), MASTER)
 	readlist(MUSICFILE)
 	parselist(music)