From 230ed14f696bb4fbaf8b4456a24bb4e45138a703 Mon Sep 17 00:00:00 2001 From: ndmg Date: Sat, 7 Nov 2026 17:27:59 +0000 Subject: [PATCH] refactoring --- README.md | 46 +++++++++++++++++++++++++++++++ core/authx.py | 2 -- core/config.py | 15 ++++------ core/db.py | 13 ++++++++- docker-compose.yml | 10 +++---- models/photo_model.py | 2 +- schemas/audio_schema.py | 8 +----- schemas/channel_schemas.py | 7 +---- schemas/photo_schemas.py | 8 +----- schemas/playlist_schema.py | 7 +---- schemas/video_schemas.py | 6 +--- search/documents.py | 4 --- search/reindex.py | 3 +- search/search_client.py | 14 ++++++++-- services/path.py | 7 +++++ static/css/style.css | 21 -------------- static/fonts/RobotoMono-Bold.ttf | Bin 87696 -> 0 bytes templates/channel_detail.html | 39 -------------------------- templates/channels.html | 21 -------------- templates/index.html | 23 ---------------- templates/playlist.html | 25 ----------------- templates/video_detail.html | 16 ----------- templates/videos.html | 29 ------------------- utils/downloader.py | 3 +- utils/sync_yt.py | 6 ++-- utils/update_audio.py | 2 +- utils/update_thumbs.py | 3 -- utils/update_yt_videos.py | 4 +++ 28 files changed, 102 insertions(+), 242 deletions(-) create mode 100644 services/path.py delete mode 100644 static/css/style.css delete mode 100644 static/fonts/RobotoMono-Bold.ttf delete mode 100644 templates/channel_detail.html delete mode 100644 templates/channels.html delete mode 100644 templates/index.html delete mode 100644 templates/playlist.html delete mode 100644 templates/video_detail.html delete mode 100644 templates/videos.html diff --git a/README.md b/README.md index e69de29..d670676 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,46 @@ +# RSS / Archive Backend + +Бэкенд для домашнего архива медиафайлов (видео, аудио, фото). +Проект представляет собой асинхронное API на FastAPI, которое собирает данные из локальных директорий, агрегирует их в PostgreSQL и отдает на фронтенд. + +## 🛠 Технологический стек + +- **Язык:** Python 3.14+ +- **Фреймворк:** FastAPI +- **База данных:** PostgreSQL 16 (Docker) +- **ORM:** SQLAlchemy 2.0 (Async) + psycopg (async) +- **Валидация:** Pydantic v2 +- **Сервер:** Uvicorn + +## 📂 Архитектура проекта + +```text +RSS/ +├── core/ # Конфигурация и подключение к БД +│ ├── config.py # Пути к медиа, константы +│ └── db.py # Движок SQLAlchemy, Base, SessionLocal +├── models/ # SQLAlchemy-модели (таблицы) +│ ├── video_model.py +│ ├── audio_model.py +│ └── photo_model.py +├── schemas/ # Pydantic-схемы (валидация и сериализация) +│ ├── video_schemas.py +│ ├── audio_schemas.py +│ └── photo_schemas.py +├── routers/ # Эндпоинты API +│ ├── video.py +│ ├── audio.py +│ ├── photo.py +│ └── ... +├── utils/ # Скрипты синхронизации (парсеры) +│ ├── update_videos.py +│ ├── update_audio.py +│ └── update_thumbs.py +├── media/ # Медиафайлы (монтируется в контейнер/отдается статикой) +│ ├── yt/ # Видео +│ ├── audio/ # Аудио +│ ├── _thumbs/ # Превью для видео +│ ├── _covers/ # Обложки для аудио +│ └── photos/ # Фото +├── main.py # Точка входа, подключение роутеров и статики +└── requirements.txt \ No newline at end of file diff --git a/core/authx.py b/core/authx.py index 34e5b51..0fee7ef 100644 --- a/core/authx.py +++ b/core/authx.py @@ -1,4 +1,3 @@ -# core/authx.py from authx import AuthX, AuthXConfig from .config import settings @@ -7,7 +6,6 @@ config = AuthXConfig( JWT_SECRET_KEY=settings.SECRET_KEY, JWT_ALGORITHM=settings.ALGORITHM, - # откуда берём токен JWT_TOKEN_LOCATION=["cookies"], # access diff --git a/core/config.py b/core/config.py index 77c8ef9..90f35d8 100644 --- a/core/config.py +++ b/core/config.py @@ -1,22 +1,17 @@ from pathlib import Path -BASE_DIR = Path(__file__).resolve().parent.parent # RSS/ +BASE_DIR = Path(__file__).resolve().parent.parent -# ---- видео (YouTube) ---- YT_DIR_NAME = "yt" -YT_DIR = BASE_DIR / "media" / YT_DIR_NAME # RSS/media/ +YT_DIR = BASE_DIR / "media" / YT_DIR_NAME THUMBS = "_thumbs" -THUMBS_DIR = YT_DIR / THUMBS # RSS/media/yt/_thumbs - -# ---- фото (если /photos отдаёт RSS/media/media) ---- -PHOTOS_DIR = BASE_DIR / "media" / "media" # RSS/media/media +THUMBS_DIR = YT_DIR / THUMBS +PHOTOS_DIR = BASE_DIR / "media" / "media" PHOTOS_DIR_RESPNS = BASE_DIR / "media" -# ---- шаблоны, статика ---- TEMPLATES_DIR = BASE_DIR / "templates" STATIC_DIR = BASE_DIR / "static" -# ---- расширения видео ---- VIDEO_EXTENSIONS = {".mp4", ".mkv", ".webm", ".avi", ".mov", ".flv", ".m4v"} AUDIO_EXTENSIONS = {".mp3"} @@ -32,7 +27,7 @@ class Settings(BaseSettings): ALGORITHM: str = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES: int = 30 REFRESH_TOKEN_EXPIRE_DAYS: int = 7 - COOKIE_SECURE: bool = False # True на продакшене (HTTPS) + COOKIE_SECURE: bool = False COOKIE_SAMESITE: str = "lax" COOKIE_DOMAIN: str | None = None diff --git a/core/db.py b/core/db.py index d217857..7421102 100644 --- a/core/db.py +++ b/core/db.py @@ -1,3 +1,6 @@ +import os + +from dotenv import load_dotenv from sqlalchemy.ext.asyncio import ( AsyncSession, async_sessionmaker, @@ -5,12 +8,20 @@ from sqlalchemy.ext.asyncio import ( ) from sqlalchemy.orm import DeclarativeBase +from .config import BASE_DIR + class Base(DeclarativeBase): pass +dotenv_path = BASE_DIR / '.env' +load_dotenv(dotenv_path=dotenv_path) -DB_URL = "postgresql+psycopg://ndmg:marselina5$@127.0.0.1:5432/rssdb" +DB_PASSWORD = os.getenv("DB_PASSWORD") +DB_USER = os.getenv("DB_USER") +DB_NAME = os.getenv("DB_NAME") + +DB_URL = f"postgresql+psycopg://{DB_USER}:{DB_PASSWORD}@127.0.0.1:5432/{DB_NAME}" engine = create_async_engine(DB_URL, echo=True) diff --git a/docker-compose.yml b/docker-compose.yml index c2cbe21..cf18b8a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,9 +3,9 @@ services: image: postgres:16 restart: unless-stopped environment: - - POSTGRES_DB=rssdb - - POSTGRES_USER=ndmg - - POSTGRES_PASSWORD=marselina5$ + - POSTGRES_DB=${DB_NAME} + - POSTGRES_USER=${DB_USER} + - POSTGRES_PASSWORD=${DB_PASSWORD} ports: - "127.0.0.1:5432:5432" volumes: @@ -15,8 +15,8 @@ services: image: getmeili/meilisearch:v1.10 ports: ["7700:7700"] environment: - MEILI_MASTER_KEY: "super-secret-master-key-change-me" - MEILI_ENV: "development" # production требует master key + MEILI_MASTER_KEY: $MEILI_KEY + MEILI_ENV: "development" MEILI_NO_ANALYTICS: "true" volumes: ["meili_data:/meili_data"] diff --git a/models/photo_model.py b/models/photo_model.py index 7f4e668..712be89 100644 --- a/models/photo_model.py +++ b/models/photo_model.py @@ -11,7 +11,7 @@ class Category(Base): id: Mapped[int] = mapped_column(primary_key=True) title: Mapped[str] = mapped_column(String(64)) - photos: Mapped[list[Photo]] = relationship(back_populates="category") # noqa: F821 + photos: Mapped[list[Photo]] = relationship(back_populates="category") class Photo(Base): diff --git a/schemas/audio_schema.py b/schemas/audio_schema.py index 70fbd69..08d2e4b 100644 --- a/schemas/audio_schema.py +++ b/schemas/audio_schema.py @@ -1,12 +1,6 @@ -from urllib.parse import quote - from pydantic import BaseModel, ConfigDict, field_validator - -def encode_path(path: str) -> str: - if not path: - return "" - return "/".join(quote(part, safe="") for part in path.split("/")) +from ..services.path import encode_path class AudioSchema(BaseModel): diff --git a/schemas/channel_schemas.py b/schemas/channel_schemas.py index a2babe5..471b702 100644 --- a/schemas/channel_schemas.py +++ b/schemas/channel_schemas.py @@ -1,13 +1,8 @@ from datetime import date as DateType -from urllib.parse import quote from pydantic import BaseModel, field_validator - -def encode_path(path: str) -> str: - if not path: - return "" - return "/".join(quote(part, safe="") for part in path.split("/")) +from ..services.path import encode_path class ChannelItem(BaseModel): diff --git a/schemas/photo_schemas.py b/schemas/photo_schemas.py index a744e09..529743f 100644 --- a/schemas/photo_schemas.py +++ b/schemas/photo_schemas.py @@ -1,14 +1,8 @@ from __future__ import annotations -from urllib.parse import quote - from pydantic import BaseModel, ConfigDict, field_validator - -def encode_path(path: str) -> str: - if not path: - return "" - return "/".join(quote(part, safe="") for part in path.split("/")) +from ..services.path import encode_path class PhotoItem(BaseModel): diff --git a/schemas/playlist_schema.py b/schemas/playlist_schema.py index ba0f4cf..51f5c14 100644 --- a/schemas/playlist_schema.py +++ b/schemas/playlist_schema.py @@ -1,13 +1,8 @@ from datetime import date as DateType -from urllib.parse import quote from pydantic import BaseModel, field_validator - -def encode_path(path: str) -> str: - if not path: - return "" - return "/".join(quote(part, safe="") for part in path.split("/")) +from ..services.path import encode_path class VideoItem(BaseModel): diff --git a/schemas/video_schemas.py b/schemas/video_schemas.py index 061f04d..564557c 100644 --- a/schemas/video_schemas.py +++ b/schemas/video_schemas.py @@ -1,13 +1,9 @@ from datetime import date as DateType -from urllib.parse import quote from pydantic import BaseModel, ConfigDict, field_validator +from ..services.path import encode_path -def encode_path(path: str) -> str: - if not path: - return "" - return "/".join(quote(part, safe="") for part in path.split("/")) class VideoOut(BaseModel): model_config = ConfigDict(from_attributes=True) diff --git a/search/documents.py b/search/documents.py index 06b92dd..7bafd9c 100644 --- a/search/documents.py +++ b/search/documents.py @@ -2,10 +2,6 @@ from ..models.video_model import Video def video_to_document(video: Video) -> dict: - """ - Требует, чтобы video.channel и video.playlist были загружены - (selectinload), иначе будет ленивая загрузка в синхронном контексте → ошибка. - """ return { "id": video.id, "title": video.title or video.filename, diff --git a/search/reindex.py b/search/reindex.py index d14bf2b..e136afd 100644 --- a/search/reindex.py +++ b/search/reindex.py @@ -1,4 +1,3 @@ -# search/reindex.py import asyncio from sqlalchemy import select @@ -37,7 +36,7 @@ async def full_reindex(): async def _push(docs: list[dict]): task = videos_index().add_documents(docs) - # дожидаемся, чтобы видеть ошибки и гарантировать применение + client.wait_for_task(task.task_uid) if __name__ == "__main__": diff --git a/search/search_client.py b/search/search_client.py index 4a17347..242e780 100644 --- a/search/search_client.py +++ b/search/search_client.py @@ -1,7 +1,15 @@ -import meilisearch +import os -MEILI_URL = "http://localhost:7700" -MEILI_KEY = "super-secret-master-key-change-me" +import meilisearch +from dotenv import load_dotenv + +from ..core.config import BASE_DIR + +load_dotenv(BASE_DIR / '.env') + + +MEILI_URL = os.getenv("MEILI_URL") +MEILI_KEY = os.getenv("MEILI_KEY") INDEX = "videos" client = meilisearch.Client(MEILI_URL, MEILI_KEY) diff --git a/services/path.py b/services/path.py new file mode 100644 index 0000000..906f1d9 --- /dev/null +++ b/services/path.py @@ -0,0 +1,7 @@ +from urllib.parse import quote + + +def encode_path(path: str) -> str: + if not path: + return "" + return "/".join(quote(part, safe="") for part in path.split("/")) diff --git a/static/css/style.css b/static/css/style.css deleted file mode 100644 index bf64aaf..0000000 --- a/static/css/style.css +++ /dev/null @@ -1,21 +0,0 @@ -@font-face { - font-family: "Roboto Mono"; - src: url("/static/fonts/RobotoMono-Bold.ttf"); -} - -* { - margin: 0; - padding: 0; - box-sizing: border-box; - font-family: "Roboto Mono"; -} - -.wrapper { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - width: 100%; -} - -.photo > img { - width: 100%; -} \ No newline at end of file diff --git a/static/fonts/RobotoMono-Bold.ttf b/static/fonts/RobotoMono-Bold.ttf deleted file mode 100644 index bef439f3b9a884d4377c193ea52f104e820afc98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 87696 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 Z?uN)pxi0>?VH-^Ty3==XU7v${{}1B`zwrP7 diff --git a/templates/channel_detail.html b/templates/channel_detail.html deleted file mode 100644 index 62e25db..0000000 --- a/templates/channel_detail.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - Document - - - - - \ No newline at end of file diff --git a/templates/channels.html b/templates/channels.html deleted file mode 100644 index d06d27a..0000000 --- a/templates/channels.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - Document - - -
- {% for channel_id, name, video_count in channels %} - -
-

