Some cleanup

This commit is contained in:
Zergling_man 2022-01-03 18:02:22 +11:00
parent 748ba97d94
commit 4c2d1043fc
3 changed files with 12 additions and 39 deletions

View File

@ -1,13 +1,12 @@
import requests as r import requests as r
import sys import sys
import os from os import listdir as ls
from os import path
import json as j import json as j
import .utils as u
getfile=lambda x: path.join(path.dirname(__file__),x) with open(u.get_file('config.json')) as b: conf=j.load(b)
with open(getfile('config.json')) as b: conf=j.load(b)
import importlib as il import importlib as il
sources=[il.import_module(f'sources.{n[:-3]}') for n in os.listdir(getfile('sources')) if n.endswith('.py')] sources=[il.import_module(f'sources.{n[:-3]}') for n in ls(u.get_file('sources')) if n.endswith('.py')]
sources=list(filter(lambda x:x.enabled,sources)) sources=list(filter(lambda x:x.enabled,sources))
class FlashyNotMountedError(Exception): class FlashyNotMountedError(Exception):
pass pass
@ -22,7 +21,7 @@ def init(args):
def local(band,song): def local(band,song):
#Local #Local
#All lyrics that are fetched from remote will automatically get saved in here for later reference #All lyrics that are fetched from remote will automatically get saved in here for later reference
#Also, any time the existence of a song on a remote is proven (eg. the remote offers an album list for a band, which in turn offers a track list, and this needs to be parsed to get the correct URL), that will be indexed locally for future reference, in ./.indices #Also, any time the existence of a song on a remote is proven (eg. the remote offers an album list for a band, which in turn offers a track list, and this needs to be parsed to get the correct URL), that will be indexed locally for future reference, in the indexcache config path.
#Deleting that folder will reset that, and should always be safe. #Deleting that folder will reset that, and should always be safe.
#Indices will be one file per source, so deleting individual files will be part of uninstalling a source. #Indices will be one file per source, so deleting individual files will be part of uninstalling a source.
try: try:
@ -34,7 +33,7 @@ def local(band,song):
except FileNotFoundError: lyrics='' except FileNotFoundError: lyrics=''
return lyrics return lyrics
except FileNotFoundError: except FileNotFoundError:
raise FlashyNotMountedError raise u.LyricsPathUnavailable()
def remote(band,song): def remote(band,song):
#Runs through sources and tries to locate the song and fetch lyrics. #Runs through sources and tries to locate the song and fetch lyrics.

View File

