{"id":26931514,"url":"https://github.com/a1unade/youtube.net","last_synced_at":"2025-08-19T09:10:59.419Z","repository":{"id":229874469,"uuid":"759466081","full_name":"a1unade/YouTube.NET","owner":"a1unade","description":"Russian YouTube","archived":false,"fork":false,"pushed_at":"2025-08-08T21:21:22.000Z","size":33084,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-11T06:53:53.612Z","etag":null,"topics":["asp-net-core","ci","clean-architecture","cqrs","entity-framework-core","jwt","microsoft-identity","postgresql","react","redis-cache","redux","s3","signalr","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/a1unade.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-02-18T17:08:57.000Z","updated_at":"2025-08-08T21:21:25.000Z","dependencies_parsed_at":"2024-08-12T01:40:05.328Z","dependency_job_id":"331aa8d9-8c32-4596-ad41-2b06f6f66855","html_url":"https://github.com/a1unade/YouTube.NET","commit_stats":null,"previous_names":["2jinx/youtube.net","a1unade/youtube.net"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/a1unade/YouTube.NET","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a1unade%2FYouTube.NET","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a1unade%2FYouTube.NET/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a1unade%2FYouTube.NET/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a1unade%2FYouTube.NET/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/a1unade","download_url":"https://codeload.github.com/a1unade/YouTube.NET/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a1unade%2FYouTube.NET/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271128722,"owners_count":24703879,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-19T02:00:09.176Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asp-net-core","ci","clean-architecture","cqrs","entity-framework-core","jwt","microsoft-identity","postgresql","react","redis-cache","redux","s3","signalr","typescript"],"created_at":"2025-04-02T07:17:47.878Z","updated_at":"2025-08-19T09:10:59.387Z","avatar_url":"https://github.com/a1unade.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Содержание\n\n- [CI status](#ci-status)\n- [Как запускать Flutter приложение](#как-запускать-flutter-приложение)\n- [GraphQL](#graphql)\n  - [Формат идентификаторов](#формат-идентификаторов)\n  - [Структура ответов](#структура-ответов)\n- [Запросы](#запросы)\n  - [1. Получение информации о канале](#1-получение-информации-о-канале)\n  - [2. Получение подписок на каналы пользователя (список каналов, на которые подписан)](#2-получение-подписок-на-каналы-пользователя-список-каналов-на-которые-подписан)\n  - [3. Получение информации о пользователе](#3-получение-информации-о-пользователе)\n  - [4. Получение ID канала пользователя](#4-получение-id-канала-пользователя)\n  - [5. Получение информации о кошельке пользователя](#5-получение-информации-о-кошельке-пользователя)\n  - [6. Получение информации о видео](#6-получение-информации-о-видео)\n  - [7. Получение списка видео с пагинацией](#7-получение-списка-видео-с-пагинацией)\n- [Мутации](#мутации)\n  - [1. Создание кошелька пользователя](#1-создание-кошелька-пользователя)\n- [Этап 2 (Flutter)](#этап-2-flutter)\n  - [Главная](#главная)\n  - [Плеер](#плеер)\n  - [Подписки](#подписки)\n  - [Профиль](#профиль)\n  - [YouTube Premium (для оплаты)](#youtube-premium-для-оплаты)\n  - [Авторизация/регистрация](#авторизациярегистрация)\n- [Как запускать проект в Docker](#как-запускать-проект-в-docker)\n- [Запуск тестов для всего проекта](#запуск-тестов-для-всего-проекта)\n\n\n## CI status\n\n![Frontend CI](https://github.com/a1unade/YouTube.NET/actions/workflows/frontend-ci.yml/badge.svg)\n[![Codecov](https://codecov.io/gh/a1unade/YouTube.NET/branch/main/graph/badge.svg?flag=frontend)](https://codecov.io/gh/a1unade/YouTube.NET)\n\n![Backend CI](https://github.com/a1unade/YouTube.NET/actions/workflows/backend-ci.yml/badge.svg)\n[![Codecov](https://codecov.io/gh/a1unade/YouTube.NET/branch/main/graph/badge.svg?flag=backend)](https://codecov.io/gh/a1unade/YouTube.NET) \n\n\n## Как запускать Flutter приложение\n\nПеред стартом мобильного приложения необходимо поднять backend в Docker =\u003e [гайд](#как-запускать-проект-в-docker).\n\nFlutter приложение не хочет 'дружить' с сервером в Docker на `localhost`, поэтому все работает только на веб-версии, команда для запуска:\n\n```bash\n    flutter run -d chrome --web-hostname=localhost --web-port=5000\n```\n\n\u003e Основные запросы сделаны на `graphql` через блоки и сервисы, авторизация/регистрация на `http.post` и моделями для ответов, дополнительно прикручен провайдер =\u003e `AuthProvider`.\n\n## GraphQL\n\nAPI позволяет получать и управлять данными пользователей, каналов, видео и кошельков через GraphQL запросы и мутации.\n\n### Формат идентификаторов\n- Все сущности идентифицируются UUID в виде строки.\n\n### Структура ответов\n- Все запросы возвращают поля `isSuccessfully` (булево), `message` (текст) для статуса операции.\n- Для пагинации используются параметры `page` (номер страницы) и `size` (размер страницы).\n\n---\n\n## Запросы\n\n### 1. Получение информации о канале\n\n```graphql\nquery {\n  channel(id: \"ab8d5a29-0e4d-4c16-98d3-0ef95e858ad2\") {\n    channel {\n      id             # UUID канала\n      name           # Название канала\n      subscribers    # Количество подписчиков\n      videoCount     # Количество видео на канале\n      description    # Описание канала\n      mainImage      # Идентификатор основного изображения (аватар)\n      bannerImage    # Идентификатор баннера канала\n    }\n  }\n}\n```\n\nПример успешного ответа\n\n```graphql\n{\n  \"data\": {\n    \"channel\": {\n      \"channel\": {\n        \"id\": \"ab8d5a290e4d4c1698d30ef95e858ad2\",\n        \"name\": \"Tamaev TV\",\n        \"subscribers\": 231232,\n        \"videoCount\": 1,\n        \"description\": \"Это моя машина\",\n        \"mainImage\": \"87be5e57-f447-4b07-aa77-df4f56eabe8e\",\n        \"bannerImage\": \"00000000-0000-0000-0000-000000000000\"\n      }\n    }\n  }\n}\n```\n\n### 2. Получение подписок на каналы пользователя (список каналов, на которые подписан)\n\n```graphql\nquery {\n  subsChannel(id: \"f73f9ccd-bea8-4bc1-a3b6-01106ef9c997\", page: 1, size: 10) {\n    isSuccessfully  # Статус запроса (true/false)\n    message        # Сообщение об ошибке или успехе\n    channels {     # Массив каналов, на которые подписан пользователь\n      id\n      name\n      description\n      subscribers\n      videoCount\n      bannerImage\n      mainImage\n    }\n  }\n}\n```\n\nПример успешного ответа\n\n```graphql\n{\n  \"data\": {\n    \"subsChannel\": {\n      \"isSuccessfully\": true,\n      \"message\": null,\n      \"channels\": [\n        {\n          \"id\": \"ab8d5a290e4d4c1698d30ef95e858ad2\",\n          \"name\": \"Tamaev TV\",\n          \"description\": \"Это моя машина\",\n          \"subscribers\": 231232,\n          \"videoCount\": 1,\n          \"bannerImage\": \"\",\n          \"mainImage\": \"87be5e57-f447-4b07-aa77-df4f56eabe8e\"\n        }\n      ]\n    }\n  }\n}\n```\n\n### 3. Получение информации о пользователе\n\n```graphql\nquery {\n  user(id: \"1fd36336-44ad-4cc9-9619-12c639c0f6a4\") {\n    isSuccessfully  # Статус запроса\n    message        # Сообщение об ошибке или успехе\n    name           # Имя пользователя\n    surName        # Фамилия пользователя\n    email          # Email пользователя\n    userName       # Полное имя / Никнейм\n    isPremium      # Статус премиум-подписки (true/false)\n    channelId      # UUID канала пользователя\n  }\n}\n```\n\nПример успешного ответа\n\n```graphql\n{\n  \"data\": {\n    \"user\": {\n      \"isSuccessfully\": true,\n      \"message\": null,\n      \"name\": \"Bulat\",\n      \"surName\": \"Nagimullin\",\n      \"email\": \"bulatfri18@gmail.com\",\n      \"userName\": \"Bulat Nagimullin\",\n      \"isPremium\": false,\n      \"channelId\": \"f73f9ccdbea84bc1a3b601106ef9c997\"\n    }\n  }\n}\n```\n\n### 4. Получение ID канала пользователя\n\n```graphql\nquery {\n  userChannel(id: \"1fd36336-44ad-4cc9-9619-12c639c0f6a4\") {\n    isSuccessfully  # Статус запроса\n    message        # Сообщение, например, с ID канала\n    entityId       # UUID канала пользователя\n  }\n}\n```\n\nПример успешного ответа\n\n```graphql\n{\n  \"data\": {\n    \"userChannel\": {\n      \"isSuccessfully\": true,\n      \"message\": \"User channel id: f73f9ccd-bea8-4bc1-a3b6-01106ef9c997\",\n      \"entityId\": \"f73f9ccdbea84bc1a3b601106ef9c997\"\n    }\n  }\n}\n\n```\n\n### 5. Получение информации о кошельке пользователя\n\n```graphql\nquery {\n  userWallet(id: \"0de80c01-b72e-47ce-b864-b9479ce57feb\") {\n    entityId      # UUID кошелька\n    balance       # Баланс на кошельке (float)\n    isSuccessfully # Статус запроса\n    message      # Сообщение об ошибке или успехе\n    walletId     # Идентификатор кошелька (UUID)\n  }\n}\n```\n\nПример успешного ответа\n\n```graphql\n{\n  \"data\": {\n    \"userWallet\": {\n      \"entityId\": \"0de80c01b72e47ceb864b9479ce57feb\",\n      \"balance\": 100.5,\n      \"isSuccessfully\": true,\n      \"message\": null,\n      \"walletId\": \"f37b7c932b0d486c9aacda7f0bbcd4e4\"\n    }\n  }\n}\n```\n\n### 6. Получение информации о видео\n\n```graphql\nquery {\n  video(id: \"2632bd2f-4db9-4d52-8cf7-18b9d10b05d4\") {\n    isSuccessfully   # Статус запроса\n    channelId       # UUID канала, которому принадлежит видео\n    videoId         # UUID видео\n    video {         # Объект с деталями видео\n      videoFileId   # UUID файла видео\n      previewId     # UUID превью видео\n      viewCount     # Количество просмотров\n      name          # Название видео\n      realiseDate   # Дата публикации\n      channelName   # Название канала\n    }\n  }\n}\n```\n\nПример успешного ответа\n\n```graphql\n{\n  \"data\": {\n    \"video\": {\n      \"isSuccessfully\": true,\n      \"channelId\": \"ab8d5a290e4d4c1698d30ef95e858ad2\",\n      \"videoId\": \"2632bd2f4db94d528cf718b9d10b05d4\",\n      \"video\": {\n        \"videoFileId\": \"5a691bd70f67497395e95548bdd6e156\",\n        \"previewId\": \"a7774f38c7424e1c9df9ea48e28a8e1b\",\n        \"viewCount\": 1245,\n        \"name\": \"Mohito\",\n        \"realiseDate\": \"2025-01-04\",\n        \"channelName\": \"Tamaev TV\"\n      }\n    }\n  }\n}\n```\n\n### 7. Получение списка видео с пагинацией\n\n```graphql\nquery {\n  videoPagination(page: 1, size: 10) {\n    isSuccessfully  # Статус запроса\n    message        # Сообщение об ошибке или успехе\n    videos {       # Массив видео с деталями\n      previewUrl       # UUID превью картинки\n      channelImageUrl  # UUID картинки канала\n      videoName        # Название видео\n      views            # Количество просмотров\n      releaseDate      # Дата публикации\n      videoId          # UUID видео\n      channelId        # UUID канала\n    }\n  }\n}\n```\n\nПример успешного ответа\n\n```graphql\n{\n  \"data\": {\n    \"videoPagination\": {\n      \"isSuccessfully\": true,\n      \"message\": null,\n      \"videos\": [\n        {\n          \"previewUrl\": \"a7774f38-c742-4e1c-9df9-ea48e28a8e1b\",\n          \"channelImageUrl\": \"87be5e57-f447-4b07-aa77-df4f56eabe8e\",\n          \"videoName\": \"Mohito\",\n          \"views\": 1245,\n          \"releaseDate\": \"2025-01-04\",\n          \"videoId\": \"2632bd2f4db94d528cf718b9d10b05d4\",\n          \"channelId\": \"ab8d5a290e4d4c1698d30ef95e858ad2\"\n        }\n      ]\n    }\n  }\n}\n```\n\n## Мутации\n\n### 1. Создание кошелька пользователя\n\n```graphql\nmutation {\n  createWallet(\n    id: \"0de80c01-b72e-47ce-b864-b9479ce57feb\",\n    balance: 100.50\n  ) {\n    isSuccessfully  # Статус операции\n    message        # Сообщение о результате\n    entityId       # UUID созданного кошелька\n  }\n}\n```\n\nПример успешного ответа\n\n```graphql\n{\n  \"data\": {\n    \"createWallet\": {\n      \"isSuccessfully\": true,\n      \"message\": \"Wallet created successfully id: b610e492-93bd-4cd2-a0b6-f1f9884fd3f4\",\n      \"entityId\": \"0de80c01b72e47ceb864b9479ce57feb\"\n    }\n  }\n}\n```\n\n## Этап 2 (Flutter)\n\nСтраницы:\n\n1) Главная (список с видео)\n\n2) Плеер\n\n3) Подписки\n\n4) Профиль\n\n5) YouTube Premium (+ 2 модалки-заглушки)\n\n6) Авторизация и регистрация\n\n### Главная \n\nОткрывается при запуске приложения, попасть можно через нижний навбар:\n\n\u003cimg src=\"./materials/flutter_1.png\" /\u003e\n\nПри нажатии на видео откроется страница плеера\n\n### Плеер\n\nДля плеера использовали готовое [решение](https://pub.dev/packages/video_player).\n\n\u003cimg src=\"./materials/flutter_2.png\" /\u003e\n\n### Подписки\n\nЭкран с каналами, на которые подписан пользователь, попасть можно через нижний навбар.\n\n\u003cimg src=\"./materials/flutter_3.png\" /\u003e\n\n### Профиль\n\nЭкран профиля пользователя, при выходе из аккаунта на странице будет показано соотвествующее сообщение, с возможностью перехода на экран авторизации.\n\n\u003cimg src=\"./materials/flutter_4.png\" /\u003e\n\n\u003cimg src=\"./materials/flutter_5.png\" /\u003e\n\n### YouTube Premium (для оплаты)\n\nСтраница с описанием фич подписки, плюс две модалки-заглушки, которые будут использованы для пополнения баланса и покупки подписки\n\n\u003cimg src=\"./materials/flutter_6.png\" /\u003e\n\n\u003cimg src=\"./materials/flutter_7.png\" /\u003e\n\n### Авторизация/регистрация\n\nЭкраны для авторизации/регистрации без валидации, просто макеты, для входа в аккаунт/регистрации достаточно просто нажать на кнопку\n\nПосле входа/регистрации - редирект на экран профиля, делали регистрацию с помощью провайдера, вынесли его отдельно, для отслеживания состояния пользователя (авторизован или нет, нужно на экране профиля)\n\n\u003cimg src=\"./materials/flutter_8.png\" /\u003e\n\n\u003cimg src=\"./materials/flutter_9.png\" /\u003e\n\n## Как запускать проект в Docker\n\nВ корневой папке нужно ввести команду ```make docker```, чтобы поднять все контейнеры.\n\nЕсли `Makefile` не установлен: \n\n```bash\n    docker:  \n\t\tdocker-compose -f frontend/docker-compose.yml down --rmi all -v\n\t\tdocker-compose -f frontend/docker-compose.yml up --build -d\n\t\tdocker-compose -f backend/Youtube/docker-compose.yml down --rmi all -v\n\t\tdocker-compose -f backend/Youtube/docker-compose.yml up --build -d\n\n    tests:  \n            cd frontend/youtube-frontend \u0026\u0026 npm install\n            cd frontend/youtube-accounts \u0026\u0026 npm install\n            cd frontend/youtube-frontend-tests \u0026\u0026 npm install \u0026\u0026 npm run test\n\n            cd backend/Youtube \u0026\u0026 dotnet test --no-build --verbosity normal --collect:\"XPlat Code Coverage\" --settings ../../.github/coverlet.runsettings\n\n    docker-backend:  \n            docker-compose -f backend/Youtube/docker-compose.yml down --rmi all -v\n            docker-compose -f backend/Youtube/docker-compose.yml up --build -d\n```\n\u003e Чтобы поднять бэкенд\n\nВ результате должен быть примерно такой вывод: \n\n\u003cimg src=\"./materials//7.png\"\u003e\n\n## Запуск тестов для всего проекта\n\nВ корневой папке нужно ввести команду ```make tests```. Запустятся тесты как для фронтенда, так и для бэкенда.\n\nВ консоли должен появиться такой вывод с результатми тестов:\n\n\u003cimg src=\"./materials//8.png\"\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fa1unade%2Fyoutube.net","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fa1unade%2Fyoutube.net","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fa1unade%2Fyoutube.net/lists"}