Compare commits

..
1 Commits
Author SHA1 Message Date
shmyga 951cb06b4c ci(action): add setup composite action
test / test (push) Failing after 46s
2026-09-27 21:08:06 +03:00
16 changed files with 70 additions and 103 deletions
+32
View File
@@ -0,0 +1,32 @@
name: "Setup"
description: "Setup project"
inputs:
python-version:
default: "3.14"
runs:
using: "composite"
steps:
- id: setup-python
uses: actions/setup-python@v7
with:
python-version: ${{ inputs.python-version }}
cache: "pip"
- run: |
pip install project-manager \
--index-url https://git.shmyga.ru/api/packages/InfernalGames/pypi/simple/ \
--extra-index-url https://pypi.org/simple
- run: pm setup:config -vv
# setup python venv
- run: pip install poetry
- id: cache-venv
uses: actions/cache@v6
with:
path: .venv
key: venv-${{ runner.os }}-python-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
- if: steps.cache-venv.outputs.cache-hit != 'true'
run: pm setup:python -vv
@@ -1,20 +1,20 @@
name: release name: build
on: on:
push: push:
tags: tags: ["*"]
- "[0-9]+.[0-9]+.[0-9]+"
jobs: jobs:
release: build:
needs: test
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- uses: https://git.shmyga.ru/InfernalGames/project-manager@master - uses: ./.gitea/actions/setup
- run: pm build:docker -vv - run: pm build:docker -vv
- uses: docker/login-action@v4 - uses: docker/login-action@v4
with: with:
registry: git.shmyga.ru registry: git.shmyga.ru
username: ${{ vars.PUBLISH_USERNAME }} username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.PUBLISH_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- run: pm publish:docker -vv - run: pm publish:docker -vv
-22
View File
@@ -1,22 +0,0 @@
name: deploy
on:
workflow_run:
workflows: [release]
types: [completed]
jobs:
deploy:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
steps:
- uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USERNAME }}
key: ${{ secrets.DEPLOY_KEY }}
passphrase: ${{ secrets.DEPLOY_PASSPHRASE }}
script: |
cd /opt/gallery
docker compose pull
docker compose up -d
+2 -4
View File
@@ -7,8 +7,6 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- uses: https://git.shmyga.ru/InfernalGames/project-manager@master - uses: ./.gitea/actions/setup
with:
setup-python: true
- run: pm lint -vv - run: pm lint -vv
- run: pm test -vv # - run: pm test -vv
+1 -1
View File
@@ -27,7 +27,7 @@ FROM python:3.14-slim
ENV PLAYWRIGHT_BROWSERS_PATH="/opt/playwright-browsers" ENV PLAYWRIGHT_BROWSERS_PATH="/opt/playwright-browsers"
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \
pip install playwright==1.62 && \ pip install playwright && \
playwright install --with-deps chromium playwright install --with-deps chromium
ENV PATH="/app/.venv/bin:$PATH" ENV PATH="/app/.venv/bin:$PATH"
-2
View File
@@ -1,7 +1,5 @@
# API Gallery # API Gallery
[![test](https://git.shmyga.ru/InfernalGames/gallery/actions/workflows/test.yaml/badge.svg?branch=master)](https://git.shmyga.ru/InfernalGames/gallery/actions?workflow=test.yaml)
Currently implemented APIs: Currently implemented APIs:
- Weather forecast - Weather forecast
+11 -23
View File
@@ -1,3 +1,4 @@
import datetime
from pathlib import Path from pathlib import Path
from urllib import parse from urllib import parse
@@ -12,7 +13,9 @@ from gallery.easel.depends.schedule import (
ScheduleApiDepends, ScheduleApiDepends,
ScheduleGroupChannelsDepends, ScheduleGroupChannelsDepends,
) )
from gallery.sketch.bundle import ApiBundle
from gallery.sketch.schedule.api import ScheduleApi from gallery.sketch.schedule.api import ScheduleApi
from gallery.sketch.schedule.model import Schedule
from ..common.utils.template import build_templates from ..common.utils.template import build_templates
from .filters import timedelta_format from .filters import timedelta_format
@@ -49,6 +52,12 @@ async def get_schedule_index(request: AppRequest, provider: str | None = None, q
) )
async def _get_group_schedule(api: ApiBundle, channels: list[tuple[str, str]], date: datetime.date) -> list[Schedule]:
return [
await api.get_api(ScheduleApi, provider).get_schedule(channel_id, date) for provider, channel_id in channels
]
@router.get("/group/{group_id}", response_class=RedirectResponse) @router.get("/group/{group_id}", response_class=RedirectResponse)
async def get_group_default(group_id: str): async def get_group_default(group_id: str):
return RedirectResponse(f"{parse.quote(group_id)}/today") return RedirectResponse(f"{parse.quote(group_id)}/today")
@@ -63,16 +72,7 @@ async def get_group(request: AppRequest, channels: ScheduleGroupChannelsDepends,
return templates.TemplateResponse( return templates.TemplateResponse(
request=request, request=request,
name="group.html", name="group.html",
context={ context={"responses": responses},
"responses": responses,
"api_route": "/".join(
[
"/api/schedule/group",
request.path_params["group_id"],
request.path_params["date"],
]
),
},
) )
@@ -87,17 +87,5 @@ async def get_channel(request: AppRequest, schedule_api: ScheduleApiDepends, cha
return templates.TemplateResponse( return templates.TemplateResponse(
request=request, request=request,
name="channel.html", name="channel.html",
context={ context={"response": response},
"response": response,
"api_route": (
"/".join(
[
"/api/schedule",
request.path_params["provider"],
request.path_params["channel"],
request.path_params["date"],
]
)
),
},
) )
@@ -10,9 +10,6 @@ locale=request.state.language)}}
{% block header %} {% block header %}
<app-link href="/schedule" <app-link href="/schedule"
icon="tv">{{_("TV program")}}</app-link> icon="tv">{{_("TV program")}}</app-link>
<app-link target="_blank"
href="{{ api_route }}"
icon="filetype-json"></app-link>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@@ -47,8 +44,7 @@ locale=request.state.language)}}
{% for value in response.values %} {% 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 ''}}"> <tr class="{{'d-none' if (live and not value.live) else 'table-success' if (not live and value.live) else ''}}">
<td> <td>
<span>{{value.start.strftime('%H:%M')}}</span>&nbsp;<span class="small ms-1">({{(value.end - value.start) | <span>{{value.start.strftime('%H:%M')}}</span>&nbsp;<span class="small ms-1">({{(value.end - value.start) | timedelta_format}})</span>
timedelta_format}})</span>
</td> </td>
<td>{{value.label}}</td> <td>{{value.label}}</td>
</tr> </tr>
@@ -9,9 +9,6 @@
{% block header %} {% block header %}
<app-link href="/schedule" <app-link href="/schedule"
icon="tv">{{_("TV program")}}</app-link> icon="tv">{{_("TV program")}}</app-link>
<app-link target="_blank"
href="{{ api_route }}"
icon="filetype-json"></app-link>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
+3 -16
View File
@@ -3,7 +3,6 @@ from typing import Literal
from fastapi import APIRouter from fastapi import APIRouter
from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.responses import HTMLResponse, RedirectResponse
from starlette.datastructures import URL
from gallery.easel.core import AppRequest from gallery.easel.core import AppRequest
from gallery.easel.depends.api import api_resolver from gallery.easel.depends.api import api_resolver
@@ -37,18 +36,7 @@ def build_weather_response(request: AppRequest, response: WeatherResponse):
return templates.TemplateResponse( return templates.TemplateResponse(
request=request, request=request,
name="weather.html", name="weather.html",
context={ context={"response": response},
"response": response,
"api_route": "/".join(
[
"/api/weather",
request.path_params["provider"],
request.path_params["location"],
response.period,
request.path_params.get("date") or request.path_params.get("days") or "",
]
),
},
) )
@@ -82,9 +70,8 @@ async def get_weather_days(request: AppRequest, weather_api: WeatherApiDepends,
# TODO: tag mode route deprecated, used in tizen app # TODO: tag mode route deprecated, used in tizen app
@router.get("/{provider}/{location}/tag/days-{days}", response_class=RedirectResponse) @router.get("/{provider}/{location}/tag/days-{days}", response_class=RedirectResponse)
async def get_tag_days(request: AppRequest, days: int): async def get_tag_days(days: int):
url = URL(path=f"../days/{days}", query=request.url.query) return RedirectResponse(f"../days/{days}")
return RedirectResponse(url)
# TODO: tag mode route deprecated, used in tizen app # TODO: tag mode route deprecated, used in tizen app
@@ -7,9 +7,6 @@ locale=request.state.language)}}{% endblock %}
{% block header %} {% block header %}
<app-link href="/weather" <app-link href="/weather"
icon="brightness-high">{{_("Weather")}}</app-link> icon="brightness-high">{{_("Weather")}}</app-link>
<app-link target="_blank"
href="{{ api_route }}"
icon="filetype-json"></app-link>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
+1 -2
View File
@@ -29,7 +29,6 @@ class YandexTVApi(ScheduleApi):
return self.channel_parser.parse(data) return self.channel_parser.parse(data)
async def get_schedule(self, channel_id: str, date: datetime.date) -> Schedule: async def get_schedule(self, channel_id: str, date: datetime.date) -> Schedule:
channel_path_part = "channel" if "-" in channel_id else "channels" endpoint = f"{self.lr}/channel/{channel_id}?date={date:%Y-%m-%d}"
endpoint = f"{self.lr}/{channel_path_part}/{channel_id}?date={date:%Y-%m-%d}"
data = await self.source.request(endpoint) data = await self.source.request(endpoint)
return self.schedule_parser.parse(ScheduleData(date, channel_id, data)) return self.schedule_parser.parse(ScheduleData(date, channel_id, data))
+9 -9
View File
@@ -1,16 +1,16 @@
[project] [project]
name = "gallery" name = "gallery"
description = "" description = ""
version = "0.10.5" version = "0.10.2"
readme = "README.md" readme = "README.md"
authors = [{ name = "shmyga", email = "shmyga.z@gmail.com" }] authors = [{ name = "shmyga", email = "shmyga.z@gmail.com" }]
requires-python = ">=3.14,<4.0" requires-python = '>=3.14,<4.0'
dependencies = [ dependencies = [
"aiohttp (>=3.14,<4.0)", 'aiohttp (>=3.14,<4.0)',
"beautifulsoup4 (>=4.15,<5.0)", 'beautifulsoup4 (>=4.15,<5.0)',
"dateparser (>=1.4,<2.0)", 'dateparser (>=1.4,<2.0)',
"pydantic (>=2.13,<3.0)", 'pydantic (>=2.13,<3.0)',
"aiocache[redis] (>=0.12,<0.13)", 'aiocache[redis] (>=0.12,<0.13)',
"playwright (>=1.62.0,<2.0.0)", "playwright (>=1.62.0,<2.0.0)",
] ]
@@ -30,10 +30,10 @@ gallery = "gallery.main:run"
[tool.poetry] [tool.poetry]
packages = [{ include = "gallery" }] packages = [{ include = "gallery" }]
requires-poetry = ">=2.0.0,<3.0.0" requires-poetry = '>=2.0.0,<3.0.0'
[build-system] [build-system]
requires = ["poetry-core (>=2.0.0,<3.0.0)"] requires = ['poetry-core (>=2.0.0,<3.0.0)']
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"
[tool.black] [tool.black]
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "gallery", "name": "gallery",
"version": "0.10.5", "version": "0.10.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "gallery", "name": "gallery",
"version": "0.10.5", "version": "0.10.2",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@popperjs/core": "^2.11.8", "@popperjs/core": "^2.11.8",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "gallery", "name": "gallery",
"version": "0.10.5", "version": "0.10.2",
"type": "module", "type": "module",
"scripts": { "scripts": {
"build": "webpack --mode production", "build": "webpack --mode production",
-3
View File
@@ -48,7 +48,4 @@
&.bi-cast { &.bi-cast {
mask-image: url(bootstrap-icons/icons/cast.svg); mask-image: url(bootstrap-icons/icons/cast.svg);
} }
&.bi-filetype-json {
mask-image: url(bootstrap-icons/icons/filetype-json.svg);
}
} }