myYouTube/UI_UX_spec.md
vrubelroman 0ed20bb838 Implement Phases 1-5: skeleton, OAuth, categories, video sync/feed, playback
- FastAPI + PostgreSQL + Alembic + React/Vite skeleton, Docker Compose, healthcheck
- Google OAuth (single allowed account), encrypted refresh token storage
- Subscriptions sync with pagination, uploads playlist batch fetch
- Categories CRUD, many-to-many channel assignment, category filtering
- Video sync (playlistItems + videos.list batching), cached feed with cursor
  pagination, background scheduler (APScheduler)
- Video detail page with YouTube embed player
- SPA fallback routing, optimistic UI updates, client-side query caching

40 backend tests covering OAuth allow-list, sync idempotency, cascade deletes,
cursor pagination, and category filtering.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 18:44:30 +00:00

28 KiB
Raw Blame History

UI/UX-спецификация: персональный YouTube-клиент

Назначение: передать coding agent вместе с основным техническим заданием.
Формат: single-user self-hosted web app, в будущем PWA/mobile client.

1. Концепция

Приложение должно ощущаться как персональная лента YouTube-подписок, но с нормальной категоризацией каналов и локальным хранением выбранных видео.

Пользовательский сценарий:

Открыть приложение
→ выбрать категорию
→ увидеть последние видео каналов этой категории
→ смотреть через YouTube
→ при желании нажать "Скачать"
→ после загрузки видеть "На сервере"
→ дальше смотреть локальную копию через MeTube

Интерфейс не должен выглядеть как admin panel. Это consumer media UI.


2. Общий layout

Desktop:

┌──────────────────────────────────────────────────────────────────────┐
│ Header                                                               │
├──────────────────────┬───────────────────────────────────────────────┤
│ Sidebar              │ Main content                                  │
│                      │                                               │
│ Все видео            │ Video feed                                    │
│ Без категории        │                                               │
│ На сервере           │                                               │
│                      │                                               │
│ КАТЕГОРИИ            │                                               │
│ Linux                │                                               │
│ Шахматы              │                                               │
│ Научпоп              │                                               │
│                      │                                               │
│ Каналы               │                                               │
│ Настройки            │                                               │
└──────────────────────┴───────────────────────────────────────────────┘

Sidebar: 220–260 px.
Header: фиксированный сверху.
Main content: responsive grid.

Mobile: sidebar превращается в drawer/hamburger.


3. Визуальный стиль

Тёмная тема по умолчанию.

Рекомендуемые design tokens:

Background main      #0F0F0F
Surface              #181818
Surface hover        #272727
Border               #303030
Primary text         #F1F1F1
Secondary text       #AAAAAA
Accent               #3EA6FF
Success/local        #2BA640
Warning/downloading  #F5A623
Error                #E53935

Использовать CSS variables, а не раскидывать цвета по компонентам.

Типографика:

Inter / Roboto / system-ui
Page title      24px / 600
Section title   18px / 600
Video title     15–16px / 500
Metadata        12–14px
Buttons         14px / 500

4. Header

Пример:

┌──────────────────────────────────────────────────────────────────────┐
│ ▶ MyTube        🔍 Поиск...                    ✓ 12 мин      ⚙      │
└──────────────────────────────────────────────────────────────────────┘

Слева:

  • логотип;
  • рабочее название MyTube или configurable app name.

Центр:

  • поиск по локальной БД: video title + channel title.

Справа:

  • sync indicator;
  • settings.

Sync states:

✓ Обновлено 12 мин назад
↻ Обновление...
⚠ Ошибка синхронизации

По клику на sync indicator:

Подписки: обновлены 20:11
Видео: обновлены 20:12
MeTube: доступен

5. Sidebar

Пример:

● Все видео
  Без категории
  На сервере

КАТЕГОРИИ

🐧 Linux
♟ Шахматы
🔬 Научпоп
🎮 Игры

+ Новая категория

────────────

Каналы
Настройки

Активная категория должна иметь заметный, но спокойный selected state.

Все видео, Без категории, На сервере — виртуальные категории.


6. Feed

Route:

/

или:

/feed

Header выбранной категории:

Linux
24 канала
Обновлено 12 минут назад                  [↻ Обновить]

Опциональные chips:

[Все] [На сервере] [Не скачаны]

Сортировка видео:

published_at DESC

7. Video grid

Responsive:

>= 1800 px  → 5 columns
>= 1400 px  → 4 columns
>= 1000 px  → 3 columns
>= 700 px   → 2 columns
mobile      → 1 column

