forked from platypush/platypush
[media
] Black/LINT for MediaPlugin
.
This commit is contained in:
parent
efdb63443d
commit
b4bf30945a
1 changed files with 119 additions and 155 deletions
|
@ -3,13 +3,13 @@ import functools
|
||||||
import os
|
import os
|
||||||
import queue
|
import queue
|
||||||
import re
|
import re
|
||||||
import requests
|
|
||||||
from abc import ABC, abstractmethod
|
|
||||||
from typing import Optional, List, Dict, Union
|
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
import threading
|
import threading
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from typing import Iterable, Optional, List, Dict, Union
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
from platypush.config import Config
|
from platypush.config import Config
|
||||||
from platypush.context import get_plugin, get_backend
|
from platypush.context import get_plugin, get_backend
|
||||||
|
@ -17,6 +17,10 @@ from platypush.plugins import Plugin, action
|
||||||
|
|
||||||
|
|
||||||
class PlayerState(enum.Enum):
|
class PlayerState(enum.Enum):
|
||||||
|
"""
|
||||||
|
Models the possible states of a media player
|
||||||
|
"""
|
||||||
|
|
||||||
STOP = 'stop'
|
STOP = 'stop'
|
||||||
PLAY = 'play'
|
PLAY = 'play'
|
||||||
PAUSE = 'pause'
|
PAUSE = 'pause'
|
||||||
|
@ -85,7 +89,6 @@ class MediaPlugin(Plugin, ABC):
|
||||||
'webm',
|
'webm',
|
||||||
'mkv',
|
'mkv',
|
||||||
'flv',
|
'flv',
|
||||||
'flv',
|
|
||||||
'vob',
|
'vob',
|
||||||
'ogv',
|
'ogv',
|
||||||
'ogg',
|
'ogg',
|
||||||
|
@ -112,17 +115,13 @@ class MediaPlugin(Plugin, ABC):
|
||||||
'mpeg',
|
'mpeg',
|
||||||
'mpe',
|
'mpe',
|
||||||
'mpv',
|
'mpv',
|
||||||
'mpg',
|
|
||||||
'mpeg',
|
|
||||||
'm2v',
|
'm2v',
|
||||||
'm4v',
|
|
||||||
'svi',
|
'svi',
|
||||||
'3gp',
|
'3gp',
|
||||||
'3g2',
|
'3g2',
|
||||||
'mxf',
|
'mxf',
|
||||||
'roq',
|
'roq',
|
||||||
'nsv',
|
'nsv',
|
||||||
'flv',
|
|
||||||
'f4v',
|
'f4v',
|
||||||
'f4p',
|
'f4p',
|
||||||
'f4a',
|
'f4a',
|
||||||
|
@ -185,10 +184,10 @@ class MediaPlugin(Plugin, ABC):
|
||||||
|
|
||||||
if self.__class__.__name__ == 'MediaPlugin':
|
if self.__class__.__name__ == 'MediaPlugin':
|
||||||
# Abstract class, initialize with the default configured player
|
# Abstract class, initialize with the default configured player
|
||||||
for plugin in Config.get_plugins().keys():
|
for plugin_name in Config.get_plugins().keys():
|
||||||
if plugin in self._supported_media_plugins:
|
if plugin_name in self._supported_media_plugins:
|
||||||
player = plugin
|
player = get_plugin(plugin_name)
|
||||||
if get_plugin(player).is_local():
|
if player and player.is_local():
|
||||||
# Local players have priority as default if configured
|
# Local players have priority as default if configured
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
@ -201,9 +200,8 @@ class MediaPlugin(Plugin, ABC):
|
||||||
|
|
||||||
if self.__class__.__name__ == 'MediaPlugin':
|
if self.__class__.__name__ == 'MediaPlugin':
|
||||||
# Populate this plugin with the actions of the configured player
|
# Populate this plugin with the actions of the configured player
|
||||||
plugin = get_plugin(player)
|
for act in player.registered_actions:
|
||||||
for act in plugin.registered_actions:
|
setattr(self, act, getattr(player, act))
|
||||||
setattr(self, act, getattr(plugin, act))
|
|
||||||
self.registered_actions.add(act)
|
self.registered_actions.add(act)
|
||||||
|
|
||||||
self._env = env or {}
|
self._env = env or {}
|
||||||
|
@ -268,7 +266,7 @@ class MediaPlugin(Plugin, ABC):
|
||||||
if not m:
|
if not m:
|
||||||
m = re.match('https://youtu.be/(.*)', resource)
|
m = re.match('https://youtu.be/(.*)', resource)
|
||||||
if m:
|
if m:
|
||||||
resource = 'https://www.youtube.com/watch?v={}'.format(m.group(1))
|
resource = f'https://www.youtube.com/watch?v={m.group(1)}'
|
||||||
|
|
||||||
if self.__class__.__name__ == 'MediaChromecastPlugin':
|
if self.__class__.__name__ == 'MediaChromecastPlugin':
|
||||||
# The Chromecast has already its native way to handle YouTube
|
# The Chromecast has already its native way to handle YouTube
|
||||||
|
@ -277,9 +275,10 @@ class MediaPlugin(Plugin, ABC):
|
||||||
resource = self.get_youtube_video_url(resource)
|
resource = self.get_youtube_video_url(resource)
|
||||||
elif resource.startswith('magnet:?'):
|
elif resource.startswith('magnet:?'):
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
'Downloading torrent {} to {}'.format(resource, self.download_dir)
|
'Downloading torrent %s to %s', resource, self.download_dir
|
||||||
)
|
)
|
||||||
torrents = get_plugin(self.torrent_plugin)
|
torrents = get_plugin(self.torrent_plugin)
|
||||||
|
assert torrents, f'{self.torrent_plugin} plugin not configured'
|
||||||
|
|
||||||
evt_queue = queue.Queue()
|
evt_queue = queue.Queue()
|
||||||
torrents.download(
|
torrents.download(
|
||||||
|
@ -290,13 +289,13 @@ class MediaPlugin(Plugin, ABC):
|
||||||
event_hndl=self._torrent_event_handler(evt_queue),
|
event_hndl=self._torrent_event_handler(evt_queue),
|
||||||
)
|
)
|
||||||
|
|
||||||
resources = [f for f in evt_queue.get()] # noqa: C416
|
resources = [f for f in evt_queue.get()] # noqa: C416,R1721
|
||||||
|
|
||||||
if resources:
|
if resources:
|
||||||
self._videos_queue = sorted(resources)
|
self._videos_queue = sorted(resources)
|
||||||
resource = self._videos_queue.pop(0)
|
resource = self._videos_queue.pop(0)
|
||||||
else:
|
else:
|
||||||
raise RuntimeError('No media file found in torrent {}'.format(resource))
|
raise RuntimeError(f'No media file found in torrent {resource}')
|
||||||
elif re.search(r'^https?://', resource):
|
elif re.search(r'^https?://', resource):
|
||||||
return resource
|
return resource
|
||||||
|
|
||||||
|
@ -304,12 +303,12 @@ class MediaPlugin(Plugin, ABC):
|
||||||
return resource
|
return resource
|
||||||
|
|
||||||
def _stop_torrent(self):
|
def _stop_torrent(self):
|
||||||
# noinspection PyBroadException
|
|
||||||
try:
|
try:
|
||||||
torrents = get_plugin(self.torrent_plugin)
|
torrents = get_plugin(self.torrent_plugin)
|
||||||
|
assert torrents, f'{self.torrent_plugin} plugin not configured'
|
||||||
torrents.quit()
|
torrents.quit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.warning(f'Could not stop torrent plugin: {str(e)}')
|
self.logger.warning('Could not stop torrent plugin: %s', e)
|
||||||
|
|
||||||
@action
|
@action
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
@ -360,6 +359,8 @@ class MediaPlugin(Plugin, ABC):
|
||||||
video = self._videos_queue.pop(0)
|
video = self._videos_queue.pop(0)
|
||||||
return self.play(video)
|
return self.play(video)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
@action
|
@action
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def toggle_subtitles(self, *args, **kwargs):
|
def toggle_subtitles(self, *args, **kwargs):
|
||||||
|
@ -413,29 +414,20 @@ class MediaPlugin(Plugin, ABC):
|
||||||
@action
|
@action
|
||||||
def search(
|
def search(
|
||||||
self,
|
self,
|
||||||
query,
|
query: str,
|
||||||
types=None,
|
types: Optional[Iterable[str]] = None,
|
||||||
queue_results=False,
|
queue_results: bool = False,
|
||||||
autoplay=False,
|
autoplay: bool = False,
|
||||||
search_timeout=_default_search_timeout,
|
timeout: float = _default_search_timeout,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Perform a video search.
|
Perform a video search.
|
||||||
|
|
||||||
:param query: Query string, video name or partial name
|
:param query: Query string, video name or partial name
|
||||||
:type query: str
|
|
||||||
|
|
||||||
:param types: Video types to search (default: ``["youtube", "file", "torrent"]``)
|
:param types: Video types to search (default: ``["youtube", "file", "torrent"]``)
|
||||||
:type types: list
|
|
||||||
|
|
||||||
:param queue_results: Append the results to the current playing queue (default: False)
|
:param queue_results: Append the results to the current playing queue (default: False)
|
||||||
:type queue_results: bool
|
|
||||||
|
|
||||||
:param autoplay: Play the first result of the search (default: False)
|
:param autoplay: Play the first result of the search (default: False)
|
||||||
:type autoplay: bool
|
:param timeout: Search timeout (default: 60 seconds)
|
||||||
|
|
||||||
:param search_timeout: Search timeout (default: 60 seconds)
|
|
||||||
:type search_timeout: float
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
results = {}
|
results = {}
|
||||||
|
@ -460,32 +452,27 @@ class MediaPlugin(Plugin, ABC):
|
||||||
|
|
||||||
for media_type in types:
|
for media_type in types:
|
||||||
try:
|
try:
|
||||||
items = results_queues[media_type].get(timeout=search_timeout)
|
items = results_queues[media_type].get(timeout=timeout)
|
||||||
if isinstance(items, Exception):
|
if isinstance(items, Exception):
|
||||||
raise items
|
raise items
|
||||||
|
|
||||||
results[media_type].extend(items)
|
results[media_type].extend(items)
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
self.logger.warning(
|
self.logger.warning(
|
||||||
'Search for "{}" media type {} timed out'.format(query, media_type)
|
'Search for "%s" media type %s timed out', query, media_type
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.warning(
|
self.logger.warning(
|
||||||
'Error while searching for "{}", media type {}'.format(
|
'Error while searching for "%s", media type %s', query, media_type
|
||||||
query, media_type
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
self.logger.exception(e)
|
self.logger.exception(e)
|
||||||
|
|
||||||
flattened_results = []
|
results = [
|
||||||
|
{'type': media_type, **result}
|
||||||
for media_type in self._supported_media_types:
|
for media_type in self._supported_media_types
|
||||||
if media_type in results:
|
for result in results.get(media_type, [])
|
||||||
for result in results[media_type]:
|
if media_type in results
|
||||||
result['type'] = media_type
|
]
|
||||||
flattened_results += results[media_type]
|
|
||||||
|
|
||||||
results = flattened_results
|
|
||||||
|
|
||||||
if results:
|
if results:
|
||||||
if queue_results:
|
if queue_results:
|
||||||
|
@ -507,7 +494,7 @@ class MediaPlugin(Plugin, ABC):
|
||||||
|
|
||||||
return thread
|
return thread
|
||||||
|
|
||||||
def _get_search_handler_by_type(self, search_type):
|
def _get_search_handler_by_type(self, search_type: str):
|
||||||
if search_type == 'file':
|
if search_type == 'file':
|
||||||
from .search import LocalMediaSearcher
|
from .search import LocalMediaSearcher
|
||||||
|
|
||||||
|
@ -529,33 +516,30 @@ class MediaPlugin(Plugin, ABC):
|
||||||
|
|
||||||
return JellyfinMediaSearcher(media_plugin=self)
|
return JellyfinMediaSearcher(media_plugin=self)
|
||||||
|
|
||||||
self.logger.warning('Unsupported search type: {}'.format(search_type))
|
self.logger.warning('Unsupported search type: %s', search_type)
|
||||||
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_video_file(cls, filename):
|
def is_video_file(cls, filename: str):
|
||||||
return filename.lower().split('.')[-1] in cls.video_extensions
|
return filename.lower().split('.')[-1] in cls.video_extensions
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_audio_file(cls, filename):
|
def is_audio_file(cls, filename: str):
|
||||||
return filename.lower().split('.')[-1] in cls.audio_extensions
|
return filename.lower().split('.')[-1] in cls.audio_extensions
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def start_streaming(self, media, subtitles=None, download=False):
|
def start_streaming(
|
||||||
|
self, media: str, subtitles: Optional[str] = None, download: bool = False
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Starts streaming local media over the specified HTTP port.
|
Starts streaming local media over the specified HTTP port.
|
||||||
The stream will be available to HTTP clients on
|
The stream will be available to HTTP clients on
|
||||||
`http://{this-ip}:{http_backend_port}/media/<media_id>`
|
`http://{this-ip}:{http_backend_port}/media/<media_id>`
|
||||||
|
|
||||||
:param media: Media to stream
|
:param media: Media to stream
|
||||||
:type media: str
|
|
||||||
|
|
||||||
:param subtitles: Path or URL to the subtitles track to be used
|
:param subtitles: Path or URL to the subtitles track to be used
|
||||||
:type subtitles: str
|
|
||||||
|
|
||||||
:param download: Set to True if you prefer to download the file from
|
:param download: Set to True if you prefer to download the file from
|
||||||
the streaming link instead of streaming it
|
the streaming link instead of streaming it
|
||||||
:type download: bool
|
|
||||||
|
|
||||||
:return: dict containing the streaming URL.Example:
|
:return: dict containing the streaming URL.Example:
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
@ -570,67 +554,30 @@ class MediaPlugin(Plugin, ABC):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
http = get_backend('http')
|
http = get_backend('http')
|
||||||
if not http:
|
assert http, f'Unable to stream {media}: HTTP backend not configured'
|
||||||
self.logger.warning(
|
|
||||||
'Unable to stream {}: HTTP backend unavailable'.format(media)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
self.logger.info('Starting streaming {}'.format(media))
|
self.logger.info('Starting streaming %s', media)
|
||||||
response = requests.put(
|
response = requests.put(
|
||||||
'{url}/media{download}'.format(
|
f'{http.local_base_url}/media' + ('?download' if download else ''),
|
||||||
url=http.local_base_url, download='?download' if download else ''
|
|
||||||
),
|
|
||||||
json={'source': media, 'subtitles': subtitles},
|
json={'source': media, 'subtitles': subtitles},
|
||||||
|
timeout=300,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not response.ok:
|
assert response.ok, response.text or response.reason
|
||||||
self.logger.warning(
|
|
||||||
'Unable to start streaming: {}'.format(response.text or response.reason)
|
|
||||||
)
|
|
||||||
return None, (response.text or response.reason)
|
|
||||||
|
|
||||||
return response.json()
|
return response.json()
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def stop_streaming(self, media_id):
|
def stop_streaming(self, media_id: str):
|
||||||
http = get_backend('http')
|
http = get_backend('http')
|
||||||
if not http:
|
assert http, f'Unable to stop streaming {media_id}: HTTP backend not configured'
|
||||||
self.logger.warning(
|
|
||||||
'Cannot unregister {}: HTTP backend unavailable'.format(media_id)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
response = requests.delete(
|
response = requests.delete(
|
||||||
'{url}/media/{id}'.format(url=http.local_base_url, id=media_id)
|
f'{http.local_base_url}/media/{media_id}', timeout=30
|
||||||
)
|
)
|
||||||
|
|
||||||
if not response.ok:
|
assert response.ok, response.text or response.reason
|
||||||
self.logger.warning(
|
|
||||||
'Unable to unregister media_id {}: {}'.format(media_id, response.reason)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
return response.json()
|
return response.json()
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _youtube_search_api(query):
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
'url': 'https://www.youtube.com/watch?v=' + item['id']['videoId'],
|
|
||||||
'title': item.get('snippet', {}).get('title', '<No Title>'),
|
|
||||||
}
|
|
||||||
for item in get_plugin('google.youtube').search(query=query).output
|
|
||||||
if item.get('id', {}).get('kind') == 'youtube#video'
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _youtube_search_html_parse(query):
|
|
||||||
from .search import YoutubeMediaSearcher
|
|
||||||
|
|
||||||
# noinspection PyProtectedMember
|
|
||||||
return YoutubeMediaSearcher()._youtube_search_html_parse(query)
|
|
||||||
|
|
||||||
def get_youtube_video_url(self, url, youtube_format: Optional[str] = None):
|
def get_youtube_video_url(self, url, youtube_format: Optional[str] = None):
|
||||||
ytdl_cmd = [
|
ytdl_cmd = [
|
||||||
'youtube-dl',
|
'youtube-dl',
|
||||||
|
@ -639,10 +586,12 @@ class MediaPlugin(Plugin, ABC):
|
||||||
'-g',
|
'-g',
|
||||||
url,
|
url,
|
||||||
]
|
]
|
||||||
self.logger.info(f'Executing command {" ".join(ytdl_cmd)}')
|
|
||||||
youtube_dl = subprocess.Popen(ytdl_cmd, stdout=subprocess.PIPE)
|
self.logger.info('Executing command %s', ' '.join(ytdl_cmd))
|
||||||
url = youtube_dl.communicate()[0].decode().strip()
|
with subprocess.Popen(ytdl_cmd, stdout=subprocess.PIPE) as youtube_dl:
|
||||||
youtube_dl.wait()
|
url = youtube_dl.communicate()[0].decode().strip()
|
||||||
|
youtube_dl.wait()
|
||||||
|
|
||||||
return url
|
return url
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -662,21 +611,30 @@ class MediaPlugin(Plugin, ABC):
|
||||||
if m:
|
if m:
|
||||||
return m.group(1)
|
return m.group(1)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def get_youtube_url(self, url, youtube_format: Optional[str] = None):
|
def get_youtube_url(self, url, youtube_format: Optional[str] = None):
|
||||||
youtube_id = self.get_youtube_id(url)
|
youtube_id = self.get_youtube_id(url)
|
||||||
if youtube_id:
|
if youtube_id:
|
||||||
url = 'https://www.youtube.com/watch?v={}'.format(youtube_id)
|
url = f'https://www.youtube.com/watch?v={youtube_id}'
|
||||||
return self.get_youtube_video_url(url, youtube_format=youtube_format)
|
return self.get_youtube_video_url(url, youtube_format=youtube_format)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def get_youtube_info(self, url):
|
def get_youtube_info(self, url):
|
||||||
m = re.match('youtube:video:(.*)', url)
|
m = re.match('youtube:video:(.*)', url)
|
||||||
if m:
|
if m:
|
||||||
url = 'https://www.youtube.com/watch?v={}'.format(m.group(1))
|
url = f'https://www.youtube.com/watch?v={m.group(1)}'
|
||||||
|
|
||||||
proc = subprocess.Popen(['youtube-dl', '-j', url], stdout=subprocess.PIPE)
|
with subprocess.Popen(
|
||||||
return proc.stdout.read().decode("utf-8", "strict")[:-1]
|
['youtube-dl', '-j', url], stdout=subprocess.PIPE
|
||||||
|
) as proc:
|
||||||
|
if proc.stdout is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return proc.stdout.read().decode("utf-8", "strict")[:-1]
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def get_media_file_duration(self, filename):
|
def get_media_file_duration(self, filename):
|
||||||
|
@ -687,40 +645,44 @@ class MediaPlugin(Plugin, ABC):
|
||||||
if filename.startswith('file://'):
|
if filename.startswith('file://'):
|
||||||
filename = filename[7:]
|
filename = filename[7:]
|
||||||
|
|
||||||
result = subprocess.Popen(
|
with subprocess.Popen(
|
||||||
["ffprobe", filename], stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
["ffprobe", filename], stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
||||||
)
|
) as result:
|
||||||
|
if not result.stdout:
|
||||||
|
return 0
|
||||||
|
|
||||||
return functools.reduce(
|
return functools.reduce(
|
||||||
lambda t, t_i: t + t_i,
|
lambda t, t_i: t + t_i,
|
||||||
[
|
[
|
||||||
float(t) * pow(60, i)
|
float(t) * pow(60, i)
|
||||||
for (i, t) in enumerate(
|
for (i, t) in enumerate(
|
||||||
re.search(
|
re.search(
|
||||||
r'^Duration:\s*([^,]+)',
|
r'^Duration:\s*([^,]+)',
|
||||||
[
|
[
|
||||||
x.decode()
|
x.decode()
|
||||||
for x in result.stdout.readlines()
|
for x in result.stdout.readlines()
|
||||||
if "Duration" in x.decode()
|
if "Duration" in x.decode()
|
||||||
]
|
]
|
||||||
.pop()
|
.pop()
|
||||||
.strip(),
|
.strip(),
|
||||||
|
)
|
||||||
|
.group(1)
|
||||||
|
.split(':')[::-1]
|
||||||
)
|
)
|
||||||
.group(1)
|
],
|
||||||
.split(':')[::-1]
|
)
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def download(self, url, filename=None, directory=None):
|
def download(
|
||||||
|
self, url: str, filename: Optional[str] = None, directory: Optional[str] = None
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Download a media URL
|
Download a media URL to a local file on the Platypush host.
|
||||||
|
|
||||||
:param url: Media URL
|
:param url: Media URL.
|
||||||
:param filename: Media filename (default: URL filename)
|
:param filename: Media filename (default: inferred from the URL basename).
|
||||||
:param directory: Destination directory (default: download_dir)
|
:param directory: Destination directory (default: ``download_dir``).
|
||||||
:return: The absolute path to the downloaded file
|
:return: The absolute path to the downloaded file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not filename:
|
if not filename:
|
||||||
|
@ -729,10 +691,12 @@ class MediaPlugin(Plugin, ABC):
|
||||||
directory = self.download_dir
|
directory = self.download_dir
|
||||||
|
|
||||||
path = os.path.join(directory, filename)
|
path = os.path.join(directory, filename)
|
||||||
content = requests.get(url).content
|
|
||||||
|
|
||||||
with open(path, 'wb') as f:
|
with requests.get(url, timeout=20, stream=True) as r:
|
||||||
f.write(content)
|
r.raise_for_status()
|
||||||
|
with open(path, 'wb') as f:
|
||||||
|
for chunk in r.iter_content(chunk_size=8192):
|
||||||
|
f.write(chunk)
|
||||||
|
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
@ -742,21 +706,21 @@ class MediaPlugin(Plugin, ABC):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_subtitles_file(subtitles):
|
def get_subtitles_file(subtitles):
|
||||||
if not subtitles:
|
if not subtitles:
|
||||||
return
|
return None
|
||||||
|
|
||||||
if subtitles.startswith('file://'):
|
if subtitles.startswith('file://'):
|
||||||
subtitles = subtitles[len('file://') :]
|
subtitles = subtitles[len('file://') :]
|
||||||
if os.path.isfile(subtitles):
|
if os.path.isfile(subtitles):
|
||||||
return os.path.abspath(subtitles)
|
return os.path.abspath(subtitles)
|
||||||
else:
|
|
||||||
content = requests.get(subtitles).content
|
|
||||||
f = tempfile.NamedTemporaryFile(
|
|
||||||
prefix='media_subs_', suffix='.srt', delete=False
|
|
||||||
)
|
|
||||||
|
|
||||||
with f:
|
content = requests.get(subtitles, timeout=20).content
|
||||||
f.write(content)
|
f = tempfile.NamedTemporaryFile(
|
||||||
return f.name
|
prefix='media_subs_', suffix='.srt', delete=False
|
||||||
|
)
|
||||||
|
|
||||||
|
with f:
|
||||||
|
f.write(content)
|
||||||
|
return f.name
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
Loading…
Reference in a new issue