Python3 Migrate
This commit is contained in:
187
venv/lib/python3.7/site-packages/mopidy/stream/actor.py
Normal file
187
venv/lib/python3.7/site-packages/mopidy/stream/actor.py
Normal file
@@ -0,0 +1,187 @@
|
||||
import fnmatch
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
import urllib
|
||||
|
||||
import pykka
|
||||
|
||||
from mopidy import audio as audio_lib
|
||||
from mopidy import backend, exceptions, stream
|
||||
from mopidy.audio import scan, tags
|
||||
from mopidy.internal import http, playlists
|
||||
from mopidy.models import Track
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class StreamBackend(pykka.ThreadingActor, backend.Backend):
|
||||
def __init__(self, config, audio):
|
||||
super().__init__()
|
||||
|
||||
self._scanner = scan.Scanner(
|
||||
timeout=config["stream"]["timeout"], proxy_config=config["proxy"]
|
||||
)
|
||||
|
||||
self._session = http.get_requests_session(
|
||||
proxy_config=config["proxy"],
|
||||
user_agent=(
|
||||
f"{stream.Extension.dist_name}/{stream.Extension.version}"
|
||||
),
|
||||
)
|
||||
|
||||
blacklist = config["stream"]["metadata_blacklist"]
|
||||
self._blacklist_re = re.compile(
|
||||
r"^(%s)$" % "|".join(fnmatch.translate(u) for u in blacklist)
|
||||
)
|
||||
|
||||
self._timeout = config["stream"]["timeout"]
|
||||
|
||||
self.library = StreamLibraryProvider(backend=self)
|
||||
self.playback = StreamPlaybackProvider(audio=audio, backend=self)
|
||||
self.playlists = None
|
||||
|
||||
self.uri_schemes = audio_lib.supported_uri_schemes(
|
||||
config["stream"]["protocols"]
|
||||
)
|
||||
|
||||
if "file" in self.uri_schemes and config["file"]["enabled"]:
|
||||
logger.warning(
|
||||
'The stream/protocols config value includes the "file" '
|
||||
'protocol. "file" playback is now handled by Mopidy-File. '
|
||||
"Please remove it from the stream/protocols config."
|
||||
)
|
||||
self.uri_schemes -= {"file"}
|
||||
|
||||
|
||||
class StreamLibraryProvider(backend.LibraryProvider):
|
||||
def lookup(self, uri):
|
||||
if urllib.parse.urlsplit(uri).scheme not in self.backend.uri_schemes:
|
||||
return []
|
||||
|
||||
if self.backend._blacklist_re.match(uri):
|
||||
logger.debug("URI matched metadata lookup blacklist: %s", uri)
|
||||
return [Track(uri=uri)]
|
||||
|
||||
_, scan_result = _unwrap_stream(
|
||||
uri,
|
||||
timeout=self.backend._timeout,
|
||||
scanner=self.backend._scanner,
|
||||
requests_session=self.backend._session,
|
||||
)
|
||||
|
||||
if scan_result:
|
||||
track = tags.convert_tags_to_track(scan_result.tags).replace(
|
||||
uri=uri, length=scan_result.duration
|
||||
)
|
||||
else:
|
||||
logger.warning("Problem looking up %s", uri)
|
||||
track = Track(uri=uri)
|
||||
|
||||
return [track]
|
||||
|
||||
|
||||
class StreamPlaybackProvider(backend.PlaybackProvider):
|
||||
def translate_uri(self, uri):
|
||||
if urllib.parse.urlsplit(uri).scheme not in self.backend.uri_schemes:
|
||||
return None
|
||||
|
||||
if self.backend._blacklist_re.match(uri):
|
||||
logger.debug("URI matched metadata lookup blacklist: %s", uri)
|
||||
return uri
|
||||
|
||||
unwrapped_uri, _ = _unwrap_stream(
|
||||
uri,
|
||||
timeout=self.backend._timeout,
|
||||
scanner=self.backend._scanner,
|
||||
requests_session=self.backend._session,
|
||||
)
|
||||
return unwrapped_uri
|
||||
|
||||
|
||||
# TODO: cleanup the return value of this.
|
||||
def _unwrap_stream(uri, timeout, scanner, requests_session):
|
||||
"""
|
||||
Get a stream URI from a playlist URI, ``uri``.
|
||||
|
||||
Unwraps nested playlists until something that's not a playlist is found or
|
||||
the ``timeout`` is reached.
|
||||
"""
|
||||
|
||||
original_uri = uri
|
||||
seen_uris = set()
|
||||
deadline = time.time() + timeout
|
||||
|
||||
while time.time() < deadline:
|
||||
if uri in seen_uris:
|
||||
logger.info(
|
||||
"Unwrapping stream from URI (%s) failed: "
|
||||
"playlist referenced itself",
|
||||
uri,
|
||||
)
|
||||
return None, None
|
||||
else:
|
||||
seen_uris.add(uri)
|
||||
|
||||
logger.debug("Unwrapping stream from URI: %s", uri)
|
||||
|
||||
try:
|
||||
scan_timeout = deadline - time.time()
|
||||
if scan_timeout < 0:
|
||||
logger.info(
|
||||
"Unwrapping stream from URI (%s) failed: "
|
||||
"timed out in %sms",
|
||||
uri,
|
||||
timeout,
|
||||
)
|
||||
return None, None
|
||||
scan_result = scanner.scan(uri, timeout=scan_timeout)
|
||||
except exceptions.ScannerError as exc:
|
||||
logger.debug("GStreamer failed scanning URI (%s): %s", uri, exc)
|
||||
scan_result = None
|
||||
|
||||
if scan_result is not None:
|
||||
has_interesting_mime = (
|
||||
scan_result.mime is not None
|
||||
and not scan_result.mime.startswith("text/")
|
||||
and not scan_result.mime.startswith("application/")
|
||||
)
|
||||
if scan_result.playable or has_interesting_mime:
|
||||
logger.debug(
|
||||
"Unwrapped potential %s stream: %s", scan_result.mime, uri
|
||||
)
|
||||
return uri, scan_result
|
||||
|
||||
download_timeout = deadline - time.time()
|
||||
if download_timeout < 0:
|
||||
logger.info(
|
||||
"Unwrapping stream from URI (%s) failed: timed out in %sms",
|
||||
uri,
|
||||
timeout,
|
||||
)
|
||||
return None, None
|
||||
content = http.download(
|
||||
requests_session, uri, timeout=download_timeout / 1000
|
||||
)
|
||||
|
||||
if content is None:
|
||||
logger.info(
|
||||
"Unwrapping stream from URI (%s) failed: "
|
||||
"error downloading URI %s",
|
||||
original_uri,
|
||||
uri,
|
||||
)
|
||||
return None, None
|
||||
|
||||
uris = playlists.parse(content)
|
||||
if not uris:
|
||||
logger.debug(
|
||||
"Failed parsing URI (%s) as playlist; found potential stream.",
|
||||
uri,
|
||||
)
|
||||
return uri, None
|
||||
|
||||
# TODO Test streams and return first that seems to be playable
|
||||
new_uri = uris[0]
|
||||
logger.debug("Parsed playlist (%s) and found new URI: %s", uri, new_uri)
|
||||
uri = urllib.parse.urljoin(uri, new_uri)
|
||||
Reference in New Issue
Block a user