feat(schedule): add tv schedule group page

This commit is contained in:
2026-09-05 16:30:37 +03:00
parent 24214ebc3a
commit d44c8076b4
6 changed files with 134 additions and 7 deletions
@@ -1,6 +1,7 @@
import datetime
import typing
from pathlib import Path
from urllib.parse import parse_qsl, urlencode, urlsplit
from babel.dates import format_date
from fastapi import Request
@@ -28,6 +29,14 @@ def base_context_processor(request: Request) -> dict:
}
def build_url(request: Request, query: dict | None = None) -> str:
parts = urlsplit(str(request.url))
if query:
query_dict = {**dict(parse_qsl(parts.query)), **query}
parts = parts._replace(query=urlencode(query_dict))
return parts.geturl()
def build_templates(
templates_dir: Path | None = None, filters: dict | None = None, context_processor: ContextProcessor | None = None
) -> Jinja2Templates:
@@ -46,6 +55,7 @@ def build_templates(
"datetime": datetime,
"tag_util": TagUtil,
"DATE_FORMAT": "E, d MMMM Y",
"build_url": build_url,
}
)
if filters:
@@ -68,3 +68,24 @@ async def get_channel_tag(request: AppRequest, schedule_api: ScheduleApiDepends,
"response": response,
},
)
@router.get("/group", response_class=HTMLResponse)
async def get_group(request: AppRequest, ids: str, date: datetime.date | None = None, tag: str | None = None):
date = date or datetime.date.today()
if tag:
date = TagUtil.parse_tag(tag).date
channels = [item.split(":") for item in ids.split(" ")]
responses = [
await request.app.state.api.get_api(ScheduleApi, provider).get_schedule(channel_id, date)
for provider, channel_id in channels
]
return templates.TemplateResponse(
request=request,
name="group.html",
context={
"tag_util": TagUtil,
"datetime": datetime,
"responses": responses,
},
)
@@ -1,4 +1,7 @@
{% extends "base.html" %}
{% set live = request.query_params.live == '1' %}
{% set date = response.date %}
{% block title %}
{{_("TV program")}} | {{response.channel.name}} | {{format_date(response.date, DATE_FORMAT,
locale=request.state.language)}}
@@ -10,19 +13,23 @@ locale=request.state.language)}}
{% endblock %}
{% block content %}
<h4>
<a class="icon-link {{'disabled' if response.date == datetime.date.today() else ''}}"
href="../tag/{{tag_util.create_tag('day', response.date, -1)}}">
<h4 class="mb-4">
<a class="icon-link {{'disabled' if date == datetime.date.today() else ''}}"
href="{{build_url(request, query={'date': date - datetime.timedelta(days=1)})}}">
<i class="bi bi-arrow-left-square"></i>
</a>
<a class="icon-link"
href="../../..">
href=".">
<i class="bi bi-arrow-up-square"></i>
</a>
<a class="icon-link {{'text-secondary' if not live else ''}}"
href="{{build_url(request, query={'live': 0 if live else 1})}}">
<i class="bi bi-cast"></i>
</a>
<span class="badge rounded-pill text-bg-secondary fs-6">{{provider}}</span>
<span>{{response.channel.name}} | {{format_date(response.date, DATE_FORMAT, locale=request.state.language)}}</span>
<a class="icon-link"
href="../tag/{{tag_util.create_tag('day', response.date, 1)}}">
href="{{build_url(request, query={'date': date + datetime.timedelta(days=1)})}}">
<i class="bi bi-arrow-right-square"></i>
</a>
</h4>
@@ -35,7 +42,7 @@ locale=request.state.language)}}
</thead>
<tbody>
{% for value in response.values %}
<tr class="{{'table-success' if value.live else ''}}">
<tr class="{{'d-none' if (live and not value.live) else 'table-success' if (not live and value.live) else ''}}">
<td>
<span>{{value.start.strftime('%H:%M')}}</span>
<span class="small ms-1">({{(value.end - value.start) | timedelta_format}})</span>
@@ -0,0 +1,59 @@
{% extends "base.html" %}
{% set live = request.query_params.live == '1' %}
{% set date = responses[0].date %}
{% block title %}
{{_("TV program")}}
{% endblock %}
{% block header %}
<app-link href="/schedule"
icon="tv">{{_("TV program")}}</app-link>
{% endblock %}
{% block content %}
<h4 class="mb-4">
<a class="icon-link {{'disabled' if date == datetime.date.today() else ''}}"
href="{{build_url(request, query={'date': date - datetime.timedelta(days=1)})}}">
<i class="bi bi-arrow-left-square"></i>
</a>
<a class="icon-link"
href=".">
<i class="bi bi-arrow-up-square"></i>
</a>
<a class="icon-link {{'text-secondary' if not live else ''}}"
href="{{build_url(request, query={'live': 0 if live else 1})}}">
<i class="bi bi-cast"></i>
</a>
<span>{{format_date(date, DATE_FORMAT, locale=request.state.language)}}</span>
<a class="icon-link"
href="{{build_url(request, query={'date': date + datetime.timedelta(days=1)})}}">
<i class="bi bi-arrow-right-square"></i>
</a>
</h4>
{% for response in responses %}
<h5>
<span class="badge rounded-pill text-bg-secondary fs-6">{{response.channel.provider}}</span>
<span>{{response.channel.name}}</span>
</h5>
<table class="table">
<thead>
<tr>
<td></td>
<td></td>
</tr>
</thead>
<tbody>
{% for value in response.values %}
<tr class="{{'d-none' if (live and not value.live) else 'table-success' if (not live and value.live) else ''}}">
<td>
<span>{{value.start.strftime('%H:%M')}}</span>
<span class="small ms-1">({{(value.end - value.start) | timedelta_format}})</span>
</td>
<td>{{value.label}}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endfor %}
{% endblock %}