Python3 Migrate
This commit is contained in:
32
venv/lib/python3.7/site-packages/mopidy/file/__init__.py
Normal file
32
venv/lib/python3.7/site-packages/mopidy/file/__init__.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
import mopidy
|
||||
from mopidy import config, ext
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Extension(ext.Extension):
|
||||
|
||||
dist_name = "Mopidy-File"
|
||||
ext_name = "file"
|
||||
version = mopidy.__version__
|
||||
|
||||
def get_default_config(self):
|
||||
conf_file = os.path.join(os.path.dirname(__file__), "ext.conf")
|
||||
return config.read(conf_file)
|
||||
|
||||
def get_config_schema(self):
|
||||
schema = super().get_config_schema()
|
||||
schema["media_dirs"] = config.List(optional=True)
|
||||
schema["excluded_file_extensions"] = config.List(optional=True)
|
||||
schema["show_dotfiles"] = config.Boolean(optional=True)
|
||||
schema["follow_symlinks"] = config.Boolean(optional=True)
|
||||
schema["metadata_timeout"] = config.Integer(optional=True)
|
||||
return schema
|
||||
|
||||
def setup(self, registry):
|
||||
from .backend import FileBackend
|
||||
|
||||
registry.add("backend", FileBackend)
|
||||
18
venv/lib/python3.7/site-packages/mopidy/file/backend.py
Normal file
18
venv/lib/python3.7/site-packages/mopidy/file/backend.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import logging
|
||||
|
||||
import pykka
|
||||
|
||||
from mopidy import backend
|
||||
from mopidy.file import library
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FileBackend(pykka.ThreadingActor, backend.Backend):
|
||||
uri_schemes = ["file"]
|
||||
|
||||
def __init__(self, config, audio):
|
||||
super().__init__()
|
||||
self.library = library.FileLibraryProvider(backend=self, config=config)
|
||||
self.playback = backend.PlaybackProvider(audio=audio, backend=self)
|
||||
self.playlists = None
|
||||
19
venv/lib/python3.7/site-packages/mopidy/file/ext.conf
Normal file
19
venv/lib/python3.7/site-packages/mopidy/file/ext.conf
Normal file
@@ -0,0 +1,19 @@
|
||||
[file]
|
||||
enabled = true
|
||||
media_dirs =
|
||||
$XDG_MUSIC_DIR|Music
|
||||
~/|Home
|
||||
show_dotfiles = false
|
||||
excluded_file_extensions =
|
||||
.directory
|
||||
.html
|
||||
.jpeg
|
||||
.jpg
|
||||
.log
|
||||
.nfo
|
||||
.pdf
|
||||
.png
|
||||
.txt
|
||||
.zip
|
||||
follow_symlinks = false
|
||||
metadata_timeout = 1000
|
||||
148
venv/lib/python3.7/site-packages/mopidy/file/library.py
Normal file
148
venv/lib/python3.7/site-packages/mopidy/file/library.py
Normal file
@@ -0,0 +1,148 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
from mopidy import backend, exceptions, models
|
||||
from mopidy.audio import scan, tags
|
||||
from mopidy.internal import path
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FileLibraryProvider(backend.LibraryProvider):
|
||||
"""Library for browsing local files."""
|
||||
|
||||
# TODO: get_images that can pull from metadata and/or .folder.png etc?
|
||||
# TODO: handle playlists?
|
||||
|
||||
@property
|
||||
def root_directory(self):
|
||||
if not self._media_dirs:
|
||||
return None
|
||||
elif len(self._media_dirs) == 1:
|
||||
uri = path.path_to_uri(self._media_dirs[0]["path"])
|
||||
else:
|
||||
uri = "file:root"
|
||||
return models.Ref.directory(name="Files", uri=uri)
|
||||
|
||||
def __init__(self, backend, config):
|
||||
super().__init__(backend)
|
||||
self._media_dirs = list(self._get_media_dirs(config))
|
||||
self._show_dotfiles = config["file"]["show_dotfiles"]
|
||||
self._excluded_file_extensions = tuple(
|
||||
file_ext.lower()
|
||||
for file_ext in config["file"]["excluded_file_extensions"]
|
||||
)
|
||||
self._follow_symlinks = config["file"]["follow_symlinks"]
|
||||
|
||||
self._scanner = scan.Scanner(timeout=config["file"]["metadata_timeout"])
|
||||
|
||||
def browse(self, uri):
|
||||
logger.debug("Browsing files at: %s", uri)
|
||||
result = []
|
||||
local_path = path.uri_to_path(uri)
|
||||
|
||||
if str(local_path) == "root":
|
||||
return list(self._get_media_dirs_refs())
|
||||
|
||||
if not self._is_in_basedir(local_path):
|
||||
logger.warning(
|
||||
"Rejected attempt to browse path (%s) outside dirs defined "
|
||||
"in file/media_dirs config.",
|
||||
uri,
|
||||
)
|
||||
return []
|
||||
|
||||
for dir_entry in local_path.iterdir():
|
||||
child_path = dir_entry.resolve()
|
||||
uri = path.path_to_uri(child_path)
|
||||
|
||||
if not self._show_dotfiles and dir_entry.name.startswith("."):
|
||||
continue
|
||||
|
||||
if (
|
||||
self._excluded_file_extensions
|
||||
and dir_entry.suffix in self._excluded_file_extensions
|
||||
):
|
||||
continue
|
||||
|
||||
if child_path.is_symlink() and not self._follow_symlinks:
|
||||
logger.debug("Ignoring symlink: %s", uri)
|
||||
continue
|
||||
|
||||
if not self._is_in_basedir(child_path):
|
||||
logger.debug("Ignoring symlink to outside base dir: %s", uri)
|
||||
continue
|
||||
|
||||
if child_path.is_dir():
|
||||
result.append(
|
||||
models.Ref.directory(name=dir_entry.name, uri=uri)
|
||||
)
|
||||
elif child_path.is_file():
|
||||
result.append(models.Ref.track(name=dir_entry.name, uri=uri))
|
||||
|
||||
def order(item):
|
||||
return (item.type != models.Ref.DIRECTORY, item.name)
|
||||
|
||||
result.sort(key=order)
|
||||
|
||||
return result
|
||||
|
||||
def lookup(self, uri):
|
||||
logger.debug("Looking up file URI: %s", uri)
|
||||
local_path = path.uri_to_path(uri)
|
||||
|
||||
try:
|
||||
result = self._scanner.scan(uri)
|
||||
track = tags.convert_tags_to_track(result.tags).replace(
|
||||
uri=uri, length=result.duration
|
||||
)
|
||||
except exceptions.ScannerError as e:
|
||||
logger.warning("Failed looking up %s: %s", uri, e)
|
||||
track = models.Track(uri=uri)
|
||||
|
||||
if not track.name:
|
||||
track = track.replace(name=local_path.name)
|
||||
|
||||
return [track]
|
||||
|
||||
def _get_media_dirs(self, config):
|
||||
for entry in config["file"]["media_dirs"]:
|
||||
media_dir = {}
|
||||
media_dir_split = entry.split("|", 1)
|
||||
local_path = path.expand_path(media_dir_split[0])
|
||||
|
||||
if local_path is None:
|
||||
logger.debug(
|
||||
"Failed expanding path (%s) from file/media_dirs config "
|
||||
"value.",
|
||||
media_dir_split[0],
|
||||
)
|
||||
continue
|
||||
elif not local_path.is_dir():
|
||||
logger.warning(
|
||||
"%s is not a directory. Please create the directory or "
|
||||
"update the file/media_dirs config value.",
|
||||
local_path,
|
||||
)
|
||||
continue
|
||||
|
||||
media_dir["path"] = local_path
|
||||
if len(media_dir_split) == 2:
|
||||
media_dir["name"] = media_dir_split[1]
|
||||
else:
|
||||
# TODO Mpd client should accept / in dir name
|
||||
media_dir["name"] = media_dir_split[0].replace(os.sep, "+")
|
||||
|
||||
yield media_dir
|
||||
|
||||
def _get_media_dirs_refs(self):
|
||||
for media_dir in self._media_dirs:
|
||||
yield models.Ref.directory(
|
||||
name=media_dir["name"], uri=path.path_to_uri(media_dir["path"])
|
||||
)
|
||||
|
||||
def _is_in_basedir(self, local_path):
|
||||
return any(
|
||||
path.is_path_inside_base_dir(local_path, media_dir["path"])
|
||||
for media_dir in self._media_dirs
|
||||
)
|
||||
Reference in New Issue
Block a user