1
0
mirror of https://github.com/spl0k/supysonic.git synced 2024-09-20 03:11:04 +00:00
supysonic/bin/supysonic-watcher

158 lines
4.5 KiB
Plaintext
Raw Normal View History

#!/usr/bin/python
# coding: utf-8
# This file is part of Supysonic.
#
# Supysonic is a Python implementation of the Subsonic server API.
# Copyright (C) 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/>.
import time, sys
import logging
from threading import Thread, Lock
from logging.handlers import TimedRotatingFileHandler
from watchdog.observers import Observer
from watchdog.events import PatternMatchingEventHandler
from supysonic import config
from supysonic.scanner import Scanner
class SupysonicWatcherEventHandler(PatternMatchingEventHandler):
def __init__(self, queue, logger):
extensions = config.get('base', 'scanner_extensions')
patterns = map(lambda e: "*." + e.lower(), extensions.split()) if extensions else None
super(SupysonicWatcherEventHandler, self).__init__(patterns = patterns, ignore_directories = True)
self.__queue = queue
self.__logger = logger
def on_created(self, event):
self.__logger.debug("File created: '%s'", event.src_path)
self.__queue.put(event.src_path)
def on_deleted(self, event):
self.__logger.debug("File deleted: '%s'", event.src_path)
track = db.Track.query.filter(db.Track.path == event.src_path).first()
if track:
folder = track.root_folder
Scanner(db.session).prune(folder)
db.session.commit()
db.session.remove()
else:
self.__logger.debug("Deleted file %s not in the database", event.src_path)
def on_modified(self, event):
self.__logger.debug("File modified: '%s'", event.src_path)
self.__queue.put(event.src_path)
def on_moved(self, event):
pass
class ScannerProcessingQueue(Thread):
def __init__(self, logger):
super(ScannerProcessingQueue, self).__init__()
self.__logger = logger
self.__lock = Lock()
self.__queue = {}
self.__running = True
def run(self):
while self.__running:
time.sleep(5)
with self.__lock:
if not self.__queue:
continue
self.__logger.debug("Instantiating scanner")
scanner = Scanner(db.session)
self.__lock.acquire()
while self.__queue:
path = sorted(self.__queue.iteritems(), key = lambda i: i[1])[0][0]
self.__lock.release()
self.__logger.info("Scanning: '%s'", path)
scanner.scan_file(path)
self.__lock.acquire()
del self.__queue[path]
self.__lock.release()
db.session.commit()
db.session.remove()
self.__logger.debug("Freeing scanner")
del scanner
def stop(self):
self.__running = False
def put(self, path):
if not self.__running:
raise RuntimeError("Trying to put an item in a stopped queue")
with self.__lock:
self.__queue[path] = time.time()
if __name__ == "__main__":
if not config.check():
sys.exit(1)
logger = logging.getLogger(__name__)
if config.get('daemon', 'log_file'):
log_handler = TimedRotatingFileHandler(config.get('daemon', 'log_file'), when = 'midnight')
else:
log_handler = logging.NullHandler()
log_handler.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(message)s"))
logger.addHandler(log_handler)
if config.get('daemon', 'log_level'):
mapping = {
'DEBUG': logging.DEBUG,
'INFO': logging.INFO,
'WARNING': logging.WARNING,
'ERROR': logging.ERROR,
'CRTICAL': logging.CRITICAL
}
logger.setLevel(mapping.get(config.get('daemon', 'log_level').upper(), logging.NOTSET))
from supysonic import db
db.init_db()
if not db.Folder.query.filter(db.Folder.root == True).count():
logger.info("No folder set. Exiting.")
sys.exit(0)
queue = ScannerProcessingQueue(logger)
handler = SupysonicWatcherEventHandler(queue, logger)
observer = Observer()
for folder in db.Folder.query.filter(db.Folder.root == True):
logger.info("Starting watcher for %s", folder.path)
observer.schedule(handler, folder.path, recursive = True)
queue.start()
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
logger.info("Stopping watcher")
observer.stop()
observer.join()
queue.stop()
queue.join()