mirror of
https://github.com/spl0k/supysonic.git
synced 2024-12-22 17:06:17 +00:00
Renamed supysonic-watcher
to supysonic-daemon
Since it no longer only watches files Also changed the way it's installed
This commit is contained in:
parent
8d9b2658d2
commit
e29ab91a97
@ -8,47 +8,11 @@
|
||||
#
|
||||
# Distributed under terms of the GNU AGPLv3 license.
|
||||
|
||||
import logging
|
||||
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
from signal import signal, SIGTERM, SIGINT
|
||||
|
||||
from supysonic.config import IniConfig
|
||||
from supysonic.daemon import Daemon
|
||||
from supysonic.db import init_database, release_database
|
||||
|
||||
logger = logging.getLogger('supysonic')
|
||||
|
||||
daemon = None
|
||||
|
||||
def setup_logging(config):
|
||||
if config['log_file']:
|
||||
if config['log_file'] == '/dev/null':
|
||||
log_handler = logging.NullHandler()
|
||||
else:
|
||||
log_handler = TimedRotatingFileHandler(config['log_file'], when = 'midnight')
|
||||
log_handler.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(message)s"))
|
||||
else:
|
||||
log_handler = logging.StreamHandler()
|
||||
log_handler.setFormatter(logging.Formatter("[%(levelname)s] %(message)s"))
|
||||
logger.addHandler(log_handler)
|
||||
if 'log_level' in config:
|
||||
level = getattr(logging, config['log_level'].upper(), logging.NOTSET)
|
||||
logger.setLevel(level)
|
||||
|
||||
def __terminate(signum, frame):
|
||||
logger.debug("Got signal %i. Stopping...", signum)
|
||||
daemon.terminate()
|
||||
release_database()
|
||||
import warnings
|
||||
from supysonic.daemon import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
config = IniConfig.from_common_locations()
|
||||
setup_logging(config.DAEMON)
|
||||
|
||||
signal(SIGTERM, __terminate)
|
||||
signal(SIGINT, __terminate)
|
||||
|
||||
init_database(config.BASE['database_uri'])
|
||||
daemon = Daemon(config)
|
||||
daemon.run()
|
||||
release_database()
|
||||
warnings.warn(
|
||||
"You're using an old version of the `supysonic-watcher` script.\nNo worries though, it will still work (for some time), but you should call `supysonic-daemon` instead.",
|
||||
DeprecationWarning)
|
||||
main()
|
||||
|
6
setup.py
6
setup.py
@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim:fenc=utf-8
|
||||
# coding: utf-8
|
||||
#
|
||||
# This file is part of Supysonic.
|
||||
# Supysonic is a Python implementation of the Subsonic server API.
|
||||
@ -38,7 +37,8 @@ setup(
|
||||
license=project.LICENSE,
|
||||
packages=find_packages(exclude=['tests*']),
|
||||
install_requires = reqs,
|
||||
scripts=['bin/supysonic-cli', 'bin/supysonic-watcher'],
|
||||
scripts=['bin/supysonic-cli'],
|
||||
entry_points={ 'console_scripts': ['supysonic-daemon=supysonic.daemon:main'] },
|
||||
zip_safe=False,
|
||||
include_package_data=True,
|
||||
test_suite='tests.suite',
|
||||
|
@ -1,102 +1,61 @@
|
||||
# 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
|
||||
# Copyright (C) 2014-2019 Alban 'spl0k' Féron
|
||||
#
|
||||
# Distributed under terms of the GNU AGPLv3 license.
|
||||
|
||||
import logging
|
||||
import time
|
||||
|
||||
from multiprocessing.connection import Listener, Client
|
||||
from pony.orm import db_session, select
|
||||
from threading import Thread, Event
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
from signal import signal, SIGTERM, SIGINT
|
||||
|
||||
from .client import DaemonCommand
|
||||
from ..db import Folder
|
||||
from ..scanner import Scanner
|
||||
from ..utils import get_secret_key
|
||||
from ..watcher import SupysonicWatcher
|
||||
from .client import DaemonClient
|
||||
from .server import Daemon
|
||||
|
||||
__all__ = [ 'Daemon' ]
|
||||
from ..config import IniConfig
|
||||
from ..db import init_database, release_database
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
__all__ = [ 'Daemon', 'DaemonClient' ]
|
||||
|
||||
class Daemon(object):
|
||||
def __init__(self, config):
|
||||
self.__config = config
|
||||
self.__listener = None
|
||||
self.__watcher = None
|
||||
self.__scanner = None
|
||||
self.__stopped = Event()
|
||||
logger = logging.getLogger("supysonic")
|
||||
|
||||
watcher = property(lambda self: self.__watcher)
|
||||
scanner = property(lambda self: self.__scanner)
|
||||
daemon = None
|
||||
|
||||
def __handle_connection(self, connection):
|
||||
cmd = connection.recv()
|
||||
logger.debug('Received %s', cmd)
|
||||
if cmd is None:
|
||||
pass
|
||||
elif isinstance(cmd, DaemonCommand):
|
||||
cmd.apply(connection, self)
|
||||
def setup_logging(config):
|
||||
if config['log_file']:
|
||||
if config['log_file'] == '/dev/null':
|
||||
log_handler = logging.NullHandler()
|
||||
else:
|
||||
logger.warn('Received unknown command %s', cmd)
|
||||
log_handler = TimedRotatingFileHandler(config['log_file'], when = 'midnight')
|
||||
log_handler.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(message)s"))
|
||||
else:
|
||||
log_handler = logging.StreamHandler()
|
||||
log_handler.setFormatter(logging.Formatter("[%(levelname)s] %(message)s"))
|
||||
logger.addHandler(log_handler)
|
||||
if 'log_level' in config:
|
||||
level = getattr(logging, config['log_level'].upper(), logging.NOTSET)
|
||||
logger.setLevel(level)
|
||||
|
||||
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)
|
||||
def __terminate(signum, frame):
|
||||
global daemon
|
||||
|
||||
if self.__config.DAEMON['run_watcher']:
|
||||
self.__watcher = SupysonicWatcher(self.__config)
|
||||
self.__watcher.start()
|
||||
logger.debug("Got signal %i. Stopping...", signum)
|
||||
daemon.terminate()
|
||||
release_database()
|
||||
|
||||
Thread(target=self.__listen).start()
|
||||
while not self.__stopped.is_set():
|
||||
time.sleep(1)
|
||||
def main():
|
||||
global daemon
|
||||
|
||||
def __listen(self):
|
||||
while not self.__stopped.is_set():
|
||||
conn = self.__listener.accept()
|
||||
self.__handle_connection(conn)
|
||||
config = IniConfig.from_common_locations()
|
||||
setup_logging(config.DAEMON)
|
||||
|
||||
def start_scan(self, folders = [], force = False):
|
||||
if not folders:
|
||||
with db_session:
|
||||
folders = select(f.name for f in Folder if f.root)[:]
|
||||
signal(SIGTERM, __terminate)
|
||||
signal(SIGINT, __terminate)
|
||||
|
||||
if self.__scanner is not None and self.__scanner.is_alive():
|
||||
for f in folders:
|
||||
self.__scanner.queue_folder(f)
|
||||
return
|
||||
|
||||
extensions = self.__config.BASE['scanner_extensions']
|
||||
if extensions:
|
||||
extensions = extensions.split(' ')
|
||||
|
||||
self.__scanner = Scanner(force = force, extensions = extensions, on_folder_start = self.__unwatch, on_folder_end = self.__watch)
|
||||
for f in folders:
|
||||
self.__scanner.queue_folder(f)
|
||||
|
||||
self.__scanner.start()
|
||||
|
||||
def __watch(self, folder):
|
||||
if self.__watcher is not None:
|
||||
self.__watcher.add_folder(folder.path)
|
||||
|
||||
def __unwatch(self, folder):
|
||||
if self.__watcher is not None:
|
||||
self.__watcher.remove_folder(folder.path)
|
||||
|
||||
def terminate(self):
|
||||
self.__stopped.set()
|
||||
with Client(self.__listener.address, authkey = self.__listener._authkey) as c:
|
||||
c.send(None)
|
||||
|
||||
if self.__scanner is not None:
|
||||
self.__scanner.stop()
|
||||
self.__scanner.join()
|
||||
if self.__watcher is not None:
|
||||
self.__watcher.stop()
|
||||
init_database(config.BASE['database_uri'])
|
||||
daemon = Daemon(config)
|
||||
daemon.run()
|
||||
release_database()
|
||||
|
14
supysonic/daemon/__main__.py
Executable file
14
supysonic/daemon/__main__.py
Executable file
@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env python
|
||||
# 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.
|
||||
|
||||
from . import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
102
supysonic/daemon/server.py
Normal file
102
supysonic/daemon/server.py
Normal file
@ -0,0 +1,102 @@
|
||||
# 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
|
||||
import time
|
||||
|
||||
from multiprocessing.connection import Listener, Client
|
||||
from pony.orm import db_session, select
|
||||
from threading import Thread, Event
|
||||
|
||||
from .client import DaemonCommand
|
||||
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
|
||||
self.__stopped = Event()
|
||||
|
||||
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 cmd is None:
|
||||
pass
|
||||
elif 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()
|
||||
|
||||
Thread(target=self.__listen).start()
|
||||
while not self.__stopped.is_set():
|
||||
time.sleep(1)
|
||||
|
||||
def __listen(self):
|
||||
while not self.__stopped.is_set():
|
||||
conn = self.__listener.accept()
|
||||
self.__handle_connection(conn)
|
||||
|
||||
def start_scan(self, folders = [], force = False):
|
||||
if not folders:
|
||||
with db_session:
|
||||
folders = select(f.name for f in Folder if f.root)[:]
|
||||
|
||||
if self.__scanner is not None and self.__scanner.is_alive():
|
||||
for f in folders:
|
||||
self.__scanner.queue_folder(f)
|
||||
return
|
||||
|
||||
extensions = self.__config.BASE['scanner_extensions']
|
||||
if extensions:
|
||||
extensions = extensions.split(' ')
|
||||
|
||||
self.__scanner = Scanner(force = force, extensions = extensions, on_folder_start = self.__unwatch, on_folder_end = self.__watch)
|
||||
for f in folders:
|
||||
self.__scanner.queue_folder(f)
|
||||
|
||||
self.__scanner.start()
|
||||
|
||||
def __watch(self, folder):
|
||||
if self.__watcher is not None:
|
||||
self.__watcher.add_folder(folder.path)
|
||||
|
||||
def __unwatch(self, folder):
|
||||
if self.__watcher is not None:
|
||||
self.__watcher.remove_folder(folder.path)
|
||||
|
||||
def terminate(self):
|
||||
self.__stopped.set()
|
||||
with Client(self.__listener.address, authkey = self.__listener._authkey) as c:
|
||||
c.send(None)
|
||||
|
||||
if self.__scanner is not None:
|
||||
self.__scanner.stop()
|
||||
self.__scanner.join()
|
||||
if self.__watcher is not None:
|
||||
self.__watcher.stop()
|
Loading…
Reference in New Issue
Block a user