{{name}}

-

videos: {{video_count}}

-
-
- {% endfor %} -
- - \ No newline at end of file diff --git a/templates/index.html b/templates/index.html deleted file mode 100644 index 5f85549..0000000 --- a/templates/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - Document - - -
- {% for photo in photos %} -
- -
- {% endfor %} - - page-1 - page+1 -
- - \ No newline at end of file diff --git a/templates/playlist.html b/templates/playlist.html deleted file mode 100644 index 61eb551..0000000 --- a/templates/playlist.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - Document - - -
-

count: {{video_count}}

- {% for video in videos %} - -
-
- -
-

{{video.filename}}

-
-
- - {% endfor %} -
- - \ No newline at end of file diff --git a/templates/video_detail.html b/templates/video_detail.html deleted file mode 100644 index 2b565e6..0000000 --- a/templates/video_detail.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - Document - - -
- -
- - \ No newline at end of file diff --git a/templates/videos.html b/templates/videos.html deleted file mode 100644 index f120fd0..0000000 --- a/templates/videos.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - Document - - - - - \ No newline at end of file diff --git a/utils/downloader.py b/utils/downloader.py index b75d4f7..c3f41ee 100644 --- a/utils/downloader.py +++ b/utils/downloader.py @@ -44,7 +44,7 @@ async def download_photos(client): try: await msg.download_media(file=full_path) - except Exception as e: # noqa: BLE001 + except Exception as e: print(e) async def main(): @@ -61,7 +61,6 @@ async def main(): image.add_data(qr.url) image.make(fit=True) image.print_ascii(invert=True) - # user = await qr.wait() await download_photos(client) diff --git a/utils/sync_yt.py b/utils/sync_yt.py index cb31364..b09280d 100644 --- a/utils/sync_yt.py +++ b/utils/sync_yt.py @@ -6,7 +6,7 @@ from pathlib import Path from ..core.config import BASE_DIR, YT_DIR -COOKIES_PATH = Path(f"{BASE_DIR}/utils/cookies.txt") # или из config +COOKIES_PATH = Path(f"{BASE_DIR}/utils/cookies.txt") GLOBAL_ARGS = [ @@ -26,8 +26,8 @@ GLOBAL_ARGS = [ DEFAULT_FORMAT = ( "bestvideo[height<=720][height>=480]+bestaudio/" - # "bestvideo[height<=1080][height>=480]+bestaudio/" - # "best[height>=480]" + "bestvideo[height<=1080][height>=480]+bestaudio/" + "best[height>=480]" ) def load_channels(config_path: Path, yt_dir: Path) -> list[dict]: diff --git a/utils/update_audio.py b/utils/update_audio.py index 5c6cf71..ecde472 100644 --- a/utils/update_audio.py +++ b/utils/update_audio.py @@ -75,7 +75,7 @@ async def update_yt(): preview_path = f"{COVERS}/{name}.jpg" path = str(relative_path) - title, date = parse_preview_path(preview_path) # noqa: RUF059 + title, date = parse_preview_path(preview_path) if filename in existing_audios: continue diff --git a/utils/update_thumbs.py b/utils/update_thumbs.py index 858aa06..ac834a7 100644 --- a/utils/update_thumbs.py +++ b/utils/update_thumbs.py @@ -19,7 +19,6 @@ async def extract_cover(video: Path, out: Path, sem: asyncio.Semaphore) -> bool: if ".Trash" in str(video) or any(part.startswith('.') for part in video.parts): return False - # 1. Пропускаем временные файлы и аудио-дорожки yt-dlp if ".temp." in video.name or ".f" in video.name and video.suffix == ".webm": return False @@ -28,7 +27,6 @@ async def extract_cover(video: Path, out: Path, sem: asyncio.Semaphore) -> bool: if out.exists() and out.stat().st_size > 0: return True - # 2. Пытаемся взять ВТОРУЮ дорожку (с вопросительным знаком, чтобы не падало) code, err = await run([ "ffmpeg", "-y", "-loglevel", "error", "-i", str(video), @@ -38,7 +36,6 @@ async def extract_cover(video: Path, out: Path, sem: asyncio.Semaphore) -> bool: ]) if code != 0: - # 3. Если второй нет, пытаемся взять ПЕРВУЮ (тоже с вопросительным знаком) code, err = await run([ "ffmpeg", "-y", "-loglevel", "error", "-i", str(video), diff --git a/utils/update_yt_videos.py b/utils/update_yt_videos.py index 6fe2921..2e28f28 100644 --- a/utils/update_yt_videos.py +++ b/utils/update_yt_videos.py @@ -116,6 +116,10 @@ async def update_yt(): else: + vkey = (channel.id, filename) + if vkey in existing_videos: + continue + video = Video(filename=filename, path=path, preview_path=preview_path, playlist=None, channel=channel, title=title, date=date) videos.append(video)