- Detect shorts by duration (SHORTS_MAX_DURATION_SECONDS, default 180)
and expose is_short in feed, video and channel-activity DTOs.
- Feed gains type=all|long|short and anchor; lists show a two-mode
'Обычные | Shorts' filter (no 'Все') persisted in the URL.
- Clicking a short opens /shorts/🆔 a vertical scroll-snap feed with
autoplay for the active slide, context-aware endpoints and infinite
loading.
- Keep the sound choice across swipes, syncing with the player's own
mute control and guarding against the widget's stale isMuted() reads.
126 lines
7.5 KiB
Markdown
126 lines
7.5 KiB
Markdown
# MyYouTube
|
||
|
||
Персональный YouTube-клиент с категориями подписок и интеграцией с существующим MeTube.
|
||
Работа агентской команды (opencode) описана в секции «Агентская команда (opencode)» ниже; роли агентов — в `.opencode/agent/`.
|
||
|
||
Текущий статус: **функционально готово** (основные этапы 1–6 и доработки по итогам тестирования). Развёрнуто и вручную протестировано на тестовом окружении (`testmyyoutube.vrubel.xyz` → hermesVM `192.168.8.173` → MeTube на mediaVM `192.168.8.177`).
|
||
|
||
### Что сделано
|
||
|
||
- Skeleton, Google OAuth (single-user allow-list), sync подписок/видео (APScheduler), категории (CRUD + many-to-many), лента с курсорной пагинацией, YouTube-плеер, полная MeTube-интеграция (download/delete, Socket.IO live-статусы, recovery после рестарта).
|
||
- Режим «Каналы» на главной (`?view=channels`): переключатель «Лента | Каналы», список подписанных каналов по свежести последнего видео с тремя последними роликами в строке; фильтр категорий из сайдбара действует в обоих режимах.
|
||
- Раздел **«На сервере»** — скачанные видео; `/saved` перенаправляет на `/local`.
|
||
- Разделение видео на **Shorts** и обычные по длительности (`SHORTS_MAX_DURATION_SECONDS`, по умолчанию 180 сек; неизвестная длительность — обычное видео): сегмент-фильтр «Обычные | Shorts» во всех списках видео (по умолчанию «Обычные», `?type=short` для Shorts; `?type=all`/`?type=long`/неизвестное трактуются как «Обычные») и вертикальная full-screen лента Shorts (`/shorts/:youtubeVideoId`) с автоплеем активного ролика и бесконечной подгрузкой в контексте открытого фильтра. REST `GET /api/feed` по-прежнему поддерживает `type=all|long|short` (совместимость с будущим mobile/PWA-клиентом), UI вариант `all` не использует.
|
||
- Тёмный адаптивный интерфейс: общая навигация, категории с постоянными URL, поиск по названиям видео и каналов, мобильное меню.
|
||
- Отписанные каналы скрываются из списка каналов.
|
||
- Реальная отписка от канала на YouTube (`subscriptions.delete`) реализована по запросу пользователя сверх исходного MVP. Из-за этого OAuth scope расширен с `youtube.readonly` до полного `youtube` (read/write) — см. `backend/app/services/google_oauth.py`.
|
||
- Удаление скачанного видео с сервера (`DELETE /api/videos/{id}/download`) — тоже сверх исходного MVP-скоупа, добавлено по запросу.
|
||
|
||
### Что осталось / сознательно отложено
|
||
|
||
- Дополнительные идеи после MVP пока не реализованы: PWA, watch later, SponsorBlock и т.д.
|
||
- Hardening (retry/recovery, тесты) выполнялся по факту находок в live-тестировании, а не отдельным проходом — см. `git log` для конкретных багфиксов (flapping статусов загрузки, неверный id в MeTube `/delete`, OAuth scope mismatch).
|
||
|
||
## Стек
|
||
|
||
- Backend: Python 3.13, FastAPI, SQLAlchemy 2.x, Alembic, Pydantic Settings
|
||
- Frontend: React, TypeScript, Vite, React Router, TanStack Query
|
||
- DB: PostgreSQL 16
|
||
- Deployment: Docker Compose
|
||
|
||
## Запуск в production (Docker Compose)
|
||
|
||
1. Скопируй `.env.example` в `.env` и заполни значения (Google OAuth credentials, секреты, MeTube URLs).
|
||
2. Собери и запусти:
|
||
|
||
```bash
|
||
docker compose up -d --build
|
||
```
|
||
|
||
3. Проверь health:
|
||
|
||
```bash
|
||
curl http://localhost:8080/api/health
|
||
```
|
||
|
||
Миграции Alembic применяются автоматически при старте контейнера `app` (см. `entrypoint.sh`).
|
||
|
||
## Локальная разработка
|
||
|
||
### Агентская команда (opencode)
|
||
|
||
Работа идёт в одной сессии opencode: она стартует агентом `orchestrator`
|
||
(`default_agent` в `opencode.json`). Порядок работы:
|
||
`orchestrator → analyst (аналитика/README) → coder → reviewer → tester`.
|
||
Orchestrator сам декомпозирует задачу и вызывает субагентов через Task tool:
|
||
`analyst` (до Coder'а фиксирует задачу в `analytics/*.md` и при необходимости
|
||
актуализирует README), `coder` (единственный меняет код продукта), `reviewer`
|
||
(read-only review, `edit: deny`) и `tester` (проверки без правок, `edit: deny`).
|
||
Роли описаны в [`.opencode/agent/`](./.opencode/agent/). Аналитика задач
|
||
ведётся в [`analytics/`](./analytics/) (см. `analytics/README.md`).
|
||
|
||
```bash
|
||
opencode
|
||
```
|
||
|
||
Задачу пиши обычным сообщением: Orchestrator передаст её сначала Analyst'у
|
||
(аналитика и README), затем Coder'у, соберёт review и QA, повторит цикл при
|
||
находках и вернёт финальный отчёт. Вручную писать субагентам не нужно.
|
||
|
||
### Backend
|
||
|
||
```bash
|
||
cd backend
|
||
uv venv .venv
|
||
uv pip install -p .venv -r requirements-dev.txt
|
||
source .venv/bin/activate
|
||
cd ..
|
||
DATABASE_URL=postgresql+psycopg://youtube_app:password@localhost:5432/youtube_app \
|
||
APP_SECRET_KEY=dev APP_SECRET_KEY=dev TOKEN_ENCRYPTION_KEY=dev \
|
||
alembic upgrade head
|
||
uvicorn app.main:app --reload --app-dir backend --port 8080
|
||
```
|
||
|
||
Тесты:
|
||
|
||
```bash
|
||
backend/.venv/bin/python -m pytest tests/ -v
|
||
```
|
||
|
||
### Frontend
|
||
|
||
```bash
|
||
cd frontend
|
||
npm install
|
||
npm run dev
|
||
```
|
||
|
||
Dev-сервер Vite проксирует `/api/*` на `http://127.0.0.1:8080` (см. `vite.config.ts`).
|
||
|
||
## Миграции
|
||
|
||
```bash
|
||
alembic revision -m "описание"
|
||
alembic upgrade head
|
||
```
|
||
|
||
`alembic.ini` и папка `migrations/` находятся в корне проекта и подключают модели из `backend/app`.
|
||
|
||
## Структура проекта
|
||
|
||
```text
|
||
myyoutube/
|
||
├── backend/ # FastAPI приложение
|
||
├── frontend/ # React SPA
|
||
├── migrations/ # Alembic migrations
|
||
├── tests/ # backend tests (pytest)
|
||
├── analytics/ # аналитика задач агентской команды
|
||
├── Dockerfile
|
||
├── compose.yml
|
||
├── .env / .env.example
|
||
└── .opencode/ # агенты команды (orchestrator/analyst/coder/reviewer/tester)
|
||
```
|
||
|
||
## Секреты
|
||
|
||
`.env` в `.gitignore`, не коммитится. Обязательные секреты: `APP_SECRET_KEY`, `TOKEN_ENCRYPTION_KEY`, `GOOGLE_CLIENT_SECRET`, `POSTGRES_PASSWORD`.
|