Browse Source

Add genres

v2
Dave Pedu 4 years ago
parent
commit
a3c354d4ef
  1. 5
      pysonic/api.py
  2. 78
      pysonic/database.py
  3. 2
      pysonic/library.py
  4. 17
      pysonic/scanner.py
  5. 19
      requirements.txt

5
pysonic/api.py

@ -523,9 +523,8 @@ class PysonicApi(object):
def getGenres_view(self, **kwargs):
response = ApiResponse()
response.add_child("genres")
response.add_child("genre", _parent="genres", value="Death Metal", songCount=420, albumCount=69)
response.add_child("genre", _parent="genres", value="Metal", songCount=52, albumCount=3)
response.add_child("genre", _parent="genres", value="Punk", songCount=34, albumCount=3)
for row in self.library.db.get_genres():
response.add_child("genre", _parent="genres", value=row["name"], songCount=420, albumCount=69)
return response
@cherrypy.expose

78
pysonic/database.py

@ -59,6 +59,9 @@ class PysonicDatabase(object):
'name' TEXT,
UNIQUE(parent, name)
)""",
"""CREATE TABLE 'genres' (
'id' INTEGER PRIMARY KEY AUTOINCREMENT,
'name' TEXT UNIQUE)""",
"""CREATE TABLE 'artists' (
'id' INTEGER PRIMARY KEY AUTOINCREMENT,
'libraryid' INTEGER,
@ -75,6 +78,7 @@ class PysonicDatabase(object):
'id' INTEGER PRIMARY KEY AUTOINCREMENT,
'library' INTEGER,
'albumid' BOOLEAN,
'genre' INTEGER DEFAULT NULL,
'file' TEXT UNIQUE, -- path from the library root
'size' INTEGER NOT NULL DEFAULT -1,
'title' TEXT NOT NULL,
@ -202,10 +206,76 @@ class PysonicDatabase(object):
return albums
@readcursor
def get_song(self, cursor, songid):
for item in cursor.execute("SELECT * FROM songs WHERE id=?", (songid, )):
return item
return None
def get_songs(self, cursor, id=None, genre=None, sortby=None, order=None, limit=None):
# TODO make this query massively uglier by joining albums and artists so that artistid etc can be a filter
# or maybe lookup those IDs in the library layer?
if order:
order = {"asc": "ASC", "desc": "DESC"}[order]
if sortby and sortby == "random":
sortby = "RANDOM()"
songs = []
q = """
SELECT
s.*,
alb.name as albumname,
alb.coverid as albumcoverid,
art.name as artistname,
g.name as genrename
FROM songs as s
INNER JOIN albums as alb
on s.albumid == alb.id
INNER JOIN artists as art
on alb.artistid = art.id
LEFT JOIN genres as g
on s.genre == g.id
"""
params = []
conditions = []
if id:
conditions.append("s.id = ?")
params.append(id)
if genre:
conditions.append("g.name = ?")
params.append(genre)
if conditions:
q += " WHERE " + " AND ".join(conditions)
if sortby:
q += " ORDER BY {}".format(sortby)
if order:
q += " {}".format(order)
if limit:
q += " LIMIT {}".format(limit) # TODO support limit pagination
print(q)
cursor.execute(q, params)
for row in cursor:
songs.append(row)
return songs
@readcursor
def get_genres(self, cursor, genre_id=None):
genres = []
q = "SELECT * FROM genres"
params = []
conditions = []
if genre_id:
conditions.append("id = ?")
params.append(genre_id)
if conditions:
q += " WHERE " + " AND ".join(conditions)
cursor.execute(q, params)
for row in cursor:
genres.append(row)
return genres

2
pysonic/library.py

@ -83,7 +83,7 @@ class PysonicLibrary(object):
return cover
def get_song(self, song_id):
song = self.db.get_song(song_id)
song = self.db.get_songs(id=song_id)[0]
library = self.db.get_libraries(song["library"])[0]
song['_fullpath'] = os.path.join(library["path"], song["file"])
return song

17
pysonic/scanner.py

@ -210,11 +210,17 @@ class PysonicFilesystemScanner(object):
writer.execute(q, params)
# If the metadata has an artist or album name, update the relevant items
# TODO ignore metadata if theyre blank
if "album" in meta:
writer.execute("UPDATE albums SET name=? WHERE id=?", (meta["album"], row["albumid"]))
if "artist" in meta:
album = writer.execute("SELECT artistid FROM albums WHERE id=?", (row['albumid'], )).fetchone()
writer.execute("UPDATE artists SET name=? WHERE id=?", (meta["artist"], album["artistid"]))
if "genre" in meta:
genre_name = meta["genre"].strip()
if genre_name:
genre_id = self.get_genre_id(writer, meta["genre"])
writer.execute("UPDATE songs SET genre=? WHERE id=?", (genre_id, row['id']))
# Commit every 50 items
processed += 1
@ -225,6 +231,13 @@ class PysonicFilesystemScanner(object):
if processed != 0:
writer.execute("COMMIT")
def get_genre_id(self, cursor, genre_name):
genre_name = genre_name.title().strip() # normalize
for row in cursor.execute("SELECT * FROM genres WHERE name=?", (genre_name, )):
return row['id']
cursor.execute("INSERT INTO genres (name) VALUES (?)", (genre_name, ))
return cursor.lastrowid
def scan_file_metadata(self, fpath):
"""
Scan the file for metadata.
@ -283,6 +296,10 @@ class PysonicFilesystemScanner(object):
meta["year"] = audio['TDRC'].text[0].year
except (KeyError, IndexError):
pass
try:
meta["genre"] = audio['TCON'].text[0]
except (KeyError, IndexError):
pass
logging.info("got all media info from %s", fpath)
return meta

19
requirements.txt

@ -1,9 +1,12 @@
beautifulsoup4==4.6.0
cheroot==5.8.3
CherryPy==11.0.0
lxml==3.8.0
mutagen==1.38
portend==2.1.2
pytz==2017.2
six==1.10.0
tempora==1.8
bs4==0.0.1
cheroot==6.0.0
CherryPy==14.0.1
lxml==4.2.1
more-itertools==4.1.0
mutagen==1.40.0
portend==2.2
pysonic==0.0.1
pytz==2018.3
six==1.11.0
tempora==1.11

Loading…
Cancel
Save