mirror of
https://github.com/spl0k/supysonic.git
synced 2024-12-22 17:06:17 +00:00
Splitting daemon stuff
This commit is contained in:
parent
9850cae241
commit
7f8369cac4
@ -16,7 +16,8 @@ import time
|
|||||||
from pony.orm import db_session
|
from pony.orm import db_session
|
||||||
from pony.orm import ObjectNotFound
|
from pony.orm import ObjectNotFound
|
||||||
|
|
||||||
from .daemon import DaemonClient, DaemonUnavailableError, ScannerAlreadyRunningError
|
from .daemon.client import DaemonClient
|
||||||
|
from .daemon.exceptions import DaemonUnavailableError, ScannerAlreadyRunningError
|
||||||
from .db import Folder, User
|
from .db import Folder, User
|
||||||
from .managers.folder import FolderManager
|
from .managers.folder import FolderManager
|
||||||
from .managers.user import UserManager
|
from .managers.user import UserManager
|
||||||
|
99
supysonic/daemon/__init__.py
Normal file
99
supysonic/daemon/__init__.py
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
#
|
||||||
|
# This file is part of Supysonic.
|
||||||
|
# Supysonic is a Python implementation of the Subsonic server API.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019 Alban 'spl0k' Féron
|
||||||
|
#
|
||||||
|
# Distributed under terms of the GNU AGPLv3 license.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from multiprocessing.connection import Listener
|
||||||
|
from pony.orm import db_session
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
|
from .client import DaemonCommand
|
||||||
|
from .exceptions import ScannerAlreadyRunningError
|
||||||
|
from ..db import Folder
|
||||||
|
from ..scanner import Scanner
|
||||||
|
from ..utils import get_secret_key
|
||||||
|
from ..watcher import SupysonicWatcher
|
||||||
|
|
||||||
|
__all__ = [ 'Daemon' ]
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class Daemon(object):
|
||||||
|
def __init__(self, config):
|
||||||
|
self.__config = config
|
||||||
|
self.__listener = None
|
||||||
|
self.__watcher = None
|
||||||
|
self.__scanner = None
|
||||||
|
|
||||||
|
watcher = property(lambda self: self.__watcher)
|
||||||
|
scanner = property(lambda self: self.__scanner)
|
||||||
|
|
||||||
|
def __handle_connection(self, connection):
|
||||||
|
cmd = connection.recv()
|
||||||
|
logger.debug('Received %s', cmd)
|
||||||
|
if isinstance(cmd, DaemonCommand):
|
||||||
|
cmd.apply(connection, self)
|
||||||
|
else:
|
||||||
|
logger.warn('Received unknown command %s', cmd)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.__listener = Listener(address = self.__config.DAEMON['socket'], authkey = get_secret_key('daemon_key'))
|
||||||
|
logger.info("Listening to %s", self.__listener.address)
|
||||||
|
|
||||||
|
if self.__config.DAEMON['run_watcher']:
|
||||||
|
self.__watcher = SupysonicWatcher(self.__config)
|
||||||
|
self.__watcher.start()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
conn = self.__listener.accept()
|
||||||
|
self.__handle_connection(conn)
|
||||||
|
|
||||||
|
def start_scan(self, folders = [], force = False):
|
||||||
|
if self.__scanner is not None and self.__scanner.is_alive():
|
||||||
|
raise ScannerAlreadyRunningError()
|
||||||
|
|
||||||
|
extensions = self.__config.BASE['scanner_extensions']
|
||||||
|
if extensions:
|
||||||
|
extensions = extensions.split(' ')
|
||||||
|
|
||||||
|
self.__scanner = ScannerThread(args = folders, kwargs = { 'force': force, 'extensions': extensions })
|
||||||
|
self.__scanner.start()
|
||||||
|
|
||||||
|
def terminate(self):
|
||||||
|
self.__listener.close()
|
||||||
|
if self.__watcher is not None:
|
||||||
|
self.__watcher.stop()
|
||||||
|
|
||||||
|
class ScannerThread(Thread):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(ScannerThread, self).__init__(*args, **kwargs)
|
||||||
|
self.__scanned = {}
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
force = self._kwargs.get('force', False)
|
||||||
|
extensions = self._kwargs.get('extensions')
|
||||||
|
s = Scanner(force = force, extensions = extensions)
|
||||||
|
|
||||||
|
with db_session:
|
||||||
|
if self._args:
|
||||||
|
folders = Folder.select(lambda f: f.root and f.name in self._args)
|
||||||
|
else:
|
||||||
|
folders = Folder.select(lambda f: f.root)
|
||||||
|
|
||||||
|
for f in folders:
|
||||||
|
name = f.name
|
||||||
|
logger.info('Scanning %s', name)
|
||||||
|
s.scan(f, lambda x: self.__scanned.update({ name: x }))
|
||||||
|
|
||||||
|
s.finish()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def scanned(self):
|
||||||
|
# This isn't quite thread-safe but locking each time a file is scanned could affect performance
|
||||||
|
return sum(self.__scanned.values())
|
@ -7,28 +7,14 @@
|
|||||||
#
|
#
|
||||||
# Distributed under terms of the GNU AGPLv3 license.
|
# Distributed under terms of the GNU AGPLv3 license.
|
||||||
|
|
||||||
import logging
|
from multiprocessing.connection import Client
|
||||||
|
|
||||||
from multiprocessing.connection import Client, Listener
|
from .exceptions import DaemonUnavailableError, ScannerAlreadyRunningError
|
||||||
from pony.orm import db_session
|
from ..config import get_current_config
|
||||||
from threading import Thread
|
from ..py23 import strtype
|
||||||
|
from ..utils import get_secret_key
|
||||||
|
|
||||||
from .db import Folder
|
__all__ = [ 'DaemonClient' ]
|
||||||
from .config import get_current_config
|
|
||||||
from .py23 import strtype
|
|
||||||
from .scanner import Scanner
|
|
||||||
from .utils import get_secret_key
|
|
||||||
from .watcher import SupysonicWatcher
|
|
||||||
|
|
||||||
__all__ = [ 'Daemon', 'DaemonClient', 'DaemonUnavailableError' ]
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
class DaemonUnavailableError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class ScannerAlreadyRunningError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class DaemonCommand(object):
|
class DaemonCommand(object):
|
||||||
def apply(self, connection, daemon):
|
def apply(self, connection, daemon):
|
||||||
@ -122,77 +108,3 @@ class DaemonClient(object):
|
|||||||
rv = c.recv()
|
rv = c.recv()
|
||||||
if rv.exception is not None:
|
if rv.exception is not None:
|
||||||
raise rv.exception
|
raise rv.exception
|
||||||
|
|
||||||
class Daemon(object):
|
|
||||||
def __init__(self, config):
|
|
||||||
self.__config = config
|
|
||||||
self.__listener = None
|
|
||||||
self.__watcher = None
|
|
||||||
self.__scanner = None
|
|
||||||
|
|
||||||
watcher = property(lambda self: self.__watcher)
|
|
||||||
scanner = property(lambda self: self.__scanner)
|
|
||||||
|
|
||||||
def __handle_connection(self, connection):
|
|
||||||
cmd = connection.recv()
|
|
||||||
logger.debug('Received %s', cmd)
|
|
||||||
if isinstance(cmd, DaemonCommand):
|
|
||||||
cmd.apply(connection, self)
|
|
||||||
else:
|
|
||||||
logger.warn('Received unknown command %s', cmd)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
self.__listener = Listener(address = self.__config.DAEMON['socket'], authkey = get_secret_key('daemon_key'))
|
|
||||||
logger.info("Listening to %s", self.__listener.address)
|
|
||||||
|
|
||||||
if self.__config.DAEMON['run_watcher']:
|
|
||||||
self.__watcher = SupysonicWatcher(self.__config)
|
|
||||||
self.__watcher.start()
|
|
||||||
|
|
||||||
while True:
|
|
||||||
conn = self.__listener.accept()
|
|
||||||
self.__handle_connection(conn)
|
|
||||||
|
|
||||||
def start_scan(self, folders = [], force = False):
|
|
||||||
if self.__scanner is not None and self.__scanner.is_alive():
|
|
||||||
raise ScannerAlreadyRunningError()
|
|
||||||
|
|
||||||
extensions = self.__config.BASE['scanner_extensions']
|
|
||||||
if extensions:
|
|
||||||
extensions = extensions.split(' ')
|
|
||||||
|
|
||||||
self.__scanner = ScannerThread(args = folders, kwargs = { 'force': force, 'extensions': extensions })
|
|
||||||
self.__scanner.start()
|
|
||||||
|
|
||||||
def terminate(self):
|
|
||||||
self.__listener.close()
|
|
||||||
if self.__watcher is not None:
|
|
||||||
self.__watcher.stop()
|
|
||||||
|
|
||||||
class ScannerThread(Thread):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(ScannerThread, self).__init__(*args, **kwargs)
|
|
||||||
self.__scanned = {}
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
force = self._kwargs.get('force', False)
|
|
||||||
extensions = self._kwargs.get('extensions')
|
|
||||||
s = Scanner(force = force, extensions = extensions)
|
|
||||||
|
|
||||||
with db_session:
|
|
||||||
if self._args:
|
|
||||||
folders = Folder.select(lambda f: f.root and f.name in self._args)
|
|
||||||
else:
|
|
||||||
folders = Folder.select(lambda f: f.root)
|
|
||||||
|
|
||||||
for f in folders:
|
|
||||||
name = f.name
|
|
||||||
logger.info('Scanning %s', name)
|
|
||||||
s.scan(f, lambda x: self.__scanned.update({ name: x }))
|
|
||||||
|
|
||||||
s.finish()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def scanned(self):
|
|
||||||
# This isn't quite thread-safe but locking each time a file is scanned could affect performance
|
|
||||||
return sum(self.__scanned.values())
|
|
14
supysonic/daemon/exceptions.py
Normal file
14
supysonic/daemon/exceptions.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
#
|
||||||
|
# This file is part of Supysonic.
|
||||||
|
# Supysonic is a Python implementation of the Subsonic server API.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019 Alban 'spl0k' Féron
|
||||||
|
#
|
||||||
|
# Distributed under terms of the GNU AGPLv3 license.
|
||||||
|
|
||||||
|
class DaemonUnavailableError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ScannerAlreadyRunningError(Exception):
|
||||||
|
pass
|
@ -13,7 +13,8 @@ import uuid
|
|||||||
from pony.orm import select
|
from pony.orm import select
|
||||||
from pony.orm import ObjectNotFound
|
from pony.orm import ObjectNotFound
|
||||||
|
|
||||||
from ..daemon import DaemonClient, DaemonUnavailableError
|
from ..daemon.client import DaemonClient
|
||||||
|
from ..daemon.exceptions import DaemonUnavailableError
|
||||||
from ..db import Folder, Track, Artist, Album, User, RatingTrack, StarredTrack
|
from ..db import Folder, Track, Artist, Album, User, RatingTrack, StarredTrack
|
||||||
from ..py23 import strtype
|
from ..py23 import strtype
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user