platypush/platypush/plugins/media/subtitles.py

208 lines
6.6 KiB
Python
Raw Normal View History

import gzip
import os
import requests
import tempfile
import threading
from platypush.message.response import Response
from platypush.plugins import Plugin, action
from platypush.utils import find_files_by_ext, get_mime_type
class MediaSubtitlesPlugin(Plugin):
"""
Plugin to get video subtitles from OpenSubtitles
Requires:
* **python-opensubtitles** (``pip install -e 'git+https://github.com/agonzalezro/python-opensubtitles#egg=python-opensubtitles'``)
* **webvtt** (``pip install webvtt-py``), optional, to convert srt subtitles into vtt format ready for web streaming
* **requests** (``pip install requests``)
"""
def __init__(self, username, password, language=None, *args, **kwargs):
"""
:param username: Your OpenSubtitles username
:type username: str
:param password: Your OpenSubtitles password
:type password: str
:param language: Preferred language name, ISO639 code or OpenSubtitles
language ID to be used for the subtitles. Also supports an (ordered)
list of preferred languages
:type language: str or list[str]
"""
from pythonopensubtitles.opensubtitles import OpenSubtitles
super().__init__(*args, **kwargs)
self._ost = OpenSubtitles()
self._token = self._ost.login(username, password)
self.languages = []
self._file_lock = threading.RLock()
if language:
if isinstance(language, str):
self.languages.append(language.lower())
elif isinstance(language, list):
self.languages.extend([l.lower() for l in language])
else:
raise AttributeError('{} is neither a string nor a list'.format(
language))
@action
def get_subtitles(self, resource, language=None):
"""
Get the subtitles data for a video resource
:param resource: Media file, torrent or URL to the media resource
:type resource: str
:param language: Language name or code (default: configured preferred
language). Choose 'all' for all the languages
:type language: str
"""
from pythonopensubtitles.utils import File
if resource.startswith('file://'):
resource = resource[len('file://'):]
resource = os.path.abspath(os.path.expanduser(resource))
if not os.path.isfile(resource):
return (None, '{} is not a valid file'.format(resource))
file = resource
cwd = os.getcwd()
media_dir = os.path.dirname(resource)
os.chdir(media_dir)
file = file.split(os.sep)[-1]
local_subs = [{
'IsLocal': True,
'MovieName': '[Local subtitle]',
'SubFileName': sub.split(os.sep)[-1],
'SubDownloadLink': 'file://' + os.path.join(media_dir, sub),
} for sub in find_files_by_ext(media_dir, '.srt', '.vtt')]
self.logger.info('Found {} local subtitles for {}'.format(
len(local_subs), file))
languages = [language.lower()] if language else self.languages
try:
file_hash = File(file).get_hash()
subs = self._ost.search_subtitles([{
'sublanguageid': 'all',
'moviehash': file_hash,
}])
subs = [
sub for sub in subs if not languages or languages[0] == 'all' or
sub.get('LanguageName', '').lower() in languages or
sub.get('SubLanguageID', '').lower() in languages or
sub.get('ISO639', '').lower() in languages
]
for sub in subs:
sub['IsLocal'] = False
self.logger.info('Found {} OpenSubtitles items for {}'.format(
len(subs), file))
return local_subs + subs
finally:
os.chdir(cwd)
@action
def download(self, link, media_resource=None, path=None, convert_to_vtt=False):
"""
Downloads a subtitle link (.srt/.vtt file or gzip/zip OpenSubtitles
archive link) to the specified directory
:param link: Local subtitles file or OpenSubtitles gzip download link
:type link: str
:param path: Path where the subtitle file will be downloaded (default:
temporary file under /tmp)
:type path: str
:param media_resource: Name of the media resource. If set and if it's a
media local file then the subtitles will be saved in the same folder
:type media_resource: str
:param convert_to_vtt: If set to True, then the downloaded subtitles
will be converted to VTT format (default: no conversion)
:type convert_to_vtt: bool
:returns: dict. Format::
{
"filename": "/path/to/subtitle/file.srt"
}
"""
if link.startswith('file://'):
link = link[len('file://'):]
if os.path.isfile(link):
if convert_to_vtt:
link = self.to_vtt(link).output
return { 'filename': link }
gzip_content = requests.get(link).content
f = None
if not path and media_resource:
if media_resource.startswith('file://'):
media_resource = media_resource[len('file://'):]
if os.path.isfile(media_resource):
media_resource = os.path.abspath(media_resource)
path = os.path.join(
os.path.dirname(media_resource),
'.'.join(os.path.basename(media_resource).split('.')[:-1])) + '.srt'
if path:
f = open(path, 'wb')
else:
f = tempfile.NamedTemporaryFile(prefix='media_subs_',
suffix='.srt', delete=False)
path = f.name
try:
with f:
f.write(gzip.decompress(gzip_content))
if convert_to_vtt:
path = self.to_vtt(path).output
except Exception as e:
os.unlink(path)
raise e
return { 'filename': path }
@action
def to_vtt(self, filename):
"""
Get the VTT content given an SRT file. Will return the original content if
the file is already in VTT format.
"""
if filename.lower().endswith('.vtt'):
return filename
import webvtt
with self._file_lock:
try:
webvtt.read(filename)
return filename
except Exception as e:
webvtt.from_srt(filename).save()
return '.'.join(filename.split('.')[:-1]) + '.vtt'
# vim:sw=4:ts=4:et: