Skip to content

Commit

Permalink
Ignore files with a badly encoded path
Browse files Browse the repository at this point in the history
Closes #85
  • Loading branch information
spl0k committed Jan 20, 2018
1 parent d570edd commit 954c75b
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 39 deletions.
20 changes: 12 additions & 8 deletions supysonic/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ def __init__(self, name, stdout, interval = 5):
self.__last_display = 0
self.__last_len = 0

def __call__(self, scanned, total):
if time.time() - self.__last_display > self.__interval or scanned == total:
def __call__(self, scanned):
if time.time() - self.__last_display > self.__interval:
if not self.__last_len:
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(progress)
self.__stdout.flush()
Expand Down Expand Up @@ -190,11 +190,15 @@ def folder_scan(self, folders, force):
self.write_line()

scanner.finish()
added, deleted = scanner.stats()

self.write_line("Scanning done")
self.write_line('Added: %i artists, %i albums, %i tracks' % (added[0], added[1], added[2]))
self.write_line('Deleted: %i artists, %i albums, %i tracks' % (deleted[0], deleted[1], deleted[2]))
stats = scanner.stats()

self.write_line('Scanning done')
self.write_line('Added: {0.artists} artists, {0.albums} albums, {0.tracks} tracks'.format(stats.added))
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_subparsers = user_parser.add_subparsers(dest = 'action')
Expand Down
12 changes: 8 additions & 4 deletions supysonic/frontend/folder.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,13 @@ def scan_folder(id = None):
scanner.scan(folder)

scanner.finish()
added, deleted = scanner.stats()

flash('Added: %i artists, %i albums, %i tracks' % (added[0], added[1], added[2]))
flash('Deleted: %i artists, %i albums, %i tracks' % (deleted[0], deleted[1], deleted[2]))
stats = scanner.stats()

flash('Added: {0.artists} artists, {0.albums} albums, {0.tracks} tracks'.format(stats.added))
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'))

65 changes: 42 additions & 23 deletions supysonic/scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,26 @@
from .db import RatingFolder, RatingTrack
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:
def __init__(self, force = False, extensions = None):
if extensions is not None and not isinstance(extensions, list):
raise TypeError('Invalid extensions type')

self.__force = force

self.__added_artists = 0
self.__added_albums = 0
self.__added_tracks = 0
self.__deleted_artists = 0
self.__deleted_albums = 0
self.__deleted_tracks = 0

self.__stats = Stats()
self.__extensions = extensions

self.__folders_to_check = set()
Expand All @@ -59,15 +65,28 @@ def scan(self, folder, progress_callback = None):
raise TypeError('Expecting Folder instance, got ' + str(type(folder)))

# 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)) ]
total = len(files)
current = 0

for path in files:
self.scan_file(path)
current += 1
if progress_callback:
progress_callback(current, total)
to_scan = [ folder.path ]
scanned = 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

if progress_callback:
progress_callback(scanned)

# Remove files that have been deleted
for track in Track.select(lambda t: t.root_folder == folder):
Expand All @@ -90,15 +109,15 @@ def finish(self):
continue

self.__artists_to_check.add(album.artist.id)
self.__deleted_albums += 1
self.__stats.deleted.albums += 1
album.delete()
self.__albums_to_check.clear()

for artist in Artist.select(lambda a: a.id in self.__artists_to_check):
if not artist.albums.is_empty() or not artist.tracks.is_empty():
continue

self.__deleted_artists += 1
self.__stats.deleted.artists += 1
artist.delete()
self.__artists_to_check.clear()

Expand Down Expand Up @@ -168,7 +187,7 @@ def scan_file(self, path):
trdict['artist'] = trartist

Track(**trdict)
self.__added_tracks += 1
self.__stats.added.tracks += 1
else:
if tr.album.id != tralbum.id:
self.__albums_to_check.add(tr.album.id)
Expand All @@ -192,7 +211,7 @@ def remove_file(self, path):
self.__folders_to_check.add(tr.folder.id)
self.__albums_to_check.add(tr.album.id)
self.__artists_to_check.add(tr.artist.id)
self.__deleted_tracks += 1
self.__stats.deleted.tracks += 1
tr.delete()

@db_session
Expand Down Expand Up @@ -231,7 +250,7 @@ def __find_album(self, artist, album):
return al

al = Album(name = album, artist = ar)
self.__added_albums += 1
self.__stats.added.albums += 1

return al

Expand All @@ -241,7 +260,7 @@ def __find_artist(self, artist):
return ar

ar = Artist(name = artist)
self.__added_artists += 1
self.__stats.added.artists += 1

return ar

Expand Down Expand Up @@ -289,5 +308,5 @@ def __try_read_tag(self, metadata, field, default = None, transform = lambda x:
return default

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

Binary file added tests/assets/�/silence.mp3
Binary file not shown.
12 changes: 8 additions & 4 deletions tests/base/test_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,8 @@ def test_scan(self):

@db_session
def test_progress(self):
def progress(processed, total):
def progress(processed):
self.assertIsInstance(processed, int)
self.assertIsInstance(total, int)
self.assertLessEqual(processed, total)

self.scanner.scan(db.Folder[self.folderid], progress)

Expand Down Expand Up @@ -192,7 +190,13 @@ def test_scan_tag_change(self):
self.assertIsNotNone(db.Album.get(name = 'Awesome album'))

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__':
unittest.main()
Expand Down

0 comments on commit 954c75b

Please sign in to comment.