Рекомендуемый CSS:

grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));

8. Video card

Пример:

┌────────────────────────────────────┐
│                                    │
│             THUMBNAIL              │
│                              18:42 │
│                                    │
├────────────────────────────────────┤
│ Linux Kernel 7.0 — What's New      │
│ The Linux Experiment               │
│ 2 часа назад                       │
│                                    │
│ [▶ Смотреть]       [⬇ Скачать]     │
└────────────────────────────────────┘

Thumbnail:

  • aspect ratio 16:9;
  • duration badge справа снизу.

Карточка содержит:

  • thumbnail;
  • title;
  • channel name;
  • publish time;
  • duration;
  • download/local state;
  • Watch;
  • Download.

9. Состояния скачивания

Не скачано

[▶ Смотреть] [⬇ Скачать]

В очереди

[▶ Смотреть] [В очереди...]

Скачивается

[▶ Смотреть] [64%]

Дополнительно progress-line внизу thumbnail.

Post-processing

[▶ Смотреть] [Обработка...]

На сервере

[▶ Смотреть] [✓ На сервере]

Ошибка

[▶ Смотреть] [⚠ Повторить]

Не показывать traceback.


10. Video page

Route:

/video/:youtubeVideoId

Desktop:

┌──────────────────────────────────────────────────────────────────┐
│                                                                  │
│                         VIDEO PLAYER                             │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

Linux Kernel 7.0 — What's New

The Linux Experiment
2 часа назад                              [⬇ Скачать]

Источник: YouTube

[Linux] [IT]

────────────────────────────────────────────────────────────────────

Описание видео...

Если local copy существует:

Источник: ● Локальная копия · mediaVM

и вместо YouTube iframe используется HTML5 <video>.

Пользователь не выбирает источник вручную по умолчанию.

Логика:

local available?
   yes → local player
   no  → YouTube player

Если local player вернул ошибку/404:

Локальная копия недоступна.
Переключено на YouTube.

11. Channels page

Route:

/channels

Главная задача — быстро распределять подписки по категориям.

Пример:

Каналы

[Поиск каналов...]                    [Только без категории]

┌────────────────────────────────────────────────────┐
│ ○ The Linux Experiment                             │
│   [Linux ×] [IT ×] [+ Категория]                  │
└────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────┐
│ ○ GothamChess                                      │
│   [Шахматы ×] [+ Категория]                       │
└────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────┐
│ ○ Veritasium                                       │
│   [Научпоп ×] [+ Категория]                       │
└────────────────────────────────────────────────────┘

Не делать отдельную страницу редактирования для каждого канала.


12. Category picker

По кнопке:

+ Категория

показывать popover:

Категории

☑ Linux
☑ IT
☐ Научпоп
☐ Игры

──────────────
+ Создать новую

Checkbox сохраняется сразу.

Отдельная кнопка Сохранить не нужна.


13. Массовая категоризация

Желательная функция.

Пользователь отмечает несколько каналов:

☑ Channel A
☑ Channel B
☑ Channel C

Появляется action bar:

Выбрано: 3

[Добавить в категорию] [Убрать из категории] [Отмена]

Если MVP затягивается, допускается реализовать после базовой версии.


14. Category management

Route:

/settings/categories

Пример:

Категории

≡ Linux       24 канала       ✎   ⋮
≡ Шахматы     12 каналов      ✎   ⋮
≡ Научпоп      8 каналов      ✎   ⋮

[+ Новая категория]

Поддержать:

  • create;
  • rename;
  • delete;
  • reorder.

При удалении:

Удалить категорию "Linux"?

Каналы удалены не будут.
Они просто перестанут относиться к этой категории.

[Отмена] [Удалить]

15. Virtual category "Без категории"

Показывает каналы/видео каналов, которые ещё не распределены.

Header:

Без категории

17 каналов пока не распределены.

[Перейти к каналам]

16. Virtual category "На сервере"

Показывает видео:

download_status = completed

Это простая локальная библиотека без отдельного сложного media-manager.


17. Empty states

Нет категорий

Категорий пока нет

Раздели подписки по темам:
Linux, Шахматы, Научпоп и т.д.

[Создать первую категорию]

В категории нет видео

Здесь пока нет видео

Добавь каналы в эту категорию
или обнови подписки.

[Добавить каналы]

YouTube ещё не подключён

Подключи свой YouTube

Мы импортируем список подписок и последние видео.
Доступ к YouTube только read-only.

[Подключить YouTube]

