# Техническое задание: Совместное прослушивание музыки ## 1. Описание продукта Веб-приложение для синхронного прослушивания музыки с друзьями в реальном времени. Пользователи создают комнаты, приглашают друзей по ссылке и слушают музыку одновременно. ## 2. Основные функции ### 2.1 Комнаты - Создание комнаты (генерация уникального ID/ссылки) - Публичные комнаты с возможностью поиска/списка - Присоединение по ссылке или из списка комнат - Отображение списка участников ### 2.2 Музыкальный плеер - Воспроизведение MP3 из S3-хранилища - Синхронизация playback между всеми участниками - Управление доступно всем участникам: play/pause, перемотка, следующий/предыдущий трек - Громкость (локальная, у каждого своя) - Очередь воспроизведения (playlist) - Отображение текущего трека, прогресса ### 2.3 Чат - Текстовый чат внутри комнаты - Сообщения видны всем участникам в реальном времени ### 2.4 Управление треками - Загрузка MP3 файлов в S3 - Общая библиотека треков (доступна всем пользователям во всех комнатах) - Добавление треков в очередь - Базовые метаданные: название, исполнитель ### 2.5 Пользователи - Обязательная регистрация/авторизация - Профиль пользователя ## 3. Технические требования ### 3.1 Стек технологий **Frontend:** - Vue 3 (Composition API) - Pinia (state management) - Vue Router - WebSocket клиент для real-time **Backend:** - Python - FastAPI (REST API + WebSocket) - SQLAlchemy (ORM) - Alembic (миграции) **База данных:** - PostgreSQL **Хранилище файлов:** - S3 (FirstVDS) ### 3.2 Синхронизация - WebSocket для real-time коммуникации - Компенсация сетевой задержки - Периодическая синхронизация позиции трека ### 3.3 Хранилище - S3 (FirstVDS) для MP3 файлов - Presigned URLs для безопасного доступа к файлам - Ограничение размера файла: 10MB - Лимит общего объёма хранилища: 90GB (проверка перед загрузкой) ### 3.4 Масштабируемость - Лимит участников на комнату (например, 50) - Автоудаление неактивных комнат ## 4. Схема базы данных ### users | Поле | Тип | Описание | |------|-----|----------| | id | UUID | Первичный ключ | | username | VARCHAR(50) | Уникальное имя пользователя | | email | VARCHAR(255) | Email (уникальный) | | password_hash | VARCHAR(255) | Хэш пароля | | created_at | TIMESTAMP | Дата регистрации | ### rooms | Поле | Тип | Описание | |------|-----|----------| | id | UUID | Первичный ключ | | name | VARCHAR(100) | Название комнаты | | owner_id | UUID (FK) | Создатель комнаты | | current_track_id | UUID (FK) | Текущий трек | | playback_position | INTEGER | Позиция воспроизведения (мс) | | is_playing | BOOLEAN | Играет ли сейчас | | created_at | TIMESTAMP | Дата создания | ### tracks | Поле | Тип | Описание | |------|-----|----------| | id | UUID | Первичный ключ | | title | VARCHAR(255) | Название трека | | artist | VARCHAR(255) | Исполнитель | | duration | INTEGER | Длительность (мс) | | s3_key | VARCHAR(500) | Путь к файлу в S3 | | uploaded_by | UUID (FK) | Кто загрузил | | created_at | TIMESTAMP | Дата загрузки | ### room_queue | Поле | Тип | Описание | |------|-----|----------| | id | UUID | Первичный ключ | | room_id | UUID (FK) | Комната | | track_id | UUID (FK) | Трек | | position | INTEGER | Позиция в очереди | | added_by | UUID (FK) | Кто добавил | ### room_participants | Поле | Тип | Описание | |------|-----|----------| | room_id | UUID (FK) | Комната | | user_id | UUID (FK) | Пользователь | | joined_at | TIMESTAMP | Время входа | ### messages | Поле | Тип | Описание | |------|-----|----------| | id | UUID | Первичный ключ | | room_id | UUID (FK) | Комната | | user_id | UUID (FK) | Автор | | text | TEXT | Текст сообщения | | created_at | TIMESTAMP | Время отправки | ## 5. API Endpoints ### Аутентификация - `POST /api/auth/register` — регистрация - `POST /api/auth/login` — вход - `POST /api/auth/logout` — выход - `GET /api/auth/me` — текущий пользователь ### Комнаты - `GET /api/rooms` — список публичных комнат - `POST /api/rooms` — создать комнату - `GET /api/rooms/{id}` — информация о комнате - `DELETE /api/rooms/{id}` — удалить комнату (только владелец) - `POST /api/rooms/{id}/join` — присоединиться - `POST /api/rooms/{id}/leave` — покинуть ### Плеер (через REST + WebSocket) - `POST /api/rooms/{id}/play` — воспроизвести - `POST /api/rooms/{id}/pause` — пауза - `POST /api/rooms/{id}/seek` — перемотка - `POST /api/rooms/{id}/next` — следующий трек - `POST /api/rooms/{id}/prev` — предыдущий трек ### Очередь - `GET /api/rooms/{id}/queue` — очередь треков - `POST /api/rooms/{id}/queue` — добавить трек в очередь - `DELETE /api/rooms/{id}/queue/{track_id}` — убрать из очереди ### Треки - `GET /api/tracks` — библиотека треков - `POST /api/tracks/upload` — загрузить трек - `DELETE /api/tracks/{id}` — удалить трек ### Чат - `GET /api/rooms/{id}/messages` — история сообщений ### WebSocket - `WS /ws/rooms/{id}` — real-time события комнаты (синхронизация плеера, чат, участники) ## 6. Структура проекта ``` enigfm/ ├── backend/ │ ├── app/ │ │ ├── __init__.py │ │ ├── main.py # Точка входа FastAPI │ │ ├── config.py # Конфигурация (env переменные) │ │ ├── database.py # Подключение к БД │ │ │ │ │ ├── models/ # SQLAlchemy модели │ │ │ ├── __init__.py │ │ │ ├── user.py │ │ │ ├── room.py │ │ │ ├── track.py │ │ │ └── message.py │ │ │ │ │ ├── schemas/ # Pydantic схемы │ │ │ ├── __init__.py │ │ │ ├── user.py │ │ │ ├── room.py │ │ │ ├── track.py │ │ │ └── message.py │ │ │ │ │ ├── routers/ # API роуты │ │ │ ├── __init__.py │ │ │ ├── auth.py │ │ │ ├── rooms.py │ │ │ ├── tracks.py │ │ │ └── websocket.py │ │ │ │ │ ├── services/ # Бизнес-логика │ │ │ ├── __init__.py │ │ │ ├── auth.py │ │ │ ├── room.py │ │ │ ├── track.py │ │ │ ├── s3.py # Работа с S3 │ │ │ └── sync.py # Синхронизация плеера │ │ │ │ │ └── utils/ # Утилиты │ │ ├── __init__.py │ │ └── security.py # JWT, хэширование │ │ │ ├── alembic/ # Миграции БД │ │ ├── versions/ │ │ └── env.py │ │ │ ├── tests/ │ ├── requirements.txt │ ├── alembic.ini │ └── .env.example │ ├── frontend/ │ ├── src/ │ │ ├── main.js # Точка входа │ │ ├── App.vue │ │ │ │ │ ├── components/ # Vue компоненты │ │ │ ├── player/ │ │ │ │ ├── AudioPlayer.vue │ │ │ │ ├── PlayerControls.vue │ │ │ │ ├── ProgressBar.vue │ │ │ │ └── VolumeControl.vue │ │ │ ├── room/ │ │ │ │ ├── RoomCard.vue │ │ │ │ ├── RoomList.vue │ │ │ │ ├── ParticipantsList.vue │ │ │ │ └── Queue.vue │ │ │ ├── chat/ │ │ │ │ ├── ChatWindow.vue │ │ │ │ └── ChatMessage.vue │ │ │ ├── tracks/ │ │ │ │ ├── TrackList.vue │ │ │ │ ├── TrackItem.vue │ │ │ │ └── UploadTrack.vue │ │ │ └── common/ │ │ │ ├── Header.vue │ │ │ └── Modal.vue │ │ │ │ │ ├── views/ # Страницы │ │ │ ├── HomeView.vue # Список комнат │ │ │ ├── RoomView.vue # Страница комнаты │ │ │ ├── LoginView.vue │ │ │ ├── RegisterView.vue │ │ │ └── TracksView.vue # Библиотека треков │ │ │ │ │ ├── stores/ # Pinia stores │ │ │ ├── auth.js │ │ │ ├── room.js │ │ │ ├── player.js │ │ │ └── tracks.js │ │ │ │ │ ├── composables/ # Vue composables │ │ │ ├── useWebSocket.js │ │ │ ├── usePlayer.js │ │ │ └── useApi.js │ │ │ │ │ ├── router/ │ │ │ └── index.js │ │ │ │ │ └── assets/ │ │ └── styles/ │ │ │ ├── public/ │ ├── package.json │ ├── vite.config.js │ └── .env.example │ ├── docker-compose.yml # PostgreSQL, Backend, Frontend ├── .gitignore └── README.md ``` ## 7. Принятые решения - **Аутентификация** — обязательная регистрация - **Права управления** — все участники могут управлять плеером - **Чат** — текстовый чат в каждой комнате - **Библиотека музыки** — общая для всех пользователей - **Приватность** — все комнаты публичные