feat(yandextv): add yandextv schedule api
This commit is contained in:
@@ -4,8 +4,7 @@ import logging
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from gallery.sketch.schedule.api import ScheduleApi
|
||||
from gallery.sketch.schedule.catalog import ChannelId
|
||||
from gallery.sketch.schedule.model import Channel, Schedule, ScheduleValue
|
||||
from gallery.sketch.schedule.model import Channel, ChannelId, Schedule, ScheduleValue
|
||||
from gallery.sketch.source import ApiSource
|
||||
|
||||
logger = logging.getLogger("matchtv")
|
||||
@@ -15,7 +14,7 @@ class MatchTvApi(ScheduleApi):
|
||||
PROVIDER = "matchtv"
|
||||
SOURCE = ApiSource("https://matchtv.ru")
|
||||
|
||||
async def get_channels(self) -> list[str]:
|
||||
async def get_channels(self) -> list[ChannelId]:
|
||||
return [
|
||||
ChannelId.MATCH_TV,
|
||||
ChannelId.MATCH_IGRA,
|
||||
@@ -27,7 +26,7 @@ class MatchTvApi(ScheduleApi):
|
||||
]
|
||||
|
||||
async def get_channel_schedule(
|
||||
self, channel_id: str, date: datetime.date
|
||||
self, channel_id: ChannelId, date: datetime.date
|
||||
) -> Schedule:
|
||||
endpoint = f"tvguide/{channel_id}?date={date:%Y%m%d}"
|
||||
data = await self.SOURCE.request(endpoint)
|
||||
@@ -46,8 +45,12 @@ class MatchTvApi(ScheduleApi):
|
||||
for item in soup.select(
|
||||
".p-tv-guide-schedule-channel-carcass__transmissions .p-tv-guide-schedule-channel-transmission"
|
||||
):
|
||||
title = item.select_one(".p-tv-guide-schedule-channel-transmission__title").text.strip()
|
||||
time_str = item.select_one(".p-tv-guide-schedule-channel-transmission__time-block").text.strip()
|
||||
title = item.select_one(
|
||||
".p-tv-guide-schedule-channel-transmission__title"
|
||||
).text.strip()
|
||||
time_str = item.select_one(
|
||||
".p-tv-guide-schedule-channel-transmission__time-block"
|
||||
).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:
|
||||
|
||||
0
gallery/painting/yandextv/__init__.py
Normal file
0
gallery/painting/yandextv/__init__.py
Normal file
82
gallery/painting/yandextv/api.py
Normal file
82
gallery/painting/yandextv/api.py
Normal file
@@ -0,0 +1,82 @@
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from gallery.sketch.schedule.api import ScheduleApi
|
||||
from gallery.sketch.schedule.model import Channel, ChannelId, Schedule, ScheduleValue
|
||||
from gallery.sketch.source import ApiSource
|
||||
|
||||
logger = logging.getLogger("matchtv")
|
||||
|
||||
CHANNELS_MAP: dict[ChannelId, str] = {
|
||||
ChannelId.MATCH_TV: "match-tv-49",
|
||||
ChannelId.MATCH_IGRA: "match-igra-1174",
|
||||
ChannelId.MATCH_ARENA: "match-arena-1173",
|
||||
ChannelId.MATCH_FUTBOL_1: "match-futbol-1-646",
|
||||
ChannelId.MATCH_FUTBOL_2: "match-futbol-2-593",
|
||||
ChannelId.MATCH_FUTBOL_3: "match-futbol-3-797",
|
||||
ChannelId.MATCH_STRANA: "match-strana-1356",
|
||||
ChannelId.MATCH_PLANETA: "match-planeta-1177",
|
||||
ChannelId.EUROSPORT: "eurosport-677",
|
||||
ChannelId.EUROSPORT_2: "eurosport-2-720",
|
||||
ChannelId.START: "start-103",
|
||||
}
|
||||
|
||||
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 get_channels(self) -> list[ChannelId]:
|
||||
return list(CHANNELS_MAP.keys())
|
||||
|
||||
async def get_channel_schedule(
|
||||
self, channel_id: ChannelId, date: datetime.date
|
||||
) -> Schedule:
|
||||
endpoint = f"channel/{CHANNELS_MAP[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), date=date, values=values
|
||||
)
|
||||
5
gallery/painting/yandextv/mock/__init__.py
Normal file
5
gallery/painting/yandextv/mock/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from pathlib import Path
|
||||
|
||||
from gallery.sketch.mock import MockData
|
||||
|
||||
YANDEXTV_MOCK_DATA = MockData(Path(__file__).parent / "data")
|
||||
88
gallery/painting/yandextv/mock/data/test.html
Normal file
88
gallery/painting/yandextv/mock/data/test.html
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user