18. Loading states

Не использовать fullscreen spinner.

Использовать skeleton video cards.

Background sync не должен блокировать интерфейс.


19. Error UX

Global toast:

Не удалось связаться с MeTube

или:

YouTube временно недоступен

Critical banner:

⚠ Требуется повторная авторизация YouTube

[Подключить заново]

Background sync не должен постоянно спамить toast notifications.


20. Settings

Route:

/settings

Sections:

YouTube
MeTube
Синхронизация
Интерфейс
О приложении

YouTube

Google account:
user@example.com

Статус: Подключено

Последняя синхронизация:
Сегодня, 20:15

[Обновить сейчас]
[Переподключить]

MeTube

MeTube

Статус: ● Доступен
Сервер: mediaVM
Адрес: 192.168.8.177:8081

[Проверить соединение]

Не показывать на основном UI filesystem path или internal job IDs.

Sync

Подписки: каждые 6 часов
Видео: каждые 60 минут

Последний запуск:
20:12

[Синхронизировать сейчас]

21. First-run onboarding

Flow:

Open app
→ Подключить YouTube
→ Google OAuth
→ initial sync
→ channels imported
→ предложить категоризацию

После initial sync:

Готово!

Импортировано:
143 канала

Теперь можно распределить их по категориям.

[Настроить категории]
[Сделать позже]

22. Mobile UX

Header:

☰   MyTube                    ↻

Категории можно показывать horizontal chips:

[Все] [Linux] [Шахматы] [Научпоп] →

Video card:

┌───────────────────────────────┐
│                               │
│           Thumbnail           │
│                               │
└───────────────────────────────┘

Video title
Channel · 2 часа назад

[▶ Смотреть] [⬇ Скачать]

Touch targets минимум ~44×44 px.


23. Navigation routes

Рекомендуемые routes:

/                         all feed
/category/:categoryId     category feed
/channels                  channels
/video/:youtubeVideoId    video detail/player
/local                     local videos
/uncategorized             uncategorized
/settings                  settings
/settings/categories       category management

Browser Back после просмотра видео должен возвращать:

  • в ту же категорию;
  • примерно на ту же scroll position.

24. Components

Frontend должен быть разделён на reusable components:

AppShell
Header
Sidebar
CategoryNav
VideoGrid
VideoCard
VideoThumbnail
DownloadButton
DownloadStatus
Player
YouTubePlayer
LocalPlayer
ChannelRow
CategoryPicker
CategoryChip
SyncIndicator
EmptyState
ErrorBanner
Toast
ConfirmDialog
SkeletonVideoCard

Не делать весь UI одним React component.


25. Feed API object

Желательно, чтобы backend сразу отдавал frontend всё необходимое для одной карточки:

{
  "youtube_video_id": "abc123",
  "title": "Linux Kernel 7.0",
  "channel": {
    "id": 12,
    "youtube_channel_id": "UC...",
    "title": "The Linux Experiment",
    "thumbnail_url": "..."
  },
  "thumbnail_url": "...",
  "published_at": "2026-09-16T18:00:00Z",
  "duration_seconds": 1042,
  "categories": [
    {
      "id": 2,
      "name": "Linux"
    }
  ],
  "local": {
    "available": true,
    "status": "completed",
    "progress_percent": 100,
    "media_url": "http://..."
  }
}

Запрещён frontend pattern:

GET feed
→ отдельный request channel для каждой карточки
→ отдельный request download status для каждой карточки

Не должно быть N+1 API requests.


26. Desktop wireframe

┌────────────────────────────────────────────────────────────────────────────┐
│ ▶ MyTube                  🔍 Поиск                         ✓ 12 мин   ⚙     │
├──────────────────────┬─────────────────────────────────────────────────────┤
│                      │                                                     │
│ ● Все видео          │  Linux                                              │
│   Без категории      │  24 канала                         [↻ Обновить]     │
│   На сервере         │                                                     │
│                      │  [Все] [На сервере] [Не скачаны]                   │
│ КАТЕГОРИИ            │                                                     │
│                      │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐    │
│ 🐧 Linux             │  │ thumbnail   │ │ thumbnail   │ │ thumbnail   │    │
│ ♟ Шахматы            │  │        18:42│ │        12:03│ │         8:41│    │
│ 🔬 Научпоп           │  └─────────────┘ └─────────────┘ └─────────────┘    │
│ 🎮 Игры              │  Kernel 7.0...   NixOS update...  New GNOME...     │
│                      │  Linux Exp.      Brodie R.       DistroTube        │
│ + Категория          │  2 часа назад    4 часа назад    вчера             │
│                      │  [▶] [⬇]         [▶] [64%]       [▶] [✓ Local]     │
│ ─────────────        │                                                     │
│ Каналы               │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐    │
│ Настройки            │  │             │ │             │ │             │    │
│                      │  └─────────────┘ └─────────────┘ └─────────────┘    │
└──────────────────────┴─────────────────────────────────────────────────────┘

