2013-09-05 15:17:25 +00:00
|
|
|
# coding: utf-8
|
|
|
|
|
2014-03-02 17:31:32 +00:00
|
|
|
# This file is part of Supysonic.
|
|
|
|
#
|
|
|
|
# Supysonic is a Python implementation of the Subsonic server API.
|
|
|
|
# Copyright (C) 2013, 2014 Alban 'spl0k' Féron
|
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU Affero General Public License as published by
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU Affero General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
2013-09-05 16:25:33 +00:00
|
|
|
import os.path, uuid
|
2014-03-09 18:12:11 +00:00
|
|
|
from db import Folder, Artist, Album, Track
|
2013-09-05 15:17:25 +00:00
|
|
|
|
|
|
|
class FolderManager:
|
|
|
|
SUCCESS = 0
|
2013-09-05 16:25:33 +00:00
|
|
|
INVALID_ID = 1
|
|
|
|
NAME_EXISTS = 2
|
|
|
|
INVALID_PATH = 3
|
|
|
|
PATH_EXISTS = 4
|
|
|
|
NO_SUCH_FOLDER = 5
|
|
|
|
|
|
|
|
@staticmethod
|
2014-03-09 18:12:11 +00:00
|
|
|
def get(store, uid):
|
2013-09-05 16:25:33 +00:00
|
|
|
if isinstance(uid, basestring):
|
|
|
|
try:
|
|
|
|
uid = uuid.UUID(uid)
|
|
|
|
except:
|
|
|
|
return FolderManager.INVALID_ID, None
|
|
|
|
elif type(uid) is uuid.UUID:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
return FolderManager.INVALID_ID, None
|
|
|
|
|
2014-03-09 18:12:11 +00:00
|
|
|
folder = store.get(Folder, uid)
|
2013-09-05 16:25:33 +00:00
|
|
|
if not folder:
|
|
|
|
return FolderManager.NO_SUCH_FOLDER, None
|
|
|
|
|
|
|
|
return FolderManager.SUCCESS, folder
|
2013-09-05 15:17:25 +00:00
|
|
|
|
|
|
|
@staticmethod
|
2014-03-09 18:12:11 +00:00
|
|
|
def add(store, name, path):
|
|
|
|
if not store.find(Folder, Folder.name == name, Folder.root == True).is_empty():
|
2013-09-05 15:17:25 +00:00
|
|
|
return FolderManager.NAME_EXISTS
|
|
|
|
|
|
|
|
path = os.path.abspath(path)
|
|
|
|
if not os.path.isdir(path):
|
|
|
|
return FolderManager.INVALID_PATH
|
2014-03-09 18:12:11 +00:00
|
|
|
if not store.find(Folder, Folder.path == path).is_empty():
|
2013-09-05 15:17:25 +00:00
|
|
|
return FolderManager.PATH_EXISTS
|
|
|
|
|
2014-03-09 18:12:11 +00:00
|
|
|
folder = Folder()
|
|
|
|
folder.root = True
|
|
|
|
folder.name = name
|
|
|
|
folder.path = path
|
|
|
|
|
|
|
|
store.add(folder)
|
|
|
|
store.commit()
|
2013-09-05 15:17:25 +00:00
|
|
|
|
|
|
|
return FolderManager.SUCCESS
|
|
|
|
|
|
|
|
@staticmethod
|
2014-03-09 18:12:11 +00:00
|
|
|
def delete(store, uid):
|
|
|
|
status, folder = FolderManager.get(store, uid)
|
2013-09-05 16:25:33 +00:00
|
|
|
if status != FolderManager.SUCCESS:
|
|
|
|
return status
|
|
|
|
|
|
|
|
if not folder.root:
|
2013-09-05 15:17:25 +00:00
|
|
|
return FolderManager.NO_SUCH_FOLDER
|
|
|
|
|
|
|
|
# delete associated tracks and prune empty albums/artists
|
2014-03-09 18:12:11 +00:00
|
|
|
potentially_removed_albums = set()
|
|
|
|
for track in store.find(Track, Track.root_folder_id == folder.id):
|
|
|
|
potentially_removed_albums.add(track.album)
|
|
|
|
store.remove(track)
|
|
|
|
potentially_removed_artists = set()
|
|
|
|
for album in filter(lambda album: album.tracks.count() == 0, potentially_removed_albums):
|
|
|
|
potentially_removed_artists.add(album.artist)
|
|
|
|
store.remove(album)
|
|
|
|
for artist in filter(lambda artist: artist.albums.count() == 0, potentially_removed_artists):
|
|
|
|
store.remove(artist)
|
2013-09-05 15:17:25 +00:00
|
|
|
|
|
|
|
def cleanup_folder(folder):
|
|
|
|
for f in folder.children:
|
|
|
|
cleanup_folder(f)
|
2014-03-09 18:12:11 +00:00
|
|
|
store.remove(folder)
|
2013-09-05 15:17:25 +00:00
|
|
|
|
|
|
|
cleanup_folder(folder)
|
2014-03-09 18:12:11 +00:00
|
|
|
store.commit()
|
2013-09-05 15:17:25 +00:00
|
|
|
|
|
|
|
return FolderManager.SUCCESS
|
|
|
|
|
|
|
|
@staticmethod
|
2014-03-09 18:12:11 +00:00
|
|
|
def delete_by_name(store, name):
|
|
|
|
folder = store.find(Folder, Folder.name == name, Folder.root == True).one()
|
2013-09-05 15:17:25 +00:00
|
|
|
if not folder:
|
|
|
|
return FolderManager.NO_SUCH_FOLDER
|
2014-03-09 18:12:11 +00:00
|
|
|
return FolderManager.delete(store, folder.id)
|
2013-09-05 15:17:25 +00:00
|
|
|
|
2013-09-05 16:25:33 +00:00
|
|
|
@staticmethod
|
2014-03-09 18:12:11 +00:00
|
|
|
def scan(store, uid, scanner, progress_callback = None):
|
|
|
|
status, folder = FolderManager.get(store, uid)
|
2013-09-05 16:25:33 +00:00
|
|
|
if status != FolderManager.SUCCESS:
|
|
|
|
return status
|
|
|
|
|
2014-01-14 13:39:03 +00:00
|
|
|
scanner.scan(folder, progress_callback)
|
2013-09-05 16:25:33 +00:00
|
|
|
scanner.prune(folder)
|
|
|
|
scanner.check_cover_art(folder)
|
|
|
|
return FolderManager.SUCCESS
|
|
|
|
|
2013-09-05 15:17:25 +00:00
|
|
|
@staticmethod
|
|
|
|
def error_str(err):
|
|
|
|
if err == FolderManager.SUCCESS:
|
|
|
|
return 'No error'
|
2013-09-05 16:25:33 +00:00
|
|
|
elif err == FolderManager.INVALID_ID:
|
|
|
|
return 'Invalid folder id'
|
2013-09-05 15:17:25 +00:00
|
|
|
elif err == FolderManager.NAME_EXISTS:
|
|
|
|
return 'There is already a folder with that name. Please pick another one.'
|
|
|
|
elif err == FolderManager.INVALID_PATH:
|
|
|
|
return "The path doesn't exists or isn't a directory"
|
|
|
|
elif err == FolderManager.PATH_EXISTS:
|
|
|
|
return 'This path is already registered'
|
|
|
|
elif err == FolderManager.NO_SUCH_FOLDER:
|
|
|
|
return 'No such folder'
|
|
|
|
return 'Unknown error'
|
|
|
|
|