mirror of
https://github.com/spl0k/supysonic.git
synced 2024-12-22 17:06:17 +00:00
parent
d570eddc6d
commit
954c75bc35
@ -40,12 +40,12 @@ class TimedProgressDisplay:
|
|||||||
self.__last_display = 0
|
self.__last_display = 0
|
||||||
self.__last_len = 0
|
self.__last_len = 0
|
||||||
|
|
||||||
def __call__(self, scanned, total):
|
def __call__(self, scanned):
|
||||||
if time.time() - self.__last_display > self.__interval or scanned == total:
|
if time.time() - self.__last_display > self.__interval:
|
||||||
if not self.__last_len:
|
if not self.__last_len:
|
||||||
self.__stdout.write("Scanning '{0}': ".format(self.__name))
|
self.__stdout.write("Scanning '{0}': ".format(self.__name))
|
||||||
|
|
||||||
progress = "{0}% ({1}/{2})".format((scanned * 100) / total, scanned, total)
|
progress = '{0} files scanned'.format(scanned)
|
||||||
self.__stdout.write('\b' * self.__last_len)
|
self.__stdout.write('\b' * self.__last_len)
|
||||||
self.__stdout.write(progress)
|
self.__stdout.write(progress)
|
||||||
self.__stdout.flush()
|
self.__stdout.flush()
|
||||||
@ -190,11 +190,15 @@ class SupysonicCLI(cmd.Cmd):
|
|||||||
self.write_line()
|
self.write_line()
|
||||||
|
|
||||||
scanner.finish()
|
scanner.finish()
|
||||||
added, deleted = scanner.stats()
|
stats = scanner.stats()
|
||||||
|
|
||||||
self.write_line("Scanning done")
|
self.write_line('Scanning done')
|
||||||
self.write_line('Added: %i artists, %i albums, %i tracks' % (added[0], added[1], added[2]))
|
self.write_line('Added: {0.artists} artists, {0.albums} albums, {0.tracks} tracks'.format(stats.added))
|
||||||
self.write_line('Deleted: %i artists, %i albums, %i tracks' % (deleted[0], deleted[1], deleted[2]))
|
self.write_line('Deleted: {0.artists} artists, {0.albums} albums, {0.tracks} tracks'.format(stats.deleted))
|
||||||
|
if stats.errors:
|
||||||
|
self.write_line('Errors in:')
|
||||||
|
for err in stats.errors:
|
||||||
|
self.write_line('- ' + err)
|
||||||
|
|
||||||
user_parser = CLIParser(prog = 'user', add_help = False)
|
user_parser = CLIParser(prog = 'user', add_help = False)
|
||||||
user_subparsers = user_parser.add_subparsers(dest = 'action')
|
user_subparsers = user_parser.add_subparsers(dest = 'action')
|
||||||
|
@ -104,9 +104,13 @@ def scan_folder(id = None):
|
|||||||
scanner.scan(folder)
|
scanner.scan(folder)
|
||||||
|
|
||||||
scanner.finish()
|
scanner.finish()
|
||||||
added, deleted = scanner.stats()
|
stats = scanner.stats()
|
||||||
|
|
||||||
flash('Added: %i artists, %i albums, %i tracks' % (added[0], added[1], added[2]))
|
flash('Added: {0.artists} artists, {0.albums} albums, {0.tracks} tracks'.format(stats.added))
|
||||||
flash('Deleted: %i artists, %i albums, %i tracks' % (deleted[0], deleted[1], deleted[2]))
|
flash('Deleted: {0.artists} artists, {0.albums} albums, {0.tracks} tracks'.format(stats.deleted))
|
||||||
|
if stats.errors:
|
||||||
|
flash('Errors in:')
|
||||||
|
for err in stats.errors:
|
||||||
|
flash('- ' + err)
|
||||||
return redirect(url_for('folder_index'))
|
return redirect(url_for('folder_index'))
|
||||||
|
|
||||||
|
@ -30,6 +30,18 @@ from .db import StarredFolder, StarredArtist, StarredAlbum, StarredTrack
|
|||||||
from .db import RatingFolder, RatingTrack
|
from .db import RatingFolder, RatingTrack
|
||||||
from .py23 import strtype
|
from .py23 import strtype
|
||||||
|
|
||||||
|
class StatsDetails(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.artists = 0
|
||||||
|
self.albums = 0
|
||||||
|
self.tracks = 0
|
||||||
|
|
||||||
|
class Stats(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.added = StatsDetails()
|
||||||
|
self.deleted = StatsDetails()
|
||||||
|
self.errors = []
|
||||||
|
|
||||||
class Scanner:
|
class Scanner:
|
||||||
def __init__(self, force = False, extensions = None):
|
def __init__(self, force = False, extensions = None):
|
||||||
if extensions is not None and not isinstance(extensions, list):
|
if extensions is not None and not isinstance(extensions, list):
|
||||||
@ -37,13 +49,7 @@ class Scanner:
|
|||||||
|
|
||||||
self.__force = force
|
self.__force = force
|
||||||
|
|
||||||
self.__added_artists = 0
|
self.__stats = Stats()
|
||||||
self.__added_albums = 0
|
|
||||||
self.__added_tracks = 0
|
|
||||||
self.__deleted_artists = 0
|
|
||||||
self.__deleted_albums = 0
|
|
||||||
self.__deleted_tracks = 0
|
|
||||||
|
|
||||||
self.__extensions = extensions
|
self.__extensions = extensions
|
||||||
|
|
||||||
self.__folders_to_check = set()
|
self.__folders_to_check = set()
|
||||||
@ -59,15 +65,28 @@ class Scanner:
|
|||||||
raise TypeError('Expecting Folder instance, got ' + str(type(folder)))
|
raise TypeError('Expecting Folder instance, got ' + str(type(folder)))
|
||||||
|
|
||||||
# Scan new/updated files
|
# Scan new/updated files
|
||||||
files = [ os.path.join(root, f) for root, _, fs in os.walk(folder.path) for f in fs if self.__is_valid_path(os.path.join(root, f)) ]
|
to_scan = [ folder.path ]
|
||||||
total = len(files)
|
scanned = 0
|
||||||
current = 0
|
while to_scan:
|
||||||
|
path = to_scan.pop()
|
||||||
|
for f in os.listdir(path):
|
||||||
|
try: # test for badly encoded filenames
|
||||||
|
f.encode('utf-8')
|
||||||
|
except UnicodeError:
|
||||||
|
self.__stats.errors.append(path)
|
||||||
|
continue
|
||||||
|
|
||||||
|
full_path = os.path.join(path, f)
|
||||||
|
if os.path.islink(full_path):
|
||||||
|
continue
|
||||||
|
elif os.path.isdir(full_path):
|
||||||
|
to_scan.append(full_path)
|
||||||
|
elif os.path.isfile(full_path) and self.__is_valid_path(full_path):
|
||||||
|
self.scan_file(full_path)
|
||||||
|
scanned += 1
|
||||||
|
|
||||||
for path in files:
|
|
||||||
self.scan_file(path)
|
|
||||||
current += 1
|
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
progress_callback(current, total)
|
progress_callback(scanned)
|
||||||
|
|
||||||
# Remove files that have been deleted
|
# Remove files that have been deleted
|
||||||
for track in Track.select(lambda t: t.root_folder == folder):
|
for track in Track.select(lambda t: t.root_folder == folder):
|
||||||
@ -90,7 +109,7 @@ class Scanner:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
self.__artists_to_check.add(album.artist.id)
|
self.__artists_to_check.add(album.artist.id)
|
||||||
self.__deleted_albums += 1
|
self.__stats.deleted.albums += 1
|
||||||
album.delete()
|
album.delete()
|
||||||
self.__albums_to_check.clear()
|
self.__albums_to_check.clear()
|
||||||
|
|
||||||
@ -98,7 +117,7 @@ class Scanner:
|
|||||||
if not artist.albums.is_empty() or not artist.tracks.is_empty():
|
if not artist.albums.is_empty() or not artist.tracks.is_empty():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.__deleted_artists += 1
|
self.__stats.deleted.artists += 1
|
||||||
artist.delete()
|
artist.delete()
|
||||||
self.__artists_to_check.clear()
|
self.__artists_to_check.clear()
|
||||||
|
|
||||||
@ -168,7 +187,7 @@ class Scanner:
|
|||||||
trdict['artist'] = trartist
|
trdict['artist'] = trartist
|
||||||
|
|
||||||
Track(**trdict)
|
Track(**trdict)
|
||||||
self.__added_tracks += 1
|
self.__stats.added.tracks += 1
|
||||||
else:
|
else:
|
||||||
if tr.album.id != tralbum.id:
|
if tr.album.id != tralbum.id:
|
||||||
self.__albums_to_check.add(tr.album.id)
|
self.__albums_to_check.add(tr.album.id)
|
||||||
@ -192,7 +211,7 @@ class Scanner:
|
|||||||
self.__folders_to_check.add(tr.folder.id)
|
self.__folders_to_check.add(tr.folder.id)
|
||||||
self.__albums_to_check.add(tr.album.id)
|
self.__albums_to_check.add(tr.album.id)
|
||||||
self.__artists_to_check.add(tr.artist.id)
|
self.__artists_to_check.add(tr.artist.id)
|
||||||
self.__deleted_tracks += 1
|
self.__stats.deleted.tracks += 1
|
||||||
tr.delete()
|
tr.delete()
|
||||||
|
|
||||||
@db_session
|
@db_session
|
||||||
@ -231,7 +250,7 @@ class Scanner:
|
|||||||
return al
|
return al
|
||||||
|
|
||||||
al = Album(name = album, artist = ar)
|
al = Album(name = album, artist = ar)
|
||||||
self.__added_albums += 1
|
self.__stats.added.albums += 1
|
||||||
|
|
||||||
return al
|
return al
|
||||||
|
|
||||||
@ -241,7 +260,7 @@ class Scanner:
|
|||||||
return ar
|
return ar
|
||||||
|
|
||||||
ar = Artist(name = artist)
|
ar = Artist(name = artist)
|
||||||
self.__added_artists += 1
|
self.__stats.added.artists += 1
|
||||||
|
|
||||||
return ar
|
return ar
|
||||||
|
|
||||||
@ -289,5 +308,5 @@ class Scanner:
|
|||||||
return default
|
return default
|
||||||
|
|
||||||
def stats(self):
|
def stats(self):
|
||||||
return (self.__added_artists, self.__added_albums, self.__added_tracks), (self.__deleted_artists, self.__deleted_albums, self.__deleted_tracks)
|
return self.__stats
|
||||||
|
|
||||||
|
BIN
tests/assets/�/silence.mp3
Executable file
BIN
tests/assets/�/silence.mp3
Executable file
Binary file not shown.
@ -56,10 +56,8 @@ class ScannerTestCase(unittest.TestCase):
|
|||||||
|
|
||||||
@db_session
|
@db_session
|
||||||
def test_progress(self):
|
def test_progress(self):
|
||||||
def progress(processed, total):
|
def progress(processed):
|
||||||
self.assertIsInstance(processed, int)
|
self.assertIsInstance(processed, int)
|
||||||
self.assertIsInstance(total, int)
|
|
||||||
self.assertLessEqual(processed, total)
|
|
||||||
|
|
||||||
self.scanner.scan(db.Folder[self.folderid], progress)
|
self.scanner.scan(db.Folder[self.folderid], progress)
|
||||||
|
|
||||||
@ -192,7 +190,13 @@ class ScannerTestCase(unittest.TestCase):
|
|||||||
self.assertIsNotNone(db.Album.get(name = 'Awesome album'))
|
self.assertIsNotNone(db.Album.get(name = 'Awesome album'))
|
||||||
|
|
||||||
def test_stats(self):
|
def test_stats(self):
|
||||||
self.assertEqual(self.scanner.stats(), ((1,1,1),(0,0,0)))
|
stats = self.scanner.stats()
|
||||||
|
self.assertEqual(stats.added.artists, 1)
|
||||||
|
self.assertEqual(stats.added.albums, 1)
|
||||||
|
self.assertEqual(stats.added.tracks, 1)
|
||||||
|
self.assertEqual(stats.deleted.artists, 0)
|
||||||
|
self.assertEqual(stats.deleted.albums, 0)
|
||||||
|
self.assertEqual(stats.deleted.tracks, 0)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Loading…
Reference in New Issue
Block a user