@ -2,7 +2,7 @@ Not actually markdown, haha tricked ya!
Documentation for files in this folder. Documentation for files in this folder.
1) Every file must be a python script 1) For a file to be treated as a source, it must be a python script (.py). Other files are ignored.
2) It must be named with the domain it serves as the filename (starting from TLD and narrowing down; dots and slashes should be converted to underscores - the fetcher won't actually try to restore it and load the URL). 2) It must be named with the domain it serves as the filename (starting from TLD and narrowing down; dots and slashes should be converted to underscores - the fetcher won't actually try to restore it and load the URL).
3) It must expose an "enabled" global boolean that, if False, will cause the fetcher to ignore its existence entirely. This is useful for sources that are temporarily not working, or for sources that are pending proper updates to the source's changes, etc. 3) It must expose an "enabled" global boolean that, if False, will cause the fetcher to ignore its existence entirely. This is useful for sources that are temporarily not working, or for sources that are pending proper updates to the source's changes, etc.
4) It must expose a lyrics(song,band='',album='') function that returns song lyrics as plaintext (str), or as arranged lyrics (dict\[sections (list),arrangement (list)\]). It's recommended that if neither band nor album are given, the function should automatically return a failure, or explicitly recommend a search, rather than attempting to work with it. 4) It must expose a lyrics(song,band='',album='') function that returns song lyrics as plaintext (str), or as arranged lyrics (dict\[sections (list),arrangement (list)\]). It's recommended that if neither band nor album are given, the function should automatically return a failure, or explicitly recommend a search, rather than attempting to work with it.
@ -12,3 +12,4 @@ Documentation for files in this folder.
- massfetch(band,album=''), which should fetch lyrics to all tracks of the band (on that album) and return them in a list - massfetch(band,album=''), which should fetch lyrics to all tracks of the band (on that album) and return them in a list
- This will, at some point, become a definitive list of optional functions that the fetcher supports; it will always accept recommendations for change. - This will, at some point, become a definitive list of optional functions that the fetcher supports; it will always accept recommendations for change.
6) Input formats: All names will be passed in exactly as the user presents the information, it is the source's job to wrangle it correctly into URLs. For example, "wither." is an album name that may be given to the source. It should generally assume that words will be separated with spaces, but there's no guarantee. Also it may occasionally get bands and songs passed in as each other. It isn't expected to figure that out. 6) Input formats: All names will be passed in exactly as the user presents the information, it is the source's job to wrangle it correctly into URLs. For example, "wither." is an album name that may be given to the source. It should generally assume that words will be separated with spaces, but there's no guarantee. Also it may occasionally get bands and songs passed in as each other. It isn't expected to figure that out.
7) Output: If a function returns more, or different, data than was expected (eg. lyrics are available on an album's track list), it is welcome to raise a utils.AdditionalDataException, containing a nested dictionary with the actual received data. The only valid keys are "name", "contents", "metadata" and "type". "type" must be one of "band", "album", "track". "metadata" will later allow other keys (such as "url", "duration", "number", "year", "artist" - for guest tracks, etc.). "contents" may be a dictionary which obeys the same rules.

View File

@ -1,34 +1,7 @@
enabled=True enabled=True
import requests as r import requests as r
import ..utils as u
def processneedle(gunk,start,needle):
try: start+=gunk[start:].index(needle[0])+len(needle[0])
except ValueError: print('failed needle:',needle); return None,None
end=gunk[start+1:].index(needle[1])+1
return gunk[start:start+end],start+end+len(needle[1])
def stringiter(gunk:str,needle:tuple=('',''),needles:list=[]):
start=0
if needles:
# Non-interactive mode. You have all the info upfront.
needle=0
while True:
out={}
for needle in needles:
res,start=processneedle(gunk,start,needle)
if res==start==None: return
try: out[needle[2]]=res
except IndexError:
try: out[''].append(res)
except KeyError: out['']=[res]
yield out
# Interactive mode. I guess don't use this as much.
if needle==('',''): needle=yield None
while True:
res,start=processneedle(gunk,start,needle)
if res==start==None: return
yield res
def bandget(band): def bandget(band):
peg=r.get(f'https://{band}.bandcamp.com/').content.decode('utf-8') # Just assume success, peg=r.get(f'https://{band}.bandcamp.com/').content.decode('utf-8') # Just assume success,
@ -38,7 +11,7 @@ def bandget(band):
needles=[('href="/album/','"','urlid'), needles=[('href="/album/','"','urlid'),
('img src="','"','coverurl'), ('img src="','"','coverurl'),
('class="title">\n ','\n','title')] ('class="title">\n ','\n','title')]
for needle in stringiter(peg,needles=needles): for needle in u.stringiter(peg,needles=needles):
albums.append(needle) albums.append(needle)
return albums return albums
@ -51,7 +24,7 @@ def albumget(band,album,mode=0):
('span class="track-title">','</span>','title'), ('span class="track-title">','</span>','title'),
('<span class="time secondaryText">\n \n ','\n','duration')] ('<span class="time secondaryText">\n \n ','\n','duration')]
# Despite the lyrics being in the pages, it's not actually safe to get them with this system because any track without lyrics will quietly delete all tracks after it, up to and including the next one with lyrics. # Despite the lyrics being in the pages, it's not actually safe to get them with this system because any track without lyrics will quietly delete all tracks after it, up to and including the next one with lyrics.
for needle in stringiter(peg,needles=needles): for needle in u.stringiter(peg,needles=needles):
tracks.append(needle) tracks.append(needle)
return tracks return tracks
@ -67,6 +40,6 @@ def lyrics(song,band='',album=''):
band=band.replace(' ',''); song=song.replace(' ','-') band=band.replace(' ',''); song=song.replace(' ','-')
peg=r.get(f'https://{band}.bandcamp.com/track/{song}').content.decode('utf-8') peg=r.get(f'https://{band}.bandcamp.com/track/{song}').content.decode('utf-8')
needle=('<div class="tralbumData lyricsText">','</div>','lyrics') needle=('<div class="tralbumData lyricsText">','</div>','lyrics')
lyrics=next(stringiter(peg,needle=needle)) lyrics=next(u.stringiter(peg,needle=needle))
lyrics=lyrics.replace('\r','').replace('\n','').replace('<br>','\n') lyrics=lyrics.replace('\r','').replace('\n','').replace('<br>','\n')
return lyrics return lyrics