mirror of
https://github.com/spl0k/supysonic.git
synced 2024-12-22 17:06:17 +00:00
parent
9736622ce1
commit
9c58b695ff
@ -14,7 +14,7 @@ import time
|
|||||||
|
|
||||||
from pony.orm import db_session
|
from pony.orm import db_session
|
||||||
|
|
||||||
from .covers import find_cover_in_folder
|
from .covers import find_cover_in_folder, CoverFile
|
||||||
from .db import Folder, Artist, Album, Track, User
|
from .db import Folder, Artist, Album, Track, User
|
||||||
from .db import StarredFolder, StarredArtist, StarredAlbum, StarredTrack
|
from .db import StarredFolder, StarredArtist, StarredAlbum, StarredTrack
|
||||||
from .db import RatingFolder, RatingTrack
|
from .db import RatingFolder, RatingTrack
|
||||||
@ -90,14 +90,7 @@ class Scanner:
|
|||||||
f.delete() # Pony will cascade
|
f.delete() # Pony will cascade
|
||||||
continue
|
continue
|
||||||
|
|
||||||
album_name = None
|
self.find_cover(f.path)
|
||||||
track = f.tracks.select().first()
|
|
||||||
if track is not None:
|
|
||||||
album_name = track.album.name
|
|
||||||
|
|
||||||
cover = find_cover_in_folder(f.path, album_name)
|
|
||||||
f.cover_art = cover.name if cover is not None else None
|
|
||||||
|
|
||||||
folders += f.children
|
folders += f.children
|
||||||
|
|
||||||
folder.last_scan = int(time.time())
|
folder.last_scan = int(time.time())
|
||||||
@ -212,6 +205,46 @@ class Scanner:
|
|||||||
tr.folder = folder
|
tr.folder = folder
|
||||||
tr.path = dst_path
|
tr.path = dst_path
|
||||||
|
|
||||||
|
@db_session
|
||||||
|
def find_cover(self, dirpath):
|
||||||
|
if not isinstance(dirpath, strtype): # pragma: nocover
|
||||||
|
raise TypeError('Expecting string, got ' + str(type(dirpath)))
|
||||||
|
|
||||||
|
folder = Folder.get(path = dirpath)
|
||||||
|
if folder is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
album_name = None
|
||||||
|
track = folder.tracks.select().first()
|
||||||
|
if track is not None:
|
||||||
|
album_name = track.album.name
|
||||||
|
|
||||||
|
cover = find_cover_in_folder(folder.path, album_name)
|
||||||
|
folder.cover_art = cover.name if cover is not None else None
|
||||||
|
|
||||||
|
@db_session
|
||||||
|
def add_cover(self, path):
|
||||||
|
if not isinstance(path, strtype): # pragma: nocover
|
||||||
|
raise TypeError('Expecting string, got ' + str(type(path)))
|
||||||
|
|
||||||
|
folder = Folder.get(path = os.path.dirname(path))
|
||||||
|
if folder is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
cover_name = os.path.basename(path)
|
||||||
|
if not folder.cover_art:
|
||||||
|
folder.cover_art = cover_name
|
||||||
|
else:
|
||||||
|
album_name = None
|
||||||
|
track = folder.tracks.select().first()
|
||||||
|
if track is not None:
|
||||||
|
album_name = track.album.name
|
||||||
|
|
||||||
|
current_cover = CoverFile(folder.cover_art, album_name)
|
||||||
|
new_cover = CoverFile(cover_name, album_name)
|
||||||
|
if new_cover.score > current_cover.score:
|
||||||
|
folder.cover_art = cover_name
|
||||||
|
|
||||||
def __find_album(self, artist, album):
|
def __find_album(self, artist, album):
|
||||||
ar = self.__find_artist(artist)
|
ar = self.__find_artist(artist)
|
||||||
al = ar.albums.select(lambda a: a.name == album).first()
|
al = ar.albums.select(lambda a: a.name == album).first()
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
# Distributed under terms of the GNU AGPLv3 license.
|
# Distributed under terms of the GNU AGPLv3 license.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import os.path
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
@ -17,6 +18,7 @@ from threading import Thread, Condition, Timer
|
|||||||
from watchdog.observers import Observer
|
from watchdog.observers import Observer
|
||||||
from watchdog.events import PatternMatchingEventHandler
|
from watchdog.events import PatternMatchingEventHandler
|
||||||
|
|
||||||
|
from . import covers
|
||||||
from .db import init_database, release_database, Folder
|
from .db import init_database, release_database, Folder
|
||||||
from .py23 import dict
|
from .py23 import dict
|
||||||
from .scanner import Scanner
|
from .scanner import Scanner
|
||||||
@ -25,10 +27,13 @@ OP_SCAN = 1
|
|||||||
OP_REMOVE = 2
|
OP_REMOVE = 2
|
||||||
OP_MOVE = 4
|
OP_MOVE = 4
|
||||||
FLAG_CREATE = 8
|
FLAG_CREATE = 8
|
||||||
|
FLAG_COVER = 16
|
||||||
|
|
||||||
class SupysonicWatcherEventHandler(PatternMatchingEventHandler):
|
class SupysonicWatcherEventHandler(PatternMatchingEventHandler):
|
||||||
def __init__(self, extensions, queue, logger):
|
def __init__(self, extensions, queue, logger):
|
||||||
patterns = map(lambda e: "*." + e.lower(), extensions.split()) if extensions else None
|
patterns = None
|
||||||
|
if extensions:
|
||||||
|
patterns = list(map(lambda e: "*." + e.lower(), extensions.split())) + list(map(lambda e: "*" + e, covers.EXTENSIONS))
|
||||||
super(SupysonicWatcherEventHandler, self).__init__(patterns = patterns, ignore_directories = True)
|
super(SupysonicWatcherEventHandler, self).__init__(patterns = patterns, ignore_directories = True)
|
||||||
|
|
||||||
self.__queue = queue
|
self.__queue = queue
|
||||||
@ -37,29 +42,51 @@ class SupysonicWatcherEventHandler(PatternMatchingEventHandler):
|
|||||||
def dispatch(self, event):
|
def dispatch(self, event):
|
||||||
try:
|
try:
|
||||||
super(SupysonicWatcherEventHandler, self).dispatch(event)
|
super(SupysonicWatcherEventHandler, self).dispatch(event)
|
||||||
except Exception as e:
|
except Exception as e: # pragma: nocover
|
||||||
self.__logger.critical(e)
|
self.__logger.critical(e)
|
||||||
|
|
||||||
def on_created(self, event):
|
def on_created(self, event):
|
||||||
self.__logger.debug("File created: '%s'", event.src_path)
|
self.__logger.debug("File created: '%s'", event.src_path)
|
||||||
self.__queue.put(event.src_path, OP_SCAN | FLAG_CREATE)
|
|
||||||
|
op = OP_SCAN | FLAG_CREATE
|
||||||
|
if not covers.is_valid_cover(event.src_path):
|
||||||
|
self.__queue.put(event.src_path, op)
|
||||||
|
|
||||||
|
dirname = os.path.dirname(event.src_path)
|
||||||
|
with db_session:
|
||||||
|
folder = Folder.get(path = dirname)
|
||||||
|
if folder is None:
|
||||||
|
self.__queue.put(dirname, op | FLAG_COVER)
|
||||||
|
else:
|
||||||
|
self.__queue.put(event.src_path, op | FLAG_COVER)
|
||||||
|
|
||||||
def on_deleted(self, event):
|
def on_deleted(self, event):
|
||||||
self.__logger.debug("File deleted: '%s'", event.src_path)
|
self.__logger.debug("File deleted: '%s'", event.src_path)
|
||||||
self.__queue.put(event.src_path, OP_REMOVE)
|
|
||||||
|
op = OP_REMOVE
|
||||||
|
_, ext = os.path.splitext(event.src_path)
|
||||||
|
if ext in covers.EXTENSIONS:
|
||||||
|
op |= FLAG_COVER
|
||||||
|
self.__queue.put(event.src_path, op)
|
||||||
|
|
||||||
def on_modified(self, event):
|
def on_modified(self, event):
|
||||||
self.__logger.debug("File modified: '%s'", event.src_path)
|
self.__logger.debug("File modified: '%s'", event.src_path)
|
||||||
self.__queue.put(event.src_path, OP_SCAN)
|
if not covers.is_valid_cover(event.src_path):
|
||||||
|
self.__queue.put(event.src_path, OP_SCAN)
|
||||||
|
|
||||||
def on_moved(self, event):
|
def on_moved(self, event):
|
||||||
self.__logger.debug("File moved: '%s' -> '%s'", event.src_path, event.dest_path)
|
self.__logger.debug("File moved: '%s' -> '%s'", event.src_path, event.dest_path)
|
||||||
self.__queue.put(event.dest_path, OP_MOVE, src_path = event.src_path)
|
|
||||||
|
op = OP_MOVE
|
||||||
|
_, ext = os.path.splitext(event.src_path)
|
||||||
|
if ext in covers.EXTENSIONS:
|
||||||
|
op |= FLAG_COVER
|
||||||
|
self.__queue.put(event.dest_path, op, src_path = event.src_path)
|
||||||
|
|
||||||
class Event(object):
|
class Event(object):
|
||||||
def __init__(self, path, operation, **kwargs):
|
def __init__(self, path, operation, **kwargs):
|
||||||
if operation & (OP_SCAN | OP_REMOVE) == (OP_SCAN | OP_REMOVE):
|
if operation & (OP_SCAN | OP_REMOVE) == (OP_SCAN | OP_REMOVE):
|
||||||
raise Exception("Flags SCAN and REMOVE both set")
|
raise Exception("Flags SCAN and REMOVE both set") # pragma: nocover
|
||||||
|
|
||||||
self.__path = path
|
self.__path = path
|
||||||
self.__time = time.time()
|
self.__time = time.time()
|
||||||
@ -68,7 +95,7 @@ class Event(object):
|
|||||||
|
|
||||||
def set(self, operation, **kwargs):
|
def set(self, operation, **kwargs):
|
||||||
if operation & (OP_SCAN | OP_REMOVE) == (OP_SCAN | OP_REMOVE):
|
if operation & (OP_SCAN | OP_REMOVE) == (OP_SCAN | OP_REMOVE):
|
||||||
raise Exception("Flags SCAN and REMOVE both set")
|
raise Exception("Flags SCAN and REMOVE both set") # pragma: nocover
|
||||||
|
|
||||||
self.__time = time.time()
|
self.__time = time.time()
|
||||||
if operation & OP_SCAN:
|
if operation & OP_SCAN:
|
||||||
@ -113,7 +140,7 @@ class ScannerProcessingQueue(Thread):
|
|||||||
def run(self):
|
def run(self):
|
||||||
try:
|
try:
|
||||||
self.__run()
|
self.__run()
|
||||||
except Exception as e:
|
except Exception as e: # pragma: nocover
|
||||||
self.__logger.critical(e)
|
self.__logger.critical(e)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
@ -132,21 +159,48 @@ class ScannerProcessingQueue(Thread):
|
|||||||
|
|
||||||
item = self.__next_item()
|
item = self.__next_item()
|
||||||
while item:
|
while item:
|
||||||
if item.operation & OP_MOVE:
|
if item.operation & FLAG_COVER:
|
||||||
self.__logger.info("Moving: '%s' -> '%s'", item.src_path, item.path)
|
self.__process_cover_item(scanner, item)
|
||||||
scanner.move_file(item.src_path, item.path)
|
else:
|
||||||
if item.operation & OP_SCAN:
|
self.__process_regular_item(scanner, item)
|
||||||
self.__logger.info("Scanning: '%s'", item.path)
|
|
||||||
scanner.scan_file(item.path)
|
|
||||||
if item.operation & OP_REMOVE:
|
|
||||||
self.__logger.info("Removing: '%s'", item.path)
|
|
||||||
scanner.remove_file(item.path)
|
|
||||||
item = self.__next_item()
|
item = self.__next_item()
|
||||||
|
|
||||||
scanner.finish()
|
scanner.finish()
|
||||||
self.__logger.debug("Freeing scanner")
|
self.__logger.debug("Freeing scanner")
|
||||||
del scanner
|
del scanner
|
||||||
|
|
||||||
|
def __process_regular_item(self, scanner, item):
|
||||||
|
if item.operation & OP_MOVE:
|
||||||
|
self.__logger.info("Moving: '%s' -> '%s'", item.src_path, item.path)
|
||||||
|
scanner.move_file(item.src_path, item.path)
|
||||||
|
|
||||||
|
if item.operation & OP_SCAN:
|
||||||
|
self.__logger.info("Scanning: '%s'", item.path)
|
||||||
|
scanner.scan_file(item.path)
|
||||||
|
|
||||||
|
if item.operation & OP_REMOVE:
|
||||||
|
self.__logger.info("Removing: '%s'", item.path)
|
||||||
|
scanner.remove_file(item.path)
|
||||||
|
|
||||||
|
def __process_cover_item(self, scanner, item):
|
||||||
|
if item.operation & OP_SCAN:
|
||||||
|
if os.path.isdir(item.path):
|
||||||
|
self.__logger.info("Looking for covers: '%s'", item.path)
|
||||||
|
scanner.find_cover(item.path)
|
||||||
|
else:
|
||||||
|
self.__logger.info("Potentially adding cover: '%s'", item.path)
|
||||||
|
scanner.add_cover(item.path)
|
||||||
|
|
||||||
|
if item.operation & OP_REMOVE:
|
||||||
|
self.__logger.info("Removing cover: '%s'", item.path)
|
||||||
|
scanner.find_cover(os.path.dirname(item.path))
|
||||||
|
|
||||||
|
if item.operation & OP_MOVE:
|
||||||
|
self.__logger.info("Moving cover: '%s' -> '%s'", item.src_path, item.path)
|
||||||
|
scanner.find_cover(os.path.dirname(item.src_path))
|
||||||
|
scanner.add_cover(item.path)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.__running = False
|
self.__running = False
|
||||||
with self.__cond:
|
with self.__cond:
|
||||||
@ -232,7 +286,7 @@ class SupysonicWatcher(object):
|
|||||||
logger.info("Starting watcher for %s", folder.path)
|
logger.info("Starting watcher for %s", folder.path)
|
||||||
observer.schedule(handler, folder.path, recursive = True)
|
observer.schedule(handler, folder.path, recursive = True)
|
||||||
|
|
||||||
try:
|
try: # pragma: nocover
|
||||||
signal(SIGTERM, self.__terminate)
|
signal(SIGTERM, self.__terminate)
|
||||||
signal(SIGINT, self.__terminate)
|
signal(SIGINT, self.__terminate)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
@ -254,5 +308,5 @@ class SupysonicWatcher(object):
|
|||||||
self.__running = False
|
self.__running = False
|
||||||
|
|
||||||
def __terminate(self, signum, frame):
|
def __terminate(self, signum, frame):
|
||||||
self.stop()
|
self.stop() # pragma: nocover
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ from hashlib import sha1
|
|||||||
from pony.orm import db_session
|
from pony.orm import db_session
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
from supysonic.db import init_database, release_database, Track, Artist
|
from supysonic.db import init_database, release_database, Track, Artist, Folder
|
||||||
from supysonic.managers.folder import FolderManager
|
from supysonic.managers.folder import FolderManager
|
||||||
from supysonic.watcher import SupysonicWatcher
|
from supysonic.watcher import SupysonicWatcher
|
||||||
|
|
||||||
@ -99,14 +99,26 @@ class WatcherTestCase(WatcherTestBase):
|
|||||||
with tempfile.NamedTemporaryFile() as f:
|
with tempfile.NamedTemporaryFile() as f:
|
||||||
return os.path.basename(f.name)
|
return os.path.basename(f.name)
|
||||||
|
|
||||||
def _temppath(self):
|
def _temppath(self, suffix, depth = 0):
|
||||||
return os.path.join(self.__dir, self._tempname() + '.mp3')
|
if depth > 0:
|
||||||
|
dirpath = os.path.join(self.__dir, *(self._tempname() for _ in range(depth)))
|
||||||
|
os.makedirs(dirpath)
|
||||||
|
else:
|
||||||
|
dirpath = self.__dir
|
||||||
|
return os.path.join(dirpath, self._tempname() + suffix)
|
||||||
|
|
||||||
def _addfile(self):
|
def _addfile(self, depth = 0):
|
||||||
path = self._temppath()
|
path = self._temppath('.mp3', depth)
|
||||||
shutil.copyfile('tests/assets/folder/silence.mp3', path)
|
shutil.copyfile('tests/assets/folder/silence.mp3', path)
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
def _addcover(self, suffix = None, depth = 0):
|
||||||
|
suffix = '.jpg' if suffix is None else (suffix + '.jpg')
|
||||||
|
path = self._temppath(suffix, depth)
|
||||||
|
shutil.copyfile('tests/assets/cover.jpg', path)
|
||||||
|
return path
|
||||||
|
|
||||||
|
class AudioWatcherTestCase(WatcherTestCase):
|
||||||
@db_session
|
@db_session
|
||||||
def assertTrackCountEqual(self, expected):
|
def assertTrackCountEqual(self, expected):
|
||||||
self.assertEqual(Track.select().count(), expected)
|
self.assertEqual(Track.select().count(), expected)
|
||||||
@ -163,7 +175,7 @@ class WatcherTestCase(WatcherTestBase):
|
|||||||
self.assertEqual(Track.select().count(), 1)
|
self.assertEqual(Track.select().count(), 1)
|
||||||
trackid = Track.select().first().id
|
trackid = Track.select().first().id
|
||||||
|
|
||||||
newpath = self._temppath()
|
newpath = self._temppath('.mp3')
|
||||||
shutil.move(path, newpath)
|
shutil.move(path, newpath)
|
||||||
self._sleep()
|
self._sleep()
|
||||||
|
|
||||||
@ -179,7 +191,7 @@ class WatcherTestCase(WatcherTestBase):
|
|||||||
filename = self._tempname() + '.mp3'
|
filename = self._tempname() + '.mp3'
|
||||||
initialpath = os.path.join(tempfile.gettempdir(), filename)
|
initialpath = os.path.join(tempfile.gettempdir(), filename)
|
||||||
shutil.copyfile('tests/assets/folder/silence.mp3', initialpath)
|
shutil.copyfile('tests/assets/folder/silence.mp3', initialpath)
|
||||||
shutil.move(initialpath, os.path.join(self.__dir, filename))
|
shutil.move(initialpath, self._temppath('.mp3'))
|
||||||
self._sleep()
|
self._sleep()
|
||||||
self.assertTrackCountEqual(1)
|
self.assertTrackCountEqual(1)
|
||||||
|
|
||||||
@ -212,7 +224,7 @@ class WatcherTestCase(WatcherTestBase):
|
|||||||
|
|
||||||
def test_add_rename(self):
|
def test_add_rename(self):
|
||||||
path = self._addfile()
|
path = self._addfile()
|
||||||
shutil.move(path, self._temppath())
|
shutil.move(path, self._temppath('.mp3'))
|
||||||
self._sleep()
|
self._sleep()
|
||||||
self.assertTrackCountEqual(1)
|
self.assertTrackCountEqual(1)
|
||||||
|
|
||||||
@ -221,7 +233,7 @@ class WatcherTestCase(WatcherTestBase):
|
|||||||
self._sleep()
|
self._sleep()
|
||||||
self.assertTrackCountEqual(1)
|
self.assertTrackCountEqual(1)
|
||||||
|
|
||||||
newpath = self._temppath()
|
newpath = self._temppath('.mp3')
|
||||||
shutil.move(path, newpath)
|
shutil.move(path, newpath)
|
||||||
os.unlink(newpath)
|
os.unlink(newpath)
|
||||||
self._sleep()
|
self._sleep()
|
||||||
@ -229,7 +241,7 @@ class WatcherTestCase(WatcherTestBase):
|
|||||||
|
|
||||||
def test_add_rename_delete(self):
|
def test_add_rename_delete(self):
|
||||||
path = self._addfile()
|
path = self._addfile()
|
||||||
newpath = self._temppath()
|
newpath = self._temppath('.mp3')
|
||||||
shutil.move(path, newpath)
|
shutil.move(path, newpath)
|
||||||
os.unlink(newpath)
|
os.unlink(newpath)
|
||||||
self._sleep()
|
self._sleep()
|
||||||
@ -240,18 +252,112 @@ class WatcherTestCase(WatcherTestBase):
|
|||||||
self._sleep()
|
self._sleep()
|
||||||
self.assertTrackCountEqual(1)
|
self.assertTrackCountEqual(1)
|
||||||
|
|
||||||
newpath = self._temppath()
|
newpath = self._temppath('.mp3')
|
||||||
finalpath = self._temppath()
|
finalpath = self._temppath('.mp3')
|
||||||
shutil.move(path, newpath)
|
shutil.move(path, newpath)
|
||||||
shutil.move(newpath, finalpath)
|
shutil.move(newpath, finalpath)
|
||||||
self._sleep()
|
self._sleep()
|
||||||
self.assertTrackCountEqual(1)
|
self.assertTrackCountEqual(1)
|
||||||
|
|
||||||
|
class CoverWatcherTestCase(WatcherTestCase):
|
||||||
|
def test_add_file_then_cover(self):
|
||||||
|
self._addfile()
|
||||||
|
path = self._addcover()
|
||||||
|
self._sleep()
|
||||||
|
|
||||||
|
with db_session:
|
||||||
|
self.assertEqual(Folder.select().first().cover_art, os.path.basename(path))
|
||||||
|
|
||||||
|
def test_add_cover_then_file(self):
|
||||||
|
path = self._addcover()
|
||||||
|
self._addfile()
|
||||||
|
self._sleep()
|
||||||
|
|
||||||
|
with db_session:
|
||||||
|
self.assertEqual(Folder.select().first().cover_art, os.path.basename(path))
|
||||||
|
|
||||||
|
def test_remove_cover(self):
|
||||||
|
self._addfile()
|
||||||
|
path = self._addcover()
|
||||||
|
self._sleep()
|
||||||
|
|
||||||
|
os.unlink(path)
|
||||||
|
self._sleep()
|
||||||
|
|
||||||
|
with db_session:
|
||||||
|
self.assertIsNone(Folder.select().first().cover_art)
|
||||||
|
|
||||||
|
def test_naming_add_good(self):
|
||||||
|
bad = os.path.basename(self._addcover())
|
||||||
|
self._sleep()
|
||||||
|
good = os.path.basename(self._addcover('cover'))
|
||||||
|
self._sleep()
|
||||||
|
|
||||||
|
with db_session:
|
||||||
|
self.assertEqual(Folder.select().first().cover_art, good)
|
||||||
|
|
||||||
|
def test_naming_add_bad(self):
|
||||||
|
good = os.path.basename(self._addcover('cover'))
|
||||||
|
self._sleep()
|
||||||
|
bad = os.path.basename(self._addcover())
|
||||||
|
self._sleep()
|
||||||
|
|
||||||
|
with db_session:
|
||||||
|
self.assertEqual(Folder.select().first().cover_art, good)
|
||||||
|
|
||||||
|
def test_naming_remove_good(self):
|
||||||
|
bad = self._addcover()
|
||||||
|
good = self._addcover('cover')
|
||||||
|
self._sleep()
|
||||||
|
os.unlink(good)
|
||||||
|
self._sleep()
|
||||||
|
|
||||||
|
with db_session:
|
||||||
|
self.assertEqual(Folder.select().first().cover_art, os.path.basename(bad))
|
||||||
|
|
||||||
|
def test_naming_remove_bad(self):
|
||||||
|
bad = self._addcover()
|
||||||
|
good = self._addcover('cover')
|
||||||
|
self._sleep()
|
||||||
|
os.unlink(bad)
|
||||||
|
self._sleep()
|
||||||
|
|
||||||
|
with db_session:
|
||||||
|
self.assertEqual(Folder.select().first().cover_art, os.path.basename(good))
|
||||||
|
|
||||||
|
def test_rename(self):
|
||||||
|
path = self._addcover()
|
||||||
|
self._sleep()
|
||||||
|
newpath = self._temppath('.jpg')
|
||||||
|
shutil.move(path, newpath)
|
||||||
|
self._sleep()
|
||||||
|
|
||||||
|
with db_session:
|
||||||
|
self.assertEqual(Folder.select().first().cover_art, os.path.basename(newpath))
|
||||||
|
|
||||||
|
def test_add_to_folder_without_track(self):
|
||||||
|
path = self._addcover(depth = 1)
|
||||||
|
self._sleep()
|
||||||
|
|
||||||
|
with db_session:
|
||||||
|
self.assertFalse(Folder.exists(cover_art = os.path.basename(path)))
|
||||||
|
|
||||||
|
def test_remove_from_folder_without_track(self):
|
||||||
|
path = self._addcover(depth = 1)
|
||||||
|
self._sleep()
|
||||||
|
os.unlink(path)
|
||||||
|
self._sleep()
|
||||||
|
|
||||||
|
def test_add_track_to_empty_folder(self):
|
||||||
|
self._addfile(1)
|
||||||
|
self._sleep()
|
||||||
|
|
||||||
def suite():
|
def suite():
|
||||||
suite = unittest.TestSuite()
|
suite = unittest.TestSuite()
|
||||||
|
|
||||||
suite.addTest(unittest.makeSuite(NothingToWatchTestCase))
|
suite.addTest(unittest.makeSuite(NothingToWatchTestCase))
|
||||||
suite.addTest(unittest.makeSuite(WatcherTestCase))
|
suite.addTest(unittest.makeSuite(AudioWatcherTestCase))
|
||||||
|
suite.addTest(unittest.makeSuite(CoverWatcherTestCase))
|
||||||
|
|
||||||
return suite
|
return suite
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user