broadcast-rooms

Платформа трансляций: комнаты, расписание, архив с транскрипцией, таймкодами, буклетами

Business
Source: ДобинДом
Files: 16
Stack:
DjangoDRFChannelsReactTanStack Query

Specification

broadcast-rooms

Назначение

Платформа для управления видео/аудио трансляциями: комнаты (разделы контента), расписание эфиров, архив записей с транскрипцией, таймкодами и генерацией PDF-буклетов. Три слоя: backend API, пользовательский фронтенд, админ-панель.

Контракты

Модели данных

class Room:
    name: CharField(100, unique=True)        # "Гостинная", "Кухня", "Своя комната"
    description: TextField
    image: ImageField                        # Обложка комнаты
    sort_order: IntegerField
    has_broadcast_text: BooleanField          # Поддержка текстовых трансляций
    is_library: BooleanField                 # Ведёт на библиотеку вместо архива

class Cycle:
    name: CharField(200, unique=True)        # Тематический цикл видео
    sort_order: IntegerField

class BroadcastSchedule:
    name: CharField(255)                     # Название трансляции
    description: TextField
    tags: CharField(500)                     # Через запятую
    broadcast_datetime: DateTimeField
    broadcast_type: CharField                # 'video' | 'audio'
    access_type: CharField                   # 'public' | 'private'
    room: ForeignKey(Room)
    audio_cover: ImageField                  # Обложка + auto-thumbnail 640px WebP
    audio_cover_thumb: ImageField            # Auto-generated
    tariffs: ManyToManyField(Tariff)         # Доступ по тарифам

class BroadcastText:
    broadcast_schedule: OneToOneField(BroadcastSchedule)
    content: TextField                       # Текст для синхронизации с видео

class TextBroadcastEvent:
    broadcast_schedule: ForeignKey(BroadcastSchedule)
    event_type: CharField                    # 'scroll' | 'highlight' | 'mark_block'
    event_data: JSONField                    # {scroll_top}, {start, end}, {block_id}
    time_seconds: FloatField                 # Время от начала эфира

class ArchivedBroadcast:
    kinescope_video_id: CharField(unique=True)  # UUID видео из видеохостинга
    title: CharField(255)
    broadcast_datetime: DateTimeField
    broadcast_type: CharField                # 'video' | 'audio'
    room: ForeignKey(Room)
    cycle: ForeignKey(Cycle)
    access_type: CharField                   # 'public' | 'private'
    is_visible: BooleanField
    audio_cover: URLField                    # Обложка (URL)
    cover_image: ImageField                  # Загруженная обложка (приоритет)
    poster_url: URLField                     # Превью из видеохостинга
    description: TextField
    sort_order: IntegerField
    # Транскрипция
    transcription_status: CharField          # 'none' | 'pending' | 'processing' | 'done' | 'error'
    transcription_error: TextField
    transcription_progress: CharField
    # Таймкоды
    timestamps_generation_status: CharField  # 'pending' | 'processing' | 'done' | 'error'
    timestamps_generation_error: TextField
    # Буклет (PDF)
    booklet_status: CharField
    booklet_file: FileField

class VideoTimestamp:
    broadcast: ForeignKey(ArchivedBroadcast)
    time_seconds: PositiveIntegerField       # Позиция в секундах
    title: CharField(200)                    # Название момента

class VideoTranscription:
    broadcast: OneToOneField(ArchivedBroadcast)
    source: CharField                        # 'kinescope' | 'whisper'
    content: TextField                       # Полный текст с временными метками

API — расписание и комнаты (админ)

МетодПутьОписание

|-------|------|----------|

GET/POST/admin/rooms/Список/создание комнат
POST/admin/rooms/{id}/remove-image/Удаление изображения
GET/POST/admin/broadcasts/Список/создание расписания
GET/PUT/DELETE/admin/broadcasts/{id}/CRUD трансляции
POST/admin/broadcasts/{id}/remove-cover/Удаление обложки
POST/admin/broadcasts/{id}/generate-cover/Генерация обложки (AI)
POST/admin/broadcasts/{id}/set-cover/Установка сгенерированной обложки
POST/admin/cabinet-text/Сохранение текста трансляции
POST/admin/cabinet-text/{id}/event/Событие синхронизации текста (WebSocket)

