absolute_path = get_absolute_path(base_path, entry.path)
stat = entry.stat(follow_symlinks=False)
return FileSystemItem(
- name=entry.name,
+ filename=entry.name,
path=get_relative_path(base_path, entry.path),
absolute_path=absolute_path,
is_file=entry.is_file(follow_symlinks=False),
def _create_item():
stat = os.stat(absolute_path, follow_symlinks=False)
return FileSystemItem(
- name=os.path.basename(file_path),
+ filename=os.path.basename(file_path),
path=get_relative_path(self.base_path, file_path),
absolute_path=absolute_path,
is_dir=os.path.isdir(absolute_path),
self.logger.warning("NOT migrating playlist: %s", item.path)
continue
# create playlist on the builtin provider
- name = item.name.replace(".m3u", "")
- new_playlist = await self.mass.music.playlists.create_playlist(name, "builtin")
+ new_playlist = await self.mass.music.playlists.create_playlist(item.name, "builtin")
# append existing uri's to the new playlist
await self.mass.music.playlists.add_playlist_tracks(new_playlist.item_id, all_uris)
# remove existing item from the library
class FileSystemItem:
"""Representation of an item (file or directory) on the filesystem.
- - name: Name (not path) of the file (or directory).
+ - filename: Name (not path) of the file (or directory).
- path: Relative path to the item on this filesystem provider.
- absolute_path: Absolute (provider dependent) path to this item.
- is_file: Boolean if item is file (not directory or symlink).
- local_path: Optional local accessible path to this (file)item, supported by ffmpeg.
"""
- name: str
+ filename: str
path: str
absolute_path: str
is_file: bool
def ext(self) -> str | None:
"""Return file extension."""
try:
- return self.name.rsplit(".", 1)[1]
+ return self.filename.rsplit(".", 1)[1]
except IndexError:
return None
+ @property
+ def name(self) -> str:
+ """Return file name (without extension)."""
+ return self.filename.rsplit(".", 1)[0]
+
class FileSystemProviderBase(MusicProvider):
"""Base Implementation of a musicprovider for files.
item_id=item.path,
provider=self.instance_id,
path=f"{self.instance_id}://{item.path}",
- name=item.name,
+ name=item.filename,
)
continue
- if "." not in item.name or not item.ext:
+ if "." not in item.filename or not item.ext:
# skip system files and files without extension
continue
media_type=MediaType.TRACK,
item_id=item.path,
provider=self.instance_id,
- name=item.name,
+ name=item.filename,
)
continue
if item.ext in PLAYLIST_EXTENSIONS:
media_type=MediaType.PLAYLIST,
item_id=item.path,
provider=self.instance_id,
- name=item.name,
+ name=item.filename,
)
await asyncio.sleep(0) # yield to eventloop
# process all deleted (or renamed) files first
cur_filenames = set()
async for item in self.listdir("", recursive=True):
- if "." not in item.name or not item.ext:
+ if "." not in item.filename or not item.ext:
# skip system files and files without extension
continue
# find all music files in the music directory and all subfolders
# we work bottom up, as-in we derive all info from the tracks
async for item in self.listdir("", recursive=True):
- if "." not in item.name or not item.ext:
+ if "." not in item.filename or not item.ext:
# skip system files and files without extension
continue
self.logger.debug("Processing: %s", item.path)
if item.ext in TRACK_EXTENSIONS:
# add/update track to db
+ # note that filesystem items are always overwriting existing info
+ # when they are detected as changed
track = await self._parse_track(item)
- await self.mass.music.tracks.add_item_to_library(track, metadata_lookup=False)
+ await self.mass.music.tracks.add_item_to_library(
+ track, metadata_lookup=False, overwrite_existing=True
+ )
elif item.ext in PLAYLIST_EXTENSIONS:
playlist = await self.get_playlist(item.path)
# add/update] playlist to db
playlist.metadata.cache_checksum = item.checksum
- # playlist is always in-library
+ # playlist is always favorite
playlist.favorite = True
await self.mass.music.playlists.add_item_to_library(
- playlist, metadata_lookup=False
+ playlist, metadata_lookup=False, overwrite_existing=True
)
except Exception as err: # pylint: disable=broad-except
# we don't want the whole sync to crash on one file so we catch all exceptions here
playlist = Playlist(
item_id=file_item.path,
provider=self.instance_id,
- name=file_item.name.replace(f".{file_item.ext}", ""),
+ name=file_item.name,
provider_mappings={
ProviderMapping(
item_id=file_item.path,
for ext in IMAGE_EXTENSIONS:
if item.ext != ext:
continue
- try:
+ # try match on filename = one of our imagetypes
+ if item.name in ImageType:
images.append(
MediaItemImage(
type=ImageType(item.name),
remotely_accessible=False,
)
)
- except ValueError:
- for filename in ("folder", "cover", "albumart", "artist"):
- if item.name.lower().startswith(filename):
- images.append(
- MediaItemImage(
- type=ImageType.THUMB,
- path=item.path,
- provider=self.instance_id,
- remotely_accessible=False,
- )
+ continue
+ # try alternative names for thumbs
+ for filename in ("folder", "cover", "albumart", "artist"):
+ if item.name.lower().startswith(filename):
+ images.append(
+ MediaItemImage(
+ type=ImageType.THUMB,
+ path=item.path,
+ provider=self.instance_id,
+ remotely_accessible=False,
)
- break
+ )
+ break
return images