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

Ignore files with a badly encoded path

Closes #85
This commit is contained in:
spl0k 2018-01-20 18:26:58 +01:00
parent d570eddc6d
commit 954c75bc35
5 changed files with 67 additions and 36 deletions

View File

@ -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')

View File

@ -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'))

View File

@ -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

Binary file not shown.

View File

@ -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()