├── LICENSE ├── MANIFEST.in ├── README.md ├── linux ├── multiprocessing_hook.py ├── mypyinstaller.sh ├── pyautosrt.pyw └── streamlink │ ├── __init__.py │ ├── __main__.py │ ├── _version.py │ ├── api.py │ ├── buffers.py │ ├── cache.py │ ├── compat.py │ ├── exceptions.py │ ├── logger.py │ ├── options.py │ ├── packages │ ├── __init__.py │ └── requests_file.py │ ├── plugin │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ ├── http_session.py │ │ ├── useragents.py │ │ ├── validate │ │ │ ├── __init__.py │ │ │ ├── __pycache__ │ │ │ │ ├── __init__.cpython-310.pyc │ │ │ │ ├── __init__.cpython-38.pyc │ │ │ │ ├── _exception.cpython-310.pyc │ │ │ │ ├── _exception.cpython-38.pyc │ │ │ │ ├── _schemas.cpython-310.pyc │ │ │ │ ├── _schemas.cpython-38.pyc │ │ │ │ ├── _validate.cpython-310.pyc │ │ │ │ ├── _validate.cpython-38.pyc │ │ │ │ ├── _validators.cpython-310.pyc │ │ │ │ └── _validators.cpython-38.pyc │ │ │ ├── _exception.py │ │ │ ├── _schemas.py │ │ │ ├── _validate.py │ │ │ └── _validators.py │ │ └── websocket.py │ └── plugin.py │ ├── plugins │ ├── __init__.py │ ├── abematv.py │ ├── adultswim.py │ ├── afreeca.py │ ├── albavision.py │ ├── aloula.py │ ├── app17.py │ ├── ard_live.py │ ├── ard_mediathek.py │ ├── artetv.py │ ├── atpchallenger.py │ ├── atresplayer.py │ ├── bbciplayer.py │ ├── bfmtv.py │ ├── bigo.py │ ├── bilibili.py │ ├── blazetv.py │ ├── bloomberg.py │ ├── booyah.py │ ├── brightcove.py │ ├── btv.py │ ├── cbsnews.py │ ├── cdnbg.py │ ├── ceskatelevize.py │ ├── cinergroup.py │ ├── clubbingtv.py │ ├── cmmedia.py │ ├── cnews.py │ ├── crunchyroll.py │ ├── dailymotion.py │ ├── dash.py │ ├── delfi.py │ ├── deutschewelle.py │ ├── dlive.py │ ├── dogan.py │ ├── dogus.py │ ├── drdk.py │ ├── earthcam.py │ ├── euronews.py │ ├── facebook.py │ ├── filmon.py │ ├── foxtr.py │ ├── galatasaraytv.py │ ├── goltelevision.py │ ├── goodgame.py │ ├── googledrive.py │ ├── gulli.py │ ├── hiplayer.py │ ├── hls.py │ ├── http.py │ ├── htv.py │ ├── huajiao.py │ ├── huya.py │ ├── idf1.py │ ├── indihometv.py │ ├── invintus.py │ ├── kugou.py │ ├── linelive.py │ ├── livestream.py │ ├── lnk.py │ ├── lrt.py │ ├── ltv_lsm_lv.py │ ├── mdstrm.py │ ├── mediaklikk.py │ ├── mediavitrina.py │ ├── mildom.py │ ├── mitele.py │ ├── mixcloud.py │ ├── mjunoon.py │ ├── mrtmk.py │ ├── n13tv.py │ ├── nbcnews.py │ ├── nhkworld.py │ ├── nicolive.py │ ├── nimotv.py │ ├── nos.py │ ├── nownews.py │ ├── nrk.py │ ├── ntv.py │ ├── okru.py │ ├── olympicchannel.py │ ├── oneplusone.py │ ├── onetv.py │ ├── openrectv.py │ ├── pandalive.py │ ├── picarto.py │ ├── piczel.py │ ├── pixiv.py │ ├── pluto.py │ ├── pluzz.py │ ├── qq.py │ ├── radiko.py │ ├── radionet.py │ ├── raiplay.py │ ├── reuters.py │ ├── rtbf.py │ ├── rtpa.py │ ├── rtpplay.py │ ├── rtve.py │ ├── rtvs.py │ ├── ruv.py │ ├── sbscokr.py │ ├── showroom.py │ ├── sportal.py │ ├── sportschau.py │ ├── ssh101.py │ ├── stadium.py │ ├── steam.py │ ├── streamable.py │ ├── streann.py │ ├── stv.py │ ├── svtplay.py │ ├── swisstxt.py │ ├── telefe.py │ ├── telemadrid.py │ ├── tf1.py │ ├── trovo.py │ ├── turkuvaz.py │ ├── tv360.py │ ├── tv3cat.py │ ├── tv4play.py │ ├── tv5monde.py │ ├── tv8.py │ ├── tv999.py │ ├── tvibo.py │ ├── tviplayer.py │ ├── tvp.py │ ├── tvrby.py │ ├── tvrplus.py │ ├── tvtoya.py │ ├── twitcasting.py │ ├── twitch.py │ ├── useetv.py │ ├── ustreamtv.py │ ├── ustvnow.py │ ├── vidio.py │ ├── vimeo.py │ ├── vinhlongtv.py │ ├── vk.py │ ├── vkplay.py │ ├── vlive.py │ ├── vtvgo.py │ ├── wasd.py │ ├── webtv.py │ ├── welt.py │ ├── wwenetwork.py │ ├── youtube.py │ ├── yupptv.py │ ├── zattoo.py │ ├── zdf_mediathek.py │ ├── zeenews.py │ ├── zengatv.py │ └── zhanqi.py │ ├── py.typed │ ├── session.py │ ├── stream │ ├── __init__.py │ ├── dash.py │ ├── dash_manifest.py │ ├── ffmpegmux.py │ ├── file.py │ ├── filtered.py │ ├── hls.py │ ├── hls_playlist.py │ ├── http.py │ ├── segmented.py │ ├── stream.py │ └── wrappers.py │ ├── user_input.py │ └── utils │ ├── __init__.py │ ├── args.py │ ├── cache.py │ ├── crypto.py │ ├── data.py │ ├── formatter.py │ ├── l10n.py │ ├── module.py │ ├── named_pipe.py │ ├── parse.py │ ├── processoutput.py │ ├── times.py │ └── url.py ├── pyautosrt └── __init__.py ├── pypi_setup.bat ├── pypi_setup.sh ├── setup.cfg ├── setup.py └── win ├── multiprocessing_hook.py ├── mypyinstaller.bat ├── pyautosrt.pyw └── streamlink ├── __init__.py ├── __main__.py ├── _version.py ├── api.py ├── buffers.py ├── cache.py ├── compat.py ├── exceptions.py ├── logger.py ├── options.py ├── packages ├── __init__.py └── requests_file.py ├── plugin ├── __init__.py ├── api │ ├── __init__.py │ ├── http_session.py │ ├── useragents.py │ ├── validate │ │ ├── __init__.py │ │ ├── _exception.py │ │ ├── _schemas.py │ │ ├── _validate.py │ │ └── _validators.py │ └── websocket.py └── plugin.py ├── plugins ├── __init__.py ├── abematv.py ├── adultswim.py ├── afreeca.py ├── albavision.py ├── aloula.py ├── app17.py ├── ard_live.py ├── ard_mediathek.py ├── artetv.py ├── atpchallenger.py ├── atresplayer.py ├── bbciplayer.py ├── bfmtv.py ├── bigo.py ├── bilibili.py ├── blazetv.py ├── bloomberg.py ├── booyah.py ├── brightcove.py ├── btv.py ├── cbsnews.py ├── cdnbg.py ├── ceskatelevize.py ├── cinergroup.py ├── clubbingtv.py ├── cmmedia.py ├── cnews.py ├── crunchyroll.py ├── dailymotion.py ├── dash.py ├── delfi.py ├── deutschewelle.py ├── dlive.py ├── dogan.py ├── dogus.py ├── drdk.py ├── earthcam.py ├── euronews.py ├── facebook.py ├── filmon.py ├── foxtr.py ├── galatasaraytv.py ├── goltelevision.py ├── goodgame.py ├── googledrive.py ├── gulli.py ├── hiplayer.py ├── hls.py ├── http.py ├── htv.py ├── huajiao.py ├── huya.py ├── idf1.py ├── indihometv.py ├── invintus.py ├── kugou.py ├── linelive.py ├── livestream.py ├── lnk.py ├── lrt.py ├── ltv_lsm_lv.py ├── mdstrm.py ├── mediaklikk.py ├── mediavitrina.py ├── mildom.py ├── mitele.py ├── mixcloud.py ├── mjunoon.py ├── mrtmk.py ├── n13tv.py ├── nbcnews.py ├── nhkworld.py ├── nicolive.py ├── nimotv.py ├── nos.py ├── nownews.py ├── nrk.py ├── ntv.py ├── okru.py ├── olympicchannel.py ├── oneplusone.py ├── onetv.py ├── openrectv.py ├── pandalive.py ├── picarto.py ├── piczel.py ├── pixiv.py ├── pluto.py ├── pluzz.py ├── qq.py ├── radiko.py ├── radionet.py ├── raiplay.py ├── reuters.py ├── rtbf.py ├── rtpa.py ├── rtpplay.py ├── rtve.py ├── rtvs.py ├── ruv.py ├── sbscokr.py ├── showroom.py ├── sportal.py ├── sportschau.py ├── ssh101.py ├── stadium.py ├── steam.py ├── streamable.py ├── streann.py ├── stv.py ├── svtplay.py ├── swisstxt.py ├── telefe.py ├── telemadrid.py ├── tf1.py ├── trovo.py ├── turkuvaz.py ├── tv360.py ├── tv3cat.py ├── tv4play.py ├── tv5monde.py ├── tv8.py ├── tv999.py ├── tvibo.py ├── tviplayer.py ├── tvp.py ├── tvrby.py ├── tvrplus.py ├── tvtoya.py ├── twitcasting.py ├── twitch.py ├── useetv.py ├── ustreamtv.py ├── ustvnow.py ├── vidio.py ├── vimeo.py ├── vinhlongtv.py ├── vk.py ├── vkplay.py ├── vlive.py ├── vtvgo.py ├── wasd.py ├── webtv.py ├── welt.py ├── wwenetwork.py ├── youtube.py ├── yupptv.py ├── zattoo.py ├── zdf_mediathek.py ├── zeenews.py ├── zengatv.py └── zhanqi.py ├── py.typed ├── session.py ├── stream ├── __init__.py ├── dash.py ├── dash_manifest.py ├── ffmpegmux.py ├── file.py ├── filtered.py ├── hls.py ├── hls_playlist.py ├── http.py ├── segmented.py ├── stream.py └── wrappers.py ├── user_input.py └── utils ├── __init__.py ├── args.py ├── cache.py ├── crypto.py ├── data.py ├── formatter.py ├── l10n.py ├── module.py ├── named_pipe.py ├── parse.py ├── processoutput.py ├── times.py └── url.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Bot Bahlul 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include LICENSE -------------------------------------------------------------------------------- /linux/multiprocessing_hook.py: -------------------------------------------------------------------------------- 1 | from PyInstaller.utils.hooks import collect_submodules 2 | hiddenimports = collect_submodules('multiprocessing') 3 | -------------------------------------------------------------------------------- /linux/streamlink/__init__.py: -------------------------------------------------------------------------------- 1 | """Streamlink extracts streams from various services. 2 | 3 | The main compontent of Streamlink is a command-line utility that 4 | launches the streams in a video player. 5 | 6 | An API is also provided that allows direct access to stream data. 7 | 8 | Full documentation is available at https://streamlink.github.io. 9 | 10 | """ 11 | from streamlink._version import __version__ 12 | 13 | __title__ = "streamlink" 14 | __license__ = "Simplified BSD" 15 | __author__ = "Streamlink" 16 | __copyright__ = "Copyright 2023 Streamlink" 17 | __credits__ = ["https://github.com/streamlink/streamlink/blob/master/AUTHORS"] 18 | 19 | from streamlink.api import streams 20 | from streamlink.exceptions import (StreamlinkError, PluginError, NoStreamsError, 21 | NoPluginError, StreamError) 22 | from streamlink.session import Streamlink 23 | -------------------------------------------------------------------------------- /linux/streamlink/__main__.py: -------------------------------------------------------------------------------- 1 | if __name__ == "__main__": 2 | from streamlink_cli.main import main 3 | main() 4 | -------------------------------------------------------------------------------- /linux/streamlink/_version.py: -------------------------------------------------------------------------------- 1 | # Always get the current version in "editable" installs 2 | # `pip install -e .` / `python setup.py develop` 3 | def _get_version() -> str: 4 | from pathlib import Path 5 | from versioningit import get_version 6 | import streamlink 7 | 8 | return get_version( 9 | project_dir=Path(streamlink.__file__).parents[2], 10 | ) 11 | 12 | 13 | # The following _get_version() call will get replaced by versioningit with a static version string when building streamlink 14 | # `pip install .` / `pip wheel .` / `python setup.py build` / `python setup.py bdist_wheel` / etc. 15 | __version__ = "5.5.1" 16 | -------------------------------------------------------------------------------- /linux/streamlink/api.py: -------------------------------------------------------------------------------- 1 | from streamlink.session import Streamlink 2 | 3 | 4 | def streams(url: str, **params): 5 | """ 6 | Initializes an empty Streamlink session, attempts to find a plugin and extracts streams from the URL if a plugin was found. 7 | 8 | :param url: a URL to match against loaded plugins 9 | :param params: Additional keyword arguments passed to :meth:`streamlink.Streamlink.streams` 10 | :raises NoPluginError: on plugin resolve failure 11 | :returns: A :class:`dict` of stream names and :class:`streamlink.stream.Stream` instances 12 | """ 13 | 14 | session = Streamlink() 15 | 16 | return session.streams(url, **params) 17 | -------------------------------------------------------------------------------- /linux/streamlink/compat.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | 5 | is_darwin = sys.platform == "darwin" 6 | is_win32 = os.name == "nt" 7 | 8 | 9 | __all__ = [ 10 | "is_darwin", 11 | "is_win32", 12 | ] 13 | -------------------------------------------------------------------------------- /linux/streamlink/exceptions.py: -------------------------------------------------------------------------------- 1 | class StreamlinkError(Exception): 2 | """ 3 | Any error caused by Streamlink will be caught with this exception. 4 | """ 5 | 6 | 7 | # TODO: don't use PluginError for failed HTTP requests or validation schema failures 8 | class PluginError(StreamlinkError): 9 | """ 10 | Plugin related error. 11 | """ 12 | 13 | 14 | class FatalPluginError(PluginError): 15 | """ 16 | Plugin related error that cannot be recovered from. 17 | 18 | Plugins should use this ``Exception`` when errors that can 19 | never be recovered from are encountered. For example, when 20 | a user's input is required and none can be given. 21 | """ 22 | 23 | 24 | class NoPluginError(StreamlinkError): 25 | """ 26 | Error raised by :py:meth:`Streamlink.resolve_url() ` 27 | and :py:meth:`Streamlink.resolve_url_no_redirect() ` 28 | when no plugin could be found for the given input URL. 29 | """ 30 | 31 | 32 | class NoStreamsError(StreamlinkError): 33 | """ 34 | Plugins should use this ``Exception`` in :py:meth:`Plugin._get_streams() ` 35 | when returning ``None`` or an empty ``dict`` is not possible, e.g. in nested function calls. 36 | """ 37 | 38 | 39 | class StreamError(StreamlinkError): 40 | """ 41 | Stream related error. 42 | """ 43 | 44 | 45 | # https://stackoverflow.com/a/49797717 46 | class _StreamlinkWarningMeta(type): 47 | def __new__(mcs, name, bases, namespace, **kw): 48 | name = namespace.get("__name__", name) 49 | return super().__new__(mcs, name, bases, namespace, **kw) 50 | 51 | 52 | class StreamlinkWarning(UserWarning, metaclass=_StreamlinkWarningMeta): 53 | pass 54 | 55 | 56 | class StreamlinkDeprecationWarning(StreamlinkWarning): 57 | __name__ = "StreamlinkDeprecation" 58 | 59 | 60 | __all__ = [ 61 | "StreamlinkError", 62 | "PluginError", 63 | "FatalPluginError", 64 | "NoPluginError", 65 | "NoStreamsError", 66 | "StreamError", 67 | "StreamlinkWarning", 68 | "StreamlinkDeprecationWarning", 69 | ] 70 | -------------------------------------------------------------------------------- /linux/streamlink/packages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botbahlul/PyAutoSRT/20942b9656030ac5cc044655bb88974a381c09b8/linux/streamlink/packages/__init__.py -------------------------------------------------------------------------------- /linux/streamlink/plugin/__init__.py: -------------------------------------------------------------------------------- 1 | from streamlink.exceptions import PluginError 2 | from streamlink.options import Argument as PluginArgument, Arguments as PluginArguments, Options as PluginOptions 3 | from streamlink.plugin.plugin import ( 4 | HIGH_PRIORITY, 5 | LOW_PRIORITY, 6 | NO_PRIORITY, 7 | NORMAL_PRIORITY, 8 | Plugin, 9 | pluginargument, 10 | pluginmatcher, 11 | ) 12 | 13 | 14 | __all__ = [ 15 | "HIGH_PRIORITY", "NORMAL_PRIORITY", "LOW_PRIORITY", "NO_PRIORITY", 16 | "Plugin", "PluginArguments", "PluginArgument", "PluginError", "PluginOptions", 17 | "pluginmatcher", "pluginargument", 18 | ] 19 | -------------------------------------------------------------------------------- /linux/streamlink/plugin/api/__init__.py: -------------------------------------------------------------------------------- 1 | from streamlink.plugin.api.http_session import HTTPSession 2 | 3 | 4 | __all__ = ["HTTPSession"] 5 | -------------------------------------------------------------------------------- /linux/streamlink/plugin/api/useragents.py: -------------------------------------------------------------------------------- 1 | ANDROID = "Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.135 Mobile Safari/537.36" 2 | CHROME = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36" 3 | CHROME_OS = "Mozilla/5.0 (X11; CrOS x86_64 15359.58.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.134 Safari/537.36" 4 | FIREFOX = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109) Gecko/20100101 Firefox/112.0" 5 | IE_11 = "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" 6 | IPHONE = "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 Mobile/15E148 Safari/604.1" 7 | OPERA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 OPR/98.0.4759.3" 8 | SAFARI = "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_3_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 Safari/605.1.15" 9 | 10 | # Backwards compatibility 11 | EDGE = CHROME 12 | FIREFOX_MAC = FIREFOX 13 | IE_6 = IE_7 = IE_8 = IE_9 = IE_11 14 | IPHONE_6 = IPAD = IPHONE 15 | SAFARI_7 = SAFARI_8 = SAFARI 16 | WINDOWS_PHONE_8 = ANDROID 17 | -------------------------------------------------------------------------------- /linux/streamlink/plugin/api/validate/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botbahlul/PyAutoSRT/20942b9656030ac5cc044655bb88974a381c09b8/linux/streamlink/plugin/api/validate/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /linux/streamlink/plugin/api/validate/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botbahlul/PyAutoSRT/20942b9656030ac5cc044655bb88974a381c09b8/linux/streamlink/plugin/api/validate/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /linux/streamlink/plugin/api/validate/__pycache__/_exception.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botbahlul/PyAutoSRT/20942b9656030ac5cc044655bb88974a381c09b8/linux/streamlink/plugin/api/validate/__pycache__/_exception.cpython-310.pyc -------------------------------------------------------------------------------- /linux/streamlink/plugin/api/validate/__pycache__/_exception.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botbahlul/PyAutoSRT/20942b9656030ac5cc044655bb88974a381c09b8/linux/streamlink/plugin/api/validate/__pycache__/_exception.cpython-38.pyc -------------------------------------------------------------------------------- /linux/streamlink/plugin/api/validate/__pycache__/_schemas.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botbahlul/PyAutoSRT/20942b9656030ac5cc044655bb88974a381c09b8/linux/streamlink/plugin/api/validate/__pycache__/_schemas.cpython-310.pyc -------------------------------------------------------------------------------- /linux/streamlink/plugin/api/validate/__pycache__/_schemas.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botbahlul/PyAutoSRT/20942b9656030ac5cc044655bb88974a381c09b8/linux/streamlink/plugin/api/validate/__pycache__/_schemas.cpython-38.pyc -------------------------------------------------------------------------------- /linux/streamlink/plugin/api/validate/__pycache__/_validate.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botbahlul/PyAutoSRT/20942b9656030ac5cc044655bb88974a381c09b8/linux/streamlink/plugin/api/validate/__pycache__/_validate.cpython-310.pyc -------------------------------------------------------------------------------- /linux/streamlink/plugin/api/validate/__pycache__/_validate.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botbahlul/PyAutoSRT/20942b9656030ac5cc044655bb88974a381c09b8/linux/streamlink/plugin/api/validate/__pycache__/_validate.cpython-38.pyc -------------------------------------------------------------------------------- /linux/streamlink/plugin/api/validate/__pycache__/_validators.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botbahlul/PyAutoSRT/20942b9656030ac5cc044655bb88974a381c09b8/linux/streamlink/plugin/api/validate/__pycache__/_validators.cpython-310.pyc -------------------------------------------------------------------------------- /linux/streamlink/plugin/api/validate/__pycache__/_validators.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botbahlul/PyAutoSRT/20942b9656030ac5cc044655bb88974a381c09b8/linux/streamlink/plugin/api/validate/__pycache__/_validators.cpython-38.pyc -------------------------------------------------------------------------------- /linux/streamlink/plugins/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | New plugins should use streamlink.plugin.Plugin instead 3 | of this module, but this is kept here for backwards 4 | compatibility. 5 | """ 6 | 7 | from streamlink.exceptions import NoPluginError, NoStreamsError, PluginError 8 | from streamlink.plugin.plugin import Plugin 9 | 10 | 11 | __all__ = ["Plugin", "PluginError", "NoStreamsError", "NoPluginError"] 12 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/app17.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Social platform delivering live broadcasts on diverse topics, from politics and music to entertainment. 3 | $url 17app.co 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream 13 | from streamlink.stream.http import HTTPStream 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | @pluginmatcher(re.compile( 20 | r"https?://17\.live/.+/live/(?P[^/&?]+)", 21 | )) 22 | class App17(Plugin): 23 | def _get_streams(self): 24 | channel = self.match.group("channel") 25 | self.session.http.headers.update({"Referer": self.url}) 26 | data = self.session.http.post( 27 | f"https://wap-api.17app.co/api/v1/lives/{channel}/viewers/alive", 28 | data={"liveStreamID": channel}, 29 | schema=validate.Schema( 30 | validate.parse_json(), 31 | validate.any( 32 | {"rtmpUrls": [{ 33 | validate.optional("provider"): validate.any(int, None), 34 | "url": validate.url(path=validate.endswith(".flv")), 35 | }]}, 36 | {"errorCode": int, "errorMessage": str}, 37 | ), 38 | ), 39 | acceptable_status=(200, 403, 404, 420)) 40 | log.trace(f"{data!r}") 41 | if data.get("errorCode"): 42 | log.error(f"{data['errorCode']} - {data['errorMessage'].replace('Something wrong: ', '')}") 43 | return 44 | 45 | flv_url = data["rtmpUrls"][0]["url"] 46 | yield "live", HTTPStream(self.session, flv_url) 47 | 48 | if "wansu-" in flv_url: 49 | hls_url = flv_url.replace(".flv", "/playlist.m3u8") 50 | else: 51 | hls_url = flv_url.replace("live-hdl", "live-hls").replace(".flv", ".m3u8") 52 | 53 | s = HLSStream.parse_variant_playlist(self.session, hls_url) 54 | if not s: 55 | yield "live", HLSStream(self.session, hls_url) 56 | elif len(s) == 1: 57 | yield "live", next(iter(s.values())) 58 | else: 59 | yield from s.items() 60 | 61 | 62 | __plugin__ = App17 63 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/atpchallenger.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Tennis tournaments organized by the Association of Tennis Professionals. 3 | $url atptour.com/en/atp-challenger-tour/challenger-tv 4 | $type live, vod 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.plugin.api import validate 11 | 12 | 13 | @pluginmatcher(re.compile( 14 | r"https?://(?:www\.)?atptour\.com/(?:en|es)/atp-challenger-tour/challenger-tv", 15 | )) 16 | class AtpChallengerTour(Plugin): 17 | def _get_streams(self): 18 | iframe_url = self.session.http.get(self.url, schema=validate.Schema( 19 | validate.parse_html(), 20 | validate.xml_xpath_string(".//iframe[starts-with(@id,'vimeoPlayer_')][@src][1]/@src"), 21 | validate.any(None, validate.url()), 22 | )) 23 | if iframe_url: 24 | return self.session.streams(iframe_url) 25 | 26 | 27 | __plugin__ = AtpChallengerTour 28 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/bigo.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Global live streaming platform for live video game broadcasts and individual live streams. 3 | $url live.bigo.tv 4 | $url bigoweb.co 5 | $type live 6 | """ 7 | 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import useragents, validate 12 | from streamlink.stream.hls import HLSStream 13 | 14 | 15 | @pluginmatcher(re.compile( 16 | r"https?://(?:www\.)?bigo\.tv/([^/]+)$", 17 | )) 18 | class Bigo(Plugin): 19 | _api_url = "https://www.bigo.tv/OInterface/getVideoParam?bigoId={0}" 20 | 21 | _video_info_schema = validate.Schema({ 22 | "code": 0, 23 | "msg": "success", 24 | "data": { 25 | "videoSrc": validate.any(None, "", validate.url()), 26 | }, 27 | }) 28 | 29 | def _get_streams(self): 30 | res = self.session.http.get( 31 | self._api_url.format(self.match.group(1)), 32 | allow_redirects=True, 33 | headers={"User-Agent": useragents.IPHONE_6}, 34 | ) 35 | data = self.session.http.json(res, schema=self._video_info_schema) 36 | videourl = data["data"]["videoSrc"] 37 | if videourl: 38 | yield "live", HLSStream(self.session, videourl) 39 | 40 | 41 | __plugin__ = Bigo 42 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/btv.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description A privately owned Bulgarian live TV channel. 3 | $url btvplus.bg 4 | $type live 5 | $region Bulgaria 6 | """ 7 | 8 | import logging 9 | import re 10 | 11 | from streamlink.plugin import Plugin, pluginmatcher 12 | from streamlink.plugin.api import validate 13 | from streamlink.stream.hls import HLSStream 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | @pluginmatcher(re.compile( 20 | r"https?://(?:www\.)?btvplus\.bg/live/?", 21 | )) 22 | class BTV(Plugin): 23 | URL_API = "https://btvplus.bg/lbin/v3/btvplus/player_config.php" 24 | 25 | def _get_streams(self): 26 | media_id = self.session.http.get(self.url, schema=validate.Schema( 27 | re.compile(r"media_id=(\d+)"), 28 | validate.any(None, validate.get(1)), 29 | )) 30 | if media_id is None: 31 | return 32 | 33 | stream_url = self.session.http.get( 34 | self.URL_API, 35 | params={ 36 | "media_id": media_id, 37 | }, 38 | schema=validate.Schema( 39 | validate.any( 40 | validate.all( 41 | validate.regex(re.compile(r"geo_blocked_stream")), 42 | validate.get(0), 43 | ), 44 | validate.all( 45 | validate.parse_json(), 46 | { 47 | "status": "ok", 48 | "info": { 49 | "file": validate.url(path=validate.endswith(".m3u8")), 50 | }, 51 | }, 52 | validate.get(("info", "file")), 53 | ), 54 | ), 55 | ), 56 | ) 57 | if not stream_url: 58 | return 59 | 60 | if stream_url == "geo_blocked_stream": 61 | log.error("The content is not available in your region") 62 | return 63 | 64 | return {"live": HLSStream(self.session, stream_url)} 65 | 66 | 67 | __plugin__ = BTV 68 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/cbsnews.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description 24-hour live-streaming world news channel, based in the United States of America. 3 | $url cbsnews.com 4 | $type live 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.plugin.api import validate 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | @pluginmatcher(re.compile( 15 | r"https?://(?:www\.)?cbsnews\.com/(?:\w+/)?live/?", 16 | )) 17 | class CBSNews(Plugin): 18 | def _get_streams(self): 19 | data = self.session.http.get(self.url, schema=validate.Schema( 20 | re.compile(r"CBSNEWS\.defaultPayload\s*=\s*(\{.*?})\s*\n"), 21 | validate.none_or_all( 22 | validate.get(1), 23 | validate.parse_json(), 24 | { 25 | "items": [{ 26 | "id": str, 27 | "canonicalTitle": str, 28 | "video": validate.url(), 29 | "format": "application/x-mpegURL", 30 | }], 31 | }, 32 | validate.get(("items", 0)), 33 | validate.union_get("id", "canonicalTitle", "video"), 34 | ), 35 | )) 36 | if not data: 37 | return 38 | 39 | self.id, self.title, hls_url = data 40 | 41 | return HLSStream.parse_variant_playlist(self.session, hls_url) 42 | 43 | 44 | __plugin__ = CBSNews 45 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/cinergroup.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Turkish live TV channels from Ciner Group, including Haberturk TV and Show TV. 3 | $url bloomberght.com 4 | $url haberturk.com 5 | $url haberturk.tv 6 | $url showmax.com.tr 7 | $url showturk.com.tr 8 | $url showtv.com.tr 9 | $type live 10 | """ 11 | 12 | import re 13 | 14 | from streamlink.plugin import Plugin, pluginmatcher 15 | from streamlink.plugin.api import validate 16 | from streamlink.stream.hls import HLSStream 17 | 18 | 19 | @pluginmatcher(re.compile(r""" 20 | https?://(?:www\.)? 21 | (?: 22 | bloomberght\.com/tv 23 | | 24 | haberturk\.(?:com|tv)(?:/tv)?/canliyayin 25 | | 26 | showmax\.com\.tr/canli-?yayin 27 | | 28 | showturk\.com\.tr/canli-?yayin(?:/showtv)? 29 | | 30 | showtv\.com\.tr/canli-yayin(?:/showtv)? 31 | )/? 32 | """, re.VERBOSE)) 33 | class CinerGroup(Plugin): 34 | @staticmethod 35 | def _schema_videourl(): 36 | return validate.Schema( 37 | validate.xml_xpath_string(".//script[contains(text(), 'videoUrl')]/text()"), 38 | validate.none_or_all( 39 | re.compile(r"""(?['"])(?P.+?)(?P=q)"""), 40 | validate.none_or_all( 41 | validate.get("url"), 42 | validate.url(), 43 | ), 44 | ), 45 | ) 46 | 47 | @staticmethod 48 | def _schema_data_ht(): 49 | return validate.Schema( 50 | validate.xml_xpath_string(".//div[@data-ht][1]/@data-ht"), 51 | validate.none_or_all( 52 | validate.parse_json(), 53 | { 54 | "ht_stream_m3u8": validate.url(), 55 | }, 56 | validate.get("ht_stream_m3u8"), 57 | ), 58 | ) 59 | 60 | def _get_streams(self): 61 | root = self.session.http.get(self.url, schema=validate.Schema(validate.parse_html())) 62 | schema_getters = self._schema_videourl, self._schema_data_ht 63 | stream_url = next((res for res in (getter().validate(root) for getter in schema_getters) if res), None) 64 | 65 | if stream_url: 66 | return HLSStream.parse_variant_playlist(self.session, stream_url) 67 | 68 | 69 | __plugin__ = CinerGroup 70 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/cnews.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description French free-to-air news channel, providing 24-hour national and global news coverage. 3 | $url cnews.fr 4 | $type live, vod 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.plugin.api import validate 11 | 12 | 13 | @pluginmatcher(re.compile( 14 | r"https?://(?:www\.)?cnews\.fr", 15 | )) 16 | class CNEWS(Plugin): 17 | _dailymotion_url = "https://www.dailymotion.com/embed/video/{}" 18 | 19 | def _get_streams(self): 20 | data = self.session.http.get(self.url, schema=validate.Schema( 21 | re.compile(r"jQuery\.extend\(Drupal\.settings, ({.*})\);"), 22 | validate.none_or_all( 23 | validate.get(1), 24 | validate.parse_json(), 25 | { 26 | validate.optional("dm_player_live_dailymotion"): { 27 | validate.optional("video_id"): str, 28 | }, 29 | validate.optional("dm_player_node_dailymotion"): { 30 | validate.optional("video_id"): str, 31 | }, 32 | }, 33 | validate.union_get("dm_player_live_dailymotion", "dm_player_node_dailymotion"), 34 | ), 35 | )) 36 | if not data: 37 | return 38 | 39 | live, node = data 40 | 41 | if node: 42 | return self.session.streams(self._dailymotion_url.format(node["video_id"])) 43 | elif live: 44 | return self.session.streams(self._dailymotion_url.format(live["video_id"])) 45 | 46 | 47 | __plugin__ = CNEWS 48 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/dash.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import re 3 | 4 | from streamlink.plugin import Plugin, pluginmatcher 5 | from streamlink.plugin.plugin import LOW_PRIORITY, parse_params, stream_weight 6 | from streamlink.stream.dash import DASHStream 7 | from streamlink.utils.url import update_scheme 8 | 9 | 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | @pluginmatcher(re.compile( 14 | r"dash://(?P\S+)(?:\s(?P.+))?", 15 | )) 16 | @pluginmatcher(priority=LOW_PRIORITY, pattern=re.compile( 17 | r"(?P\S+\.mpd(?:\?\S*)?)(?:\s(?P.+))?", 18 | )) 19 | class MPEGDASH(Plugin): 20 | @classmethod 21 | def stream_weight(cls, stream): 22 | match = re.match(r"^(?:(.*)\+)?(?:a(\d+)k)$", stream) 23 | if match and match.group(1) and match.group(2): 24 | weight, group = stream_weight(match.group(1)) 25 | weight += int(match.group(2)) 26 | return weight, group 27 | elif match and match.group(2): 28 | return stream_weight(f"{match.group(2)}k") 29 | else: 30 | return stream_weight(stream) 31 | 32 | def _get_streams(self): 33 | data = self.match.groupdict() 34 | url = update_scheme("https://", data.get("url"), force=False) 35 | params = parse_params(data.get("params")) 36 | log.debug(f"URL={url}; params={params}") 37 | 38 | return DASHStream.parse_manifest(self.session, url, **params) 39 | 40 | 41 | __plugin__ = MPEGDASH 42 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/dogus.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Turkish live TV channels from Dogus Group, including Euro Star, Star and NTV. 3 | $url eurostartv.com.tr 4 | $url kralmuzik.com.tr 5 | $url ntv.com.tr 6 | $url startv.com.tr 7 | $type live 8 | """ 9 | 10 | import re 11 | 12 | from streamlink.plugin import Plugin, pluginmatcher 13 | from streamlink.plugin.api import validate 14 | from streamlink.stream.hls import HLSStream 15 | 16 | 17 | @pluginmatcher(re.compile(r"https?://(?:www\.)?eurostartv\.com\.tr/canli-izle")) 18 | @pluginmatcher(re.compile(r"https?://(?:www\.)?kralmuzik\.com\.tr/tv/.+")) 19 | @pluginmatcher(re.compile(r"https?://(?:www\.)?ntv\.com\.tr/canli-yayin/ntv")) 20 | @pluginmatcher(re.compile(r"https?://(?:www\.)?startv\.com\.tr/canli-yayin")) 21 | class Dogus(Plugin): 22 | _re_live_hls = re.compile(r"'(https?://[^']+/live/hls/[^']+)'") 23 | _re_yt_script = re.compile(r"youtube\.init\('([\w-]{11})'") 24 | 25 | def _get_streams(self): 26 | root = self.session.http.get(self.url, schema=validate.Schema(validate.parse_html())) 27 | 28 | # https://www.ntv.com.tr/canli-yayin/ntv?youtube=true 29 | yt_iframe = root.xpath("string(.//iframe[contains(@src,'youtube.com')][1]/@src)") 30 | # https://www.startv.com.tr/canli-yayin 31 | dm_iframe = root.xpath("string(.//iframe[contains(@src,'dailymotion.com')][1]/@src)") 32 | # https://www.kralmuzik.com.tr/tv/kral-tv 33 | # https://www.kralmuzik.com.tr/tv/kral-pop-tv 34 | yt_script = root.xpath("string(.//script[contains(text(), 'youtube.init')][1]/text())") 35 | if yt_script: 36 | m = self._re_yt_script.search(yt_script) 37 | if m: 38 | yt_iframe = f"https://www.youtube.com/watch?v={m.group(1)}" 39 | 40 | iframe = yt_iframe or dm_iframe 41 | if iframe: 42 | return self.session.streams(iframe) 43 | 44 | # http://eurostartv.com.tr/canli-izle 45 | dd_script = root.xpath("string(.//script[contains(text(), '/live/hls/')][1]/text())") 46 | if dd_script: 47 | m = self._re_live_hls.search(dd_script) 48 | if m: 49 | return HLSStream.parse_variant_playlist(self.session, m.group(1)) 50 | 51 | 52 | __plugin__ = Dogus 53 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/drdk.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels from DR, a Danish public, state-owned broadcaster. 3 | $url dr.dk 4 | $type live 5 | $region Denmark 6 | """ 7 | 8 | import logging 9 | import re 10 | 11 | from streamlink.plugin import Plugin, pluginmatcher 12 | from streamlink.plugin.api import validate 13 | from streamlink.stream.hls import HLSStream 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | @pluginmatcher(re.compile( 20 | r"https?://(?:www\.)?dr\.dk/drtv(/kanal/[\w-]+)", 21 | )) 22 | class DRDK(Plugin): 23 | live_api_url = "https://www.dr-massive.com/api/page" 24 | 25 | _live_data_schema = validate.Schema( 26 | {"item": {"customFields": { 27 | validate.optional("hlsURL"): validate.url(), 28 | validate.optional("hlsWithSubtitlesURL"): validate.url(), 29 | }}}, 30 | validate.get("item"), 31 | validate.get("customFields"), 32 | ) 33 | 34 | def _get_live(self, path): 35 | params = dict( 36 | ff="idp", 37 | path=path, 38 | ) 39 | res = self.session.http.get(self.live_api_url, params=params) 40 | playlists = self.session.http.json(res, schema=self._live_data_schema) 41 | 42 | streams = {} 43 | for name, url in playlists.items(): 44 | name_prefix = "" 45 | if name == "hlsWithSubtitlesURL": 46 | name_prefix = "subtitled_" 47 | 48 | streams.update(HLSStream.parse_variant_playlist( 49 | self.session, 50 | url, 51 | name_prefix=name_prefix, 52 | )) 53 | 54 | return streams 55 | 56 | def _get_streams(self): 57 | path = self.match.group(1) 58 | log.debug("Path={0}".format(path)) 59 | 60 | return self._get_live(path) 61 | 62 | 63 | __plugin__ = DRDK 64 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/foxtr.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Turkish live TV channel owned by Fox Network. 3 | $url fox.com.tr 4 | $type live 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.plugin.api import validate 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | @pluginmatcher(re.compile( 15 | r"https?://(?:www\.)?fox(?:play)?\.com\.tr/", 16 | )) 17 | class FoxTR(Plugin): 18 | def _get_streams(self): 19 | re_streams = re.compile(r"""(['"])(?Phttps://\S+/foxtv\.m3u8\S+)\1""") 20 | res = self.session.http.get(self.url, schema=validate.Schema( 21 | validate.transform(re_streams.findall), 22 | )) 23 | for _, stream_url in res: 24 | return HLSStream.parse_variant_playlist(self.session, stream_url) 25 | 26 | 27 | __plugin__ = FoxTR 28 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/galatasaraytv.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Turkish live TV channel owned by Galatasaray TV. 3 | $url galatasaray.com 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | log = logging.getLogger(__name__) 15 | 16 | 17 | @pluginmatcher(re.compile( 18 | r"https?://(?:www\.)?galatasaray\.com", 19 | )) 20 | class GalatasarayTV(Plugin): 21 | playervars_re = re.compile(r"sources\s*:\s*\[\s*\{\s*type\s*:\s*\"(.*?)\",\s*src\s*:\s*\"(.*?)\"", re.DOTALL) 22 | 23 | title = "Galatasaray TV" 24 | 25 | def _get_streams(self): 26 | res = self.session.http.get(self.url) 27 | match = self.playervars_re.search(res.text) 28 | if match: 29 | stream_url = match.group(2) 30 | log.debug("URL={0}".format(stream_url)) 31 | return HLSStream.parse_variant_playlist(self.session, stream_url) 32 | 33 | 34 | __plugin__ = GalatasarayTV 35 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/goltelevision.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Spanish live TV sports channel owned by Gol Network. 3 | $url goltelevision.com 4 | $type live 5 | $region Spain 6 | """ 7 | 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream 13 | 14 | 15 | @pluginmatcher(re.compile( 16 | r"https?://(?:www\.)?goltelevision\.com/en-directo", 17 | )) 18 | class GOLTelevision(Plugin): 19 | def _get_streams(self): 20 | self.session.http.headers.update({ 21 | "Origin": "https://goltelevision.com", 22 | "Referer": "https://goltelevision.com/", 23 | }) 24 | url = self.session.http.get( 25 | "https://play.goltelevision.com/api/stream/live", 26 | schema=validate.Schema( 27 | validate.parse_json(), 28 | {"manifest": validate.url()}, 29 | validate.get("manifest"), 30 | ), 31 | ) 32 | return HLSStream.parse_variant_playlist(self.session, url) 33 | 34 | 35 | __plugin__ = GOLTelevision 36 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/goodgame.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Russian live streaming platform for live video game broadcasts. 3 | $url goodgame.ru 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.stream.hls import HLSStream 12 | from streamlink.utils.parse import parse_json 13 | 14 | 15 | log = logging.getLogger(__name__) 16 | 17 | HLS_URL_FORMAT = "https://hls.goodgame.ru/hls/{0}{1}.m3u8" 18 | QUALITIES = { 19 | "1080p": "", 20 | "720p": "_720", 21 | "480p": "_480", 22 | "240p": "_240", 23 | } 24 | 25 | _apidata_re = re.compile(r"""(?P["']?)channel(?P=quote)\s*:\s*(?P{.*?})\s*,""") 26 | _ddos_re = re.compile(r'document.cookie="(__DDOS_[^;]+)') 27 | 28 | 29 | @pluginmatcher(re.compile( 30 | r"https?://(?:www\.)?goodgame\.ru/channel/(?P[^/]+)", 31 | )) 32 | class GoodGame(Plugin): 33 | def _check_stream(self, url): 34 | res = self.session.http.get(url, acceptable_status=(200, 404)) 35 | if res.status_code == 200: 36 | return True 37 | 38 | def _get_streams(self): 39 | headers = { 40 | "Referer": self.url, 41 | } 42 | res = self.session.http.get(self.url, headers=headers) 43 | 44 | match = _ddos_re.search(res.text) 45 | if match: 46 | log.debug("Anti-DDOS bypass...") 47 | headers["Cookie"] = match.group(1) 48 | res = self.session.http.get(self.url, headers=headers) 49 | 50 | match = _apidata_re.search(res.text) 51 | channel_info = match and parse_json(match.group("data")) 52 | if not channel_info: 53 | log.error("Could not find channel info") 54 | return 55 | 56 | log.debug("Found channel info: id={id} channelkey={channelkey} pid={streamkey} online={status}".format(**channel_info)) 57 | if not channel_info["status"]: 58 | log.debug("Channel appears to be offline") 59 | 60 | streams = {} 61 | for name, url_suffix in QUALITIES.items(): 62 | url = HLS_URL_FORMAT.format(channel_info["streamkey"], url_suffix) 63 | if not self._check_stream(url): 64 | continue 65 | 66 | streams[name] = HLSStream(self.session, url) 67 | 68 | return streams 69 | 70 | 71 | __plugin__ = GoodGame 72 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/googledrive.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Stream videos stored in Google Drive. 3 | $url docs.google.com 4 | $url drive.google.com 5 | $type vod 6 | """ 7 | 8 | import logging 9 | import re 10 | from urllib.parse import parse_qsl 11 | 12 | from streamlink.plugin import Plugin, pluginmatcher 13 | from streamlink.stream.http import HTTPStream 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | @pluginmatcher(re.compile( 20 | r"https?://(?:drive|docs)\.google\.com/file/d/([^/]+)/?", 21 | )) 22 | class GoogleDocs(Plugin): 23 | api_url = "https://docs.google.com/get_video_info" 24 | 25 | def _get_streams(self): 26 | docid = self.match.group(1) 27 | log.debug("Google Docs ID: {0}".format(docid)) 28 | res = self.session.http.get(self.api_url, params=dict(docid=docid)) 29 | data = dict(parse_qsl(res.text)) 30 | 31 | if data["status"] == "ok": 32 | fmts = dict([s.split("/")[:2] for s in data["fmt_list"].split(",")]) 33 | streams = [s.split("|") for s in data["fmt_stream_map"].split(",")] 34 | for qcode, url in streams: 35 | _, h = fmts[qcode].split("x") 36 | yield "{0}p".format(h), HTTPStream(self.session, url) 37 | else: 38 | log.error("{0} (ID: {1})".format(data["reason"], docid)) 39 | 40 | 41 | __plugin__ = GoogleDocs 42 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/hls.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import re 3 | 4 | from streamlink.plugin import Plugin, pluginmatcher 5 | from streamlink.plugin.plugin import LOW_PRIORITY, parse_params 6 | from streamlink.stream.hls import HLSStream 7 | from streamlink.utils.url import update_scheme 8 | 9 | 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | @pluginmatcher(re.compile( 14 | r"hls(?:variant)?://(?P\S+)(?:\s(?P.+))?", 15 | )) 16 | @pluginmatcher(priority=LOW_PRIORITY, pattern=re.compile( 17 | r"(?P\S+\.m3u8(?:\?\S*)?)(?:\s(?P.+))?", 18 | )) 19 | class HLSPlugin(Plugin): 20 | def _get_streams(self): 21 | data = self.match.groupdict() 22 | url = update_scheme("https://", data.get("url"), force=False) 23 | params = parse_params(data.get("params")) 24 | log.debug(f"URL={url}; params={params}") 25 | 26 | streams = HLSStream.parse_variant_playlist(self.session, url, **params) 27 | 28 | return streams or {"live": HLSStream(self.session, url, **params)} 29 | 30 | 31 | __plugin__ = HLSPlugin 32 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/http.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import re 3 | 4 | from streamlink.plugin import Plugin, pluginmatcher 5 | from streamlink.plugin.plugin import parse_params 6 | from streamlink.stream.http import HTTPStream 7 | from streamlink.utils.url import update_scheme 8 | 9 | 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | @pluginmatcher(re.compile( 14 | r"httpstream://(?P\S+)(?:\s(?P.+))?", 15 | )) 16 | class HTTPStreamPlugin(Plugin): 17 | def _get_streams(self): 18 | data = self.match.groupdict() 19 | url = update_scheme("https://", data.get("url"), force=False) 20 | params = parse_params(data.get("params")) 21 | log.debug(f"URL={url}; params={params}") 22 | 23 | return {"live": HTTPStream(self.session, url, **params)} 24 | 25 | 26 | __plugin__ = HTTPStreamPlugin 27 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/idf1.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description French live TV channel and video on-demand service owned by IDF1. 3 | $url idf1.fr 4 | $type live, vod 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream 13 | 14 | 15 | log = logging.getLogger(__name__) 16 | 17 | 18 | @pluginmatcher(re.compile( 19 | r"https?://www\.idf1\.fr/(videos/[^/]+/[^/]+\.html|live\b)", 20 | )) 21 | class IDF1(Plugin): 22 | def _get_streams(self): 23 | self.id, self.title = self.session.http.get( 24 | self.url, 25 | schema=validate.Schema( 26 | validate.parse_html(), 27 | validate.union(( 28 | validate.xml_xpath_string(".//script[@class='dacast-video'][@id]/@id"), 29 | validate.xml_xpath_string(".//head/title[1]/text()"), 30 | )), 31 | ), 32 | ) 33 | 34 | if not self.id: 35 | return 36 | 37 | if re.match(r"\w+_\w+_\w+", self.id): 38 | provider = "dacast" 39 | else: 40 | provider = "universe" 41 | 42 | data = self.session.http.get( 43 | f"https://playback.dacast.com/content/access?contentId={self.id}&provider={provider}", 44 | acceptable_status=(200, 400, 403, 404), 45 | schema=validate.Schema( 46 | validate.parse_json(), 47 | validate.any( 48 | {"error": str}, 49 | {"hls": validate.url()}, 50 | ), 51 | ), 52 | ) 53 | 54 | if data.get("error"): 55 | log.error(data["error"]) 56 | return 57 | 58 | return HLSStream.parse_variant_playlist(self.session, data["hls"]) 59 | 60 | 61 | __plugin__ = IDF1 62 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/indihometv.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels and video on-demand service from IndiHome TV, owned by Telkom Indonesia. 3 | $url indihometv.com 4 | $type live, vod 5 | $region Indonesia 6 | """ 7 | 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.dash import DASHStream 13 | from streamlink.stream.hls import HLSStream 14 | 15 | 16 | @pluginmatcher(re.compile(r"https?://(?:www\.)?indihometv\.com/")) 17 | class IndiHomeTV(Plugin): 18 | def _get_streams(self): 19 | url = self.session.http.get(self.url, schema=validate.Schema( 20 | validate.parse_html(), 21 | validate.any( 22 | validate.all( 23 | validate.xml_xpath_string(""" 24 | .//script[contains(text(), 'laylist.m3u8') or contains(text(), 'manifest.mpd')][1]/text() 25 | """), 26 | str, 27 | re.compile(r"""(?P['"])(?Phttps://.*?/(?:[Pp]laylist\.m3u8|manifest\.mpd).+?)(?P=q)"""), 28 | validate.none_or_all( 29 | validate.get("url"), 30 | validate.url(), 31 | ), 32 | ), 33 | validate.all( 34 | validate.xml_xpath_string(".//video[@id='video-player'][1]/source[1]/@src"), 35 | validate.none_or_all( 36 | validate.url(), 37 | ), 38 | ), 39 | ), 40 | )) 41 | 42 | if url and ".m3u8" in url: 43 | return HLSStream.parse_variant_playlist(self.session, url) 44 | elif url and ".mpd" in url: 45 | return DASHStream.parse_manifest(self.session, url) 46 | 47 | 48 | __plugin__ = IndiHomeTV 49 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/invintus.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Global live streaming and video on-demand hosting platform. 3 | $url player.invintus.com 4 | $type live, vod 5 | """ 6 | 7 | import json 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream 13 | from streamlink.utils.url import update_scheme 14 | 15 | 16 | @pluginmatcher(re.compile( 17 | r"https?://player\.invintus\.com/\?clientID=(\d+)&eventID=(\d+)", 18 | )) 19 | class InvintusMedia(Plugin): 20 | WSC_API_KEY = "7WhiEBzijpritypp8bqcU7pfU9uicDR" # hard coded in the middle of https://player.invintus.com/app.js 21 | API_URL = "https://api.v3.invintusmedia.com/v2/Event/getDetailed" 22 | 23 | api_response_schema = validate.Schema({"data": {"streamingURIs": {"main": validate.url()}}}) 24 | 25 | def _get_streams(self): 26 | postdata = { 27 | "clientID": self.match.group(1), 28 | "showEncoder": True, 29 | "showMediaAssets": True, 30 | "showStreams": True, 31 | "includePrivate": False, 32 | "advancedDetails": True, 33 | "VAST": True, 34 | "eventID": self.match.group(2), 35 | } 36 | headers = { 37 | "Content-Type": "application/json", 38 | "wsc-api-key": self.WSC_API_KEY, 39 | "Authorization": "embedder", 40 | } 41 | res = self.session.http.post(self.API_URL, data=json.dumps(postdata), headers=headers) 42 | api_response = self.session.http.json(res, schema=self.api_response_schema) 43 | if api_response is None: 44 | return 45 | 46 | hls_url = api_response["data"]["streamingURIs"]["main"] 47 | return HLSStream.parse_variant_playlist(self.session, update_scheme("https://", hls_url)) 48 | 49 | 50 | __plugin__ = InvintusMedia 51 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/linelive.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Japanese live-streaming and video hosting social platform. 3 | $url live.line.me 4 | $type live, vod 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.plugin.api import validate 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | @pluginmatcher(re.compile( 15 | r"https?://live\.line\.me/channels/(?P\d+)/broadcast/(?P\d+)", 16 | )) 17 | class LineLive(Plugin): 18 | _URL_API = "https://live-api.line-apps.com/web/v4.0/channel/{channel}/broadcast/{broadcast}/player_status" 19 | 20 | def _get_streams(self): 21 | channel = self.match.group("channel") 22 | broadcast = self.match.group("broadcast") 23 | 24 | schema_hls_urls = validate.any(None, { 25 | str: validate.any(None, validate.url(path=validate.endswith(".m3u8"))), 26 | }) 27 | 28 | status, liveUrls, vodUrls = self.session.http.get( 29 | self._URL_API.format(channel=channel, broadcast=broadcast), 30 | schema=validate.Schema( 31 | validate.parse_json(), 32 | { 33 | "liveStatus": str, 34 | "liveHLSURLs": schema_hls_urls, 35 | "archivedHLSURLs": schema_hls_urls, 36 | }, 37 | validate.union_get("liveStatus", "liveHLSURLs", "archivedHLSURLs"), 38 | ), 39 | ) 40 | streams = {"LIVE": liveUrls, "FINISHED": vodUrls}.get(status, {}) 41 | 42 | if streams.get("abr"): 43 | return HLSStream.parse_variant_playlist(self.session, streams.get("abr")) 44 | 45 | return { 46 | f"{quality}p": HLSStream(self.session, url) 47 | for quality, url in streams.items() 48 | if url and quality.isdecimal() 49 | } 50 | 51 | 52 | __plugin__ = LineLive 53 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/lnk.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Lithuanian live TV channels from LNK Group, including 2TV, BTV, Info TV, LNK and TV1. 3 | $url lnk.lt 4 | $type live 5 | $region Lithuania 6 | """ 7 | 8 | import logging 9 | import re 10 | 11 | from streamlink.plugin import Plugin, pluginmatcher 12 | from streamlink.plugin.api import validate 13 | from streamlink.stream.hls import HLSStream 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | @pluginmatcher(re.compile( 20 | r"https?://(?:www\.)?lnk\.lt/tiesiogiai(?:#(?P[a-z0-9]+))?", 21 | )) 22 | class LNK(Plugin): 23 | API_URL = "https://lnk.lt/api/video/video-config/{0}" 24 | 25 | CHANNEL_MAP = { 26 | "lnk": 137535, 27 | "btv": 137534, 28 | "2tv": 95343, 29 | "infotv": 137748, 30 | "tv1": 106791, 31 | } 32 | 33 | def _get_streams(self): 34 | channel = self.match.groupdict().get("channel") or "lnk" 35 | if channel not in self.CHANNEL_MAP: 36 | log.error(f"Unknown channel: {channel}") 37 | return 38 | 39 | self.id = self.CHANNEL_MAP.get(channel) 40 | self.author, self.category, self.title, hls_url = self.session.http.get( 41 | self.API_URL.format(self.id), 42 | schema=validate.Schema( 43 | validate.parse_json(), 44 | {"videoInfo": { 45 | "channel": str, 46 | "genre": validate.any(None, str), 47 | "title": validate.any(None, str), 48 | "videoUrl": validate.any( 49 | "", 50 | validate.url(path=validate.endswith(".m3u8")), 51 | ), 52 | }}, 53 | validate.get("videoInfo"), 54 | validate.union_get("channel", "genre", "title", "videoUrl"), 55 | ), 56 | ) 57 | if not hls_url: 58 | log.error("The stream is not available in your region") 59 | return 60 | 61 | return HLSStream.parse_variant_playlist(self.session, hls_url) 62 | 63 | 64 | __plugin__ = LNK 65 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/lrt.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels from LRT, a Lithuanian public, state-owned broadcaster. 3 | $url lrt.lt 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | log = logging.getLogger(__name__) 15 | 16 | 17 | @pluginmatcher(re.compile( 18 | r"https?://(?:www\.)?lrt\.lt/mediateka/tiesiogiai/", 19 | )) 20 | class LRT(Plugin): 21 | _video_id_re = re.compile(r"""var\svideo_id\s*=\s*["'](?P\w+)["']""") 22 | API_URL = "https://www.lrt.lt/servisai/stream_url/live/get_live_url.php?channel={0}" 23 | 24 | def _get_streams(self): 25 | page = self.session.http.get(self.url) 26 | m = self._video_id_re.search(page.text) 27 | if m: 28 | video_id = m.group("video_id") 29 | data = self.session.http.get(self.API_URL.format(video_id)).json() 30 | hls_url = data["response"]["data"]["content"] 31 | 32 | yield from HLSStream.parse_variant_playlist(self.session, hls_url).items() 33 | else: 34 | log.debug("No match for video_id regex") 35 | 36 | 37 | __plugin__ = LRT 38 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/mrtmk.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels and video on-demand service from MRT, a North Macedonian public, state-owned broadcaster. 3 | $url play.mrt.com.mk 4 | $type live, vod 5 | $region North Macedonia 6 | """ 7 | 8 | import logging 9 | import re 10 | 11 | from streamlink.plugin import Plugin, pluginmatcher 12 | from streamlink.plugin.api import validate 13 | from streamlink.stream.hls import HLSStream 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | @pluginmatcher(re.compile( 20 | r"https?://play\.mrt\.com\.mk/(live|play)/", 21 | )) 22 | class MRTmk(Plugin): 23 | file_re = re.compile(r"""(?Phttps?://vod-[\d\w]+\.interspace\.com[^"',]+\.m3u8[^"',]*)""") 24 | 25 | stream_schema = validate.Schema( 26 | validate.all( 27 | validate.transform(file_re.finditer), 28 | validate.transform(list), 29 | [validate.get("url")], 30 | # remove duplicates 31 | validate.transform(set), 32 | validate.transform(list), 33 | ), 34 | ) 35 | 36 | def _get_streams(self): 37 | res = self.session.http.get(self.url) 38 | stream_urls = self.stream_schema.validate(res.text) 39 | log.debug("Found streams: {0}".format(len(stream_urls))) 40 | if not stream_urls: 41 | return 42 | 43 | for stream_url in stream_urls: 44 | try: 45 | yield from HLSStream.parse_variant_playlist(self.session, stream_url).items() 46 | except OSError as err: 47 | if "403 Client Error" in str(err): 48 | log.error("Failed to access stream, may be due to geo-restriction") 49 | else: 50 | raise err 51 | 52 | 53 | __plugin__ = MRTmk 54 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/nhkworld.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Current affairs and cultural channel owned by NHK, a Japanese public, state-owned broadcaster. 3 | $url nhk.or.jp/nhkworld 4 | $type live 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.stream.hls import HLSStream 11 | 12 | 13 | API_URL = "http://{}.nhk.or.jp/nhkworld/app/tv/hlslive_web.json" 14 | 15 | 16 | @pluginmatcher(re.compile( 17 | r"https?://(?:(\w+)\.)?nhk\.or\.jp/nhkworld", 18 | )) 19 | class NHKWorld(Plugin): 20 | def _get_streams(self): 21 | # get the HLS json from the same sub domain as the main url, defaulting to www 22 | sdomain = self.match.group(1) or "www" 23 | res = self.session.http.get(API_URL.format(sdomain)) 24 | 25 | stream_url = self.session.http.json(res)["main"]["wstrm"] 26 | return HLSStream.parse_variant_playlist(self.session, stream_url) 27 | 28 | 29 | __plugin__ = NHKWorld 30 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/nownews.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live streaming news channel owned by Now/PCCW Group, based in Hong Kong. 3 | $url news.now.com 4 | $type live 5 | """ 6 | 7 | import json 8 | import logging 9 | import re 10 | 11 | from streamlink.plugin import Plugin, pluginmatcher 12 | from streamlink.stream.hls import HLSStream 13 | 14 | 15 | log = logging.getLogger(__name__) 16 | 17 | 18 | @pluginmatcher(re.compile( 19 | r"https?://news\.now\.com/home/live", 20 | )) 21 | class NowNews(Plugin): 22 | epg_re = re.compile(r"""epg.getEPG\("(\d+)"\);""") 23 | api_url = "https://hkt-mobile-api.nowtv.now.com/09/1/getLiveURL" 24 | backup_332_api = "https://d7lz7jwg8uwgn.cloudfront.net/apps_resource/news/live.json" 25 | backup_332_stream = "https://d3i3yn6xwv1jpw.cloudfront.net/live/now332/playlist.m3u8" 26 | 27 | def _get_streams(self): 28 | res = self.session.http.get(self.url) 29 | m = self.epg_re.search(res.text) 30 | channel_id = m and m.group(1) 31 | if channel_id: 32 | log.debug("Channel ID: {0}".format(channel_id)) 33 | 34 | if channel_id == "332": 35 | # there is a special backup stream for channel 332 36 | bk_res = self.session.http.get(self.backup_332_api) 37 | bk_data = self.session.http.json(bk_res) 38 | if bk_data and bk_data["backup"]: 39 | log.info("Using backup stream for channel 332") 40 | return HLSStream.parse_variant_playlist(self.session, self.backup_332_stream) 41 | 42 | api_res = self.session.http.post(self.api_url, 43 | headers={"Content-Type": "application/json"}, 44 | data=json.dumps(dict(channelno=channel_id, 45 | mode="prod", 46 | audioCode="", 47 | format="HLS", 48 | callerReferenceNo="20140702122500"))) 49 | data = self.session.http.json(api_res) 50 | for stream_url in data.get("asset", {}).get("hls", {}).get("adaptive", []): 51 | return HLSStream.parse_variant_playlist(self.session, stream_url) 52 | 53 | 54 | __plugin__ = NowNews 55 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/ntv.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Russian live TV channel owned by Gazprom Media. 3 | $url ntv.ru 4 | $type live 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.stream.hls import HLSStream 11 | 12 | 13 | @pluginmatcher(re.compile( 14 | r"https?://www\.ntv\.ru/air/", 15 | )) 16 | class NTV(Plugin): 17 | def _get_streams(self): 18 | body = self.session.http.get(self.url).text 19 | mrl = None 20 | match = re.search(r"var camHlsURL = \'(.*)\'", body) 21 | if match: 22 | mrl = f"http:{match.group(1)}" 23 | else: 24 | match = re.search(r"var hlsURL = \'(.*)\'", body) 25 | if match: 26 | mrl = match.group(1) 27 | if mrl: 28 | return HLSStream.parse_variant_playlist(self.session, mrl) 29 | 30 | 31 | __plugin__ = NTV 32 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/onetv.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description A state/privately owned Russian live TV channel. 3 | $url 1tv.ru 4 | $type live 5 | $region Russia 6 | """ 7 | 8 | import logging 9 | import random 10 | import re 11 | from urllib.parse import unquote 12 | 13 | from streamlink.plugin import Plugin, pluginmatcher 14 | from streamlink.plugin.api import validate 15 | from streamlink.stream.hls import HLSStream 16 | from streamlink.utils.url import update_qsd 17 | 18 | 19 | log = logging.getLogger(__name__) 20 | 21 | 22 | @pluginmatcher(re.compile( 23 | r"https?://(?:www\.)?1tv\.ru/live", 24 | )) 25 | @pluginmatcher(re.compile( 26 | r"https?://static\.1tv\.ru/eump/(?:embeds|pages)/1tv_live(?:_orbit-plus-4)?\.html", 27 | )) 28 | class OneTV(Plugin): 29 | def _get_streams(self): 30 | if "orbit-plus-4" in self.url: 31 | channel = "1tv-orbit-plus-4" 32 | self.title = "Первый канал HD (+4)" 33 | else: 34 | channel = "1tvch" 35 | self.title = "Первый канал HD" 36 | 37 | url = self.session.http.get( 38 | f"https://stream.1tv.ru/api/playlist/{channel}_as_array.json", 39 | data={"r": random.randint(1, 100000)}, 40 | schema=validate.Schema( 41 | validate.parse_json(), 42 | {"hls": [validate.url()]}, 43 | validate.get(("hls", 0)), 44 | )) 45 | 46 | if not url: 47 | return 48 | 49 | log.debug(f"{url}") 50 | if "georestrictions" in url: 51 | log.error("Stream is geo-restricted") 52 | return 53 | 54 | hls_session = self.session.http.get( 55 | "https://stream.1tv.ru/get_hls_session", 56 | schema=validate.Schema( 57 | validate.parse_json(), 58 | {"s": validate.transform(unquote)}, 59 | )) 60 | url = update_qsd(url, qsd=hls_session, safe="/:") 61 | 62 | return HLSStream.parse_variant_playlist(self.session, url, name_fmt="{pixels}_{bitrate}") 63 | 64 | 65 | __plugin__ = OneTV 66 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/piczel.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Global live-streaming platform for the creative community. 3 | $url piczel.tv 4 | $type live 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.plugin.api import validate 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | @pluginmatcher(re.compile( 15 | r"https?://piczel\.tv/watch/(?P\w+)", 16 | )) 17 | class Piczel(Plugin): 18 | _URL_STREAMS = "https://piczel.tv/api/streams" 19 | _URL_HLS = "https://piczel.tv/hls/{id}/index.m3u8" 20 | 21 | def _get_streams(self): 22 | channel = self.match.group("channel") 23 | 24 | data = self.session.http.get( 25 | self._URL_STREAMS, 26 | params={ 27 | "followedStreams": "false", 28 | "live_only": "false", 29 | "sfw": "false", 30 | }, 31 | schema=validate.Schema( 32 | validate.parse_json(), 33 | [{ 34 | "slug": str, 35 | "live": bool, 36 | "id": int, 37 | "username": str, 38 | "title": str, 39 | }], 40 | validate.filter(lambda item: item["slug"] == channel), 41 | validate.get(0), 42 | validate.any(None, validate.union_get( 43 | "id", 44 | "username", 45 | "title", 46 | "live", 47 | )), 48 | ), 49 | ) 50 | if not data: 51 | return 52 | 53 | self.id, self.author, self.title, is_live = data 54 | if not is_live: 55 | return 56 | 57 | return {"live": HLSStream(self.session, self._URL_HLS.format(id=self.id))} 58 | 59 | 60 | __plugin__ = Piczel 61 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/rtpa.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels and video on-demand service from RTPA, a Spanish public broadcaster. 3 | $url rtpa.es 4 | $type live, vod 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.plugin.api import validate 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | @pluginmatcher(re.compile(r"https?://(?:www\.)?rtpa\.es")) 15 | class RTPA(Plugin): 16 | def _get_streams(self): 17 | hls_url, self.title = self.session.http.get(self.url, schema=validate.Schema( 18 | validate.parse_html(), 19 | validate.union(( 20 | validate.xml_xpath_string(".//video/source[@src][@type='application/x-mpegURL'][1]/@src"), 21 | validate.xml_xpath_string(".//head/title[1]/text()"), 22 | )), 23 | )) 24 | if not hls_url: 25 | return 26 | return HLSStream.parse_variant_playlist(self.session, hls_url, headers={"Referer": self.url}) 27 | 28 | 29 | __plugin__ = RTPA 30 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/rtvs.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels from RTVS, a Slovak public, state-owned broadcaster. 3 | $url rtvs.sk 4 | $type live 5 | $region Slovakia 6 | """ 7 | 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream 13 | from streamlink.utils.parse import parse_json 14 | 15 | 16 | @pluginmatcher(re.compile( 17 | r"https?://www\.rtvs\.sk/televizia/live-[\w-]+", 18 | )) 19 | class Rtvs(Plugin): 20 | _re_channel_id = re.compile(r"'stream':\s*'live-(\d+)'") 21 | 22 | def _get_streams(self): 23 | res = self.session.http.get(self.url) 24 | m = self._re_channel_id.search(res.text) 25 | if not m: 26 | return 27 | 28 | res = self.session.http.get( 29 | "https://www.rtvs.sk/json/live5f.json", 30 | params={ 31 | "c": m.group(1), 32 | "b": "mozilla", 33 | "p": "win", 34 | "f": "0", 35 | "d": "1", 36 | }, 37 | ) 38 | videos = parse_json(res.text, schema=validate.Schema({ 39 | "clip": { 40 | "sources": [{ 41 | "src": validate.url(), 42 | "type": str, 43 | }], 44 | }}, 45 | validate.get(("clip", "sources")), 46 | validate.filter(lambda n: n["type"] == "application/x-mpegurl"), 47 | )) 48 | for video in videos: 49 | yield from HLSStream.parse_variant_playlist(self.session, video["src"]).items() 50 | 51 | 52 | __plugin__ = Rtvs 53 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/sportal.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Sporting channel live stream owned by Sportal, a Bulgarian sports media website. 3 | $url sportal.bg 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | log = logging.getLogger(__name__) 15 | 16 | 17 | @pluginmatcher(re.compile( 18 | r"https?://(?:www\.)?sportal\.bg/sportal_live_tv\.php", 19 | )) 20 | class Sportal(Plugin): 21 | _hls_re = re.compile(r"""["'](?P[^"']+\.m3u8[^"']*?)["']""") 22 | 23 | def _get_streams(self): 24 | res = self.session.http.get(self.url) 25 | m = self._hls_re.search(res.text) 26 | if not m: 27 | return 28 | 29 | hls_url = m.group("url") 30 | log.debug("URL={0}".format(hls_url)) 31 | log.warning("SSL certificate verification is disabled.") 32 | return HLSStream.parse_variant_playlist( 33 | self.session, hls_url, verify=False).items() 34 | 35 | 36 | __plugin__ = Sportal 37 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/sportschau.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description German sports magazine live stream, owned by ARD. 3 | $url sportschau.de 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream 13 | from streamlink.stream.http import HTTPStream 14 | from streamlink.utils.url import update_scheme 15 | 16 | 17 | log = logging.getLogger(__name__) 18 | 19 | 20 | @pluginmatcher(re.compile( 21 | r"https?://(?:\w+\.)*sportschau\.de/", 22 | )) 23 | class Sportschau(Plugin): 24 | def _get_streams(self): 25 | player_js = self.session.http.get(self.url, schema=validate.Schema( 26 | re.compile(r"https?:(//deviceids-medp.wdr.de/ondemand/\S+\.js)"), 27 | validate.none_or_all( 28 | validate.get(1), 29 | validate.transform(lambda url: update_scheme("https://", url)), 30 | ), 31 | )) 32 | if not player_js: 33 | return 34 | 35 | log.debug(f"Found player js {player_js}") 36 | data = self.session.http.get(player_js, schema=validate.Schema( 37 | validate.regex(re.compile(r"\$mediaObject\.jsonpHelper\.storeAndPlay\(({.+})\);?")), 38 | validate.get(1), 39 | validate.parse_json(), 40 | validate.get("mediaResource"), 41 | validate.get("dflt"), 42 | { 43 | validate.optional("audioURL"): validate.url(), 44 | validate.optional("videoURL"): validate.url(), 45 | }, 46 | )) 47 | 48 | if data.get("videoURL"): 49 | yield from HLSStream.parse_variant_playlist(self.session, update_scheme("https:", data.get("videoURL"))).items() 50 | if data.get("audioURL"): 51 | yield "audio", HTTPStream(self.session, update_scheme("https:", data.get("audioURL"))) 52 | 53 | 54 | __plugin__ = Sportschau 55 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/ssh101.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Global live streaming platform. 3 | $url ssh101.com 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream 13 | 14 | 15 | log = logging.getLogger(__name__) 16 | 17 | 18 | @pluginmatcher(re.compile( 19 | r"https?://(?:www\.)?ssh101\.com/(?:(?:secure)?live/|detail\.php\?id=\w+)", 20 | )) 21 | class SSH101(Plugin): 22 | def _get_streams(self): 23 | hls_url = self.session.http.get(self.url, schema=validate.Schema( 24 | validate.parse_html(), 25 | validate.xml_xpath_string(".//source[contains(@src,'.m3u8')]/@src"), 26 | )) 27 | if not hls_url: 28 | return 29 | 30 | res = self.session.http.get(hls_url, acceptable_status=(200, 403, 404)) 31 | if res.status_code != 200 or len(res.text) <= 10: 32 | log.error("This stream is currently offline") 33 | return 34 | 35 | return {"live": HLSStream(self.session, hls_url)} 36 | 37 | 38 | __plugin__ = SSH101 39 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/streamable.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Global video hosting platform. 3 | $url streamable.com 4 | $type vod 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.plugin.api import validate 11 | from streamlink.stream.http import HTTPStream 12 | from streamlink.utils.url import update_scheme 13 | 14 | 15 | @pluginmatcher(re.compile( 16 | r"https?://(?:www\.)?streamable\.com/(.+)", 17 | )) 18 | class Streamable(Plugin): 19 | def _get_streams(self): 20 | data = self.session.http.get(self.url, schema=validate.Schema( 21 | re.compile(r"var\s*videoObject\s*=\s*({.*?});"), 22 | validate.none_or_all( 23 | validate.get(1), 24 | validate.parse_json(), 25 | { 26 | "files": { 27 | str: { 28 | "url": validate.url(), 29 | "width": int, 30 | "height": int, 31 | "bitrate": int, 32 | }, 33 | }, 34 | }, 35 | ), 36 | )) 37 | 38 | for info in data["files"].values(): 39 | stream_url = update_scheme("https://", info["url"]) 40 | # pick the smaller of the two dimensions, for landscape v. portrait videos 41 | res = min(info["width"], info["height"]) 42 | yield f"{res}p", HTTPStream(self.session, stream_url) 43 | 44 | 45 | __plugin__ = Streamable 46 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/stv.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels from STV, a Scottish free-to-air broadcaster. 3 | $url player.stv.tv 4 | $type live 5 | $region United Kingdom 6 | """ 7 | 8 | import logging 9 | import re 10 | 11 | from streamlink.plugin import Plugin, PluginError, pluginmatcher 12 | from streamlink.stream.hls import HLSStream 13 | 14 | 15 | log = logging.getLogger(__name__) 16 | 17 | 18 | @pluginmatcher(re.compile( 19 | r"https?://player\.stv\.tv/live", 20 | )) 21 | class STV(Plugin): 22 | API_URL = "https://player.api.stv.tv/v1/streams/stv/" 23 | 24 | def get_title(self): 25 | if self.title is None: 26 | self._get_api_results() 27 | return self.title 28 | 29 | def _get_api_results(self): 30 | res = self.session.http.get(self.API_URL) 31 | data = self.session.http.json(res) 32 | 33 | if data["success"] is False: 34 | raise PluginError(data["reason"]["message"]) 35 | 36 | try: 37 | self.title = data["results"]["now"]["title"] 38 | except KeyError: 39 | self.title = "STV" 40 | 41 | return data 42 | 43 | def _get_streams(self): 44 | hls_url = self._get_api_results()["results"]["streamUrl"] 45 | return HLSStream.parse_variant_playlist(self.session, hls_url) 46 | 47 | 48 | __plugin__ = STV 49 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/swisstxt.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels from RSI and SRF, operations of SRG SSR, a Swiss public broadcaster. 3 | $url srf.ch 4 | $url rsi.ch 5 | $type live 6 | $region Switzerland 7 | """ 8 | 9 | import logging 10 | import re 11 | from urllib.parse import parse_qsl, urlparse, urlunparse 12 | 13 | from streamlink.plugin import Plugin, pluginmatcher 14 | from streamlink.stream.hls import HLSStream 15 | 16 | 17 | log = logging.getLogger(__name__) 18 | 19 | 20 | @pluginmatcher(re.compile(r""" 21 | https?://(?: 22 | live\.(rsi)\.ch/| 23 | (?:www\.)?(srf)\.ch/sport/resultcenter 24 | ) 25 | """, re.VERBOSE)) 26 | class Swisstxt(Plugin): 27 | api_url = "http://event.api.swisstxt.ch/v1/stream/{site}/byEventItemIdAndType/{id}/HLS" 28 | 29 | def get_stream_url(self, event_id): 30 | site = self.match.group(1) or self.match.group(2) 31 | api_url = self.api_url.format(id=event_id, site=site.upper()) 32 | log.debug("Calling API: {0}".format(api_url)) 33 | 34 | stream_url = self.session.http.get(api_url).text.strip("\"'") 35 | 36 | parsed = urlparse(stream_url) 37 | query = dict(parse_qsl(parsed.query)) 38 | return urlunparse(parsed._replace(query="")), query 39 | 40 | def _get_streams(self): 41 | event_id = dict(parse_qsl(urlparse(self.url).query.lower())).get("eventid") 42 | if event_id is None: 43 | return 44 | 45 | stream_url, params = self.get_stream_url(event_id) 46 | return HLSStream.parse_variant_playlist(self.session, 47 | stream_url, 48 | params=params) 49 | 50 | 51 | __plugin__ = Swisstxt 52 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/telefe.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Video content from Telefe, an Argentine TV station. 3 | $url mitelefe.com 4 | $type live 5 | $region Argentina 6 | """ 7 | 8 | import logging 9 | import re 10 | from urllib.parse import urljoin 11 | 12 | from streamlink.plugin import Plugin, pluginmatcher 13 | from streamlink.plugin.api import validate 14 | from streamlink.stream.hls import HLSStream 15 | 16 | 17 | log = logging.getLogger(__name__) 18 | 19 | 20 | @pluginmatcher(re.compile(r"https://mitelefe\.com/vivo")) 21 | class Telefe(Plugin): 22 | def _get_streams(self): 23 | self.title, hls_url = self.session.http.get( 24 | self.url, 25 | schema=validate.Schema( 26 | validate.parse_html(), 27 | validate.xml_xpath_string(".//script[contains(text(), 'HLS')]/text()"), 28 | validate.none_or_all( 29 | re.compile(r"=\s*(\{.+?});", re.DOTALL | re.MULTILINE), 30 | validate.none_or_all( 31 | validate.get(1), 32 | validate.parse_json(), 33 | {str: {"children": {"top": {"model": {"videos": [{ 34 | "title": str, 35 | "sources": validate.all( 36 | [{"url": str, "type": str}], 37 | validate.filter(lambda p: p["type"].lower() == "hls"), 38 | validate.get((0, "url")), 39 | ), 40 | }]}}}}}, 41 | validate.transform(lambda k: next(iter(k.values()))), 42 | validate.get(("children", "top", "model", "videos", 0)), 43 | validate.union_get("title", "sources"), 44 | ), 45 | ), 46 | ), 47 | ) 48 | return HLSStream.parse_variant_playlist(self.session, urljoin(self.url, hls_url)) 49 | 50 | 51 | __plugin__ = Telefe 52 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/telemadrid.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Spanish live TV channel for Telemadrid, a public regional television station. 3 | $url telemadrid.es 4 | $type live, vod 5 | $region Spain 6 | """ 7 | 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.plugins.brightcove import BrightcovePlayer 13 | 14 | 15 | @pluginmatcher(re.compile( 16 | r"https?://(?:www\.)?telemadrid\.es/", 17 | )) 18 | class Telemadrid(Plugin): 19 | 20 | def _get_streams(self): 21 | data = self.session.http.get(self.url, schema=validate.Schema( 22 | validate.parse_html(), 23 | validate.xml_find(".//video[@class='video-js'][@data-video-id][@data-account][@data-player][1]"), 24 | validate.union_get("data-video-id", "data-account", "data-player"), 25 | )) 26 | data_video_id, data_account, data_player = data 27 | player = BrightcovePlayer(self.session, data_account, f"{data_player}_default") 28 | return player.get_streams(data_video_id) 29 | 30 | 31 | __plugin__ = Telemadrid 32 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/tv360.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description A privately owned Turkish live TV channel. 3 | $url tv360.com.tr 4 | $type live 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.plugin.api import validate 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | @pluginmatcher(re.compile( 15 | r"https?://(?:www\.)?tv360\.com\.tr/canli-yayin", 16 | )) 17 | class TV360(Plugin): 18 | def _get_streams(self): 19 | hls_url = self.session.http.get(self.url, schema=validate.Schema( 20 | validate.parse_html(), 21 | validate.xml_xpath_string(".//video/source[@src][@type='application/x-mpegURL'][1]/@src"), 22 | validate.none_or_all(validate.url()), 23 | )) 24 | if hls_url: 25 | return HLSStream.parse_variant_playlist(self.session, hls_url) 26 | 27 | 28 | __plugin__ = TV360 29 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/tv3cat.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels and video on-demand service from CCMA, a Catalan public, state-owned broadcaster. 3 | $url ccma.cat 4 | $type live, vod 5 | $region Spain 6 | """ 7 | 8 | import logging 9 | import re 10 | 11 | from streamlink.plugin import Plugin, pluginmatcher 12 | from streamlink.plugin.api import validate 13 | from streamlink.stream.hls import HLSStream 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | @pluginmatcher(re.compile( 20 | r"https?://(?:www\.)?ccma\.cat/tv3/directe/(?P.+?)/", 21 | )) 22 | class TV3Cat(Plugin): 23 | _URL_STREAM_INFO = "https://dinamics.ccma.cat/pvideo/media.jsp" 24 | 25 | _MAP_CHANNELS = { 26 | "tv3": "tvi", 27 | } 28 | 29 | def _get_streams(self): 30 | ident = self.match.group("ident") 31 | 32 | schema_media = { 33 | "geo": str, 34 | "url": validate.url(path=validate.endswith(".m3u8")), 35 | } 36 | 37 | stream_infos = self.session.http.get( 38 | self._URL_STREAM_INFO, 39 | params={ 40 | "media": "video", 41 | "versio": "vast", 42 | "idint": self._MAP_CHANNELS.get(ident, ident), 43 | "profile": "pc", 44 | "desplacament": "0", 45 | "broadcast": "false", 46 | }, 47 | schema=validate.Schema( 48 | validate.parse_json(), 49 | { 50 | "media": validate.any( 51 | [schema_media], 52 | validate.all( 53 | schema_media, 54 | validate.transform(lambda item: [item]), 55 | ), 56 | ), 57 | }, 58 | validate.get("media"), 59 | ), 60 | ) 61 | 62 | for stream in stream_infos: 63 | log.info(f"Accessing stream from region {stream['geo']}") 64 | try: 65 | return HLSStream.parse_variant_playlist(self.session, stream["url"], name_fmt="{pixels}_{bitrate}") 66 | except OSError: 67 | pass 68 | 69 | 70 | __plugin__ = TV3Cat 71 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/tv8.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Turkish live TV channel owned by Acun Medya Group. 3 | $url tv8.com.tr 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream, HLSStreamReader, HLSStreamWriter 13 | 14 | 15 | log = logging.getLogger(__name__) 16 | 17 | 18 | class TV8HLSStreamWriter(HLSStreamWriter): 19 | ad_re = re.compile(r"/ad/|/crea/") 20 | 21 | def should_filter_sequence(self, sequence): 22 | return self.ad_re.search(sequence.segment.uri) is not None or super().should_filter_sequence(sequence) 23 | 24 | 25 | class TV8HLSStreamReader(HLSStreamReader): 26 | __writer__ = TV8HLSStreamWriter 27 | 28 | 29 | class TV8HLSStream(HLSStream): 30 | __reader__ = TV8HLSStreamReader 31 | 32 | 33 | @pluginmatcher(re.compile( 34 | r"https?://www\.tv8\.com\.tr/canli-yayin", 35 | )) 36 | class TV8(Plugin): 37 | def _get_streams(self): 38 | hls_url = self.session.http.get(self.url, schema=validate.Schema( 39 | re.compile(r"""var\s+videoUrl\s*=\s*(?P["'])(?Phttps?://.*?\.m3u8.*?)(?P=q)"""), 40 | validate.any(None, validate.get("hls_url")), 41 | )) 42 | if hls_url is not None: 43 | return TV8HLSStream.parse_variant_playlist(self.session, hls_url) 44 | 45 | 46 | __plugin__ = TV8 47 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/tv999.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Turkish live TV channel owned by Detelina Media. 3 | $url tv999.bg 4 | $type live 5 | $region Bulgaria 6 | """ 7 | 8 | import logging 9 | import re 10 | 11 | from streamlink.plugin import Plugin, pluginmatcher 12 | from streamlink.plugin.api import validate 13 | from streamlink.stream.hls import HLSStream 14 | from streamlink.utils.url import update_scheme 15 | 16 | 17 | log = logging.getLogger(__name__) 18 | 19 | 20 | @pluginmatcher(re.compile( 21 | r"https?://(?:www\.)?tv999\.bg/live", 22 | )) 23 | class TV999(Plugin): 24 | title = "TV999" 25 | 26 | def _get_xpath_string(self, url, xpath): 27 | return self.session.http.get( 28 | url, 29 | schema=validate.Schema( 30 | validate.parse_html(), 31 | validate.xml_xpath_string(xpath), 32 | validate.any(None, validate.url()), 33 | ), 34 | ) 35 | 36 | def _get_streams(self): 37 | iframe_url = self._get_xpath_string(self.url, ".//iframe[@src]/@src") 38 | if not iframe_url: 39 | return 40 | hls_url = self._get_xpath_string(iframe_url, ".//source[contains(@src,'m3u8')]/@src") 41 | if not hls_url: 42 | return 43 | return {"live": HLSStream(self.session, update_scheme("http://", hls_url))} 44 | 45 | 46 | __plugin__ = TV999 47 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/tvibo.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Global live streaming and video hosting platform. 3 | $url player.tvibo.com 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | log = logging.getLogger(__name__) 15 | 16 | 17 | @pluginmatcher(re.compile( 18 | r"https?://player\.tvibo\.com/\w+/(?P\d+)", 19 | )) 20 | class Tvibo(Plugin): 21 | _api_url = "http://panel.tvibo.com/api/player/streamurl/{id}" 22 | 23 | def _get_streams(self): 24 | channel_id = self.match.group("id") 25 | 26 | api_response = self.session.http.get( 27 | self._api_url.format(id=channel_id), 28 | acceptable_status=(200, 404)) 29 | 30 | data = self.session.http.json(api_response) 31 | log.trace("{0!r}".format(data)) 32 | if data.get("st"): 33 | yield "source", HLSStream(self.session, data["st"]) 34 | elif data.get("error"): 35 | log.error(data["error"]["message"]) 36 | 37 | 38 | __plugin__ = Tvibo 39 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/tvrby.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels from BTRC, a Belarusian public, state-owned broadcaster. 3 | $url tvr.by 4 | $type live 5 | $region Belarus 6 | """ 7 | 8 | import logging 9 | import re 10 | 11 | from streamlink.plugin import Plugin, pluginmatcher 12 | from streamlink.plugin.api import validate 13 | from streamlink.stream.hls import HLSStream 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | @pluginmatcher(re.compile( 20 | r"https?://(?:www\.)?tvr\.by/televidenie/belarus", 21 | )) 22 | class TVRBy(Plugin): 23 | file_re = re.compile(r"""(?Phttps://stream\.hoster\.by[^"',]+\.m3u8[^"',]*)""") 24 | player_re = re.compile(r"""["'](?P[^"']+tvr\.by/plugines/online-tv-main\.php[^"']+)["']""") 25 | 26 | stream_schema = validate.Schema( 27 | validate.all( 28 | validate.transform(file_re.finditer), 29 | validate.transform(list), 30 | [validate.get("url")], 31 | # remove duplicates 32 | validate.transform(set), 33 | validate.transform(list), 34 | ), 35 | ) 36 | 37 | def __init__(self, *args, **kwargs): 38 | super().__init__(*args, **kwargs) 39 | # ensure the URL ends with a / 40 | if not self.url.endswith("/"): 41 | self.url += "/" 42 | 43 | def _get_streams(self): 44 | res = self.session.http.get(self.url) 45 | m = self.player_re.search(res.text) 46 | if not m: 47 | return 48 | 49 | player_url = m.group("url") 50 | res = self.session.http.get(player_url) 51 | stream_urls = self.stream_schema.validate(res.text) 52 | log.debug("Found {0} stream URL{1}".format(len(stream_urls), "" if len(stream_urls) == 1 else "s")) 53 | 54 | for stream_url in stream_urls: 55 | yield from HLSStream.parse_variant_playlist(self.session, stream_url).items() 56 | 57 | 58 | __plugin__ = TVRBy 59 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/tvrplus.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels from TVR, a Romanian public, state-owned broadcaster. 3 | $url tvrplus.ro 4 | $type live 5 | $region Romania 6 | """ 7 | 8 | import re 9 | 10 | from streamlink.plugin import Plugin, PluginError, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream 13 | 14 | 15 | @pluginmatcher(re.compile( 16 | r"https?://(?:www\.)?tvrplus\.ro(?:/live/.+|/?$)", 17 | )) 18 | class TVRPlus(Plugin): 19 | def _get_streams(self): 20 | try: 21 | hls_url = self.session.http.get( 22 | self.url, 23 | schema=validate.Schema( 24 | re.compile(r"""(?P["'])(?Phttps?://\S+?\.m3u8\S*?)(?P=q)"""), 25 | validate.get("url"), 26 | ), 27 | ) 28 | except PluginError: 29 | return 30 | 31 | return HLSStream.parse_variant_playlist(self.session, hls_url, headers={"Referer": self.url}) 32 | 33 | 34 | __plugin__ = TVRPlus 35 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/tvtoya.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Polish live TV channel owned by Toya. 3 | $url tvtoya.pl 4 | $type live 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, PluginError, pluginmatcher 10 | from streamlink.plugin.api import validate 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | @pluginmatcher(re.compile( 15 | r"https?://(?:www\.)?tvtoya\.pl/player/live", 16 | )) 17 | class TVToya(Plugin): 18 | def _get_streams(self): 19 | try: 20 | hls = self.session.http.get(self.url, schema=validate.Schema( 21 | validate.parse_html(), 22 | validate.xml_xpath_string(".//script[@type='application/json'][@id='__NEXT_DATA__']/text()"), 23 | str, 24 | validate.parse_json(), 25 | { 26 | "props": { 27 | "pageProps": { 28 | "type": "live", 29 | "url": validate.all( 30 | str, 31 | validate.transform(lambda url: url.replace("https:////", "https://")), 32 | validate.url(path=validate.endswith(".m3u8")), 33 | ), 34 | }, 35 | }, 36 | }, 37 | validate.get(("props", "pageProps", "url")), 38 | )) 39 | except PluginError: 40 | return 41 | 42 | return HLSStream.parse_variant_playlist(self.session, hls) 43 | 44 | 45 | __plugin__ = TVToya 46 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/useetv.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels and video on-demand service from UseeTV, owned by Telkom Indonesia. 3 | $url useetv.com 4 | $type live, vod 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.dash import DASHStream 13 | from streamlink.stream.hls import HLSStream 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | @pluginmatcher(re.compile(r"https?://(?:www\.)?useetv\.com/")) 20 | class UseeTV(Plugin): 21 | def _get_streams(self): 22 | root = self.session.http.get(self.url, schema=validate.Schema(validate.parse_html())) 23 | 24 | for needle, errormsg in ( 25 | ( 26 | "\"This service is not available in your Country\"", 27 | "The content is not available in your region", 28 | ), 29 | ( 30 | "\"Silahkan login Menggunakan akun MyIndihome dan berlangganan minipack\"", 31 | "The content is not available without a subscription", 32 | ), 33 | ): 34 | if validate.Schema(validate.xml_xpath(".//script[contains(text(),$needle)]", needle=needle)).validate(root): 35 | log.error(errormsg) 36 | return 37 | 38 | url = validate.Schema( 39 | validate.any( 40 | validate.all( 41 | validate.xml_xpath_string(""" 42 | .//script[contains(text(), 'laylist.m3u8') or contains(text(), 'manifest.mpd')][1]/text() 43 | """), 44 | str, 45 | re.compile(r"""(?P['"])(?Phttps://.*?/(?:[Pp]laylist\.m3u8|manifest\.mpd).+?)(?P=q)"""), 46 | validate.none_or_all(validate.get("url"), validate.url()), 47 | ), 48 | validate.all( 49 | validate.xml_xpath_string(".//video[@id='video-player']/source/@src"), 50 | validate.any(None, validate.url()), 51 | ), 52 | ), 53 | ).validate(root) 54 | 55 | if url and ".m3u8" in url: 56 | return HLSStream.parse_variant_playlist(self.session, url) 57 | elif url and ".mpd" in url: 58 | return DASHStream.parse_manifest(self.session, url) 59 | 60 | 61 | __plugin__ = UseeTV 62 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/vinhlongtv.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Vietnamese live TV channels from THVL, including THVL1, THVL2, THVL3 and THVL4. 3 | $url thvli.vn 4 | $type live 5 | $region Vietnam 6 | """ 7 | 8 | import logging 9 | import re 10 | from datetime import datetime, timezone 11 | from hashlib import md5 12 | 13 | from streamlink.plugin import Plugin, pluginmatcher 14 | from streamlink.plugin.api import validate 15 | from streamlink.stream.hls import HLSStream 16 | 17 | 18 | log = logging.getLogger(__name__) 19 | 20 | 21 | @pluginmatcher(re.compile( 22 | r"https?://(?:www\.)?thvli\.vn/live/(?P[^/]+)", 23 | )) 24 | class VinhLongTV(Plugin): 25 | _API_URL = "https://api.thvli.vn/backend/cm/get_detail/{channel}/" 26 | _API_KEY_DATE = "Kh0ngDuLieu" 27 | _API_KEY_TIME = "C0R0i" 28 | _API_KEY_SECRET = "Kh0aAnT0an" 29 | 30 | def _get_headers(self): 31 | now = datetime.now(tz=timezone.utc) 32 | date = now.strftime("%Y%m%d") 33 | time = now.strftime("%H%M%S") 34 | dtstr = f"{date}{time}" 35 | dthash = md5(dtstr.encode()).hexdigest() 36 | key_value = f"{dthash[:3]}{dthash[-3:]}" 37 | key_access = f"{self._API_KEY_DATE}{date}{self._API_KEY_TIME}{time}{self._API_KEY_SECRET}{key_value}" 38 | 39 | return { 40 | "X-SFD-Date": dtstr, 41 | "X-SFD-Key": md5(key_access.encode()).hexdigest(), 42 | } 43 | 44 | def _get_streams(self): 45 | channel = self.match.group("channel") 46 | params = {"timezone": "UTC"} 47 | headers = self._get_headers() 48 | 49 | self.id, self.title, hls_url = self.session.http.get( 50 | self._API_URL.format(channel=channel), 51 | params=params, 52 | headers=headers, 53 | schema=validate.Schema( 54 | validate.parse_json(), 55 | { 56 | "id": str, 57 | "title": str, 58 | "link_play": str, 59 | }, 60 | validate.union_get( 61 | "id", 62 | "title", 63 | "link_play", 64 | ), 65 | ), 66 | ) 67 | 68 | return HLSStream.parse_variant_playlist(self.session, hls_url) 69 | 70 | 71 | __plugin__ = VinhLongTV 72 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/vtvgo.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels from VTV, a Vietnamese public, state-owned broadcaster. 3 | $url vtvgo.vn 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream 13 | 14 | 15 | log = logging.getLogger(__name__) 16 | 17 | 18 | @pluginmatcher(re.compile( 19 | r"https?://vtvgo\.vn/xem-truc-tuyen-kenh-", 20 | )) 21 | class VTVgo(Plugin): 22 | AJAX_URL = "https://vtvgo.vn/ajax-get-stream" 23 | 24 | def _get_streams(self): 25 | # get cookies 26 | self.session.http.get("https://vtvgo.vn/") 27 | 28 | self.session.http.headers.update({ 29 | "Origin": "https://vtvgo.vn", 30 | "Referer": self.url, 31 | "Sec-Fetch-Site": "same-origin", 32 | "X-Requested-With": "XMLHttpRequest", 33 | }) 34 | 35 | params = self.session.http.get(self.url, schema=validate.Schema( 36 | validate.parse_html(), 37 | validate.xml_xpath_string(".//script[contains(text(),'setplayer(')][1]/text()"), 38 | validate.none_or_all( 39 | validate.regex( 40 | re.compile(r"""var\s+(?P(?:type_)?id|time|token)\s*=\s*["']?(?P[^"']+)["']?;"""), 41 | method="findall", 42 | ), 43 | [ 44 | ("id", int), 45 | ("type_id", str), 46 | ("time", str), 47 | ("token", str), 48 | ], 49 | ), 50 | )) 51 | if not params: 52 | return 53 | 54 | log.trace(f"{params!r}") 55 | hls_url = self.session.http.post( 56 | self.AJAX_URL, 57 | data=dict(params), 58 | schema=validate.Schema( 59 | validate.parse_json(), 60 | {"stream_url": [validate.url()]}, 61 | validate.get(("stream_url", 0)), 62 | ), 63 | ) 64 | 65 | return HLSStream.parse_variant_playlist(self.session, hls_url) 66 | 67 | 68 | __plugin__ = VTVgo 69 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/welt.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description German news and documentaries TV channel, owned by Axel Springer SE. 3 | $url welt.de 4 | $type live, vod 5 | $region Germany 6 | """ 7 | 8 | import re 9 | from urllib.parse import quote 10 | 11 | from streamlink.plugin import Plugin, pluginmatcher 12 | from streamlink.plugin.api import validate 13 | from streamlink.stream.hls import HLSStream 14 | 15 | 16 | @pluginmatcher(re.compile( 17 | r"https?://(\w+\.)?welt\.de/?", 18 | )) 19 | class Welt(Plugin): 20 | _url_vod = "https://www.welt.de/onward/video/play/{0}" 21 | _schema = validate.Schema( 22 | validate.parse_html(), 23 | validate.xml_findtext(".//script[@type='application/json'][@data-content='VideoPlayer.Config']"), 24 | validate.parse_json(), 25 | validate.get("sources"), 26 | validate.filter(lambda obj: obj["extension"] == "m3u8"), 27 | validate.get((0, "src")), 28 | ) 29 | 30 | def _get_streams(self): 31 | hls_url = self.session.http.get(self.url, schema=self._schema) 32 | 33 | if "mediathek" in self.url.lower(): 34 | url = self._url_vod.format(quote(hls_url, safe="")) 35 | hls_url = self.session.http.get(url, headers={"Referer": self.url}).url 36 | 37 | return HLSStream.parse_variant_playlist(self.session, hls_url, headers={"Referer": self.url}) 38 | 39 | 40 | __plugin__ = Welt 41 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/zeenews.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Indian Hindi-language news channel covering world & Indian news, business, entertainment and sport. 3 | $url zeenews.india.com 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | log = logging.getLogger(__name__) 15 | 16 | 17 | @pluginmatcher(re.compile( 18 | r"https?://zeenews\.india\.com/live-tv", 19 | )) 20 | class ZeeNews(Plugin): 21 | HLS_URL = "https://z5ams.akamaized.net/zeenews/index.m3u8{0}" 22 | TOKEN_URL = "https://useraction.zee5.com/token/live.php" 23 | 24 | title = "Zee News" 25 | 26 | def _get_streams(self): 27 | res = self.session.http.get(self.TOKEN_URL) 28 | token = self.session.http.json(res)["video_token"] 29 | log.debug("video_token: {0}".format(token)) 30 | yield from HLSStream.parse_variant_playlist(self.session, self.HLS_URL.format(token)).items() 31 | 32 | 33 | __plugin__ = ZeeNews 34 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/zengatv.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Indian live TV channels. OTT service from Zenga TV. 3 | $url zengatv.com 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | log = logging.getLogger(__name__) 15 | 16 | 17 | @pluginmatcher(re.compile( 18 | r"https?://(www\.)?zengatv\.com/\w+", 19 | )) 20 | class ZengaTV(Plugin): 21 | """Streamlink Plugin for livestreams on zengatv.com""" 22 | 23 | _id_re = re.compile(r"""id=(?P["'])dvrid(?P=q)\svalue=(?P=q)(?P[^"']+)(?P=q)""") 24 | _id_2_re = re.compile(r"""LivePlayer\(.+["'](?PD\d+)["']""") 25 | 26 | api_url = "http://www.zengatv.com/changeResulation/" 27 | 28 | def _get_streams(self): 29 | headers = {"Referer": self.url} 30 | 31 | res = self.session.http.get(self.url, headers=headers) 32 | for id_re in (self._id_re, self._id_2_re): 33 | m = id_re.search(res.text) 34 | if not m: 35 | continue 36 | break 37 | 38 | if not m: 39 | log.error("No video id found") 40 | return 41 | 42 | dvr_id = m.group("id") 43 | log.debug("Found video id: {0}".format(dvr_id)) 44 | data = {"feed": "hd", "dvrId": dvr_id} 45 | res = self.session.http.post(self.api_url, headers=headers, data=data) 46 | if res.status_code == 200: 47 | yield from HLSStream.parse_variant_playlist(self.session, res.text, headers=headers).items() 48 | 49 | 50 | __plugin__ = ZengaTV 51 | -------------------------------------------------------------------------------- /linux/streamlink/plugins/zhanqi.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Chinese live streaming platform for live video game broadcasts. 3 | $url zhanqi.tv 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream 13 | from streamlink.stream.http import HTTPStream 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | API_URL = "https://www.zhanqi.tv/api/static/v2.1/room/domain/{0}.json" 19 | 20 | STATUS_ONLINE = 4 21 | STATUS_OFFLINE = 0 22 | 23 | _room_schema = validate.Schema( 24 | { 25 | "data": validate.any(None, { 26 | "status": validate.all( 27 | str, 28 | validate.transform(int), 29 | ), 30 | "videoId": str, 31 | }), 32 | }, 33 | validate.get("data"), 34 | ) 35 | 36 | 37 | @pluginmatcher(re.compile( 38 | r"https?://(www\.)?zhanqi\.tv/(?P[^/]+)", 39 | )) 40 | class Zhanqitv(Plugin): 41 | def _get_streams(self): 42 | channel = self.match.group("channel") 43 | 44 | res = self.session.http.get(API_URL.format(channel)) 45 | room = self.session.http.json(res, schema=_room_schema) 46 | if not room: 47 | log.info("Not a valid room url.") 48 | return 49 | 50 | if room["status"] != STATUS_ONLINE: 51 | log.info("Stream currently unavailable.") 52 | return 53 | 54 | url = "http://wshdl.load.cdn.zhanqi.tv/zqlive/{room[videoId]}.flv?get_url=".format(room=room) 55 | stream = HTTPStream(self.session, url) 56 | yield "live", stream 57 | 58 | url = "http://dlhls.cdn.zhanqi.tv/zqlive/{room[videoId]}_1024/index.m3u8?Dnion_vsnae={room[videoId]}".format(room=room) 59 | stream = HLSStream(self.session, url) 60 | yield "live", stream 61 | 62 | 63 | __plugin__ = Zhanqitv 64 | -------------------------------------------------------------------------------- /linux/streamlink/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botbahlul/PyAutoSRT/20942b9656030ac5cc044655bb88974a381c09b8/linux/streamlink/py.typed -------------------------------------------------------------------------------- /linux/streamlink/stream/__init__.py: -------------------------------------------------------------------------------- 1 | from streamlink.stream.dash import DASHStream 2 | from streamlink.stream.ffmpegmux import MuxedStream 3 | from streamlink.stream.hls import HLSStream, MuxedHLSStream 4 | from streamlink.stream.http import HTTPStream 5 | from streamlink.stream.stream import Stream, StreamIO 6 | from streamlink.stream.wrappers import StreamIOIterWrapper, StreamIOThreadWrapper, StreamIOWrapper 7 | -------------------------------------------------------------------------------- /linux/streamlink/stream/file.py: -------------------------------------------------------------------------------- 1 | """ 2 | Stream wrapper around a file 3 | """ 4 | from streamlink.stream.stream import Stream 5 | 6 | 7 | class FileStream(Stream): 8 | __shortname__ = "file" 9 | 10 | def __init__(self, session, path=None, fileobj=None): 11 | super().__init__(session) 12 | self.path = path 13 | self.fileobj = fileobj 14 | if not self.path and not self.fileobj: 15 | raise ValueError("path or fileobj must be set") 16 | 17 | def __json__(self): 18 | json = super().__json__() 19 | 20 | if self.path: 21 | json["path"] = self.path 22 | 23 | return json 24 | 25 | def to_url(self): 26 | if self.path is None: 27 | return super().to_url() 28 | 29 | return self.path 30 | 31 | def open(self): 32 | return self.fileobj or open(self.path) 33 | -------------------------------------------------------------------------------- /linux/streamlink/stream/filtered.py: -------------------------------------------------------------------------------- 1 | from threading import Event 2 | 3 | from streamlink.buffers import Buffer 4 | from streamlink.stream.stream import StreamIO 5 | 6 | 7 | class FilteredStream(StreamIO): 8 | """StreamIO mixin for being able to pause read calls while filtering content""" 9 | 10 | buffer: Buffer 11 | 12 | def __init__(self, *args, **kwargs): 13 | self._event_filter = Event() 14 | self._event_filter.set() 15 | super().__init__(*args, **kwargs) 16 | 17 | def read(self, *args, **kwargs) -> bytes: 18 | read = super().read 19 | while True: 20 | try: 21 | return read(*args, **kwargs) 22 | except OSError: 23 | # wait indefinitely until filtering ends 24 | self._event_filter.wait() 25 | if self.buffer.closed: 26 | return b"" 27 | # if data is available, try reading again 28 | if self.buffer.length > 0: 29 | continue 30 | # raise if not filtering and no data available 31 | raise 32 | 33 | def close(self) -> None: 34 | super().close() 35 | self._event_filter.set() 36 | 37 | def is_paused(self) -> bool: 38 | return not self._event_filter.is_set() 39 | 40 | def pause(self) -> None: 41 | self._event_filter.clear() 42 | 43 | def resume(self) -> None: 44 | self._event_filter.set() 45 | 46 | def filter_wait(self, timeout=None): 47 | return self._event_filter.wait(timeout) 48 | -------------------------------------------------------------------------------- /linux/streamlink/stream/stream.py: -------------------------------------------------------------------------------- 1 | import io 2 | import json 3 | import logging 4 | 5 | from streamlink.session import Streamlink 6 | 7 | 8 | log = logging.getLogger(__name__) 9 | 10 | 11 | class Stream: 12 | """ 13 | This is a base class that should be inherited when implementing 14 | different stream types. Should only be created by plugins. 15 | """ 16 | 17 | __shortname__ = "stream" 18 | 19 | @classmethod 20 | def shortname(cls): 21 | return cls.__shortname__ 22 | 23 | def __init__(self, session: Streamlink): 24 | """ 25 | :param session: Streamlink session instance 26 | """ 27 | 28 | self.session: Streamlink = session 29 | 30 | def __repr__(self): 31 | params = [repr(self.shortname())] 32 | for method in self.to_url, self.to_manifest_url: 33 | try: 34 | params.append(repr(method())) 35 | except TypeError: 36 | pass 37 | 38 | return f"<{self.__class__.__name__} [{', '.join(params)}]>" 39 | 40 | def __json__(self): 41 | return dict(type=self.shortname()) 42 | 43 | @property 44 | def json(self): 45 | obj = self.__json__() 46 | return json.dumps(obj) 47 | 48 | def to_url(self): 49 | raise TypeError(f"<{self.__class__.__name__} [{self.shortname()}]> cannot be translated to a URL") 50 | 51 | def to_manifest_url(self): 52 | raise TypeError(f"<{self.__class__.__name__} [{self.shortname()}]> cannot be translated to a manifest URL") 53 | 54 | def open(self) -> "StreamIO": 55 | """ 56 | Attempts to open a connection to the stream. 57 | Returns a file-like object that can be used to read the stream data. 58 | 59 | :raises StreamError: on failure 60 | """ 61 | 62 | raise NotImplementedError 63 | 64 | 65 | class StreamIO(io.IOBase): 66 | pass 67 | 68 | 69 | __all__ = ["Stream", "StreamIO"] 70 | -------------------------------------------------------------------------------- /linux/streamlink/user_input.py: -------------------------------------------------------------------------------- 1 | import abc 2 | 3 | 4 | class UserInputRequester(abc.ABC): 5 | """ 6 | Base Class / Interface for requesting user input 7 | 8 | e.g. from the console 9 | """ 10 | @abc.abstractmethod 11 | def ask(self, prompt: str) -> str: 12 | """ 13 | Ask the user for a text input, the input is not sensitive 14 | and can be echoed to the user 15 | 16 | :param prompt: message to display when asking for the input 17 | :return: the value of the user input 18 | """ 19 | raise NotImplementedError 20 | 21 | @abc.abstractmethod 22 | def ask_password(self, prompt: str) -> str: 23 | """ 24 | Ask the user for a text input, the input _is_ sensitive 25 | and should be masked as the user gives the input 26 | 27 | :param prompt: message to display when asking for the input 28 | :return: the value of the user input 29 | """ 30 | raise NotImplementedError 31 | -------------------------------------------------------------------------------- /linux/streamlink/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from streamlink.utils.cache import LRUCache 2 | from streamlink.utils.data import search_dict 3 | from streamlink.utils.module import load_module 4 | from streamlink.utils.named_pipe import NamedPipe 5 | from streamlink.utils.parse import parse_html, parse_json, parse_qsd, parse_xml 6 | from streamlink.utils.url import absolute_url, prepend_www, update_qsd, update_scheme, url_concat, url_equal 7 | 8 | 9 | __all__ = [ 10 | "LRUCache", 11 | "search_dict", 12 | "load_module", 13 | "NamedPipe", 14 | "parse_html", "parse_json", "parse_qsd", "parse_xml", 15 | "absolute_url", "prepend_www", "update_qsd", "update_scheme", "url_concat", "url_equal", 16 | ] 17 | -------------------------------------------------------------------------------- /linux/streamlink/utils/cache.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | from typing import Generic, Optional, OrderedDict as TOrderedDict, TypeVar 3 | 4 | 5 | TCacheKey = TypeVar("TCacheKey") 6 | TCacheValue = TypeVar("TCacheValue") 7 | 8 | 9 | class LRUCache(Generic[TCacheKey, TCacheValue]): 10 | def __init__(self, num: int): 11 | self.cache: TOrderedDict[TCacheKey, TCacheValue] = OrderedDict() 12 | self.num = num 13 | 14 | def get(self, key: TCacheKey) -> Optional[TCacheValue]: 15 | if key not in self.cache: 16 | return None 17 | self.cache.move_to_end(key) 18 | return self.cache[key] 19 | 20 | def set(self, key: TCacheKey, value: TCacheValue) -> None: 21 | self.cache[key] = value 22 | self.cache.move_to_end(key) 23 | if len(self.cache) > self.num: 24 | self.cache.popitem(last=False) 25 | -------------------------------------------------------------------------------- /linux/streamlink/utils/crypto.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | 3 | from Crypto.Cipher import AES 4 | 5 | 6 | def evp_bytestokey(password, salt, key_len, iv_len): 7 | """ 8 | Python implementation of OpenSSL's EVP_BytesToKey() 9 | :param password: or passphrase 10 | :param salt: 8 byte salt 11 | :param key_len: length of key in bytes 12 | :param iv_len: length of IV in bytes 13 | :return: (key, iv) 14 | """ 15 | d = d_i = b"" 16 | while len(d) < key_len + iv_len: 17 | d_i = hashlib.md5(d_i + password + salt).digest() 18 | d += d_i 19 | return d[:key_len], d[key_len:key_len + iv_len] 20 | 21 | 22 | def decrypt_openssl(data, passphrase, key_length=32): 23 | if data.startswith(b"Salted__"): 24 | salt = data[len(b"Salted__"):AES.block_size] 25 | key, iv = evp_bytestokey(passphrase, salt, key_length, AES.block_size) 26 | d = AES.new(key, AES.MODE_CBC, iv) 27 | out = d.decrypt(data[AES.block_size:]) 28 | return unpad_pkcs5(out) 29 | 30 | 31 | def unpad_pkcs5(padded): 32 | return padded[:-padded[-1]] 33 | -------------------------------------------------------------------------------- /linux/streamlink/utils/data.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Dict, List, Union 2 | 3 | 4 | def search_dict(data: Union[Dict, List], key: Any): 5 | """ 6 | Search for a key in a nested dict, or list of nested dicts, and return the values. 7 | 8 | :param data: dict/list to search 9 | :param key: key to find 10 | :return: matches for key 11 | """ 12 | if isinstance(data, dict): 13 | for dkey, value in data.items(): 14 | if dkey == key: 15 | yield value 16 | yield from search_dict(value, key) 17 | elif isinstance(data, list): 18 | for value in data: 19 | yield from search_dict(value, key) 20 | -------------------------------------------------------------------------------- /linux/streamlink/utils/module.py: -------------------------------------------------------------------------------- 1 | from importlib.machinery import SOURCE_SUFFIXES, FileFinder, SourceFileLoader 2 | from importlib.util import module_from_spec 3 | 4 | 5 | _loader_details = [(SourceFileLoader, SOURCE_SUFFIXES)] 6 | 7 | 8 | def load_module(name, path=None): 9 | finder = FileFinder(path, *_loader_details) 10 | spec = finder.find_spec(name) 11 | if not spec or not spec.loader: 12 | raise ImportError(f"no module named {name}") 13 | mod = module_from_spec(spec) 14 | spec.loader.exec_module(mod) 15 | return mod 16 | -------------------------------------------------------------------------------- /pypi_setup.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | set "folderToDelete1=.\build" 5 | set "folderToDelete2=.\dist" 6 | set "folderToDelete3=.\vosk_autosrt.egg-info" 7 | 8 | if exist "%folderToDelete1%" ( 9 | rmdir /s /q "%folderToDelete1%" 10 | if errorlevel 1 ( 11 | echo Error occurred while deleting the folder. 12 | ) 13 | ) 14 | 15 | if exist "%folderToDelete2%" ( 16 | rmdir /s /q "%folderToDelete2%" 17 | if errorlevel 1 ( 18 | echo Error occurred while deleting the folder. 19 | ) 20 | ) 21 | 22 | if exist "%folderToDelete3%" ( 23 | rmdir /s /q "%folderToDelete3%" 24 | if errorlevel 1 ( 25 | echo Error occurred while deleting the folder. 26 | ) 27 | ) 28 | 29 | python setup.py sdist 30 | rem python setup.py bdist_wheel --plat-name win_amd64 31 | python setup.py bdist_wheel 32 | 33 | endlocal 34 | -------------------------------------------------------------------------------- /pypi_setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | folder1="./build" 4 | folder2="./dist" 5 | folder3="./vosk_autosrt.egg-info" 6 | 7 | if [ -d "$folder1" ]; then 8 | rm -rf "$folder1" 9 | fi 10 | 11 | if [ -d "$folder2" ]; then 12 | rm -rf "$folder2" 13 | fi 14 | 15 | if [ -d "$folder3" ]; then 16 | rm -rf "$folder3" 17 | fi 18 | 19 | python3.8 setup.py sdist 20 | python3.8 setup.py bdist_wheel --plat-name manylinux_2_17_x86_64 21 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | # the `universal` setting means that the project runs unmodified on both Python 2 and 3, 3 | # and doesn't use any C extensions to Python 4 | universal=1 5 | [metadata] 6 | description-file=README.md 7 | license_files=LICENSE.rst 8 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3.8 2 | from __future__ import unicode_literals 3 | import platform 4 | from pyautosrt import VERSION 5 | import warnings 6 | warnings.filterwarnings("ignore", category=DeprecationWarning, module='setuptools') 7 | warnings.filterwarnings("ignore", category=UserWarning, module='setuptools') 8 | warnings.filterwarnings("ignore", message=".*is deprecated*") 9 | 10 | try: 11 | from setuptools import setup, find_packages 12 | except ImportError: 13 | from distutils.core import setup 14 | 15 | long_description = ( 16 | 'pyautosrt is a python based desktop app for automatic speech recognition and ' 17 | 'subtitle generation. It takes a video or an audio file as input, performs voice' 18 | 'activity detection to find speech regions, makes parallel requests to Google ' 19 | 'Web Speech API to generate transcriptions for those regions, then translates' 20 | 'them to a different language, and finally saves the resulting subtitles to disk.' 21 | 'It supports a variety of input and output languages and can currently produce ' 22 | 'subtitles in SRT, VTT, JSON, and RAW format.' 23 | ) 24 | 25 | install_requires=[ 26 | "requests>=2.3.0", 27 | "httpx>=0.13.3", 28 | "pysrt>=1.0.1", 29 | "six>=1.11.0", 30 | "pysimplegui>=4.60.1", 31 | "streamlink>=5.3.1", 32 | "urllib3>=1.26.0,<3.0", 33 | ] 34 | 35 | setup( 36 | name="pyautosrt", 37 | version=VERSION, 38 | description="pyautosrt is a python based desktop app to generate subtitle and translated subtitle file", 39 | long_description = long_description, 40 | author="Bot Bahlul", 41 | author_email="bot.bahlul@gmail.com", 42 | url="https://github.com/botbahlul/pyautosrt", 43 | packages=find_packages(), 44 | entry_points={ 45 | "console_scripts": [ 46 | "pyautosrt = pyautosrt:main", 47 | ], 48 | }, 49 | install_requires=install_requires, 50 | license=open("LICENSE").read() 51 | ) 52 | -------------------------------------------------------------------------------- /win/multiprocessing_hook.py: -------------------------------------------------------------------------------- 1 | from PyInstaller.utils.hooks import collect_submodules 2 | hiddenimports = collect_submodules('multiprocessing') 3 | -------------------------------------------------------------------------------- /win/streamlink/__init__.py: -------------------------------------------------------------------------------- 1 | """Streamlink extracts streams from various services. 2 | 3 | The main compontent of Streamlink is a command-line utility that 4 | launches the streams in a video player. 5 | 6 | An API is also provided that allows direct access to stream data. 7 | 8 | Full documentation is available at https://streamlink.github.io. 9 | 10 | """ 11 | from streamlink._version import __version__ 12 | 13 | __title__ = "streamlink" 14 | __license__ = "Simplified BSD" 15 | __author__ = "Streamlink" 16 | __copyright__ = "Copyright 2023 Streamlink" 17 | __credits__ = ["https://github.com/streamlink/streamlink/blob/master/AUTHORS"] 18 | 19 | from streamlink.api import streams 20 | from streamlink.exceptions import (StreamlinkError, PluginError, NoStreamsError, 21 | NoPluginError, StreamError) 22 | from streamlink.session import Streamlink 23 | -------------------------------------------------------------------------------- /win/streamlink/__main__.py: -------------------------------------------------------------------------------- 1 | if __name__ == "__main__": 2 | from streamlink_cli.main import main 3 | main() 4 | -------------------------------------------------------------------------------- /win/streamlink/_version.py: -------------------------------------------------------------------------------- 1 | # Always get the current version in "editable" installs 2 | # `pip install -e .` / `python setup.py develop` 3 | def _get_version() -> str: 4 | from pathlib import Path 5 | from versioningit import get_version 6 | import streamlink 7 | 8 | return get_version( 9 | project_dir=Path(streamlink.__file__).parents[2], 10 | ) 11 | 12 | 13 | # The following _get_version() call will get replaced by versioningit with a static version string when building streamlink 14 | # `pip install .` / `pip wheel .` / `python setup.py build` / `python setup.py bdist_wheel` / etc. 15 | __version__ = "5.5.1" 16 | -------------------------------------------------------------------------------- /win/streamlink/api.py: -------------------------------------------------------------------------------- 1 | from streamlink.session import Streamlink 2 | 3 | 4 | def streams(url: str, **params): 5 | """ 6 | Initializes an empty Streamlink session, attempts to find a plugin and extracts streams from the URL if a plugin was found. 7 | 8 | :param url: a URL to match against loaded plugins 9 | :param params: Additional keyword arguments passed to :meth:`streamlink.Streamlink.streams` 10 | :raises NoPluginError: on plugin resolve failure 11 | :returns: A :class:`dict` of stream names and :class:`streamlink.stream.Stream` instances 12 | """ 13 | 14 | session = Streamlink() 15 | 16 | return session.streams(url, **params) 17 | -------------------------------------------------------------------------------- /win/streamlink/compat.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | 5 | is_darwin = sys.platform == "darwin" 6 | is_win32 = os.name == "nt" 7 | 8 | 9 | __all__ = [ 10 | "is_darwin", 11 | "is_win32", 12 | ] 13 | -------------------------------------------------------------------------------- /win/streamlink/exceptions.py: -------------------------------------------------------------------------------- 1 | class StreamlinkError(Exception): 2 | """ 3 | Any error caused by Streamlink will be caught with this exception. 4 | """ 5 | 6 | 7 | # TODO: don't use PluginError for failed HTTP requests or validation schema failures 8 | class PluginError(StreamlinkError): 9 | """ 10 | Plugin related error. 11 | """ 12 | 13 | 14 | class FatalPluginError(PluginError): 15 | """ 16 | Plugin related error that cannot be recovered from. 17 | 18 | Plugins should use this ``Exception`` when errors that can 19 | never be recovered from are encountered. For example, when 20 | a user's input is required and none can be given. 21 | """ 22 | 23 | 24 | class NoPluginError(StreamlinkError): 25 | """ 26 | Error raised by :py:meth:`Streamlink.resolve_url() ` 27 | and :py:meth:`Streamlink.resolve_url_no_redirect() ` 28 | when no plugin could be found for the given input URL. 29 | """ 30 | 31 | 32 | class NoStreamsError(StreamlinkError): 33 | """ 34 | Plugins should use this ``Exception`` in :py:meth:`Plugin._get_streams() ` 35 | when returning ``None`` or an empty ``dict`` is not possible, e.g. in nested function calls. 36 | """ 37 | 38 | 39 | class StreamError(StreamlinkError): 40 | """ 41 | Stream related error. 42 | """ 43 | 44 | 45 | # https://stackoverflow.com/a/49797717 46 | class _StreamlinkWarningMeta(type): 47 | def __new__(mcs, name, bases, namespace, **kw): 48 | name = namespace.get("__name__", name) 49 | return super().__new__(mcs, name, bases, namespace, **kw) 50 | 51 | 52 | class StreamlinkWarning(UserWarning, metaclass=_StreamlinkWarningMeta): 53 | pass 54 | 55 | 56 | class StreamlinkDeprecationWarning(StreamlinkWarning): 57 | __name__ = "StreamlinkDeprecation" 58 | 59 | 60 | __all__ = [ 61 | "StreamlinkError", 62 | "PluginError", 63 | "FatalPluginError", 64 | "NoPluginError", 65 | "NoStreamsError", 66 | "StreamError", 67 | "StreamlinkWarning", 68 | "StreamlinkDeprecationWarning", 69 | ] 70 | -------------------------------------------------------------------------------- /win/streamlink/packages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botbahlul/PyAutoSRT/20942b9656030ac5cc044655bb88974a381c09b8/win/streamlink/packages/__init__.py -------------------------------------------------------------------------------- /win/streamlink/plugin/__init__.py: -------------------------------------------------------------------------------- 1 | from streamlink.exceptions import PluginError 2 | from streamlink.options import Argument as PluginArgument, Arguments as PluginArguments, Options as PluginOptions 3 | from streamlink.plugin.plugin import ( 4 | HIGH_PRIORITY, 5 | LOW_PRIORITY, 6 | NO_PRIORITY, 7 | NORMAL_PRIORITY, 8 | Plugin, 9 | pluginargument, 10 | pluginmatcher, 11 | ) 12 | 13 | 14 | __all__ = [ 15 | "HIGH_PRIORITY", "NORMAL_PRIORITY", "LOW_PRIORITY", "NO_PRIORITY", 16 | "Plugin", "PluginArguments", "PluginArgument", "PluginError", "PluginOptions", 17 | "pluginmatcher", "pluginargument", 18 | ] 19 | -------------------------------------------------------------------------------- /win/streamlink/plugin/api/__init__.py: -------------------------------------------------------------------------------- 1 | from streamlink.plugin.api.http_session import HTTPSession 2 | 3 | 4 | __all__ = ["HTTPSession"] 5 | -------------------------------------------------------------------------------- /win/streamlink/plugin/api/useragents.py: -------------------------------------------------------------------------------- 1 | ANDROID = "Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.135 Mobile Safari/537.36" 2 | CHROME = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36" 3 | CHROME_OS = "Mozilla/5.0 (X11; CrOS x86_64 15359.58.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.134 Safari/537.36" 4 | FIREFOX = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109) Gecko/20100101 Firefox/112.0" 5 | IE_11 = "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" 6 | IPHONE = "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 Mobile/15E148 Safari/604.1" 7 | OPERA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 OPR/98.0.4759.3" 8 | SAFARI = "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_3_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 Safari/605.1.15" 9 | 10 | # Backwards compatibility 11 | EDGE = CHROME 12 | FIREFOX_MAC = FIREFOX 13 | IE_6 = IE_7 = IE_8 = IE_9 = IE_11 14 | IPHONE_6 = IPAD = IPHONE 15 | SAFARI_7 = SAFARI_8 = SAFARI 16 | WINDOWS_PHONE_8 = ANDROID 17 | -------------------------------------------------------------------------------- /win/streamlink/plugins/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | New plugins should use streamlink.plugin.Plugin instead 3 | of this module, but this is kept here for backwards 4 | compatibility. 5 | """ 6 | 7 | from streamlink.exceptions import NoPluginError, NoStreamsError, PluginError 8 | from streamlink.plugin.plugin import Plugin 9 | 10 | 11 | __all__ = ["Plugin", "PluginError", "NoStreamsError", "NoPluginError"] 12 | -------------------------------------------------------------------------------- /win/streamlink/plugins/app17.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Social platform delivering live broadcasts on diverse topics, from politics and music to entertainment. 3 | $url 17app.co 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream 13 | from streamlink.stream.http import HTTPStream 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | @pluginmatcher(re.compile( 20 | r"https?://17\.live/.+/live/(?P[^/&?]+)", 21 | )) 22 | class App17(Plugin): 23 | def _get_streams(self): 24 | channel = self.match.group("channel") 25 | self.session.http.headers.update({"Referer": self.url}) 26 | data = self.session.http.post( 27 | f"https://wap-api.17app.co/api/v1/lives/{channel}/viewers/alive", 28 | data={"liveStreamID": channel}, 29 | schema=validate.Schema( 30 | validate.parse_json(), 31 | validate.any( 32 | {"rtmpUrls": [{ 33 | validate.optional("provider"): validate.any(int, None), 34 | "url": validate.url(path=validate.endswith(".flv")), 35 | }]}, 36 | {"errorCode": int, "errorMessage": str}, 37 | ), 38 | ), 39 | acceptable_status=(200, 403, 404, 420)) 40 | log.trace(f"{data!r}") 41 | if data.get("errorCode"): 42 | log.error(f"{data['errorCode']} - {data['errorMessage'].replace('Something wrong: ', '')}") 43 | return 44 | 45 | flv_url = data["rtmpUrls"][0]["url"] 46 | yield "live", HTTPStream(self.session, flv_url) 47 | 48 | if "wansu-" in flv_url: 49 | hls_url = flv_url.replace(".flv", "/playlist.m3u8") 50 | else: 51 | hls_url = flv_url.replace("live-hdl", "live-hls").replace(".flv", ".m3u8") 52 | 53 | s = HLSStream.parse_variant_playlist(self.session, hls_url) 54 | if not s: 55 | yield "live", HLSStream(self.session, hls_url) 56 | elif len(s) == 1: 57 | yield "live", next(iter(s.values())) 58 | else: 59 | yield from s.items() 60 | 61 | 62 | __plugin__ = App17 63 | -------------------------------------------------------------------------------- /win/streamlink/plugins/atpchallenger.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Tennis tournaments organized by the Association of Tennis Professionals. 3 | $url atptour.com/en/atp-challenger-tour/challenger-tv 4 | $type live, vod 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.plugin.api import validate 11 | 12 | 13 | @pluginmatcher(re.compile( 14 | r"https?://(?:www\.)?atptour\.com/(?:en|es)/atp-challenger-tour/challenger-tv", 15 | )) 16 | class AtpChallengerTour(Plugin): 17 | def _get_streams(self): 18 | iframe_url = self.session.http.get(self.url, schema=validate.Schema( 19 | validate.parse_html(), 20 | validate.xml_xpath_string(".//iframe[starts-with(@id,'vimeoPlayer_')][@src][1]/@src"), 21 | validate.any(None, validate.url()), 22 | )) 23 | if iframe_url: 24 | return self.session.streams(iframe_url) 25 | 26 | 27 | __plugin__ = AtpChallengerTour 28 | -------------------------------------------------------------------------------- /win/streamlink/plugins/bigo.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Global live streaming platform for live video game broadcasts and individual live streams. 3 | $url live.bigo.tv 4 | $url bigoweb.co 5 | $type live 6 | """ 7 | 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import useragents, validate 12 | from streamlink.stream.hls import HLSStream 13 | 14 | 15 | @pluginmatcher(re.compile( 16 | r"https?://(?:www\.)?bigo\.tv/([^/]+)$", 17 | )) 18 | class Bigo(Plugin): 19 | _api_url = "https://www.bigo.tv/OInterface/getVideoParam?bigoId={0}" 20 | 21 | _video_info_schema = validate.Schema({ 22 | "code": 0, 23 | "msg": "success", 24 | "data": { 25 | "videoSrc": validate.any(None, "", validate.url()), 26 | }, 27 | }) 28 | 29 | def _get_streams(self): 30 | res = self.session.http.get( 31 | self._api_url.format(self.match.group(1)), 32 | allow_redirects=True, 33 | headers={"User-Agent": useragents.IPHONE_6}, 34 | ) 35 | data = self.session.http.json(res, schema=self._video_info_schema) 36 | videourl = data["data"]["videoSrc"] 37 | if videourl: 38 | yield "live", HLSStream(self.session, videourl) 39 | 40 | 41 | __plugin__ = Bigo 42 | -------------------------------------------------------------------------------- /win/streamlink/plugins/btv.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description A privately owned Bulgarian live TV channel. 3 | $url btvplus.bg 4 | $type live 5 | $region Bulgaria 6 | """ 7 | 8 | import logging 9 | import re 10 | 11 | from streamlink.plugin import Plugin, pluginmatcher 12 | from streamlink.plugin.api import validate 13 | from streamlink.stream.hls import HLSStream 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | @pluginmatcher(re.compile( 20 | r"https?://(?:www\.)?btvplus\.bg/live/?", 21 | )) 22 | class BTV(Plugin): 23 | URL_API = "https://btvplus.bg/lbin/v3/btvplus/player_config.php" 24 | 25 | def _get_streams(self): 26 | media_id = self.session.http.get(self.url, schema=validate.Schema( 27 | re.compile(r"media_id=(\d+)"), 28 | validate.any(None, validate.get(1)), 29 | )) 30 | if media_id is None: 31 | return 32 | 33 | stream_url = self.session.http.get( 34 | self.URL_API, 35 | params={ 36 | "media_id": media_id, 37 | }, 38 | schema=validate.Schema( 39 | validate.any( 40 | validate.all( 41 | validate.regex(re.compile(r"geo_blocked_stream")), 42 | validate.get(0), 43 | ), 44 | validate.all( 45 | validate.parse_json(), 46 | { 47 | "status": "ok", 48 | "info": { 49 | "file": validate.url(path=validate.endswith(".m3u8")), 50 | }, 51 | }, 52 | validate.get(("info", "file")), 53 | ), 54 | ), 55 | ), 56 | ) 57 | if not stream_url: 58 | return 59 | 60 | if stream_url == "geo_blocked_stream": 61 | log.error("The content is not available in your region") 62 | return 63 | 64 | return {"live": HLSStream(self.session, stream_url)} 65 | 66 | 67 | __plugin__ = BTV 68 | -------------------------------------------------------------------------------- /win/streamlink/plugins/cbsnews.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description 24-hour live-streaming world news channel, based in the United States of America. 3 | $url cbsnews.com 4 | $type live 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.plugin.api import validate 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | @pluginmatcher(re.compile( 15 | r"https?://(?:www\.)?cbsnews\.com/(?:\w+/)?live/?", 16 | )) 17 | class CBSNews(Plugin): 18 | def _get_streams(self): 19 | data = self.session.http.get(self.url, schema=validate.Schema( 20 | re.compile(r"CBSNEWS\.defaultPayload\s*=\s*(\{.*?})\s*\n"), 21 | validate.none_or_all( 22 | validate.get(1), 23 | validate.parse_json(), 24 | { 25 | "items": [{ 26 | "id": str, 27 | "canonicalTitle": str, 28 | "video": validate.url(), 29 | "format": "application/x-mpegURL", 30 | }], 31 | }, 32 | validate.get(("items", 0)), 33 | validate.union_get("id", "canonicalTitle", "video"), 34 | ), 35 | )) 36 | if not data: 37 | return 38 | 39 | self.id, self.title, hls_url = data 40 | 41 | return HLSStream.parse_variant_playlist(self.session, hls_url) 42 | 43 | 44 | __plugin__ = CBSNews 45 | -------------------------------------------------------------------------------- /win/streamlink/plugins/cinergroup.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Turkish live TV channels from Ciner Group, including Haberturk TV and Show TV. 3 | $url bloomberght.com 4 | $url haberturk.com 5 | $url haberturk.tv 6 | $url showmax.com.tr 7 | $url showturk.com.tr 8 | $url showtv.com.tr 9 | $type live 10 | """ 11 | 12 | import re 13 | 14 | from streamlink.plugin import Plugin, pluginmatcher 15 | from streamlink.plugin.api import validate 16 | from streamlink.stream.hls import HLSStream 17 | 18 | 19 | @pluginmatcher(re.compile(r""" 20 | https?://(?:www\.)? 21 | (?: 22 | bloomberght\.com/tv 23 | | 24 | haberturk\.(?:com|tv)(?:/tv)?/canliyayin 25 | | 26 | showmax\.com\.tr/canli-?yayin 27 | | 28 | showturk\.com\.tr/canli-?yayin(?:/showtv)? 29 | | 30 | showtv\.com\.tr/canli-yayin(?:/showtv)? 31 | )/? 32 | """, re.VERBOSE)) 33 | class CinerGroup(Plugin): 34 | @staticmethod 35 | def _schema_videourl(): 36 | return validate.Schema( 37 | validate.xml_xpath_string(".//script[contains(text(), 'videoUrl')]/text()"), 38 | validate.none_or_all( 39 | re.compile(r"""(?['"])(?P.+?)(?P=q)"""), 40 | validate.none_or_all( 41 | validate.get("url"), 42 | validate.url(), 43 | ), 44 | ), 45 | ) 46 | 47 | @staticmethod 48 | def _schema_data_ht(): 49 | return validate.Schema( 50 | validate.xml_xpath_string(".//div[@data-ht][1]/@data-ht"), 51 | validate.none_or_all( 52 | validate.parse_json(), 53 | { 54 | "ht_stream_m3u8": validate.url(), 55 | }, 56 | validate.get("ht_stream_m3u8"), 57 | ), 58 | ) 59 | 60 | def _get_streams(self): 61 | root = self.session.http.get(self.url, schema=validate.Schema(validate.parse_html())) 62 | schema_getters = self._schema_videourl, self._schema_data_ht 63 | stream_url = next((res for res in (getter().validate(root) for getter in schema_getters) if res), None) 64 | 65 | if stream_url: 66 | return HLSStream.parse_variant_playlist(self.session, stream_url) 67 | 68 | 69 | __plugin__ = CinerGroup 70 | -------------------------------------------------------------------------------- /win/streamlink/plugins/cnews.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description French free-to-air news channel, providing 24-hour national and global news coverage. 3 | $url cnews.fr 4 | $type live, vod 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.plugin.api import validate 11 | 12 | 13 | @pluginmatcher(re.compile( 14 | r"https?://(?:www\.)?cnews\.fr", 15 | )) 16 | class CNEWS(Plugin): 17 | _dailymotion_url = "https://www.dailymotion.com/embed/video/{}" 18 | 19 | def _get_streams(self): 20 | data = self.session.http.get(self.url, schema=validate.Schema( 21 | re.compile(r"jQuery\.extend\(Drupal\.settings, ({.*})\);"), 22 | validate.none_or_all( 23 | validate.get(1), 24 | validate.parse_json(), 25 | { 26 | validate.optional("dm_player_live_dailymotion"): { 27 | validate.optional("video_id"): str, 28 | }, 29 | validate.optional("dm_player_node_dailymotion"): { 30 | validate.optional("video_id"): str, 31 | }, 32 | }, 33 | validate.union_get("dm_player_live_dailymotion", "dm_player_node_dailymotion"), 34 | ), 35 | )) 36 | if not data: 37 | return 38 | 39 | live, node = data 40 | 41 | if node: 42 | return self.session.streams(self._dailymotion_url.format(node["video_id"])) 43 | elif live: 44 | return self.session.streams(self._dailymotion_url.format(live["video_id"])) 45 | 46 | 47 | __plugin__ = CNEWS 48 | -------------------------------------------------------------------------------- /win/streamlink/plugins/dash.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import re 3 | 4 | from streamlink.plugin import Plugin, pluginmatcher 5 | from streamlink.plugin.plugin import LOW_PRIORITY, parse_params, stream_weight 6 | from streamlink.stream.dash import DASHStream 7 | from streamlink.utils.url import update_scheme 8 | 9 | 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | @pluginmatcher(re.compile( 14 | r"dash://(?P\S+)(?:\s(?P.+))?", 15 | )) 16 | @pluginmatcher(priority=LOW_PRIORITY, pattern=re.compile( 17 | r"(?P\S+\.mpd(?:\?\S*)?)(?:\s(?P.+))?", 18 | )) 19 | class MPEGDASH(Plugin): 20 | @classmethod 21 | def stream_weight(cls, stream): 22 | match = re.match(r"^(?:(.*)\+)?(?:a(\d+)k)$", stream) 23 | if match and match.group(1) and match.group(2): 24 | weight, group = stream_weight(match.group(1)) 25 | weight += int(match.group(2)) 26 | return weight, group 27 | elif match and match.group(2): 28 | return stream_weight(f"{match.group(2)}k") 29 | else: 30 | return stream_weight(stream) 31 | 32 | def _get_streams(self): 33 | data = self.match.groupdict() 34 | url = update_scheme("https://", data.get("url"), force=False) 35 | params = parse_params(data.get("params")) 36 | log.debug(f"URL={url}; params={params}") 37 | 38 | return DASHStream.parse_manifest(self.session, url, **params) 39 | 40 | 41 | __plugin__ = MPEGDASH 42 | -------------------------------------------------------------------------------- /win/streamlink/plugins/dogus.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Turkish live TV channels from Dogus Group, including Euro Star, Star and NTV. 3 | $url eurostartv.com.tr 4 | $url kralmuzik.com.tr 5 | $url ntv.com.tr 6 | $url startv.com.tr 7 | $type live 8 | """ 9 | 10 | import re 11 | 12 | from streamlink.plugin import Plugin, pluginmatcher 13 | from streamlink.plugin.api import validate 14 | from streamlink.stream.hls import HLSStream 15 | 16 | 17 | @pluginmatcher(re.compile(r"https?://(?:www\.)?eurostartv\.com\.tr/canli-izle")) 18 | @pluginmatcher(re.compile(r"https?://(?:www\.)?kralmuzik\.com\.tr/tv/.+")) 19 | @pluginmatcher(re.compile(r"https?://(?:www\.)?ntv\.com\.tr/canli-yayin/ntv")) 20 | @pluginmatcher(re.compile(r"https?://(?:www\.)?startv\.com\.tr/canli-yayin")) 21 | class Dogus(Plugin): 22 | _re_live_hls = re.compile(r"'(https?://[^']+/live/hls/[^']+)'") 23 | _re_yt_script = re.compile(r"youtube\.init\('([\w-]{11})'") 24 | 25 | def _get_streams(self): 26 | root = self.session.http.get(self.url, schema=validate.Schema(validate.parse_html())) 27 | 28 | # https://www.ntv.com.tr/canli-yayin/ntv?youtube=true 29 | yt_iframe = root.xpath("string(.//iframe[contains(@src,'youtube.com')][1]/@src)") 30 | # https://www.startv.com.tr/canli-yayin 31 | dm_iframe = root.xpath("string(.//iframe[contains(@src,'dailymotion.com')][1]/@src)") 32 | # https://www.kralmuzik.com.tr/tv/kral-tv 33 | # https://www.kralmuzik.com.tr/tv/kral-pop-tv 34 | yt_script = root.xpath("string(.//script[contains(text(), 'youtube.init')][1]/text())") 35 | if yt_script: 36 | m = self._re_yt_script.search(yt_script) 37 | if m: 38 | yt_iframe = f"https://www.youtube.com/watch?v={m.group(1)}" 39 | 40 | iframe = yt_iframe or dm_iframe 41 | if iframe: 42 | return self.session.streams(iframe) 43 | 44 | # http://eurostartv.com.tr/canli-izle 45 | dd_script = root.xpath("string(.//script[contains(text(), '/live/hls/')][1]/text())") 46 | if dd_script: 47 | m = self._re_live_hls.search(dd_script) 48 | if m: 49 | return HLSStream.parse_variant_playlist(self.session, m.group(1)) 50 | 51 | 52 | __plugin__ = Dogus 53 | -------------------------------------------------------------------------------- /win/streamlink/plugins/drdk.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels from DR, a Danish public, state-owned broadcaster. 3 | $url dr.dk 4 | $type live 5 | $region Denmark 6 | """ 7 | 8 | import logging 9 | import re 10 | 11 | from streamlink.plugin import Plugin, pluginmatcher 12 | from streamlink.plugin.api import validate 13 | from streamlink.stream.hls import HLSStream 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | @pluginmatcher(re.compile( 20 | r"https?://(?:www\.)?dr\.dk/drtv(/kanal/[\w-]+)", 21 | )) 22 | class DRDK(Plugin): 23 | live_api_url = "https://www.dr-massive.com/api/page" 24 | 25 | _live_data_schema = validate.Schema( 26 | {"item": {"customFields": { 27 | validate.optional("hlsURL"): validate.url(), 28 | validate.optional("hlsWithSubtitlesURL"): validate.url(), 29 | }}}, 30 | validate.get("item"), 31 | validate.get("customFields"), 32 | ) 33 | 34 | def _get_live(self, path): 35 | params = dict( 36 | ff="idp", 37 | path=path, 38 | ) 39 | res = self.session.http.get(self.live_api_url, params=params) 40 | playlists = self.session.http.json(res, schema=self._live_data_schema) 41 | 42 | streams = {} 43 | for name, url in playlists.items(): 44 | name_prefix = "" 45 | if name == "hlsWithSubtitlesURL": 46 | name_prefix = "subtitled_" 47 | 48 | streams.update(HLSStream.parse_variant_playlist( 49 | self.session, 50 | url, 51 | name_prefix=name_prefix, 52 | )) 53 | 54 | return streams 55 | 56 | def _get_streams(self): 57 | path = self.match.group(1) 58 | log.debug("Path={0}".format(path)) 59 | 60 | return self._get_live(path) 61 | 62 | 63 | __plugin__ = DRDK 64 | -------------------------------------------------------------------------------- /win/streamlink/plugins/foxtr.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Turkish live TV channel owned by Fox Network. 3 | $url fox.com.tr 4 | $type live 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.plugin.api import validate 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | @pluginmatcher(re.compile( 15 | r"https?://(?:www\.)?fox(?:play)?\.com\.tr/", 16 | )) 17 | class FoxTR(Plugin): 18 | def _get_streams(self): 19 | re_streams = re.compile(r"""(['"])(?Phttps://\S+/foxtv\.m3u8\S+)\1""") 20 | res = self.session.http.get(self.url, schema=validate.Schema( 21 | validate.transform(re_streams.findall), 22 | )) 23 | for _, stream_url in res: 24 | return HLSStream.parse_variant_playlist(self.session, stream_url) 25 | 26 | 27 | __plugin__ = FoxTR 28 | -------------------------------------------------------------------------------- /win/streamlink/plugins/galatasaraytv.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Turkish live TV channel owned by Galatasaray TV. 3 | $url galatasaray.com 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | log = logging.getLogger(__name__) 15 | 16 | 17 | @pluginmatcher(re.compile( 18 | r"https?://(?:www\.)?galatasaray\.com", 19 | )) 20 | class GalatasarayTV(Plugin): 21 | playervars_re = re.compile(r"sources\s*:\s*\[\s*\{\s*type\s*:\s*\"(.*?)\",\s*src\s*:\s*\"(.*?)\"", re.DOTALL) 22 | 23 | title = "Galatasaray TV" 24 | 25 | def _get_streams(self): 26 | res = self.session.http.get(self.url) 27 | match = self.playervars_re.search(res.text) 28 | if match: 29 | stream_url = match.group(2) 30 | log.debug("URL={0}".format(stream_url)) 31 | return HLSStream.parse_variant_playlist(self.session, stream_url) 32 | 33 | 34 | __plugin__ = GalatasarayTV 35 | -------------------------------------------------------------------------------- /win/streamlink/plugins/goltelevision.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Spanish live TV sports channel owned by Gol Network. 3 | $url goltelevision.com 4 | $type live 5 | $region Spain 6 | """ 7 | 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream 13 | 14 | 15 | @pluginmatcher(re.compile( 16 | r"https?://(?:www\.)?goltelevision\.com/en-directo", 17 | )) 18 | class GOLTelevision(Plugin): 19 | def _get_streams(self): 20 | self.session.http.headers.update({ 21 | "Origin": "https://goltelevision.com", 22 | "Referer": "https://goltelevision.com/", 23 | }) 24 | url = self.session.http.get( 25 | "https://play.goltelevision.com/api/stream/live", 26 | schema=validate.Schema( 27 | validate.parse_json(), 28 | {"manifest": validate.url()}, 29 | validate.get("manifest"), 30 | ), 31 | ) 32 | return HLSStream.parse_variant_playlist(self.session, url) 33 | 34 | 35 | __plugin__ = GOLTelevision 36 | -------------------------------------------------------------------------------- /win/streamlink/plugins/goodgame.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Russian live streaming platform for live video game broadcasts. 3 | $url goodgame.ru 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.stream.hls import HLSStream 12 | from streamlink.utils.parse import parse_json 13 | 14 | 15 | log = logging.getLogger(__name__) 16 | 17 | HLS_URL_FORMAT = "https://hls.goodgame.ru/hls/{0}{1}.m3u8" 18 | QUALITIES = { 19 | "1080p": "", 20 | "720p": "_720", 21 | "480p": "_480", 22 | "240p": "_240", 23 | } 24 | 25 | _apidata_re = re.compile(r"""(?P["']?)channel(?P=quote)\s*:\s*(?P{.*?})\s*,""") 26 | _ddos_re = re.compile(r'document.cookie="(__DDOS_[^;]+)') 27 | 28 | 29 | @pluginmatcher(re.compile( 30 | r"https?://(?:www\.)?goodgame\.ru/channel/(?P[^/]+)", 31 | )) 32 | class GoodGame(Plugin): 33 | def _check_stream(self, url): 34 | res = self.session.http.get(url, acceptable_status=(200, 404)) 35 | if res.status_code == 200: 36 | return True 37 | 38 | def _get_streams(self): 39 | headers = { 40 | "Referer": self.url, 41 | } 42 | res = self.session.http.get(self.url, headers=headers) 43 | 44 | match = _ddos_re.search(res.text) 45 | if match: 46 | log.debug("Anti-DDOS bypass...") 47 | headers["Cookie"] = match.group(1) 48 | res = self.session.http.get(self.url, headers=headers) 49 | 50 | match = _apidata_re.search(res.text) 51 | channel_info = match and parse_json(match.group("data")) 52 | if not channel_info: 53 | log.error("Could not find channel info") 54 | return 55 | 56 | log.debug("Found channel info: id={id} channelkey={channelkey} pid={streamkey} online={status}".format(**channel_info)) 57 | if not channel_info["status"]: 58 | log.debug("Channel appears to be offline") 59 | 60 | streams = {} 61 | for name, url_suffix in QUALITIES.items(): 62 | url = HLS_URL_FORMAT.format(channel_info["streamkey"], url_suffix) 63 | if not self._check_stream(url): 64 | continue 65 | 66 | streams[name] = HLSStream(self.session, url) 67 | 68 | return streams 69 | 70 | 71 | __plugin__ = GoodGame 72 | -------------------------------------------------------------------------------- /win/streamlink/plugins/googledrive.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Stream videos stored in Google Drive. 3 | $url docs.google.com 4 | $url drive.google.com 5 | $type vod 6 | """ 7 | 8 | import logging 9 | import re 10 | from urllib.parse import parse_qsl 11 | 12 | from streamlink.plugin import Plugin, pluginmatcher 13 | from streamlink.stream.http import HTTPStream 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | @pluginmatcher(re.compile( 20 | r"https?://(?:drive|docs)\.google\.com/file/d/([^/]+)/?", 21 | )) 22 | class GoogleDocs(Plugin): 23 | api_url = "https://docs.google.com/get_video_info" 24 | 25 | def _get_streams(self): 26 | docid = self.match.group(1) 27 | log.debug("Google Docs ID: {0}".format(docid)) 28 | res = self.session.http.get(self.api_url, params=dict(docid=docid)) 29 | data = dict(parse_qsl(res.text)) 30 | 31 | if data["status"] == "ok": 32 | fmts = dict([s.split("/")[:2] for s in data["fmt_list"].split(",")]) 33 | streams = [s.split("|") for s in data["fmt_stream_map"].split(",")] 34 | for qcode, url in streams: 35 | _, h = fmts[qcode].split("x") 36 | yield "{0}p".format(h), HTTPStream(self.session, url) 37 | else: 38 | log.error("{0} (ID: {1})".format(data["reason"], docid)) 39 | 40 | 41 | __plugin__ = GoogleDocs 42 | -------------------------------------------------------------------------------- /win/streamlink/plugins/hls.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import re 3 | 4 | from streamlink.plugin import Plugin, pluginmatcher 5 | from streamlink.plugin.plugin import LOW_PRIORITY, parse_params 6 | from streamlink.stream.hls import HLSStream 7 | from streamlink.utils.url import update_scheme 8 | 9 | 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | @pluginmatcher(re.compile( 14 | r"hls(?:variant)?://(?P\S+)(?:\s(?P.+))?", 15 | )) 16 | @pluginmatcher(priority=LOW_PRIORITY, pattern=re.compile( 17 | r"(?P\S+\.m3u8(?:\?\S*)?)(?:\s(?P.+))?", 18 | )) 19 | class HLSPlugin(Plugin): 20 | def _get_streams(self): 21 | data = self.match.groupdict() 22 | url = update_scheme("https://", data.get("url"), force=False) 23 | params = parse_params(data.get("params")) 24 | log.debug(f"URL={url}; params={params}") 25 | 26 | streams = HLSStream.parse_variant_playlist(self.session, url, **params) 27 | 28 | return streams or {"live": HLSStream(self.session, url, **params)} 29 | 30 | 31 | __plugin__ = HLSPlugin 32 | -------------------------------------------------------------------------------- /win/streamlink/plugins/http.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import re 3 | 4 | from streamlink.plugin import Plugin, pluginmatcher 5 | from streamlink.plugin.plugin import parse_params 6 | from streamlink.stream.http import HTTPStream 7 | from streamlink.utils.url import update_scheme 8 | 9 | 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | @pluginmatcher(re.compile( 14 | r"httpstream://(?P\S+)(?:\s(?P.+))?", 15 | )) 16 | class HTTPStreamPlugin(Plugin): 17 | def _get_streams(self): 18 | data = self.match.groupdict() 19 | url = update_scheme("https://", data.get("url"), force=False) 20 | params = parse_params(data.get("params")) 21 | log.debug(f"URL={url}; params={params}") 22 | 23 | return {"live": HTTPStream(self.session, url, **params)} 24 | 25 | 26 | __plugin__ = HTTPStreamPlugin 27 | -------------------------------------------------------------------------------- /win/streamlink/plugins/idf1.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description French live TV channel and video on-demand service owned by IDF1. 3 | $url idf1.fr 4 | $type live, vod 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream 13 | 14 | 15 | log = logging.getLogger(__name__) 16 | 17 | 18 | @pluginmatcher(re.compile( 19 | r"https?://www\.idf1\.fr/(videos/[^/]+/[^/]+\.html|live\b)", 20 | )) 21 | class IDF1(Plugin): 22 | def _get_streams(self): 23 | self.id, self.title = self.session.http.get( 24 | self.url, 25 | schema=validate.Schema( 26 | validate.parse_html(), 27 | validate.union(( 28 | validate.xml_xpath_string(".//script[@class='dacast-video'][@id]/@id"), 29 | validate.xml_xpath_string(".//head/title[1]/text()"), 30 | )), 31 | ), 32 | ) 33 | 34 | if not self.id: 35 | return 36 | 37 | if re.match(r"\w+_\w+_\w+", self.id): 38 | provider = "dacast" 39 | else: 40 | provider = "universe" 41 | 42 | data = self.session.http.get( 43 | f"https://playback.dacast.com/content/access?contentId={self.id}&provider={provider}", 44 | acceptable_status=(200, 400, 403, 404), 45 | schema=validate.Schema( 46 | validate.parse_json(), 47 | validate.any( 48 | {"error": str}, 49 | {"hls": validate.url()}, 50 | ), 51 | ), 52 | ) 53 | 54 | if data.get("error"): 55 | log.error(data["error"]) 56 | return 57 | 58 | return HLSStream.parse_variant_playlist(self.session, data["hls"]) 59 | 60 | 61 | __plugin__ = IDF1 62 | -------------------------------------------------------------------------------- /win/streamlink/plugins/indihometv.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels and video on-demand service from IndiHome TV, owned by Telkom Indonesia. 3 | $url indihometv.com 4 | $type live, vod 5 | $region Indonesia 6 | """ 7 | 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.dash import DASHStream 13 | from streamlink.stream.hls import HLSStream 14 | 15 | 16 | @pluginmatcher(re.compile(r"https?://(?:www\.)?indihometv\.com/")) 17 | class IndiHomeTV(Plugin): 18 | def _get_streams(self): 19 | url = self.session.http.get(self.url, schema=validate.Schema( 20 | validate.parse_html(), 21 | validate.any( 22 | validate.all( 23 | validate.xml_xpath_string(""" 24 | .//script[contains(text(), 'laylist.m3u8') or contains(text(), 'manifest.mpd')][1]/text() 25 | """), 26 | str, 27 | re.compile(r"""(?P['"])(?Phttps://.*?/(?:[Pp]laylist\.m3u8|manifest\.mpd).+?)(?P=q)"""), 28 | validate.none_or_all( 29 | validate.get("url"), 30 | validate.url(), 31 | ), 32 | ), 33 | validate.all( 34 | validate.xml_xpath_string(".//video[@id='video-player'][1]/source[1]/@src"), 35 | validate.none_or_all( 36 | validate.url(), 37 | ), 38 | ), 39 | ), 40 | )) 41 | 42 | if url and ".m3u8" in url: 43 | return HLSStream.parse_variant_playlist(self.session, url) 44 | elif url and ".mpd" in url: 45 | return DASHStream.parse_manifest(self.session, url) 46 | 47 | 48 | __plugin__ = IndiHomeTV 49 | -------------------------------------------------------------------------------- /win/streamlink/plugins/invintus.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Global live streaming and video on-demand hosting platform. 3 | $url player.invintus.com 4 | $type live, vod 5 | """ 6 | 7 | import json 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream 13 | from streamlink.utils.url import update_scheme 14 | 15 | 16 | @pluginmatcher(re.compile( 17 | r"https?://player\.invintus\.com/\?clientID=(\d+)&eventID=(\d+)", 18 | )) 19 | class InvintusMedia(Plugin): 20 | WSC_API_KEY = "7WhiEBzijpritypp8bqcU7pfU9uicDR" # hard coded in the middle of https://player.invintus.com/app.js 21 | API_URL = "https://api.v3.invintusmedia.com/v2/Event/getDetailed" 22 | 23 | api_response_schema = validate.Schema({"data": {"streamingURIs": {"main": validate.url()}}}) 24 | 25 | def _get_streams(self): 26 | postdata = { 27 | "clientID": self.match.group(1), 28 | "showEncoder": True, 29 | "showMediaAssets": True, 30 | "showStreams": True, 31 | "includePrivate": False, 32 | "advancedDetails": True, 33 | "VAST": True, 34 | "eventID": self.match.group(2), 35 | } 36 | headers = { 37 | "Content-Type": "application/json", 38 | "wsc-api-key": self.WSC_API_KEY, 39 | "Authorization": "embedder", 40 | } 41 | res = self.session.http.post(self.API_URL, data=json.dumps(postdata), headers=headers) 42 | api_response = self.session.http.json(res, schema=self.api_response_schema) 43 | if api_response is None: 44 | return 45 | 46 | hls_url = api_response["data"]["streamingURIs"]["main"] 47 | return HLSStream.parse_variant_playlist(self.session, update_scheme("https://", hls_url)) 48 | 49 | 50 | __plugin__ = InvintusMedia 51 | -------------------------------------------------------------------------------- /win/streamlink/plugins/linelive.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Japanese live-streaming and video hosting social platform. 3 | $url live.line.me 4 | $type live, vod 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.plugin.api import validate 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | @pluginmatcher(re.compile( 15 | r"https?://live\.line\.me/channels/(?P\d+)/broadcast/(?P\d+)", 16 | )) 17 | class LineLive(Plugin): 18 | _URL_API = "https://live-api.line-apps.com/web/v4.0/channel/{channel}/broadcast/{broadcast}/player_status" 19 | 20 | def _get_streams(self): 21 | channel = self.match.group("channel") 22 | broadcast = self.match.group("broadcast") 23 | 24 | schema_hls_urls = validate.any(None, { 25 | str: validate.any(None, validate.url(path=validate.endswith(".m3u8"))), 26 | }) 27 | 28 | status, liveUrls, vodUrls = self.session.http.get( 29 | self._URL_API.format(channel=channel, broadcast=broadcast), 30 | schema=validate.Schema( 31 | validate.parse_json(), 32 | { 33 | "liveStatus": str, 34 | "liveHLSURLs": schema_hls_urls, 35 | "archivedHLSURLs": schema_hls_urls, 36 | }, 37 | validate.union_get("liveStatus", "liveHLSURLs", "archivedHLSURLs"), 38 | ), 39 | ) 40 | streams = {"LIVE": liveUrls, "FINISHED": vodUrls}.get(status, {}) 41 | 42 | if streams.get("abr"): 43 | return HLSStream.parse_variant_playlist(self.session, streams.get("abr")) 44 | 45 | return { 46 | f"{quality}p": HLSStream(self.session, url) 47 | for quality, url in streams.items() 48 | if url and quality.isdecimal() 49 | } 50 | 51 | 52 | __plugin__ = LineLive 53 | -------------------------------------------------------------------------------- /win/streamlink/plugins/lnk.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Lithuanian live TV channels from LNK Group, including 2TV, BTV, Info TV, LNK and TV1. 3 | $url lnk.lt 4 | $type live 5 | $region Lithuania 6 | """ 7 | 8 | import logging 9 | import re 10 | 11 | from streamlink.plugin import Plugin, pluginmatcher 12 | from streamlink.plugin.api import validate 13 | from streamlink.stream.hls import HLSStream 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | @pluginmatcher(re.compile( 20 | r"https?://(?:www\.)?lnk\.lt/tiesiogiai(?:#(?P[a-z0-9]+))?", 21 | )) 22 | class LNK(Plugin): 23 | API_URL = "https://lnk.lt/api/video/video-config/{0}" 24 | 25 | CHANNEL_MAP = { 26 | "lnk": 137535, 27 | "btv": 137534, 28 | "2tv": 95343, 29 | "infotv": 137748, 30 | "tv1": 106791, 31 | } 32 | 33 | def _get_streams(self): 34 | channel = self.match.groupdict().get("channel") or "lnk" 35 | if channel not in self.CHANNEL_MAP: 36 | log.error(f"Unknown channel: {channel}") 37 | return 38 | 39 | self.id = self.CHANNEL_MAP.get(channel) 40 | self.author, self.category, self.title, hls_url = self.session.http.get( 41 | self.API_URL.format(self.id), 42 | schema=validate.Schema( 43 | validate.parse_json(), 44 | {"videoInfo": { 45 | "channel": str, 46 | "genre": validate.any(None, str), 47 | "title": validate.any(None, str), 48 | "videoUrl": validate.any( 49 | "", 50 | validate.url(path=validate.endswith(".m3u8")), 51 | ), 52 | }}, 53 | validate.get("videoInfo"), 54 | validate.union_get("channel", "genre", "title", "videoUrl"), 55 | ), 56 | ) 57 | if not hls_url: 58 | log.error("The stream is not available in your region") 59 | return 60 | 61 | return HLSStream.parse_variant_playlist(self.session, hls_url) 62 | 63 | 64 | __plugin__ = LNK 65 | -------------------------------------------------------------------------------- /win/streamlink/plugins/lrt.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels from LRT, a Lithuanian public, state-owned broadcaster. 3 | $url lrt.lt 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | log = logging.getLogger(__name__) 15 | 16 | 17 | @pluginmatcher(re.compile( 18 | r"https?://(?:www\.)?lrt\.lt/mediateka/tiesiogiai/", 19 | )) 20 | class LRT(Plugin): 21 | _video_id_re = re.compile(r"""var\svideo_id\s*=\s*["'](?P\w+)["']""") 22 | API_URL = "https://www.lrt.lt/servisai/stream_url/live/get_live_url.php?channel={0}" 23 | 24 | def _get_streams(self): 25 | page = self.session.http.get(self.url) 26 | m = self._video_id_re.search(page.text) 27 | if m: 28 | video_id = m.group("video_id") 29 | data = self.session.http.get(self.API_URL.format(video_id)).json() 30 | hls_url = data["response"]["data"]["content"] 31 | 32 | yield from HLSStream.parse_variant_playlist(self.session, hls_url).items() 33 | else: 34 | log.debug("No match for video_id regex") 35 | 36 | 37 | __plugin__ = LRT 38 | -------------------------------------------------------------------------------- /win/streamlink/plugins/mrtmk.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels and video on-demand service from MRT, a North Macedonian public, state-owned broadcaster. 3 | $url play.mrt.com.mk 4 | $type live, vod 5 | $region North Macedonia 6 | """ 7 | 8 | import logging 9 | import re 10 | 11 | from streamlink.plugin import Plugin, pluginmatcher 12 | from streamlink.plugin.api import validate 13 | from streamlink.stream.hls import HLSStream 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | @pluginmatcher(re.compile( 20 | r"https?://play\.mrt\.com\.mk/(live|play)/", 21 | )) 22 | class MRTmk(Plugin): 23 | file_re = re.compile(r"""(?Phttps?://vod-[\d\w]+\.interspace\.com[^"',]+\.m3u8[^"',]*)""") 24 | 25 | stream_schema = validate.Schema( 26 | validate.all( 27 | validate.transform(file_re.finditer), 28 | validate.transform(list), 29 | [validate.get("url")], 30 | # remove duplicates 31 | validate.transform(set), 32 | validate.transform(list), 33 | ), 34 | ) 35 | 36 | def _get_streams(self): 37 | res = self.session.http.get(self.url) 38 | stream_urls = self.stream_schema.validate(res.text) 39 | log.debug("Found streams: {0}".format(len(stream_urls))) 40 | if not stream_urls: 41 | return 42 | 43 | for stream_url in stream_urls: 44 | try: 45 | yield from HLSStream.parse_variant_playlist(self.session, stream_url).items() 46 | except OSError as err: 47 | if "403 Client Error" in str(err): 48 | log.error("Failed to access stream, may be due to geo-restriction") 49 | else: 50 | raise err 51 | 52 | 53 | __plugin__ = MRTmk 54 | -------------------------------------------------------------------------------- /win/streamlink/plugins/nhkworld.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Current affairs and cultural channel owned by NHK, a Japanese public, state-owned broadcaster. 3 | $url nhk.or.jp/nhkworld 4 | $type live 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.stream.hls import HLSStream 11 | 12 | 13 | API_URL = "http://{}.nhk.or.jp/nhkworld/app/tv/hlslive_web.json" 14 | 15 | 16 | @pluginmatcher(re.compile( 17 | r"https?://(?:(\w+)\.)?nhk\.or\.jp/nhkworld", 18 | )) 19 | class NHKWorld(Plugin): 20 | def _get_streams(self): 21 | # get the HLS json from the same sub domain as the main url, defaulting to www 22 | sdomain = self.match.group(1) or "www" 23 | res = self.session.http.get(API_URL.format(sdomain)) 24 | 25 | stream_url = self.session.http.json(res)["main"]["wstrm"] 26 | return HLSStream.parse_variant_playlist(self.session, stream_url) 27 | 28 | 29 | __plugin__ = NHKWorld 30 | -------------------------------------------------------------------------------- /win/streamlink/plugins/ntv.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Russian live TV channel owned by Gazprom Media. 3 | $url ntv.ru 4 | $type live 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.stream.hls import HLSStream 11 | 12 | 13 | @pluginmatcher(re.compile( 14 | r"https?://www\.ntv\.ru/air/", 15 | )) 16 | class NTV(Plugin): 17 | def _get_streams(self): 18 | body = self.session.http.get(self.url).text 19 | mrl = None 20 | match = re.search(r"var camHlsURL = \'(.*)\'", body) 21 | if match: 22 | mrl = f"http:{match.group(1)}" 23 | else: 24 | match = re.search(r"var hlsURL = \'(.*)\'", body) 25 | if match: 26 | mrl = match.group(1) 27 | if mrl: 28 | return HLSStream.parse_variant_playlist(self.session, mrl) 29 | 30 | 31 | __plugin__ = NTV 32 | -------------------------------------------------------------------------------- /win/streamlink/plugins/onetv.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description A state/privately owned Russian live TV channel. 3 | $url 1tv.ru 4 | $type live 5 | $region Russia 6 | """ 7 | 8 | import logging 9 | import random 10 | import re 11 | from urllib.parse import unquote 12 | 13 | from streamlink.plugin import Plugin, pluginmatcher 14 | from streamlink.plugin.api import validate 15 | from streamlink.stream.hls import HLSStream 16 | from streamlink.utils.url import update_qsd 17 | 18 | 19 | log = logging.getLogger(__name__) 20 | 21 | 22 | @pluginmatcher(re.compile( 23 | r"https?://(?:www\.)?1tv\.ru/live", 24 | )) 25 | @pluginmatcher(re.compile( 26 | r"https?://static\.1tv\.ru/eump/(?:embeds|pages)/1tv_live(?:_orbit-plus-4)?\.html", 27 | )) 28 | class OneTV(Plugin): 29 | def _get_streams(self): 30 | if "orbit-plus-4" in self.url: 31 | channel = "1tv-orbit-plus-4" 32 | self.title = "Первый канал HD (+4)" 33 | else: 34 | channel = "1tvch" 35 | self.title = "Первый канал HD" 36 | 37 | url = self.session.http.get( 38 | f"https://stream.1tv.ru/api/playlist/{channel}_as_array.json", 39 | data={"r": random.randint(1, 100000)}, 40 | schema=validate.Schema( 41 | validate.parse_json(), 42 | {"hls": [validate.url()]}, 43 | validate.get(("hls", 0)), 44 | )) 45 | 46 | if not url: 47 | return 48 | 49 | log.debug(f"{url}") 50 | if "georestrictions" in url: 51 | log.error("Stream is geo-restricted") 52 | return 53 | 54 | hls_session = self.session.http.get( 55 | "https://stream.1tv.ru/get_hls_session", 56 | schema=validate.Schema( 57 | validate.parse_json(), 58 | {"s": validate.transform(unquote)}, 59 | )) 60 | url = update_qsd(url, qsd=hls_session, safe="/:") 61 | 62 | return HLSStream.parse_variant_playlist(self.session, url, name_fmt="{pixels}_{bitrate}") 63 | 64 | 65 | __plugin__ = OneTV 66 | -------------------------------------------------------------------------------- /win/streamlink/plugins/piczel.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Global live-streaming platform for the creative community. 3 | $url piczel.tv 4 | $type live 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.plugin.api import validate 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | @pluginmatcher(re.compile( 15 | r"https?://piczel\.tv/watch/(?P\w+)", 16 | )) 17 | class Piczel(Plugin): 18 | _URL_STREAMS = "https://piczel.tv/api/streams" 19 | _URL_HLS = "https://piczel.tv/hls/{id}/index.m3u8" 20 | 21 | def _get_streams(self): 22 | channel = self.match.group("channel") 23 | 24 | data = self.session.http.get( 25 | self._URL_STREAMS, 26 | params={ 27 | "followedStreams": "false", 28 | "live_only": "false", 29 | "sfw": "false", 30 | }, 31 | schema=validate.Schema( 32 | validate.parse_json(), 33 | [{ 34 | "slug": str, 35 | "live": bool, 36 | "id": int, 37 | "username": str, 38 | "title": str, 39 | }], 40 | validate.filter(lambda item: item["slug"] == channel), 41 | validate.get(0), 42 | validate.any(None, validate.union_get( 43 | "id", 44 | "username", 45 | "title", 46 | "live", 47 | )), 48 | ), 49 | ) 50 | if not data: 51 | return 52 | 53 | self.id, self.author, self.title, is_live = data 54 | if not is_live: 55 | return 56 | 57 | return {"live": HLSStream(self.session, self._URL_HLS.format(id=self.id))} 58 | 59 | 60 | __plugin__ = Piczel 61 | -------------------------------------------------------------------------------- /win/streamlink/plugins/rtpa.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels and video on-demand service from RTPA, a Spanish public broadcaster. 3 | $url rtpa.es 4 | $type live, vod 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.plugin.api import validate 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | @pluginmatcher(re.compile(r"https?://(?:www\.)?rtpa\.es")) 15 | class RTPA(Plugin): 16 | def _get_streams(self): 17 | hls_url, self.title = self.session.http.get(self.url, schema=validate.Schema( 18 | validate.parse_html(), 19 | validate.union(( 20 | validate.xml_xpath_string(".//video/source[@src][@type='application/x-mpegURL'][1]/@src"), 21 | validate.xml_xpath_string(".//head/title[1]/text()"), 22 | )), 23 | )) 24 | if not hls_url: 25 | return 26 | return HLSStream.parse_variant_playlist(self.session, hls_url, headers={"Referer": self.url}) 27 | 28 | 29 | __plugin__ = RTPA 30 | -------------------------------------------------------------------------------- /win/streamlink/plugins/rtvs.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels from RTVS, a Slovak public, state-owned broadcaster. 3 | $url rtvs.sk 4 | $type live 5 | $region Slovakia 6 | """ 7 | 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream 13 | from streamlink.utils.parse import parse_json 14 | 15 | 16 | @pluginmatcher(re.compile( 17 | r"https?://www\.rtvs\.sk/televizia/live-[\w-]+", 18 | )) 19 | class Rtvs(Plugin): 20 | _re_channel_id = re.compile(r"'stream':\s*'live-(\d+)'") 21 | 22 | def _get_streams(self): 23 | res = self.session.http.get(self.url) 24 | m = self._re_channel_id.search(res.text) 25 | if not m: 26 | return 27 | 28 | res = self.session.http.get( 29 | "https://www.rtvs.sk/json/live5f.json", 30 | params={ 31 | "c": m.group(1), 32 | "b": "mozilla", 33 | "p": "win", 34 | "f": "0", 35 | "d": "1", 36 | }, 37 | ) 38 | videos = parse_json(res.text, schema=validate.Schema({ 39 | "clip": { 40 | "sources": [{ 41 | "src": validate.url(), 42 | "type": str, 43 | }], 44 | }}, 45 | validate.get(("clip", "sources")), 46 | validate.filter(lambda n: n["type"] == "application/x-mpegurl"), 47 | )) 48 | for video in videos: 49 | yield from HLSStream.parse_variant_playlist(self.session, video["src"]).items() 50 | 51 | 52 | __plugin__ = Rtvs 53 | -------------------------------------------------------------------------------- /win/streamlink/plugins/sportal.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Sporting channel live stream owned by Sportal, a Bulgarian sports media website. 3 | $url sportal.bg 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | log = logging.getLogger(__name__) 15 | 16 | 17 | @pluginmatcher(re.compile( 18 | r"https?://(?:www\.)?sportal\.bg/sportal_live_tv\.php", 19 | )) 20 | class Sportal(Plugin): 21 | _hls_re = re.compile(r"""["'](?P[^"']+\.m3u8[^"']*?)["']""") 22 | 23 | def _get_streams(self): 24 | res = self.session.http.get(self.url) 25 | m = self._hls_re.search(res.text) 26 | if not m: 27 | return 28 | 29 | hls_url = m.group("url") 30 | log.debug("URL={0}".format(hls_url)) 31 | log.warning("SSL certificate verification is disabled.") 32 | return HLSStream.parse_variant_playlist( 33 | self.session, hls_url, verify=False).items() 34 | 35 | 36 | __plugin__ = Sportal 37 | -------------------------------------------------------------------------------- /win/streamlink/plugins/sportschau.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description German sports magazine live stream, owned by ARD. 3 | $url sportschau.de 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream 13 | from streamlink.stream.http import HTTPStream 14 | from streamlink.utils.url import update_scheme 15 | 16 | 17 | log = logging.getLogger(__name__) 18 | 19 | 20 | @pluginmatcher(re.compile( 21 | r"https?://(?:\w+\.)*sportschau\.de/", 22 | )) 23 | class Sportschau(Plugin): 24 | def _get_streams(self): 25 | player_js = self.session.http.get(self.url, schema=validate.Schema( 26 | re.compile(r"https?:(//deviceids-medp.wdr.de/ondemand/\S+\.js)"), 27 | validate.none_or_all( 28 | validate.get(1), 29 | validate.transform(lambda url: update_scheme("https://", url)), 30 | ), 31 | )) 32 | if not player_js: 33 | return 34 | 35 | log.debug(f"Found player js {player_js}") 36 | data = self.session.http.get(player_js, schema=validate.Schema( 37 | validate.regex(re.compile(r"\$mediaObject\.jsonpHelper\.storeAndPlay\(({.+})\);?")), 38 | validate.get(1), 39 | validate.parse_json(), 40 | validate.get("mediaResource"), 41 | validate.get("dflt"), 42 | { 43 | validate.optional("audioURL"): validate.url(), 44 | validate.optional("videoURL"): validate.url(), 45 | }, 46 | )) 47 | 48 | if data.get("videoURL"): 49 | yield from HLSStream.parse_variant_playlist(self.session, update_scheme("https:", data.get("videoURL"))).items() 50 | if data.get("audioURL"): 51 | yield "audio", HTTPStream(self.session, update_scheme("https:", data.get("audioURL"))) 52 | 53 | 54 | __plugin__ = Sportschau 55 | -------------------------------------------------------------------------------- /win/streamlink/plugins/ssh101.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Global live streaming platform. 3 | $url ssh101.com 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream 13 | 14 | 15 | log = logging.getLogger(__name__) 16 | 17 | 18 | @pluginmatcher(re.compile( 19 | r"https?://(?:www\.)?ssh101\.com/(?:(?:secure)?live/|detail\.php\?id=\w+)", 20 | )) 21 | class SSH101(Plugin): 22 | def _get_streams(self): 23 | hls_url = self.session.http.get(self.url, schema=validate.Schema( 24 | validate.parse_html(), 25 | validate.xml_xpath_string(".//source[contains(@src,'.m3u8')]/@src"), 26 | )) 27 | if not hls_url: 28 | return 29 | 30 | res = self.session.http.get(hls_url, acceptable_status=(200, 403, 404)) 31 | if res.status_code != 200 or len(res.text) <= 10: 32 | log.error("This stream is currently offline") 33 | return 34 | 35 | return {"live": HLSStream(self.session, hls_url)} 36 | 37 | 38 | __plugin__ = SSH101 39 | -------------------------------------------------------------------------------- /win/streamlink/plugins/streamable.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Global video hosting platform. 3 | $url streamable.com 4 | $type vod 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.plugin.api import validate 11 | from streamlink.stream.http import HTTPStream 12 | from streamlink.utils.url import update_scheme 13 | 14 | 15 | @pluginmatcher(re.compile( 16 | r"https?://(?:www\.)?streamable\.com/(.+)", 17 | )) 18 | class Streamable(Plugin): 19 | def _get_streams(self): 20 | data = self.session.http.get(self.url, schema=validate.Schema( 21 | re.compile(r"var\s*videoObject\s*=\s*({.*?});"), 22 | validate.none_or_all( 23 | validate.get(1), 24 | validate.parse_json(), 25 | { 26 | "files": { 27 | str: { 28 | "url": validate.url(), 29 | "width": int, 30 | "height": int, 31 | "bitrate": int, 32 | }, 33 | }, 34 | }, 35 | ), 36 | )) 37 | 38 | for info in data["files"].values(): 39 | stream_url = update_scheme("https://", info["url"]) 40 | # pick the smaller of the two dimensions, for landscape v. portrait videos 41 | res = min(info["width"], info["height"]) 42 | yield f"{res}p", HTTPStream(self.session, stream_url) 43 | 44 | 45 | __plugin__ = Streamable 46 | -------------------------------------------------------------------------------- /win/streamlink/plugins/stv.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels from STV, a Scottish free-to-air broadcaster. 3 | $url player.stv.tv 4 | $type live 5 | $region United Kingdom 6 | """ 7 | 8 | import logging 9 | import re 10 | 11 | from streamlink.plugin import Plugin, PluginError, pluginmatcher 12 | from streamlink.stream.hls import HLSStream 13 | 14 | 15 | log = logging.getLogger(__name__) 16 | 17 | 18 | @pluginmatcher(re.compile( 19 | r"https?://player\.stv\.tv/live", 20 | )) 21 | class STV(Plugin): 22 | API_URL = "https://player.api.stv.tv/v1/streams/stv/" 23 | 24 | def get_title(self): 25 | if self.title is None: 26 | self._get_api_results() 27 | return self.title 28 | 29 | def _get_api_results(self): 30 | res = self.session.http.get(self.API_URL) 31 | data = self.session.http.json(res) 32 | 33 | if data["success"] is False: 34 | raise PluginError(data["reason"]["message"]) 35 | 36 | try: 37 | self.title = data["results"]["now"]["title"] 38 | except KeyError: 39 | self.title = "STV" 40 | 41 | return data 42 | 43 | def _get_streams(self): 44 | hls_url = self._get_api_results()["results"]["streamUrl"] 45 | return HLSStream.parse_variant_playlist(self.session, hls_url) 46 | 47 | 48 | __plugin__ = STV 49 | -------------------------------------------------------------------------------- /win/streamlink/plugins/swisstxt.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels from RSI and SRF, operations of SRG SSR, a Swiss public broadcaster. 3 | $url srf.ch 4 | $url rsi.ch 5 | $type live 6 | $region Switzerland 7 | """ 8 | 9 | import logging 10 | import re 11 | from urllib.parse import parse_qsl, urlparse, urlunparse 12 | 13 | from streamlink.plugin import Plugin, pluginmatcher 14 | from streamlink.stream.hls import HLSStream 15 | 16 | 17 | log = logging.getLogger(__name__) 18 | 19 | 20 | @pluginmatcher(re.compile(r""" 21 | https?://(?: 22 | live\.(rsi)\.ch/| 23 | (?:www\.)?(srf)\.ch/sport/resultcenter 24 | ) 25 | """, re.VERBOSE)) 26 | class Swisstxt(Plugin): 27 | api_url = "http://event.api.swisstxt.ch/v1/stream/{site}/byEventItemIdAndType/{id}/HLS" 28 | 29 | def get_stream_url(self, event_id): 30 | site = self.match.group(1) or self.match.group(2) 31 | api_url = self.api_url.format(id=event_id, site=site.upper()) 32 | log.debug("Calling API: {0}".format(api_url)) 33 | 34 | stream_url = self.session.http.get(api_url).text.strip("\"'") 35 | 36 | parsed = urlparse(stream_url) 37 | query = dict(parse_qsl(parsed.query)) 38 | return urlunparse(parsed._replace(query="")), query 39 | 40 | def _get_streams(self): 41 | event_id = dict(parse_qsl(urlparse(self.url).query.lower())).get("eventid") 42 | if event_id is None: 43 | return 44 | 45 | stream_url, params = self.get_stream_url(event_id) 46 | return HLSStream.parse_variant_playlist(self.session, 47 | stream_url, 48 | params=params) 49 | 50 | 51 | __plugin__ = Swisstxt 52 | -------------------------------------------------------------------------------- /win/streamlink/plugins/telefe.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Video content from Telefe, an Argentine TV station. 3 | $url mitelefe.com 4 | $type live 5 | $region Argentina 6 | """ 7 | 8 | import logging 9 | import re 10 | from urllib.parse import urljoin 11 | 12 | from streamlink.plugin import Plugin, pluginmatcher 13 | from streamlink.plugin.api import validate 14 | from streamlink.stream.hls import HLSStream 15 | 16 | 17 | log = logging.getLogger(__name__) 18 | 19 | 20 | @pluginmatcher(re.compile(r"https://mitelefe\.com/vivo")) 21 | class Telefe(Plugin): 22 | def _get_streams(self): 23 | self.title, hls_url = self.session.http.get( 24 | self.url, 25 | schema=validate.Schema( 26 | validate.parse_html(), 27 | validate.xml_xpath_string(".//script[contains(text(), 'HLS')]/text()"), 28 | validate.none_or_all( 29 | re.compile(r"=\s*(\{.+?});", re.DOTALL | re.MULTILINE), 30 | validate.none_or_all( 31 | validate.get(1), 32 | validate.parse_json(), 33 | {str: {"children": {"top": {"model": {"videos": [{ 34 | "title": str, 35 | "sources": validate.all( 36 | [{"url": str, "type": str}], 37 | validate.filter(lambda p: p["type"].lower() == "hls"), 38 | validate.get((0, "url")), 39 | ), 40 | }]}}}}}, 41 | validate.transform(lambda k: next(iter(k.values()))), 42 | validate.get(("children", "top", "model", "videos", 0)), 43 | validate.union_get("title", "sources"), 44 | ), 45 | ), 46 | ), 47 | ) 48 | return HLSStream.parse_variant_playlist(self.session, urljoin(self.url, hls_url)) 49 | 50 | 51 | __plugin__ = Telefe 52 | -------------------------------------------------------------------------------- /win/streamlink/plugins/telemadrid.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Spanish live TV channel for Telemadrid, a public regional television station. 3 | $url telemadrid.es 4 | $type live, vod 5 | $region Spain 6 | """ 7 | 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.plugins.brightcove import BrightcovePlayer 13 | 14 | 15 | @pluginmatcher(re.compile( 16 | r"https?://(?:www\.)?telemadrid\.es/", 17 | )) 18 | class Telemadrid(Plugin): 19 | 20 | def _get_streams(self): 21 | data = self.session.http.get(self.url, schema=validate.Schema( 22 | validate.parse_html(), 23 | validate.xml_find(".//video[@class='video-js'][@data-video-id][@data-account][@data-player][1]"), 24 | validate.union_get("data-video-id", "data-account", "data-player"), 25 | )) 26 | data_video_id, data_account, data_player = data 27 | player = BrightcovePlayer(self.session, data_account, f"{data_player}_default") 28 | return player.get_streams(data_video_id) 29 | 30 | 31 | __plugin__ = Telemadrid 32 | -------------------------------------------------------------------------------- /win/streamlink/plugins/tv360.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description A privately owned Turkish live TV channel. 3 | $url tv360.com.tr 4 | $type live 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, pluginmatcher 10 | from streamlink.plugin.api import validate 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | @pluginmatcher(re.compile( 15 | r"https?://(?:www\.)?tv360\.com\.tr/canli-yayin", 16 | )) 17 | class TV360(Plugin): 18 | def _get_streams(self): 19 | hls_url = self.session.http.get(self.url, schema=validate.Schema( 20 | validate.parse_html(), 21 | validate.xml_xpath_string(".//video/source[@src][@type='application/x-mpegURL'][1]/@src"), 22 | validate.none_or_all(validate.url()), 23 | )) 24 | if hls_url: 25 | return HLSStream.parse_variant_playlist(self.session, hls_url) 26 | 27 | 28 | __plugin__ = TV360 29 | -------------------------------------------------------------------------------- /win/streamlink/plugins/tv3cat.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels and video on-demand service from CCMA, a Catalan public, state-owned broadcaster. 3 | $url ccma.cat 4 | $type live, vod 5 | $region Spain 6 | """ 7 | 8 | import logging 9 | import re 10 | 11 | from streamlink.plugin import Plugin, pluginmatcher 12 | from streamlink.plugin.api import validate 13 | from streamlink.stream.hls import HLSStream 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | @pluginmatcher(re.compile( 20 | r"https?://(?:www\.)?ccma\.cat/tv3/directe/(?P.+?)/", 21 | )) 22 | class TV3Cat(Plugin): 23 | _URL_STREAM_INFO = "https://dinamics.ccma.cat/pvideo/media.jsp" 24 | 25 | _MAP_CHANNELS = { 26 | "tv3": "tvi", 27 | } 28 | 29 | def _get_streams(self): 30 | ident = self.match.group("ident") 31 | 32 | schema_media = { 33 | "geo": str, 34 | "url": validate.url(path=validate.endswith(".m3u8")), 35 | } 36 | 37 | stream_infos = self.session.http.get( 38 | self._URL_STREAM_INFO, 39 | params={ 40 | "media": "video", 41 | "versio": "vast", 42 | "idint": self._MAP_CHANNELS.get(ident, ident), 43 | "profile": "pc", 44 | "desplacament": "0", 45 | "broadcast": "false", 46 | }, 47 | schema=validate.Schema( 48 | validate.parse_json(), 49 | { 50 | "media": validate.any( 51 | [schema_media], 52 | validate.all( 53 | schema_media, 54 | validate.transform(lambda item: [item]), 55 | ), 56 | ), 57 | }, 58 | validate.get("media"), 59 | ), 60 | ) 61 | 62 | for stream in stream_infos: 63 | log.info(f"Accessing stream from region {stream['geo']}") 64 | try: 65 | return HLSStream.parse_variant_playlist(self.session, stream["url"], name_fmt="{pixels}_{bitrate}") 66 | except OSError: 67 | pass 68 | 69 | 70 | __plugin__ = TV3Cat 71 | -------------------------------------------------------------------------------- /win/streamlink/plugins/tv8.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Turkish live TV channel owned by Acun Medya Group. 3 | $url tv8.com.tr 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream, HLSStreamReader, HLSStreamWriter 13 | 14 | 15 | log = logging.getLogger(__name__) 16 | 17 | 18 | class TV8HLSStreamWriter(HLSStreamWriter): 19 | ad_re = re.compile(r"/ad/|/crea/") 20 | 21 | def should_filter_sequence(self, sequence): 22 | return self.ad_re.search(sequence.segment.uri) is not None or super().should_filter_sequence(sequence) 23 | 24 | 25 | class TV8HLSStreamReader(HLSStreamReader): 26 | __writer__ = TV8HLSStreamWriter 27 | 28 | 29 | class TV8HLSStream(HLSStream): 30 | __reader__ = TV8HLSStreamReader 31 | 32 | 33 | @pluginmatcher(re.compile( 34 | r"https?://www\.tv8\.com\.tr/canli-yayin", 35 | )) 36 | class TV8(Plugin): 37 | def _get_streams(self): 38 | hls_url = self.session.http.get(self.url, schema=validate.Schema( 39 | re.compile(r"""var\s+videoUrl\s*=\s*(?P["'])(?Phttps?://.*?\.m3u8.*?)(?P=q)"""), 40 | validate.any(None, validate.get("hls_url")), 41 | )) 42 | if hls_url is not None: 43 | return TV8HLSStream.parse_variant_playlist(self.session, hls_url) 44 | 45 | 46 | __plugin__ = TV8 47 | -------------------------------------------------------------------------------- /win/streamlink/plugins/tv999.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Turkish live TV channel owned by Detelina Media. 3 | $url tv999.bg 4 | $type live 5 | $region Bulgaria 6 | """ 7 | 8 | import logging 9 | import re 10 | 11 | from streamlink.plugin import Plugin, pluginmatcher 12 | from streamlink.plugin.api import validate 13 | from streamlink.stream.hls import HLSStream 14 | from streamlink.utils.url import update_scheme 15 | 16 | 17 | log = logging.getLogger(__name__) 18 | 19 | 20 | @pluginmatcher(re.compile( 21 | r"https?://(?:www\.)?tv999\.bg/live", 22 | )) 23 | class TV999(Plugin): 24 | title = "TV999" 25 | 26 | def _get_xpath_string(self, url, xpath): 27 | return self.session.http.get( 28 | url, 29 | schema=validate.Schema( 30 | validate.parse_html(), 31 | validate.xml_xpath_string(xpath), 32 | validate.any(None, validate.url()), 33 | ), 34 | ) 35 | 36 | def _get_streams(self): 37 | iframe_url = self._get_xpath_string(self.url, ".//iframe[@src]/@src") 38 | if not iframe_url: 39 | return 40 | hls_url = self._get_xpath_string(iframe_url, ".//source[contains(@src,'m3u8')]/@src") 41 | if not hls_url: 42 | return 43 | return {"live": HLSStream(self.session, update_scheme("http://", hls_url))} 44 | 45 | 46 | __plugin__ = TV999 47 | -------------------------------------------------------------------------------- /win/streamlink/plugins/tvibo.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Global live streaming and video hosting platform. 3 | $url player.tvibo.com 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | log = logging.getLogger(__name__) 15 | 16 | 17 | @pluginmatcher(re.compile( 18 | r"https?://player\.tvibo\.com/\w+/(?P\d+)", 19 | )) 20 | class Tvibo(Plugin): 21 | _api_url = "http://panel.tvibo.com/api/player/streamurl/{id}" 22 | 23 | def _get_streams(self): 24 | channel_id = self.match.group("id") 25 | 26 | api_response = self.session.http.get( 27 | self._api_url.format(id=channel_id), 28 | acceptable_status=(200, 404)) 29 | 30 | data = self.session.http.json(api_response) 31 | log.trace("{0!r}".format(data)) 32 | if data.get("st"): 33 | yield "source", HLSStream(self.session, data["st"]) 34 | elif data.get("error"): 35 | log.error(data["error"]["message"]) 36 | 37 | 38 | __plugin__ = Tvibo 39 | -------------------------------------------------------------------------------- /win/streamlink/plugins/tvrby.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels from BTRC, a Belarusian public, state-owned broadcaster. 3 | $url tvr.by 4 | $type live 5 | $region Belarus 6 | """ 7 | 8 | import logging 9 | import re 10 | 11 | from streamlink.plugin import Plugin, pluginmatcher 12 | from streamlink.plugin.api import validate 13 | from streamlink.stream.hls import HLSStream 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | @pluginmatcher(re.compile( 20 | r"https?://(?:www\.)?tvr\.by/televidenie/belarus", 21 | )) 22 | class TVRBy(Plugin): 23 | file_re = re.compile(r"""(?Phttps://stream\.hoster\.by[^"',]+\.m3u8[^"',]*)""") 24 | player_re = re.compile(r"""["'](?P[^"']+tvr\.by/plugines/online-tv-main\.php[^"']+)["']""") 25 | 26 | stream_schema = validate.Schema( 27 | validate.all( 28 | validate.transform(file_re.finditer), 29 | validate.transform(list), 30 | [validate.get("url")], 31 | # remove duplicates 32 | validate.transform(set), 33 | validate.transform(list), 34 | ), 35 | ) 36 | 37 | def __init__(self, *args, **kwargs): 38 | super().__init__(*args, **kwargs) 39 | # ensure the URL ends with a / 40 | if not self.url.endswith("/"): 41 | self.url += "/" 42 | 43 | def _get_streams(self): 44 | res = self.session.http.get(self.url) 45 | m = self.player_re.search(res.text) 46 | if not m: 47 | return 48 | 49 | player_url = m.group("url") 50 | res = self.session.http.get(player_url) 51 | stream_urls = self.stream_schema.validate(res.text) 52 | log.debug("Found {0} stream URL{1}".format(len(stream_urls), "" if len(stream_urls) == 1 else "s")) 53 | 54 | for stream_url in stream_urls: 55 | yield from HLSStream.parse_variant_playlist(self.session, stream_url).items() 56 | 57 | 58 | __plugin__ = TVRBy 59 | -------------------------------------------------------------------------------- /win/streamlink/plugins/tvrplus.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels from TVR, a Romanian public, state-owned broadcaster. 3 | $url tvrplus.ro 4 | $type live 5 | $region Romania 6 | """ 7 | 8 | import re 9 | 10 | from streamlink.plugin import Plugin, PluginError, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream 13 | 14 | 15 | @pluginmatcher(re.compile( 16 | r"https?://(?:www\.)?tvrplus\.ro(?:/live/.+|/?$)", 17 | )) 18 | class TVRPlus(Plugin): 19 | def _get_streams(self): 20 | try: 21 | hls_url = self.session.http.get( 22 | self.url, 23 | schema=validate.Schema( 24 | re.compile(r"""(?P["'])(?Phttps?://\S+?\.m3u8\S*?)(?P=q)"""), 25 | validate.get("url"), 26 | ), 27 | ) 28 | except PluginError: 29 | return 30 | 31 | return HLSStream.parse_variant_playlist(self.session, hls_url, headers={"Referer": self.url}) 32 | 33 | 34 | __plugin__ = TVRPlus 35 | -------------------------------------------------------------------------------- /win/streamlink/plugins/tvtoya.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Polish live TV channel owned by Toya. 3 | $url tvtoya.pl 4 | $type live 5 | """ 6 | 7 | import re 8 | 9 | from streamlink.plugin import Plugin, PluginError, pluginmatcher 10 | from streamlink.plugin.api import validate 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | @pluginmatcher(re.compile( 15 | r"https?://(?:www\.)?tvtoya\.pl/player/live", 16 | )) 17 | class TVToya(Plugin): 18 | def _get_streams(self): 19 | try: 20 | hls = self.session.http.get(self.url, schema=validate.Schema( 21 | validate.parse_html(), 22 | validate.xml_xpath_string(".//script[@type='application/json'][@id='__NEXT_DATA__']/text()"), 23 | str, 24 | validate.parse_json(), 25 | { 26 | "props": { 27 | "pageProps": { 28 | "type": "live", 29 | "url": validate.all( 30 | str, 31 | validate.transform(lambda url: url.replace("https:////", "https://")), 32 | validate.url(path=validate.endswith(".m3u8")), 33 | ), 34 | }, 35 | }, 36 | }, 37 | validate.get(("props", "pageProps", "url")), 38 | )) 39 | except PluginError: 40 | return 41 | 42 | return HLSStream.parse_variant_playlist(self.session, hls) 43 | 44 | 45 | __plugin__ = TVToya 46 | -------------------------------------------------------------------------------- /win/streamlink/plugins/useetv.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels and video on-demand service from UseeTV, owned by Telkom Indonesia. 3 | $url useetv.com 4 | $type live, vod 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.dash import DASHStream 13 | from streamlink.stream.hls import HLSStream 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | @pluginmatcher(re.compile(r"https?://(?:www\.)?useetv\.com/")) 20 | class UseeTV(Plugin): 21 | def _get_streams(self): 22 | root = self.session.http.get(self.url, schema=validate.Schema(validate.parse_html())) 23 | 24 | for needle, errormsg in ( 25 | ( 26 | "\"This service is not available in your Country\"", 27 | "The content is not available in your region", 28 | ), 29 | ( 30 | "\"Silahkan login Menggunakan akun MyIndihome dan berlangganan minipack\"", 31 | "The content is not available without a subscription", 32 | ), 33 | ): 34 | if validate.Schema(validate.xml_xpath(".//script[contains(text(),$needle)]", needle=needle)).validate(root): 35 | log.error(errormsg) 36 | return 37 | 38 | url = validate.Schema( 39 | validate.any( 40 | validate.all( 41 | validate.xml_xpath_string(""" 42 | .//script[contains(text(), 'laylist.m3u8') or contains(text(), 'manifest.mpd')][1]/text() 43 | """), 44 | str, 45 | re.compile(r"""(?P['"])(?Phttps://.*?/(?:[Pp]laylist\.m3u8|manifest\.mpd).+?)(?P=q)"""), 46 | validate.none_or_all(validate.get("url"), validate.url()), 47 | ), 48 | validate.all( 49 | validate.xml_xpath_string(".//video[@id='video-player']/source/@src"), 50 | validate.any(None, validate.url()), 51 | ), 52 | ), 53 | ).validate(root) 54 | 55 | if url and ".m3u8" in url: 56 | return HLSStream.parse_variant_playlist(self.session, url) 57 | elif url and ".mpd" in url: 58 | return DASHStream.parse_manifest(self.session, url) 59 | 60 | 61 | __plugin__ = UseeTV 62 | -------------------------------------------------------------------------------- /win/streamlink/plugins/vinhlongtv.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Vietnamese live TV channels from THVL, including THVL1, THVL2, THVL3 and THVL4. 3 | $url thvli.vn 4 | $type live 5 | $region Vietnam 6 | """ 7 | 8 | import logging 9 | import re 10 | from datetime import datetime, timezone 11 | from hashlib import md5 12 | 13 | from streamlink.plugin import Plugin, pluginmatcher 14 | from streamlink.plugin.api import validate 15 | from streamlink.stream.hls import HLSStream 16 | 17 | 18 | log = logging.getLogger(__name__) 19 | 20 | 21 | @pluginmatcher(re.compile( 22 | r"https?://(?:www\.)?thvli\.vn/live/(?P[^/]+)", 23 | )) 24 | class VinhLongTV(Plugin): 25 | _API_URL = "https://api.thvli.vn/backend/cm/get_detail/{channel}/" 26 | _API_KEY_DATE = "Kh0ngDuLieu" 27 | _API_KEY_TIME = "C0R0i" 28 | _API_KEY_SECRET = "Kh0aAnT0an" 29 | 30 | def _get_headers(self): 31 | now = datetime.now(tz=timezone.utc) 32 | date = now.strftime("%Y%m%d") 33 | time = now.strftime("%H%M%S") 34 | dtstr = f"{date}{time}" 35 | dthash = md5(dtstr.encode()).hexdigest() 36 | key_value = f"{dthash[:3]}{dthash[-3:]}" 37 | key_access = f"{self._API_KEY_DATE}{date}{self._API_KEY_TIME}{time}{self._API_KEY_SECRET}{key_value}" 38 | 39 | return { 40 | "X-SFD-Date": dtstr, 41 | "X-SFD-Key": md5(key_access.encode()).hexdigest(), 42 | } 43 | 44 | def _get_streams(self): 45 | channel = self.match.group("channel") 46 | params = {"timezone": "UTC"} 47 | headers = self._get_headers() 48 | 49 | self.id, self.title, hls_url = self.session.http.get( 50 | self._API_URL.format(channel=channel), 51 | params=params, 52 | headers=headers, 53 | schema=validate.Schema( 54 | validate.parse_json(), 55 | { 56 | "id": str, 57 | "title": str, 58 | "link_play": str, 59 | }, 60 | validate.union_get( 61 | "id", 62 | "title", 63 | "link_play", 64 | ), 65 | ), 66 | ) 67 | 68 | return HLSStream.parse_variant_playlist(self.session, hls_url) 69 | 70 | 71 | __plugin__ = VinhLongTV 72 | -------------------------------------------------------------------------------- /win/streamlink/plugins/vtvgo.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Live TV channels from VTV, a Vietnamese public, state-owned broadcaster. 3 | $url vtvgo.vn 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream 13 | 14 | 15 | log = logging.getLogger(__name__) 16 | 17 | 18 | @pluginmatcher(re.compile( 19 | r"https?://vtvgo\.vn/xem-truc-tuyen-kenh-", 20 | )) 21 | class VTVgo(Plugin): 22 | AJAX_URL = "https://vtvgo.vn/ajax-get-stream" 23 | 24 | def _get_streams(self): 25 | # get cookies 26 | self.session.http.get("https://vtvgo.vn/") 27 | 28 | self.session.http.headers.update({ 29 | "Origin": "https://vtvgo.vn", 30 | "Referer": self.url, 31 | "Sec-Fetch-Site": "same-origin", 32 | "X-Requested-With": "XMLHttpRequest", 33 | }) 34 | 35 | params = self.session.http.get(self.url, schema=validate.Schema( 36 | validate.parse_html(), 37 | validate.xml_xpath_string(".//script[contains(text(),'setplayer(')][1]/text()"), 38 | validate.none_or_all( 39 | validate.regex( 40 | re.compile(r"""var\s+(?P(?:type_)?id|time|token)\s*=\s*["']?(?P[^"']+)["']?;"""), 41 | method="findall", 42 | ), 43 | [ 44 | ("id", int), 45 | ("type_id", str), 46 | ("time", str), 47 | ("token", str), 48 | ], 49 | ), 50 | )) 51 | if not params: 52 | return 53 | 54 | log.trace(f"{params!r}") 55 | hls_url = self.session.http.post( 56 | self.AJAX_URL, 57 | data=dict(params), 58 | schema=validate.Schema( 59 | validate.parse_json(), 60 | {"stream_url": [validate.url()]}, 61 | validate.get(("stream_url", 0)), 62 | ), 63 | ) 64 | 65 | return HLSStream.parse_variant_playlist(self.session, hls_url) 66 | 67 | 68 | __plugin__ = VTVgo 69 | -------------------------------------------------------------------------------- /win/streamlink/plugins/welt.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description German news and documentaries TV channel, owned by Axel Springer SE. 3 | $url welt.de 4 | $type live, vod 5 | $region Germany 6 | """ 7 | 8 | import re 9 | from urllib.parse import quote 10 | 11 | from streamlink.plugin import Plugin, pluginmatcher 12 | from streamlink.plugin.api import validate 13 | from streamlink.stream.hls import HLSStream 14 | 15 | 16 | @pluginmatcher(re.compile( 17 | r"https?://(\w+\.)?welt\.de/?", 18 | )) 19 | class Welt(Plugin): 20 | _url_vod = "https://www.welt.de/onward/video/play/{0}" 21 | _schema = validate.Schema( 22 | validate.parse_html(), 23 | validate.xml_findtext(".//script[@type='application/json'][@data-content='VideoPlayer.Config']"), 24 | validate.parse_json(), 25 | validate.get("sources"), 26 | validate.filter(lambda obj: obj["extension"] == "m3u8"), 27 | validate.get((0, "src")), 28 | ) 29 | 30 | def _get_streams(self): 31 | hls_url = self.session.http.get(self.url, schema=self._schema) 32 | 33 | if "mediathek" in self.url.lower(): 34 | url = self._url_vod.format(quote(hls_url, safe="")) 35 | hls_url = self.session.http.get(url, headers={"Referer": self.url}).url 36 | 37 | return HLSStream.parse_variant_playlist(self.session, hls_url, headers={"Referer": self.url}) 38 | 39 | 40 | __plugin__ = Welt 41 | -------------------------------------------------------------------------------- /win/streamlink/plugins/zeenews.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Indian Hindi-language news channel covering world & Indian news, business, entertainment and sport. 3 | $url zeenews.india.com 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | log = logging.getLogger(__name__) 15 | 16 | 17 | @pluginmatcher(re.compile( 18 | r"https?://zeenews\.india\.com/live-tv", 19 | )) 20 | class ZeeNews(Plugin): 21 | HLS_URL = "https://z5ams.akamaized.net/zeenews/index.m3u8{0}" 22 | TOKEN_URL = "https://useraction.zee5.com/token/live.php" 23 | 24 | title = "Zee News" 25 | 26 | def _get_streams(self): 27 | res = self.session.http.get(self.TOKEN_URL) 28 | token = self.session.http.json(res)["video_token"] 29 | log.debug("video_token: {0}".format(token)) 30 | yield from HLSStream.parse_variant_playlist(self.session, self.HLS_URL.format(token)).items() 31 | 32 | 33 | __plugin__ = ZeeNews 34 | -------------------------------------------------------------------------------- /win/streamlink/plugins/zengatv.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Indian live TV channels. OTT service from Zenga TV. 3 | $url zengatv.com 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.stream.hls import HLSStream 12 | 13 | 14 | log = logging.getLogger(__name__) 15 | 16 | 17 | @pluginmatcher(re.compile( 18 | r"https?://(www\.)?zengatv\.com/\w+", 19 | )) 20 | class ZengaTV(Plugin): 21 | """Streamlink Plugin for livestreams on zengatv.com""" 22 | 23 | _id_re = re.compile(r"""id=(?P["'])dvrid(?P=q)\svalue=(?P=q)(?P[^"']+)(?P=q)""") 24 | _id_2_re = re.compile(r"""LivePlayer\(.+["'](?PD\d+)["']""") 25 | 26 | api_url = "http://www.zengatv.com/changeResulation/" 27 | 28 | def _get_streams(self): 29 | headers = {"Referer": self.url} 30 | 31 | res = self.session.http.get(self.url, headers=headers) 32 | for id_re in (self._id_re, self._id_2_re): 33 | m = id_re.search(res.text) 34 | if not m: 35 | continue 36 | break 37 | 38 | if not m: 39 | log.error("No video id found") 40 | return 41 | 42 | dvr_id = m.group("id") 43 | log.debug("Found video id: {0}".format(dvr_id)) 44 | data = {"feed": "hd", "dvrId": dvr_id} 45 | res = self.session.http.post(self.api_url, headers=headers, data=data) 46 | if res.status_code == 200: 47 | yield from HLSStream.parse_variant_playlist(self.session, res.text, headers=headers).items() 48 | 49 | 50 | __plugin__ = ZengaTV 51 | -------------------------------------------------------------------------------- /win/streamlink/plugins/zhanqi.py: -------------------------------------------------------------------------------- 1 | """ 2 | $description Chinese live streaming platform for live video game broadcasts. 3 | $url zhanqi.tv 4 | $type live 5 | """ 6 | 7 | import logging 8 | import re 9 | 10 | from streamlink.plugin import Plugin, pluginmatcher 11 | from streamlink.plugin.api import validate 12 | from streamlink.stream.hls import HLSStream 13 | from streamlink.stream.http import HTTPStream 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | API_URL = "https://www.zhanqi.tv/api/static/v2.1/room/domain/{0}.json" 19 | 20 | STATUS_ONLINE = 4 21 | STATUS_OFFLINE = 0 22 | 23 | _room_schema = validate.Schema( 24 | { 25 | "data": validate.any(None, { 26 | "status": validate.all( 27 | str, 28 | validate.transform(int), 29 | ), 30 | "videoId": str, 31 | }), 32 | }, 33 | validate.get("data"), 34 | ) 35 | 36 | 37 | @pluginmatcher(re.compile( 38 | r"https?://(www\.)?zhanqi\.tv/(?P[^/]+)", 39 | )) 40 | class Zhanqitv(Plugin): 41 | def _get_streams(self): 42 | channel = self.match.group("channel") 43 | 44 | res = self.session.http.get(API_URL.format(channel)) 45 | room = self.session.http.json(res, schema=_room_schema) 46 | if not room: 47 | log.info("Not a valid room url.") 48 | return 49 | 50 | if room["status"] != STATUS_ONLINE: 51 | log.info("Stream currently unavailable.") 52 | return 53 | 54 | url = "http://wshdl.load.cdn.zhanqi.tv/zqlive/{room[videoId]}.flv?get_url=".format(room=room) 55 | stream = HTTPStream(self.session, url) 56 | yield "live", stream 57 | 58 | url = "http://dlhls.cdn.zhanqi.tv/zqlive/{room[videoId]}_1024/index.m3u8?Dnion_vsnae={room[videoId]}".format(room=room) 59 | stream = HLSStream(self.session, url) 60 | yield "live", stream 61 | 62 | 63 | __plugin__ = Zhanqitv 64 | -------------------------------------------------------------------------------- /win/streamlink/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botbahlul/PyAutoSRT/20942b9656030ac5cc044655bb88974a381c09b8/win/streamlink/py.typed -------------------------------------------------------------------------------- /win/streamlink/stream/__init__.py: -------------------------------------------------------------------------------- 1 | from streamlink.stream.dash import DASHStream 2 | from streamlink.stream.ffmpegmux import MuxedStream 3 | from streamlink.stream.hls import HLSStream, MuxedHLSStream 4 | from streamlink.stream.http import HTTPStream 5 | from streamlink.stream.stream import Stream, StreamIO 6 | from streamlink.stream.wrappers import StreamIOIterWrapper, StreamIOThreadWrapper, StreamIOWrapper 7 | -------------------------------------------------------------------------------- /win/streamlink/stream/file.py: -------------------------------------------------------------------------------- 1 | """ 2 | Stream wrapper around a file 3 | """ 4 | from streamlink.stream.stream import Stream 5 | 6 | 7 | class FileStream(Stream): 8 | __shortname__ = "file" 9 | 10 | def __init__(self, session, path=None, fileobj=None): 11 | super().__init__(session) 12 | self.path = path 13 | self.fileobj = fileobj 14 | if not self.path and not self.fileobj: 15 | raise ValueError("path or fileobj must be set") 16 | 17 | def __json__(self): 18 | json = super().__json__() 19 | 20 | if self.path: 21 | json["path"] = self.path 22 | 23 | return json 24 | 25 | def to_url(self): 26 | if self.path is None: 27 | return super().to_url() 28 | 29 | return self.path 30 | 31 | def open(self): 32 | return self.fileobj or open(self.path) 33 | -------------------------------------------------------------------------------- /win/streamlink/stream/filtered.py: -------------------------------------------------------------------------------- 1 | from threading import Event 2 | 3 | from streamlink.buffers import Buffer 4 | from streamlink.stream.stream import StreamIO 5 | 6 | 7 | class FilteredStream(StreamIO): 8 | """StreamIO mixin for being able to pause read calls while filtering content""" 9 | 10 | buffer: Buffer 11 | 12 | def __init__(self, *args, **kwargs): 13 | self._event_filter = Event() 14 | self._event_filter.set() 15 | super().__init__(*args, **kwargs) 16 | 17 | def read(self, *args, **kwargs) -> bytes: 18 | read = super().read 19 | while True: 20 | try: 21 | return read(*args, **kwargs) 22 | except OSError: 23 | # wait indefinitely until filtering ends 24 | self._event_filter.wait() 25 | if self.buffer.closed: 26 | return b"" 27 | # if data is available, try reading again 28 | if self.buffer.length > 0: 29 | continue 30 | # raise if not filtering and no data available 31 | raise 32 | 33 | def close(self) -> None: 34 | super().close() 35 | self._event_filter.set() 36 | 37 | def is_paused(self) -> bool: 38 | return not self._event_filter.is_set() 39 | 40 | def pause(self) -> None: 41 | self._event_filter.clear() 42 | 43 | def resume(self) -> None: 44 | self._event_filter.set() 45 | 46 | def filter_wait(self, timeout=None): 47 | return self._event_filter.wait(timeout) 48 | -------------------------------------------------------------------------------- /win/streamlink/stream/stream.py: -------------------------------------------------------------------------------- 1 | import io 2 | import json 3 | import logging 4 | 5 | from streamlink.session import Streamlink 6 | 7 | 8 | log = logging.getLogger(__name__) 9 | 10 | 11 | class Stream: 12 | """ 13 | This is a base class that should be inherited when implementing 14 | different stream types. Should only be created by plugins. 15 | """ 16 | 17 | __shortname__ = "stream" 18 | 19 | @classmethod 20 | def shortname(cls): 21 | return cls.__shortname__ 22 | 23 | def __init__(self, session: Streamlink): 24 | """ 25 | :param session: Streamlink session instance 26 | """ 27 | 28 | self.session: Streamlink = session 29 | 30 | def __repr__(self): 31 | params = [repr(self.shortname())] 32 | for method in self.to_url, self.to_manifest_url: 33 | try: 34 | params.append(repr(method())) 35 | except TypeError: 36 | pass 37 | 38 | return f"<{self.__class__.__name__} [{', '.join(params)}]>" 39 | 40 | def __json__(self): 41 | return dict(type=self.shortname()) 42 | 43 | @property 44 | def json(self): 45 | obj = self.__json__() 46 | return json.dumps(obj) 47 | 48 | def to_url(self): 49 | raise TypeError(f"<{self.__class__.__name__} [{self.shortname()}]> cannot be translated to a URL") 50 | 51 | def to_manifest_url(self): 52 | raise TypeError(f"<{self.__class__.__name__} [{self.shortname()}]> cannot be translated to a manifest URL") 53 | 54 | def open(self) -> "StreamIO": 55 | """ 56 | Attempts to open a connection to the stream. 57 | Returns a file-like object that can be used to read the stream data. 58 | 59 | :raises StreamError: on failure 60 | """ 61 | 62 | raise NotImplementedError 63 | 64 | 65 | class StreamIO(io.IOBase): 66 | pass 67 | 68 | 69 | __all__ = ["Stream", "StreamIO"] 70 | -------------------------------------------------------------------------------- /win/streamlink/user_input.py: -------------------------------------------------------------------------------- 1 | import abc 2 | 3 | 4 | class UserInputRequester(abc.ABC): 5 | """ 6 | Base Class / Interface for requesting user input 7 | 8 | e.g. from the console 9 | """ 10 | @abc.abstractmethod 11 | def ask(self, prompt: str) -> str: 12 | """ 13 | Ask the user for a text input, the input is not sensitive 14 | and can be echoed to the user 15 | 16 | :param prompt: message to display when asking for the input 17 | :return: the value of the user input 18 | """ 19 | raise NotImplementedError 20 | 21 | @abc.abstractmethod 22 | def ask_password(self, prompt: str) -> str: 23 | """ 24 | Ask the user for a text input, the input _is_ sensitive 25 | and should be masked as the user gives the input 26 | 27 | :param prompt: message to display when asking for the input 28 | :return: the value of the user input 29 | """ 30 | raise NotImplementedError 31 | -------------------------------------------------------------------------------- /win/streamlink/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from streamlink.utils.cache import LRUCache 2 | from streamlink.utils.data import search_dict 3 | from streamlink.utils.module import load_module 4 | from streamlink.utils.named_pipe import NamedPipe 5 | from streamlink.utils.parse import parse_html, parse_json, parse_qsd, parse_xml 6 | from streamlink.utils.url import absolute_url, prepend_www, update_qsd, update_scheme, url_concat, url_equal 7 | 8 | 9 | __all__ = [ 10 | "LRUCache", 11 | "search_dict", 12 | "load_module", 13 | "NamedPipe", 14 | "parse_html", "parse_json", "parse_qsd", "parse_xml", 15 | "absolute_url", "prepend_www", "update_qsd", "update_scheme", "url_concat", "url_equal", 16 | ] 17 | -------------------------------------------------------------------------------- /win/streamlink/utils/cache.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | from typing import Generic, Optional, OrderedDict as TOrderedDict, TypeVar 3 | 4 | 5 | TCacheKey = TypeVar("TCacheKey") 6 | TCacheValue = TypeVar("TCacheValue") 7 | 8 | 9 | class LRUCache(Generic[TCacheKey, TCacheValue]): 10 | def __init__(self, num: int): 11 | self.cache: TOrderedDict[TCacheKey, TCacheValue] = OrderedDict() 12 | self.num = num 13 | 14 | def get(self, key: TCacheKey) -> Optional[TCacheValue]: 15 | if key not in self.cache: 16 | return None 17 | self.cache.move_to_end(key) 18 | return self.cache[key] 19 | 20 | def set(self, key: TCacheKey, value: TCacheValue) -> None: 21 | self.cache[key] = value 22 | self.cache.move_to_end(key) 23 | if len(self.cache) > self.num: 24 | self.cache.popitem(last=False) 25 | -------------------------------------------------------------------------------- /win/streamlink/utils/crypto.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | 3 | from Crypto.Cipher import AES 4 | 5 | 6 | def evp_bytestokey(password, salt, key_len, iv_len): 7 | """ 8 | Python implementation of OpenSSL's EVP_BytesToKey() 9 | :param password: or passphrase 10 | :param salt: 8 byte salt 11 | :param key_len: length of key in bytes 12 | :param iv_len: length of IV in bytes 13 | :return: (key, iv) 14 | """ 15 | d = d_i = b"" 16 | while len(d) < key_len + iv_len: 17 | d_i = hashlib.md5(d_i + password + salt).digest() 18 | d += d_i 19 | return d[:key_len], d[key_len:key_len + iv_len] 20 | 21 | 22 | def decrypt_openssl(data, passphrase, key_length=32): 23 | if data.startswith(b"Salted__"): 24 | salt = data[len(b"Salted__"):AES.block_size] 25 | key, iv = evp_bytestokey(passphrase, salt, key_length, AES.block_size) 26 | d = AES.new(key, AES.MODE_CBC, iv) 27 | out = d.decrypt(data[AES.block_size:]) 28 | return unpad_pkcs5(out) 29 | 30 | 31 | def unpad_pkcs5(padded): 32 | return padded[:-padded[-1]] 33 | -------------------------------------------------------------------------------- /win/streamlink/utils/data.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Dict, List, Union 2 | 3 | 4 | def search_dict(data: Union[Dict, List], key: Any): 5 | """ 6 | Search for a key in a nested dict, or list of nested dicts, and return the values. 7 | 8 | :param data: dict/list to search 9 | :param key: key to find 10 | :return: matches for key 11 | """ 12 | if isinstance(data, dict): 13 | for dkey, value in data.items(): 14 | if dkey == key: 15 | yield value 16 | yield from search_dict(value, key) 17 | elif isinstance(data, list): 18 | for value in data: 19 | yield from search_dict(value, key) 20 | -------------------------------------------------------------------------------- /win/streamlink/utils/module.py: -------------------------------------------------------------------------------- 1 | from importlib.machinery import SOURCE_SUFFIXES, FileFinder, SourceFileLoader 2 | from importlib.util import module_from_spec 3 | 4 | 5 | _loader_details = [(SourceFileLoader, SOURCE_SUFFIXES)] 6 | 7 | 8 | def load_module(name, path=None): 9 | finder = FileFinder(path, *_loader_details) 10 | spec = finder.find_spec(name) 11 | if not spec or not spec.loader: 12 | raise ImportError(f"no module named {name}") 13 | mod = module_from_spec(spec) 14 | spec.loader.exec_module(mod) 15 | return mod 16 | --------------------------------------------------------------------------------