1
0
mirror of https://github.com/spl0k/supysonic.git synced 2024-09-19 19:01:03 +00:00

Taking folder hierarchy into account

This commit is contained in:
Alban 2012-10-21 16:18:35 +02:00
parent 4d4706a43a
commit 7e2e93bcfd
4 changed files with 69 additions and 25 deletions

View File

@ -2,7 +2,7 @@
from flask import request
from web import app
from db import MusicFolder, Artist, Album, Track
from db import Folder, Artist, Album, Track
import uuid, time, string
@app.route('/rest/getMusicFolders.view')
@ -12,7 +12,7 @@ def list_folders():
'musicFolder': [ {
'id': str(f.id),
'name': f.name
} for f in MusicFolder.query.order_by(MusicFolder.name).all() ]
} for f in Folder.query.filter(Folder.root == True).order_by(Folder.name).all() ]
}
})
@ -27,16 +27,16 @@ def list_indexes():
return request.error_formatter(0, 'Invalid timestamp')
if musicFolderId is None:
folder = MusicFolder.query.all()
folder = Folder.query.filter(Folder.root == True).all()
else:
try:
mfid = uuid.UUID(musicFolderId)
except:
return request.error_formatter(0, 'Invalid id')
folder = MusicFolder.query.get(mfid)
folder = Folder.query.get(mfid)
if not folder:
if not folder or not folder.root:
return request.error_formatter(70, 'Folder not found')
last_modif = max(map(lambda f: f.last_scan, folder)) if type(folder) is list else folder.last_scan

18
db.py
View File

@ -55,14 +55,18 @@ class User(Base):
salt = Column(String(6))
admin = Column(Boolean)
class MusicFolder(Base):
class Folder(Base):
__tablename__ = 'folder'
id = UUID.gen_id_column()
name = Column(String, unique = True)
path = Column(String)
root = Column(Boolean, default = False)
name = Column(String)
path = Column(String, unique = True)
last_scan = Column(DateTime, default = datetime.datetime.min)
parent_id = Column(UUID, ForeignKey('folder.id'), nullable = True)
children = relationship('Folder', backref = backref('parent', remote_side = [ id ]))
class Artist(Base):
__tablename__ = 'artist'
@ -92,13 +96,15 @@ class Track(Base):
path = Column(String, unique = True)
bitrate = Column(Integer)
root_folder_id = Column(UUID, ForeignKey('folder.id'))
root_folder = relationship('Folder', primaryjoin = Folder.id == root_folder_id)
folder_id = Column(UUID, ForeignKey('folder.id'))
folder = relationship('MusicFolder')
folder = relationship('Folder', primaryjoin = Folder.id == folder_id, backref = 'tracks')
def as_subsonic_child(self):
info = {
'id': str(self.id),
'parent': str(self.album.id),
'parent': str(self.folder.id),
'isDir': False,
'title': self.title,
'album': self.album.name,
@ -109,7 +115,7 @@ class Track(Base):
'suffix': 'mp3', # same as above
'duration': self.duration,
'bitRate': self.bitrate,
'path': self.path[len(self.folder.path) + 1:],
'path': self.path[len(self.root_folder.path) + 1:],
'isVideo': False,
'discNumber': self.disc,
'albumId': str(self.album.id),

View File

@ -1,6 +1,6 @@
# coding: utf-8
import os.path
import os, os.path
import datetime
import eyeD3
@ -10,6 +10,8 @@ class Scanner:
def __init__(self, session):
self.__session = session
self.__artists = db.Artist.query.all()
self.__folders = db.Folder.query.all()
self.__added_artists = 0
self.__added_albums = 0
self.__added_tracks = 0
@ -27,9 +29,10 @@ class Scanner:
def prune(self, folder):
for artist in db.Artist.query.all():
for album in artist.albums[:]:
for track in filter(lambda t: t.folder.id == folder.id, album.tracks):
for track in filter(lambda t: t.root_folder.id == folder.id, album.tracks):
if not os.path.exists(track.path):
album.tracks.remove(track)
track.folder.tracks.remove(track)
self.__session.delete(track)
self.__deleted_tracks += 1
if len(album.tracks) == 0:
@ -40,6 +43,8 @@ class Scanner:
self.__session.delete(artist)
self.__deleted_artists += 1
self.__cleanup_folder(folder)
def __scan_file(self, path, folder):
tag = eyeD3.Tag()
tag.link(path)
@ -48,7 +53,7 @@ class Scanner:
al = self.__find_album(tag.getArtist(), tag.getAlbum())
tr = filter(lambda t: t.path == path, al.tracks)
if not tr:
tr = db.Track(path = path, folder = folder)
tr = db.Track(path = path, root_folder = folder, folder = self.__find_folder(path, folder))
self.__added_tracks += 1
else:
tr = tr[0]
@ -85,6 +90,33 @@ class Scanner:
return ar
def __find_folder(self, path, folder):
path = os.path.dirname(path)
fold = filter(lambda f: f.path == path, self.__folders)
if fold:
return fold[0]
full_path = folder.path
path = path[len(folder.path) + 1:]
for name in path.split(os.sep):
full_path = os.path.join(full_path, name)
fold = filter(lambda f: f.path == full_path, self.__folders)
if fold:
folder = fold[0]
else:
folder = db.Folder(root = False, name = name, path = full_path, parent = folder)
self.__folders.append(folder)
return folder
def __cleanup_folder(self, folder):
for f in folder.children:
self.__cleanup_folder(f)
if len(folder.children) == 0 and len(folder.tracks) == 0 and not folder.root:
folder.parent = None
self.__session.delete(folder)
def stats(self):
return (self.__added_artists, self.__added_albums, self.__added_tracks), (self.__deleted_artists, self.__deleted_albums, self.__deleted_tracks)

28
web.py
View File

@ -23,7 +23,7 @@ def index():
flash('Not configured. Please create the first admin user')
return redirect(url_for('add_user'))
"""
return render_template('home.html', users = db.User.query.all(), folders = db.MusicFolder.query.all(),
return render_template('home.html', users = db.User.query.all(), folders = db.Folder.query.filter(db.Folder.root == True).all(),
artists = db.Artist.query.order_by(db.Artist.name).all(),
albums = db.Album.query.join(db.Album.artist).order_by(db.Artist.name, db.Album.name).all())
@ -92,7 +92,7 @@ def add_folder():
if name in (None, ''):
flash('The name is required.')
error = True
elif db.MusicFolder.query.filter(db.MusicFolder.name == name).first():
elif db.Folder.query.filter(db.Folder.name == name and db.Folder.root).first():
flash('There is already a folder with that name. Please pick another one.')
error = True
if path in (None, ''):
@ -103,14 +103,14 @@ def add_folder():
if not os.path.isdir(path):
flash("The path '%s' doesn't exists or isn't a directory" % path)
error = True
folder = db.MusicFolder.query.filter(db.MusicFolder.name == name).first()
folder = db.Folder.query.filter(db.Folder.path == path).first()
if folder:
flash("This path is already registered with the name '%s'" % folder.name)
flash("This path is already registered")
error = True
if error:
return render_template('addfolder.html')
folder = db.MusicFolder(name = name, path = path)
folder = db.Folder(root = True, name = name, path = path)
db.session.add(folder)
db.session.commit()
flash("Folder '%s' created. You should now run a scan" % name)
@ -125,7 +125,7 @@ def del_folder(id):
flash('Invalid folder id')
return redirect(url_for('index'))
folder = db.MusicFolder.query.get(idid)
folder = db.Folder.query.get(idid)
if folder is None:
flash('No such folder')
return redirect(url_for('index'))
@ -133,7 +133,7 @@ def del_folder(id):
# delete associated tracks and prune empty albums/artists
for artist in db.Artist.query.all():
for album in artist.albums[:]:
for track in filter(lambda t: t.folder.id == folder.id, album.tracks):
for track in filter(lambda t: t.root_folder.id == folder.id, album.tracks):
album.tracks.remove(track)
db.session.delete(track)
if len(album.tracks) == 0:
@ -141,7 +141,13 @@ def del_folder(id):
db.session.delete(album)
if len(artist.albums) == 0:
db.session.delete(artist)
db.session.delete(folder)
def cleanup_folder(folder):
for f in folder.children:
cleanup_folder(f)
db.session.delete(folder)
cleanup_folder(folder)
db.session.commit()
flash("Deleted folder '%s'" % folder.name)
@ -153,7 +159,7 @@ def del_folder(id):
def scan_folder(id = None):
s = Scanner(db.session)
if id is None:
for folder in db.MusicFolder.query.all():
for folder in db.Folder.query.filter(db.Folder.root == True).all():
s.scan(folder)
s.prune(folder)
else:
@ -163,8 +169,8 @@ def scan_folder(id = None):
flash('Invalid folder id')
return redirect(url_for('index'))
folder = db.MusicFolder.query.get(idid)
if folder is None:
folder = db.Folder.query.get(idid)
if folder is None or not folder.root:
flash('No such folder')
return redirect(url_for('index'))