Skip to content

Commit

Permalink
initial web api structure
Browse files Browse the repository at this point in the history
  • Loading branch information
andrew (from workstation) committed Dec 8, 2020
1 parent 90bc276 commit f3f57bb
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 43 deletions.
7 changes: 4 additions & 3 deletions smart_tv_telegram/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import unittest

from smart_tv_telegram import Http, Mtproto, Config, Bot
from smart_tv_telegram.devices import FINDERS
from smart_tv_telegram.tests import test_tools, test_http


Expand All @@ -27,10 +28,10 @@ def open_config(parser: argparse.ArgumentParser, arg: str) -> Config:


async def async_main(config: Config):
finders = [f() for f in FINDERS if f.is_enabled(config)]
mtproto = Mtproto(config)
http = Http(mtproto, config)

bot = Bot(mtproto, config, http)
http = Http(mtproto, config, finders)
bot = Bot(mtproto, config, http, finders)
bot.prepare()

await mtproto.start()
Expand Down
24 changes: 8 additions & 16 deletions smart_tv_telegram/bot.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import abc
import enum
import functools
import html
import traceback
import typing
import enum
import html

import async_timeout
from pyrogram import Client, filters
Expand All @@ -12,7 +12,7 @@
from pyrogram.types import ReplyKeyboardRemove, Message, KeyboardButton, ReplyKeyboardMarkup

from . import Config, Mtproto, Http
from .devices import UpnpDeviceFinder, ChromecastDeviceFinder, VlcDeviceFinder, XbmcDeviceFinder, Device
from .devices import Device, DeviceFinder
from .tools import build_uri, pyrogram_filename, secret_token


Expand Down Expand Up @@ -77,11 +77,13 @@ class Bot:
_state_machine: TelegramStateMachine
_mtproto: Mtproto
_http: Http
_finders: typing.List[DeviceFinder]

def __init__(self, mtproto: Mtproto, config: Config, http: Http):
def __init__(self, mtproto: Mtproto, config: Config, http: Http, finders: typing.List[DeviceFinder]):
self._config = config
self._mtproto = mtproto
self._http = http
self._finders = finders
self._state_machine = TelegramStateMachine()

def prepare(self):
Expand Down Expand Up @@ -144,18 +146,8 @@ async def _select_device(self, _: Client, message: Message):
async def _new_document(self, _: Client, message: Message):
devices = []

if self._config.upnp_enabled:
devices.extend(await UpnpDeviceFinder.find(self._config))

if self._config.chromecast_enabled:
# noinspection PyUnresolvedReferences
devices.extend(await ChromecastDeviceFinder.find(self._config))

if self._config.xbmc_enabled:
devices.extend(await XbmcDeviceFinder.find(self._config))

if self._config.vlc_enabled:
devices.extend(await VlcDeviceFinder.find(self._config))
for finder in self._finders:
devices.extend(await finder.find(self._config))

if devices:
try:
Expand Down
16 changes: 14 additions & 2 deletions smart_tv_telegram/devices/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
from .device import Device, DeviceFinder
import typing

from .device import Device, DeviceFinder, ROUTERS_RET_TYPE
from .upnp_device import UpnpDevice, UpnpDeviceFinder
from .chromecast_device import ChromecastDevice, ChromecastDeviceFinder
from .vlc_device import VlcDeviceFinder, VlcDevice
from .xbmc_device import XbmcDevice, XbmcDeviceFinder


FINDERS: typing.List[typing.Type[DeviceFinder]] = [
UpnpDeviceFinder,
ChromecastDeviceFinder,
XbmcDeviceFinder,
VlcDeviceFinder
]


__all__ = [
"Device",
"DeviceFinder",
Expand All @@ -15,5 +25,7 @@
"XbmcDevice",
"XbmcDeviceFinder",
"VlcDevice",
"VlcDeviceFinder"
"VlcDeviceFinder",
"FINDERS",
"ROUTERS_RET_TYPE"
]
11 changes: 10 additions & 1 deletion smart_tv_telegram/devices/chromecast_device.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import typing

import pychromecast
from aiohttp.web_request import Request
from aiohttp.web_response import Response

from . import Device, DeviceFinder
from . import Device, DeviceFinder, ROUTERS_RET_TYPE
from .. import Config
from ..tools import run_method_in_executor

Expand Down Expand Up @@ -42,3 +44,10 @@ def find(config: Config) -> typing.List[Device]:
for device in pychromecast.get_chromecasts(
timeout=config.chromecast_scan_timeout)[0]
]

@staticmethod
def is_enabled(config: Config) -> bool:
return config.chromecast_enabled

async def get_routers(self, config: Config) -> ROUTERS_RET_TYPE:
return []
18 changes: 17 additions & 1 deletion smart_tv_telegram/devices/device.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import abc
import typing

from aiohttp.web_request import Request
from aiohttp.web_response import Response

from .. import Config


ROUTERS_RET_TYPE = typing.List[typing.Tuple[str, typing.Callable[[Request], typing.Awaitable[Response]]]]


__all__ = [
"Device",
"DeviceFinder"
"DeviceFinder",
"ROUTERS_RET_TYPE"
]


Expand Down Expand Up @@ -37,3 +44,12 @@ class DeviceFinder(abc.ABC):
@abc.abstractmethod
async def find(config: Config) -> typing.List[Device]:
raise NotImplementedError