API — архив (админ)

МетодПутьОписание

|-------|------|----------|

GET/POST/admin/archive/Список/создание архивных записей (фильтры: room, is_visible, search, transcription_status)
POST/admin/archive/{id}/transcribe/Запуск транскрипции
POST/admin/archive/transcribe-all/Массовая транскрипция
GET/PATCH/admin/archive/{id}/transcription/Текст транскрипции
GET/POST/admin/archive/{id}/generate-timestamps/Генерация таймкодов (LLM)
GET/POST/admin/archive/{id}/timestamps/CRUD таймкодов
GET/PUT/DELETE/admin/archive/{id}/timestamps/{ts_id}/Детали таймкода
GET/POST/admin/archive/{id}/generate-booklet/Генерация PDF-буклета

API — пользовательский фронтенд

МетодПутьОписание

|-------|------|----------|

GET/rooms/Список комнат
GET/archive/{id}/Детали видео с таймкодами
GET/broadcasts/Текущее расписание
GET/broadcasts/live/Активная трансляция

Frontend hooks

// Пользовательский фронтенд
useRooms(): Room[]
useArchive(roomId?, accessType?): ArchivedBroadcast[]
usePublicBroadcast(): LiveBroadcast | null

// Админ-панель
useRooms(): Room[]
useCreateRoom(): mutation
useUpdateRoom(): mutation
useBroadcasts(): BroadcastSchedule[]
useCreateBroadcast(): mutation
useArchive(filters): ArchivedBroadcast[]
useTranscribe(id): mutation
useGenerateTimestamps(id): mutation
useGenerateBooklet(id): mutation

Рабочие процессы

**Создание трансляции:**

Admin → выбор комнаты → название, дата/время, тип (video/audio)
  → загрузка/генерация обложки → привязка тарифов доступа
  → (опционально) загрузка текстового контента
  → интеграция с видеохостингом → запуск live-события

**Архивирование:**

Трансляция завершена → webhook → создание ArchivedBroadcast
  → транскрипция (Whisper / API видеохостинга)
  → генерация таймкодов (LLM) → генерация PDF-буклета
  → видео доступно в архиве пользователям

**Текстовые трансляции:**

Admin загружает текст → начало эфира
  → спикер прокручивает / выделяет блоки
  → события по WebSocket → клиенты синхронизируют отображение
  → события сохраняются в БД для воспроизведения архива

Зависимости

Django + DRF
Celery/Huey (фоновые задачи: транскрипция, таймкоды, буклеты)
django-solo (singleton settings)
Django Channels (WebSocket для текстовых трансляций)
Pillow (обложки, thumbnail)
API видеохостинга (Kinescope, Vimeo, YouTube Live, или свой)
LLM API (для генерации таймкодов и описаний)
Whisper API (для транскрипции)
React + TanStack Query (фронтенд)

Реализации

СтекПутьИсточникСтатус

|------|------|----------|--------|

Django + React (TypeScript)implementations/django-react/ДобинДомпроверен

Адаптация

**Видеохостинг** — заменить Kinescope API на свой (Vimeo, YouTube, самохостинг). Точка интеграции: `services/api_client.py`.
**Модель Tariff** — заменить связь `tariffs` M2M на свою систему подписок/доступа.
**Комнаты** — переименовать «комнаты» в «каналы», «категории» или другую иерархию.
**Текстовые трансляции** — опционально: убрать `BroadcastText` + `TextBroadcastEvent` если не нужна синхронизация текста.
**Обложки** — заменить AI-генерацию (Gemini) на свой сервис или убрать.
**Буклеты** — опционально: убрать генерацию PDF если не нужна.
**LLM для таймкодов** — заменить промпт и модель в `services/timestamp_generator.py`.
**Permissions** — заменить `IsStaffUser` на свою систему прав.