1
0
mirror of https://github.com/spl0k/supysonic.git synced 2024-12-22 17:06:17 +00:00

Scanner uses scandir

This commit is contained in:
Alban Féron 2019-06-30 16:29:28 +02:00
parent 7c8a75d45c
commit 007a6e139b
No known key found for this signature in database
GPG Key ID: 8CE0313646D16165
2 changed files with 41 additions and 38 deletions

View File

@ -10,9 +10,9 @@
# Try built-in scandir, fall back to the package for Python 2.7 # Try built-in scandir, fall back to the package for Python 2.7
try: try:
from os import scandir from os import scandir, DirEntry
except ImportError: except ImportError:
from scandir import scandir from scandir import scandir, DirEntry
# os.replace was added in Python 3.3, provide a fallback for Python 2.7 # os.replace was added in Python 3.3, provide a fallback for Python 2.7
try: try:

View File

@ -20,7 +20,7 @@ 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
from .py23 import strtype, Queue, QueueEmpty from .py23 import scandir, strtype, DirEntry, Queue, QueueEmpty
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -128,36 +128,29 @@ class Scanner(Thread):
scanned = 0 scanned = 0
while not self.__stopped.is_set() and to_scan: while not self.__stopped.is_set() and to_scan:
path = to_scan.pop() path = to_scan.pop()
for entry in scandir(path):
try: if entry.name.startswith("."):
entries = os.listdir(path)
except OSError:
continue
for f in entries:
try: # test for badly encoded filenames
f.encode("utf-8")
except UnicodeError:
self.__stats.errors.append(path)
continue continue
# TODO add config setting to allow following symlinks
full_path = os.path.join(path, f) if entry.is_symlink():
if os.path.islink(full_path):
continue continue
elif os.path.isdir(full_path): elif entry.is_dir():
to_scan.append(full_path) to_scan.append(entry.path)
elif os.path.isfile(full_path) and self.__is_valid_path(full_path): elif entry.is_file() and self.__check_extension(entry.path):
self.scan_file(full_path) self.scan_file(entry)
self.__stats.scanned += 1 self.__stats.scanned += 1
scanned += 1 scanned += 1
self.__report_progress(folder.name, scanned) self.__report_progress(folder.name, scanned)
# Remove files that have been deleted # Remove files that have been deleted
# Could be more efficient if done above
if not self.__stopped.is_set(): if not self.__stopped.is_set():
with db_session: with db_session:
for track in Track.select(lambda t: t.root_folder == folder): for track in Track.select(lambda t: t.root_folder == folder):
if not self.__is_valid_path(track.path): if not os.path.exists(track.path) or not self.__check_extension(
track.path
):
self.remove_file(track.path) self.remove_file(track.path)
# Remove deleted/moved folders and update cover art info # Remove deleted/moved folders and update cover art info
@ -166,9 +159,8 @@ class Scanner(Thread):
f = folders.pop() f = folders.pop()
with db_session: with db_session:
f = Folder[ # f has been fetched from another session, refetch or Pony will complain
f.id f = Folder[f.id]
] # f has been fetched from another session, refetch or Pony will complain
if not f.root and not os.path.isdir(f.path): if not f.root and not os.path.isdir(f.path):
f.delete() # Pony will cascade f.delete() # Pony will cascade
@ -193,22 +185,35 @@ class Scanner(Thread):
self.__stats.deleted.artists = Artist.prune() self.__stats.deleted.artists = Artist.prune()
Folder.prune() Folder.prune()
def __is_valid_path(self, path): def __check_extension(self, path):
if not os.path.exists(path):
return False
if not self.__extensions: if not self.__extensions:
return True return True
return os.path.splitext(path)[1][1:].lower() in self.__extensions return os.path.splitext(path)[1][1:].lower() in self.__extensions
@db_session @db_session
def scan_file(self, path): def scan_file(self, path_or_direntry):
if not isinstance(path, strtype): if not isinstance(path_or_direntry, (strtype, DirEntry)):
raise TypeError("Expecting string, got " + str(type(path))) raise TypeError(
"Expecting string or DirEntry, got " + str(type(path_or_direntry))
)
if isinstance(path_or_direntry, DirEntry):
path = path_or_direntry.path
basename = path_or_direntry.name
stat = path_or_direntry.stat()
else:
path = path_or_direntry
if not os.path.exists(path):
return
basename = os.path.basename(path)
stat = os.stat(path)
mtime = int(stat.st_mtime)
size = stat.st_size
tr = Track.get(path=path) tr = Track.get(path=path)
mtime = (
int(os.path.getmtime(path)) if os.path.exists(path) else 0
) # condition for some tests
if tr is not None: if tr is not None:
if not self.__force and not mtime > tr.last_modification: if not self.__force and not mtime > tr.last_modification:
return return
@ -235,9 +240,7 @@ class Scanner(Thread):
trdict["number"] = self.__try_read_tag( trdict["number"] = self.__try_read_tag(
tag, "tracknumber", 1, lambda x: int(x.split("/")[0]) tag, "tracknumber", 1, lambda x: int(x.split("/")[0])
) )
trdict["title"] = self.__try_read_tag(tag, "title", os.path.basename(path))[ trdict["title"] = self.__try_read_tag(tag, "title", basename)[:255]
:255
]
trdict["year"] = self.__try_read_tag( trdict["year"] = self.__try_read_tag(
tag, "date", None, lambda x: int(x.split("-")[0]) tag, "date", None, lambda x: int(x.split("-")[0])
) )
@ -249,7 +252,7 @@ class Scanner(Thread):
int( int(
tag.info.bitrate tag.info.bitrate
if hasattr(tag.info, "bitrate") if hasattr(tag.info, "bitrate")
else os.path.getsize(path) * 8 / tag.info.length else size * 8 / tag.info.length
) )
// 1000 // 1000
) )