Downloadable Album Art

Rewickered album art routines to download from MusicBrainz.
Requires new musicbrainzngs pip3 module.
This commit is contained in:
markjfine 2021-04-22 20:58:55 -04:00 committed by GitHub
parent 560cf47efd
commit 02a72d312a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 52 additions and 58 deletions

View File

@ -15,6 +15,7 @@ The folowing programs are required to run NRSC5-DUI
* [Python Dateutil](https://pypi.org/project/python-dateutil) * [Python Dateutil](https://pypi.org/project/python-dateutil)
* [urllib3](https://pypi.org/project/urllib3) * [urllib3](https://pypi.org/project/urllib3)
* [pyOpenSSL](https://pypi.org/project/pyOpenSSL) * [pyOpenSSL](https://pypi.org/project/pyOpenSSL)
* [musicbrainzngs](https://pypi.org/project/musicbrainzngs)
* [nrsc5](https://github.com/theori-io/nrsc5) * [nrsc5](https://github.com/theori-io/nrsc5)
* [sox](https://github.com/chirlu/sox) * [sox](https://github.com/chirlu/sox)
@ -39,17 +40,17 @@ to include the path to nrsc5 when using scripts (like Apple Script) that seeming
You may first change some optional parameters of how nrsc5 works from the Settings tab in nrsc5-dui: You may first change some optional parameters of how nrsc5 works from the Settings tab in nrsc5-dui:
Set the gain to Auto, or optionally enter an RF gain in dB that has known to work well for some stations. Set the gain to Auto, or optionally enter an RF gain in dB that has known to work well for some stations.
Enter a PPM correction value if your RTL-SDR dongle has an offset. Enter a PPM correction value if your RTL-SDR dongle has an offset.
Enter the IP address that rtl_tcp is listening to and check the Enabled box if you are using a remote RTL-SDR. Enter the IP address that rtl_tcp is listening to and check the Enabled box if you are using a remote RTL-SDR.
Enter the number of the desired device if you have more than one RTL-SDR dongle. Enter the number of the desired device if you have more than one RTL-SDR dongle.
Check `Log to file` to enable writing debug information from nrsc5 to nrsc5.log. Check `Log to file` to enable writing debug information from nrsc5 to nrsc5.log.
`Temporarily Disabled:` Check `DL Album Art` to enable automated downloading of album art. Check `DL Album Art` to enable automated downloading of album art from MusicBrainz.
## Playing ## Playing
Enter the frequency in MHz of the station you want to play and either click the triangular Play button on the toolbar, or just hit return. When the receiver attains synchronization, the pilot in the lower left corner of the status bar will turn green. It will return to gray if synchronization is lost. If the device itself becomes 'lost', the pilot will turn red to indicate an error has occurred (this is the theory, though I've yet to see this status message happen in practice). The synchronization process may take about 10 seconds, and the station will begin to play. This depends upon signal strength and whether it's relatively free from adjacent interference. After a short while, the station name will appear to the right of the frequency, and the available streams will show on a row of buttons just beneath the frequency entry. Clicking one of these buttons will change to that particular stream. Note: No settings other than stream may be changed while the device is playing. Enter the frequency in MHz of the station you want to play and either click the triangular Play button on the toolbar, or just hit return. When the receiver attains synchronization, the pilot in the lower left corner of the status bar will turn green. It will return to gray if synchronization is lost. If the device itself becomes 'lost', the pilot will turn red to indicate an error has occurred (this is the theory, though I've yet to see this status message happen in practice). The synchronization process may take about 10 seconds, and the station will begin to play. This depends upon signal strength and whether it's relatively free from adjacent interference. After a short while, the station name will appear to the right of the frequency, and the available streams will show on a row of buttons just beneath the frequency entry. Clicking one of these buttons will change to that particular stream. Note: No settings other than stream may be changed while the device is playing.
## Album Art & Track Info ## Album Art & Track Info
Some stations will send album art and station logos. These will fill the Album Art tab, as they are made available by the station. Most stations will send the song title, artist, album, and genre. These are displayed in the Track Info pane, also if available. Some stations will send album art and station logos. These will fill the Album Art tab, as they are made available by the station. Most stations will send the song title, artist, album, and genre. These are displayed in the Track Info pane, also if available.
`Temporarily Disabled:` The user can override what the stations send by enabling the DL Album Art setting. This will use the Title and Artist information to retrieve album art. If no album art is found, the station logo will be used, if available. The user can override what the stations send by enabling the DL Album Art setting. This will use the Title and Artist information to retrieve album art from MusicBrainz. If no album art is found, the station logo will be used, if available.
## Bookmarks ## Bookmarks
When a station is playing, you can click the Bookmark Station button to add it to the bookmarks list. You can click on the Name in the bookmarks list to edit it. Double click the Station to tune to that particular station and stream. Click the Delete Bookmark button to delete it. Note that some stations use the default MPS/SPS or HDn naming for their streams. In this case, the bookmark will be used to name the stream button. When a station is playing, you can click the Bookmark Station button to add it to the bookmarks list. You can click on the Name in the bookmarks list to edit it. Double click the Station to tune to that particular station and stream. Click the Delete Bookmark button to delete it. Note that some stations use the default MPS/SPS or HDn naming for their streams. In this case, the bookmark will be used to name the stream button.

View File

@ -33,6 +33,7 @@ from gi.repository import Gtk, GObject, Gdk, GdkPixbuf, GLib
import urllib3 import urllib3
from OpenSSL import SSL from OpenSSL import SSL
import musicbrainzngs
# print debug messages to stdout (if debugger is attached) # print debug messages to stdout (if debugger is attached)
debugMessages = (sys.gettrace() != None) debugMessages = (sys.gettrace() != None)
@ -58,7 +59,6 @@ class NRSC5_DUI(object):
self.getControls() # get controls and windows self.getControls() # get controls and windows
self.initStreamInfo() # initilize stream info and clear status widgets self.initStreamInfo() # initilize stream info and clear status widgets
self.http = urllib3.PoolManager() self.http = urllib3.PoolManager()
# self.headers = urllib3.util.make_headers(keep_alive=True, accept_encoding=True, user_agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:87.0) Gecko/20100101 Firefox/87.0")
self.debugLog("Local path determined as " + runtimeDir) self.debugLog("Local path determined as " + runtimeDir)
@ -77,6 +77,12 @@ class NRSC5_DUI(object):
self.debugLog("OS Determination: Windows = {}".format(self.windowsOS)) self.debugLog("OS Determination: Windows = {}".format(self.windowsOS))
self.app_name = "NRSC5-DUI"
self.version = "2.1.0"
self.web_addr = "https://github.com/markjfine/nrsc5-dui"
self.copyright = "Copyright © 2017-2019 Cody Nybo & Clayton Smith, 2019 zefie, 2021 Mark J. Fine"
musicbrainzngs.set_useragent(self.app_name,self.version,self.web_addr)
self.mapFile = os.path.join(resDir, "map.png") self.mapFile = os.path.join(resDir, "map.png")
self.defaultSize = [490,250] # default width,height of main app self.defaultSize = [490,250] # default width,height of main app
self.nrsc5 = None # nrsc5 process self.nrsc5 = None # nrsc5 process
@ -327,30 +333,6 @@ class NRSC5_DUI(object):
newArtist = self.streamInfo["Artist"].strip() newArtist = self.streamInfo["Artist"].strip()
return ((newArtist != oldArtist) and (newTitle != oldTitle)) return ((newArtist != oldArtist) and (newTitle != oldTitle))
def get_cover_data(self, response):
check = -1
resultUrl = ""
resultArtist = ""
m = re.search(r"card card_large float_fix",response)
if (m.start() > -1):
response = response[m.start():]
m = re.search(r"<img data-src=\"",response)
if (m.start() > -1):
response = response[m.start()+15:]
m = re.search(r"\"",response)
if (m.start() > -1):
resultUrl = response[:m.start()]
response = response[m.start()+1:]
m = re.search(r"<span title=\"",response)
if (m.start() > -1):
response = response[m.start()+13:]
m = re.search(r"\"",response)
if (m.start() > -1):
resultArtist = response[:m.start()]
response = response[m.start()+1:]
check = 0
return check, response, resultUrl, resultArtist
def fix_artist(self): def fix_artist(self):
newArtist = self.streamInfo["Artist"] newArtist = self.streamInfo["Artist"]
if ("/" in newArtist): if ("/" in newArtist):
@ -368,36 +350,47 @@ class NRSC5_DUI(object):
# only care about the first artist listed if separated by slashes # only care about the first artist listed if separated by slashes
newArtist = self.fix_artist() newArtist = self.fix_artist()
baseStr = str(newArtist +" - "+self.streamInfo["Title"]).replace("/","_").replace(":","_") baseStr = str(newArtist+" - "+self.streamInfo["Title"])
saveStr = os.path.join(aasDir, baseStr.replace(" ","_")+".jpg") saveStr = os.path.join(aasDir, baseStr.replace(" ","_").replace("/","_").replace(":","_")+".jpg")
searchStr = baseStr.replace(" ","+") #searchStr = baseStr.replace(" ","+")
#print("Searching for:"+searchStr)
# does it already exist? # does it already exist?
if (os.path.isfile(saveStr)): if (os.path.isfile(saveStr)):
self.coverImage = saveStr self.coverImage = saveStr
# if not, get it from Discogs # if not, get it from MusicBrainz
else: else:
try: try:
searchStr = "https://www.discogs.com/search/?q="+searchStr+"&type=all" result = musicbrainzngs.search_releases(artist=self.streamInfo["Artist"], release=self.streamInfo["Title"])
#r = self.http.request('GET', searchStr, self.headers)
r = self.http.request('GET', searchStr)
if (r.status == 200):
response = r.data.decode('utf-8')
# loop through the page until you either get an artist match or you run out of page (check) if result['release-list']:
while (not got_cover):
resultUrl = "" # loop through the list until you get a match
resultArtist = "" #print()
check, response, resultUrl, resultArtist = self.get_cover_data(response) resultID = ""
got_cover = (newArtist.lower() in resultArtist.lower()) and (check == 0) for (idx, release) in enumerate(result['release-list']):
#print(release["artist-credit-phrase"]+" - "+release['title']+" "+release['id'])
# if you got a match, save it if (newArtist.lower() in release["artist-credit-phrase"].lower()) and (self.streamInfo["Title"].lower() in release['title'].lower()):
if (resultUrl != ""): resultID = release['id']
with self.http.request('GET', resultUrl, preload_content=False) as r, open(saveStr, 'wb') as out_file:
if (r.status == 200): # got a match, now get the cover art
shutil.copyfileobj(r, out_file) #print("Found {} - {}, {}".format(release["artist-credit-phrase"], release['title'], resultID))
self.coverImage = saveStr imageList = musicbrainzngs.get_image_list(resultID)
for image in imageList["images"]:
if "Front" in image["types"] and image["approved"]:
resultURL = image["thumbnails"]["large"]
#print("{} is an approved front image".format(resultURL))
# now save it
# TODO - Use built-in MusicBrainz routine to eliminate need for urllib3 and OpenSSL for just this remaining call
#with musicbrainzngs.get_image_front(resultID, size="500") as r, open(saveStr, 'wb') as out_file:
with self.http.request('GET', resultURL, preload_content=False) as r, open(saveStr, 'wb') as out_file:
if (r.status == 200):
shutil.copyfileobj(r, out_file)
self.coverImage = saveStr
break
break
# If no match use the station logo if there is one # If no match use the station logo if there is one
else: else:
@ -673,11 +666,11 @@ class NRSC5_DUI(object):
about_dialog = Gtk.AboutDialog() about_dialog = Gtk.AboutDialog()
about_dialog.set_transient_for(self.mainWindow) about_dialog.set_transient_for(self.mainWindow)
about_dialog.set_destroy_with_parent(True) about_dialog.set_destroy_with_parent(True)
about_dialog.set_name("NRSC5 DUI") about_dialog.set_name(self.app_name)
about_dialog.set_program_name("NRSC5 DUI") about_dialog.set_program_name(self.app_name)
about_dialog.set_version("2.1.0") about_dialog.set_version(self.version)
about_dialog.set_copyright("Copyright © 2017-2019 Cody Nybo & Clayton Smith, 2019 zefie, 2021 Mark J. Fine") about_dialog.set_copyright(self.copyright)
about_dialog.set_website("https://github.com/markjfine/nrsc5-dui") about_dialog.set_website(self.web_addr)
about_dialog.set_comments("A second-generation graphical interface for nrsc5.") about_dialog.set_comments("A second-generation graphical interface for nrsc5.")
about_dialog.set_authors(authors) about_dialog.set_authors(authors)
about_dialog.set_license(license) about_dialog.set_license(license)
@ -980,8 +973,8 @@ class NRSC5_DUI(object):
self.debugLog("Image Changed") self.debugLog("Image Changed")
# Disable downloaded cover images until fixed with MusicBrainz # Disable downloaded cover images until fixed with MusicBrainz
#if (self.cbCovers.get_active() and self.id3Changed): if (self.cbCovers.get_active() and self.id3Changed):
#self.get_cover_image_online() self.get_cover_image_online()
finally: finally:
#Gdk.threads_leave() #Gdk.threads_leave()