diff --git a/database.py b/database.py index 137b6fb..32b9343 100644 --- a/database.py +++ b/database.py @@ -6,8 +6,70 @@ import datetime DATABASE = [] -ARTISTS = [] -TRACKS = [] +SCROBBLES = [] # Format: tuple(track_ref,timestamp,saved) +ARTISTS = [] # Format: artist +TRACKS = [] # Format: tuple(frozenset(artist_ref,...),title) + + +# by id +#def getScrobbleObject(o): +# #return {"artists":getTrackObject(SCROBBLES[o][0])["artists"],"title":getTrackObject(SCROBBLES[o][0])["title"],"time":SCROBBLES[o][1],"saved":SCROBBLES[o][2]} +# return {"artists":getTrackObject(SCROBBLES[o][0])["artists"],"title":getTrackObject(SCROBBLES[o][0])["title"],"time":SCROBBLES[o][1]} +# +#def getArtistObject(o): +# return ARTISTS[o] +# +#def getTrackObject(o): +# return {"artists":[getArtistObject(a) for a in TRACKS[o][0]],"title":TRACKS[o][1]} + +# by object + +def getScrobbleObject(o): + #return {"artists":getTrackObject(SCROBBLES[o][0])["artists"],"title":getTrackObject(SCROBBLES[o][0])["title"],"time":SCROBBLES[o][1],"saved":SCROBBLES[o][2]} + track = getTrackObject(TRACKS[o[0]]) + return {"artists":track["artists"],"title":track["title"],"time":o[1]} + +def getArtistObject(o): + return o + +def getTrackObject(o): + artists = [getArtistObject(ARTISTS[a]) for a in o[0]] + return {"artists":artists,"title":o[1]} + + + +def createScrobble(artists,title,time): + i = getTrackID(artists,title) + obj = (i,time,False) + SCROBBLES.append(obj) + +def readScrobble(artists,title,time): + i = getTrackID(artists,title) + obj = (i,time,True) + SCROBBLES.append(obj) + +def getArtistID(name): + + obj = name + try: + i = ARTISTS.index(obj) + except: + i = len(ARTISTS) + ARTISTS.append(obj) + return i + +def getTrackID(artists,title): + artistset = set() + for a in artists: + artistset.add(getArtistID(name=a)) + obj = (frozenset(artistset),title) + + try: + i = TRACKS.index(obj) + except: + i = len(TRACKS) + TRACKS.append(obj) + return i @route("/scrobbles") @@ -33,9 +95,25 @@ def get_scrobbles(): def get_tracks(): artist = request.query.get("artist") + global TRACKS + + # turn the tupel of frozensets into a jsonable object - tracklist = [{"artists":list(a[0]),"title":a[1]} for a in TRACKS] - ls = [t for t in tracklist if (artist in t["artists"]) or (artist==None)] + #tracklist = [{"artists":list(a[0]),"title":a[1]} for a in TRACKS] + + #ls = [t for t in tracklist if (artist in t["artists"]) or (artist==None)] + + + ### WHICH ONE IS FASTER + import time + + # Option 1 + ls = [getTrackObject(t) for t in TRACKS if (artist in t[0]) or (artist==None)] + + # Option 2 is a bit more elegant but much slower + #tracklist = [getTrackObject(t) for t in TRACKS] + #ls = [t for t in tracklist if (artist in t["artists"]) or (artist==None)] + return {"list":ls} @route("/artists") @@ -48,17 +126,51 @@ def get_artists(): def get_charts(): since = request.query.get("since") to = request.query.get("to") - results = db_query(since=since,to=to) - return {"list":results} + + #better do something here to sum up the totals on db level (before converting to dicts) + + #results = db_query(since=since,to=to) + #return {"list":results} # Starts the server def runserver(DATABASE_PORT): - reload() - buildh() + #reload() + #buildh() + build_db() run(host='0.0.0.0', port=DATABASE_PORT, server='waitress') + + +def build_db(): + newscrobbles = [t for t in SCROBBLES if not t[2]] + + for f in os.listdir("logs/"): + #print(f) + + if not (".csv" in f): + continue + + logfile = open("logs/" + f) + for l in logfile: + + l = l.replace("\n","") + data = l.split(",") + #print(l) + + + ## saving album in the scrobbles is supported, but for now we don't use it. It shouldn't be a defining part of the track (same song from Album or EP), but derived information + artists = data[1].split("/") + #album = data[3] + title = data[2] + time = int(data[0]) + + readScrobble(artists,title,time) + + #DATABASE.append({"artists":artists,"title":title,"time":time,"saved":True}) + + # builds database of artists and tracks # UNUSED as it is very resource-heavy, use buildh() instead @@ -105,6 +217,7 @@ def build(): # builds database of artists and tracks # uses better data types to quickly find all unique tracks +# now also UNUSED since we build everything in one step with build_db() def buildh(): global ARTISTS global TRACKS @@ -130,6 +243,7 @@ def buildh(): # Rebuilds the database from disk, keeps cached entries +# unused, this is now done in build_db() def reload(): newdb = [t for t in DATABASE if not t["saved"]] @@ -174,7 +288,7 @@ def flush(): # Queries the database -def db_query(artist=None,title=None,since=0,to=9999999999): +def db_query(artist=None,track=None,since=0,to=9999999999): if isinstance(since, str): sdate = [int(x) for x in since.split("/")] date = [1970,1,1,0,0] @@ -185,9 +299,19 @@ def db_query(artist=None,title=None,since=0,to=9999999999): date = [1970,1,1,0,0] date[:len(sdate)] = sdate to = int(datetime.datetime(date[0],date[1],date[2],date[3],date[4],tzinfo=datetime.timezone.utc).timestamp()) + + # this is not meant as a search function. we *can* query the db with a string, but it only works if it matches exactly (and title string simply picks the first track with that name) + if isinstance(artist, str): + artist = ARTISTS.index(artist) + if isinstance(track, str): + track = TRACKS.index(track) + + return [getScrobbleObject(s) for s in SCROBBLES if (s[0] == track or track==None) and (artist in TRACKS[s[0]][0] or artist==None) and (since < s[1] < to)] + # pointless to check for artist when track is checked because every track has a fixed set of artists, but it's more elegant this way + - thingsweneed = ["artists","title","time"] - return [{key:t[key] for key in thingsweneed} for t in DATABASE if (artist in t["artists"] or artist==None) and (t["title"]==title or title==None) and (since < t["time"] < to)] + #thingsweneed = ["artists","title","time"] + #return [{key:t[key] for key in thingsweneed} for t in DATABASE if (artist in t["artists"] or artist==None) and (t["title"]==title or title==None) and (since < t["time"] < to)] # Search for strings def db_search(query,type=None):