91 lines
3.5 KiB
Python
91 lines
3.5 KiB
Python
import datetime
|
|
import json
|
|
import logging
|
|
|
|
from bs4 import BeautifulSoup
|
|
|
|
from gallery.sketch.schedule.api import ScheduleApi
|
|
from gallery.sketch.schedule.model import Channel, Schedule, ScheduleValue
|
|
from gallery.sketch.source import ApiSource
|
|
|
|
logger = logging.getLogger("matchtv")
|
|
|
|
|
|
HEADERS: dict[str, str] = {
|
|
"Accept": (
|
|
"text/html,"
|
|
"application/xhtml+xml,"
|
|
"application/xml;q=0.9,"
|
|
"image/avif,image/webp,"
|
|
"image/apng,*/*;q=0.8,"
|
|
"application/signed-exchange;v=b3;q=0.9"
|
|
),
|
|
"Accept-Encoding": "gzip, deflate, br",
|
|
"Accept-Language": "en-US,en;q=0.9",
|
|
"Connection": "keep-alive",
|
|
"Host": "tv.yandex.ru",
|
|
"sec-ch-ua": '"Chromium";v="100", " Not A;Brand";v="99"',
|
|
"sec-ch-ua-mobile": "?0",
|
|
"sec-ch-ua-platform": '"Linux"',
|
|
"Sec-Fetch-Dest": "document",
|
|
"Sec-Fetch-Mode": "navigate",
|
|
"Sec-Fetch-Site": "none",
|
|
"Sec-Fetch-User": "?1",
|
|
"Upgrade-Insecure-Requests": "1",
|
|
"User-Agent": (
|
|
"Mozilla/5.0 (X11; Linux x86_64) "
|
|
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
"Chrome/100.0.4896.133 "
|
|
"Safari/537.36"
|
|
),
|
|
}
|
|
|
|
|
|
class YandexTvApi(ScheduleApi):
|
|
PROVIDER = "yandextv"
|
|
SOURCE = ApiSource("https://tv.yandex.ru", headers=HEADERS)
|
|
|
|
async def find_channels(self, query: str) -> list[Channel]:
|
|
url = (
|
|
"https://suggest-multi.yandex.ru/suggest-tv2?"
|
|
f"v=4&uil=ru&lr=10&count_channels=4&count_programs=4&sn=50&part={query}"
|
|
)
|
|
_, values = json.loads(await self.SOURCE.request(url))
|
|
result = []
|
|
for _, name, content in values:
|
|
if content["label"] == "Каналы":
|
|
channel_id = content["url"].split("/")[-1]
|
|
result.append(Channel(id=channel_id, name=name, provider=self.provider))
|
|
return result
|
|
|
|
async def get_schedule(self, channel_id: str, date: datetime.date) -> Schedule:
|
|
endpoint = f"channels/{channel_id}?date={date:%Y-%m-%d}"
|
|
data = await self.SOURCE.request(endpoint)
|
|
soup = BeautifulSoup(data, features="html.parser")
|
|
if soup.select_one(".CheckboxCaptcha") is not None:
|
|
raise RuntimeError("Captcha")
|
|
values = []
|
|
channel_name = soup.select_one(".channel-header__text").text.strip()
|
|
current_day = datetime.datetime.combine(date.today(), datetime.datetime.min.time())
|
|
end = current_day + datetime.timedelta(days=1, hours=6)
|
|
prev_value: ScheduleValue | None = None
|
|
for item in soup.select(".channel-schedule .channel-schedule__event"):
|
|
title = item.select_one(".channel-schedule__title").text.strip()
|
|
time_str = item.select_one(".channel-schedule__time").text.strip()
|
|
hours, minutes = map(int, time_str.split(":"))
|
|
item_date = current_day.replace(hour=hours, minute=minutes)
|
|
if prev_value is not None and item_date.hour < prev_value.start.hour:
|
|
current_day += datetime.timedelta(days=1)
|
|
item_date += datetime.timedelta(days=1)
|
|
live = item.select_one(".channel-schedule__info .icon_live") is not None
|
|
value = ScheduleValue(start=item_date, end=end, label=title, live=live)
|
|
values.append(value)
|
|
if prev_value is not None:
|
|
prev_value.end = item_date
|
|
prev_value = value
|
|
return Schedule(
|
|
channel=Channel(id=channel_id, name=channel_name, provider=self.provider),
|
|
date=date,
|
|
values=values,
|
|
)
|