27. Channels wireframe

┌──────────────────────────────────────────────────────────────────────┐
│ Каналы                                                              │
│                                                                      │
│ 🔍 Поиск каналов...                         [Только без категории]   │
│                                                                      │
├──────────────────────────────────────────────────────────────────────┤
│ ○  The Linux Experiment                                              │
│    [Linux ×] [IT ×] [+ Категория]                                   │
├──────────────────────────────────────────────────────────────────────┤
│ ○  GothamChess                                                       │
│    [Шахматы ×] [+ Категория]                                        │
├──────────────────────────────────────────────────────────────────────┤
│ ○  Veritasium                                                        │
│    [Научпоп ×] [+ Категория]                                        │
├──────────────────────────────────────────────────────────────────────┤
│ ○  Some Channel                                                      │
│    [+ Категория]                                                     │
└──────────────────────────────────────────────────────────────────────┘

28. Video page wireframe

┌──────────────────────────────────────────────────────────────────────┐
│                                                                      │
│                         VIDEO PLAYER                                 │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘

Linux Kernel 7.0 — What's New

The Linux Experiment
2 часа назад

● Локальная копия · mediaVM                       [✓ На сервере]

[Linux] [IT]

────────────────────────────────────────────────────────────────────────

Описание видео...

Когда local отсутствует:

Источник: YouTube                                  [⬇ Скачать]

29. UX-антипаттерны

Не делать:

  • Bootstrap-like admin tables как основной UI;
  • raw JSON;
  • container names/job IDs на главном экране;
  • filesystem paths;
  • огромные формы;
  • отдельный Save после каждого checkbox;
  • модальное окно на каждое простое действие;
  • fullscreen spinner при background sync;
  • N+1 API requests;
  • слишком много badges на thumbnail;
  • сильные hover zoom animations.

30. Accessibility

Минимум:

  • semantic HTML;
  • keyboard navigation;
  • visible focus ring;
  • aria-label для icon buttons;
  • alt у изображений;
  • нормальный contrast;
  • <button> для кнопок;
  • состояние не должно кодироваться только цветом.

31. Icons

Использовать один icon set, например Lucide React.

Основные icons:

Home
Folder
Download
Play
Check
RefreshCw
Settings
Search
MoreVertical
AlertTriangle
Server
Youtube
Menu

32. Анимации

Минимально:

  • hover 150–200 ms;
  • popover fade;
  • drawer slide;
  • progress;
  • skeleton pulse.

Не делать сложные page transitions.


33. Рекомендуемый порядок реализации UI

Сначала сделать интерфейс на mock data:

App shell
Sidebar
Video grid
Video card
Channels page
Category picker
Video page
Mobile responsive

Только после визуальной проверки подключать реальные backend API.


34. Definition of Done UI

UI MVP готов, если:

  1. Пользователь может подключить YouTube.
  2. После sync видит подписанные каналы.
  3. Может создать категорию.
  4. Может назначить каналу одну или несколько категорий.
  5. Может выбрать категорию и увидеть соответствующую video feed.
  6. Карточка показывает thumbnail/title/channel/time/duration.
  7. Можно открыть video page.
  8. Нескачанный ролик играет через YouTube.
  9. Работает Download.
  10. Отображаются queued/downloading/completed/failed.
  11. Completed ролик имеет статус На сервере.
  12. Completed ролик играет через MeTube local URL.
  13. Есть loading/error/empty states.
  14. UI usable на desktop и mobile.
  15. Browser refresh сохраняет route.
  16. UI не показывает secrets/internal paths/job IDs.
  17. На feed нет N+1 requests.
  18. Основной экран воспринимается как видеоприложение, а не административная панель.

35. Главное UX-правило

Пользователь должен воспринимать сервис так:

Это моя лента YouTube-подписок, только каналы нормально разложены по темам, а понравившееся видео я могу одним кликом оставить у себя.

Технические детали:

OAuth
YouTube Data API
PostgreSQL
MeTube
Socket.IO
mediaVM
filenames

должны быть скрыты за простым интерфейсом.