@staticmethod
@abc.abstractmethod
def is_enabled(config: Config) -> bool:
raise NotImplementedError

@abc.abstractmethod
async def get_routers(self, config: Config) -> ROUTERS_RET_TYPE:
raise NotImplementedError
33 changes: 17 additions & 16 deletions smart_tv_telegram/devices/upnp_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
from xml.sax.saxutils import escape

import async_upnp_client
from aiohttp.web_request import Request
from aiohttp.web_response import Response
from async_upnp_client import UpnpFactory, UpnpError
from async_upnp_client.aiohttp import AiohttpRequester
from async_upnp_client.search import async_search

from . import Device, DeviceFinder
from . import Device, DeviceFinder, ROUTERS_RET_TYPE
from .. import Config
from ..tools import ascii_only

Expand All @@ -20,21 +22,13 @@

_AVTRANSPORT_SCHEMA = "urn:schemas-upnp-org:service:AVTransport:1"

_DLL_METADATA = """
<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"
xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/"
xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/">
<item id="R:0/0/0" parentID="R:0/0" restricted="true">
<dc:title>{title}</dc:title>
<upnp:class>object.item.videoItem.movie</upnp:class>
<desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">
SA_RINCON65031_
</desc>
<res protocolInfo="http-get:*:video/mp4:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000">{url}</res>
</item>
</DIDL-Lite>
"""
_DLL_METADATA = """<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/"
xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"> <item id="R:0/0/0" parentID="R:0/0" restricted="true">
<dc:title>{title}</dc:title> <upnp:class>object.item.videoItem.movie</upnp:class> <desc id="cdudn"
nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/"> SA_RINCON65031_ </desc> <res
protocolInfo="http-get:*:video/mp4:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000">{
url}</res> </item> </DIDL-Lite> """


class UpnpDevice(Device):
Expand Down Expand Up @@ -84,3 +78,10 @@ async def on_response(data: typing.Mapping[str, typing.Any]) -> None:
async_callback=on_response)

return [UpnpDevice(device) for device in devices]

@staticmethod
def is_enabled(config: Config) -> bool:
return config.upnp_enabled

async def get_routers(self, config: Config) -> ROUTERS_RET_TYPE:
return []
12 changes: 11 additions & 1 deletion smart_tv_telegram/devices/vlc_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import logging
import typing

from . import DeviceFinder, Device
from aiohttp.web_request import Request
from aiohttp.web_response import Response

from . import DeviceFinder, Device, ROUTERS_RET_TYPE
from .. import Config


Expand Down Expand Up @@ -97,3 +100,10 @@ async def find(config: Config) -> typing.List[Device]:
VlcDevice(VlcDeviceParams(params))
for params in config.vlc_devices
]

@staticmethod
def is_enabled(config: Config) -> bool:
return config.vlc_enabled

async def get_routers(self, config: Config) -> ROUTERS_RET_TYPE:
return []
12 changes: 10 additions & 2 deletions smart_tv_telegram/devices/xbmc_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
import typing

import aiohttp
from aiohttp.web_request import Request
from aiohttp.web_response import Response

from .. import Config
from . import Device, DeviceFinder

from . import Device, DeviceFinder, ROUTERS_RET_TYPE

__all__ = [
"XbmcDevice",
Expand Down Expand Up @@ -145,3 +146,10 @@ async def find(config: Config) -> typing.List[Device]:
XbmcDevice(XbmcDeviceParams(params))
for params in config.xbmc_devices
]

@staticmethod
def is_enabled(config: Config) -> bool:
return config.xbmc_enabled

async def get_routers(self, config: Config) -> ROUTERS_RET_TYPE:
return []
11 changes: 10 additions & 1 deletion smart_tv_telegram/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from pyrogram.raw.types import MessageMediaDocument, Document

from . import Config, Mtproto
from .devices import DeviceFinder
from .tools import parse_http_range, mtproto_filename, serialize_token, AsyncDebounce


Expand All @@ -19,15 +20,17 @@
class Http:
_mtproto: Mtproto
_config: Config
_finders: typing.List[DeviceFinder]

_tokens: typing.Set[int]
_downloaded_blocks: typing.Dict[int, typing.Set[int]]
_stream_debounce: typing.Dict[int, AsyncDebounce]
_stream_trasports: typing.Dict[int, typing.Set[asyncio.Transport]]

def __init__(self, mtproto: Mtproto, config: Config):
def __init__(self, mtproto: Mtproto, config: Config, finders: typing.List[DeviceFinder]):
self._mtproto = mtproto
self._config = config
self._finders = finders

self._tokens = set()
self._downloaded_blocks = dict()
Expand All @@ -41,6 +44,12 @@ async def start(self):
app.add_routes([web.put("/stream/{message_id}/{token}", self._upnp_discovery_handler)])
app.add_routes([web.get("/healthcheck", self._health_check_handler)])

for finder in self._finders:
routers = await finder.get_routers(self._config)

for path, handler in routers:
app.add_routes([web.get(path, handler)])

# noinspection PyProtectedMember
await web._run_app(app, host=self._config.listen_host, port=self._config.listen_port)

Expand Down

0 comments on commit f3f57bb

Please sign in to comment.