diff --git a/README.md b/README.md index 02df13b..2b30be4 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,53 @@ IT-Радио — радиостанция про сферу технологийи развитие в IT *** -![Static Badge](https://img.shields.io/badge/Flexites-white?link=https%3A%2F%2Fflexites.org%2F) -![Static Badge](https://img.shields.io/badge/AzuraCast-blue?link=https%3A%2F%2Fwww.azuracast.com%2F) -![Static Badge](https://img.shields.io/badge/Python-ffd429?logo=Python&logoSize=3&link=https%3A%2F%2Fwww.python.org%2F) -![Static Badge](https://img.shields.io/badge/Quasar-87cefa?logo=Quasar&logoSize=3&link=https%3A%2F%2Fquasar.dev%2F) +[![Flexites](https://img.shields.io/badge/Flexites-white)](https://flexites.org/) +[![AzuraCast](https://img.shields.io/badge/AzuraCast-blue)](https://www.azuracast.com/) +[![Python](https://img.shields.io/badge/Python-ffd429?logo=Python&logoSize=3)](https://www.python.org/) +[![Quasar](https://img.shields.io/badge/Quasar-87cefa?logo=Quasar&logoSize=3)](https://quasar.dev/) +[![DRF](https://img.shields.io/badge/Django_REST_Framework-red)](https://www.django-rest-framework.org/) +[![Nginx](https://img.shields.io/badge/Nginx-green?logo=Nginx)](https://nginx.org/ru/) +[![Gunicorn](https://img.shields.io/badge/Gunicorn-white?logo=Gunicorn)](https://gunicorn.org/) + +[![IT-Radio-Logo](docs/logo-it-radio.jpg)](https://itradio.team) + +## Установка +У вас должны быть установлены [зависимости проекта](https://git.flexites.org/Students/ITRadio/src/branch/master/server/requirements.txt) +1. Клонирование репозитория + +```git clone https://git.flexites.org/Students/ITRadio.git``` + +2. Переход в директорию ITRadio + +```cd ITRadio``` + +3. Создание виртуального окружения + +```python -m venv venv``` + +4. Активация виртуального окружения + +```cd venv/``` +```scripts/activate``` +5. Установка зависимостей +```cd server/``` +```pip install -r requirements.txt``` + + +## Документация +Пользовательскую документацию можно получить по [этой ссылке](./docs/ru/index.md). + + + + + +## Инструкция для работы в панели администратора +Инструкцию можно получить по [этой ссылке](./docs/ru/instruk.md). + + +## Документация для разработчиков +Проект развёрнут с помощью Gunicorn и Nginx. API документация находится по [адресу](https://itradio.team/api/). Там же и описаны все контроллеры (views), используемые в проекте + -[![](docs/logo-it-radio.jpg)](https://itradio.team) \ No newline at end of file diff --git a/docs/ru/admin_django.md b/docs/ru/admin_django.md new file mode 100644 index 0000000..abc2d3a --- /dev/null +++ b/docs/ru/admin_django.md @@ -0,0 +1,49 @@ +# IT-Радио Docs RU +Инструкция по навигации в панели администратора проекта IT-Радио + +### [Оглавление](./instruk.md) +С добавлением треков разобрались, плавно перейдем к панели администратора _Django_ +___ +На данный момент в панели администратора доступны следующие приложения и таблицы: ![alt text](image-4.png) +Пойдем по порядку: +1. То, что написано на синим фоне, допустим, "AUDIO" - является приложением внутри нашего сайта, грубо говоря раздел, в котором доступны некоторые _таблицы_ + +1. На примере приложения "AUDIO", разберем сущность _таблицы_ + * Избранные треки + * Плейлисты + * Подкасты + * Треки + +1. Но прежде чем, мы перейдем к таблицам, разберемся в кнопках + * Ссылка на таблицу ![alt text](image-11.png) позволяет перейти в таблицу и посмотреть существующие записи + * Кнопка ![alt text](image-10.png) - позволяет добавить запись в таблицу + * Кнопки ![alt text](image-12.png) - содержатся внутри записи и позволяют создать новую запись, сохранить и создать еще одну запись, сохранить и продолжить редактирование __соответственно__ + * Ах да, не стоит обделять вниманием кнопку ![alt text](image-13.png), которая отображается при редактировании существующего объекта. Думаю, что ее назначение объяснять не нужно + +1. Описанные в первом пункте таблицы хранят уникальные записи, например, в таблице **Избранные треки** мы храним всего лишь два идентификатора: + название поля | тип поля | обязательное ? + :-------------|:--------:|---------------: + id Пользователя | целое число | Да + id Трека | целое число | Да + + ![alt text](image-6.png) +1. Перейдем к плейлистам. Здесь ситуация немного поинтереснее, так как для грамотного создания нам нужно указать: + название поля | тип поля | обязательное ? + :-------------|:--------:|---------------: + Название плейлиста | строка | Нет + Добавленные треки | целое число | Нет + Пользователь | целое число | Нет + Изображение плейлиста | строка | Нет + + ![alt text](image-7.png) + На картинке выше видно, что мы можем просмотреть какие треки мы хотим добавить, эти треки уже существуют в базе данных +1. С подкастам ситуация полегче: + название поля | тип поля | обязательное ? + :-------------|:--------:|---------------: + Название подкаста | строка | Да + Файл подкаста | строка | Да + Изображение подкаста | строка | Нет + + ![alt text](image-8.png) + На данный момент, файлом подкаста может являться только _аудио_ + diff --git a/docs/ru/admin_song.md b/docs/ru/admin_song.md new file mode 100644 index 0000000..f5724bb --- /dev/null +++ b/docs/ru/admin_song.md @@ -0,0 +1,23 @@ +# IT-Радио Docs RU +Инструкция по навигации в панели администратора проекта IT-Радио + +### [Оглавление](./instruk.md) + +Для того, чтобы добавить очередной трек в базу данных, необходимо перейти в панель [AzuraCast](https://azuracast.itradio.team/station/1/files). Здесь мы наблюдаем следущую картину: ![alt text](image.png) +1. Сверху объявлена область, в которую мы можем перенести загруженные треки и они появятся в трансляции + +1. Не стоит забывать о том, что трек появляется в трансляции, только в том случае, когда __находится в плейлисте _default___ + +1. Для того, чтобы добавить трек в плейлист, необходимо нажать кнопку "Редактировать", в котором будет доступно некоторое количество параметров для трека, такие как: + * __название файла__ + * __название песни__ + * __исполнитель__ +и так далее +![alt text](image-1.png) + +1. Но в нашем случае, нам необходимо перейти во вкладку "Плейлисты" и выбрать необходимый плейлист ![alt text](image-2.png) + +1. Наслаждаемся добавленным в трансляцию треком :) ![alt text](image-3.png) + + + \ No newline at end of file diff --git a/docs/ru/azuracast.md b/docs/ru/azuracast.md new file mode 100644 index 0000000..d3ffa55 --- /dev/null +++ b/docs/ru/azuracast.md @@ -0,0 +1,16 @@ +# IT-Радио Docs RU +Пользовательская документация IT-Радио на русском + +### [Оглавление](./index.md) + +## Что такое AzuraCast? +AzuraCast - это открытое программное обеспечение, предназначенное для упрощения управления интернет-радиостанциями. Оно представляет собой портативную веб-радиостанцию, которая включает в себя веб-радио-сервер, планировщик музыки и инструменты для управления медиафайлами. + +___Почему выбрали данное решение?___ +За счёт открытого исходного кода, примера интеграции с __Django__, относительно быстрой скорости подключения к проекту + +___Технические характеристики___ +AzuraCast работает на базе __Docker__ и __Ansible__, что обеспечивает его портативность и гибкость. Оно поддерживает различные форматы аудиофайлов и позволяет управлять плейлистами, включая автоматическое создание плейлистов на основе тегов. + +___Интеграция и управление___ +AzuraCast предоставляет API для интеграции с другими сервисами и поддерживает веб-интерфейс для управления радиостанцией. Он включает в себя функции, такие как управление DJ, управление рекламой, управление слушателями и статистика. \ No newline at end of file diff --git a/docs/ru/getting-started.md b/docs/ru/getting-started.md new file mode 100644 index 0000000..34a6e5a --- /dev/null +++ b/docs/ru/getting-started.md @@ -0,0 +1,9 @@ +# IT-Радио Docs RU +Пользовательская документация IT-Радио на русском + +### [Оглавление](./index.md) + +## Введение +IT-радио — это уникальный проект, который объединяет в себе самых ярких представителей IT-индустрии, а также экспертов из различных областей, чтобы поделиться своими знаниями и опытом с широкой аудиторией. + +Каждое шоу на IT-радио включает в себя актуальные темы, новости, обзоры, интервью с экспертами и многое другое \ No newline at end of file diff --git a/docs/ru/image-1.png b/docs/ru/image-1.png new file mode 100644 index 0000000..95ee3cd Binary files /dev/null and b/docs/ru/image-1.png differ diff --git a/docs/ru/image-10.png b/docs/ru/image-10.png new file mode 100644 index 0000000..c479580 Binary files /dev/null and b/docs/ru/image-10.png differ diff --git a/docs/ru/image-11.png b/docs/ru/image-11.png new file mode 100644 index 0000000..159de68 Binary files /dev/null and b/docs/ru/image-11.png differ diff --git a/docs/ru/image-12.png b/docs/ru/image-12.png new file mode 100644 index 0000000..2db1d4b Binary files /dev/null and b/docs/ru/image-12.png differ diff --git a/docs/ru/image-13.png b/docs/ru/image-13.png new file mode 100644 index 0000000..29e6022 Binary files /dev/null and b/docs/ru/image-13.png differ diff --git a/docs/ru/image-2.png b/docs/ru/image-2.png new file mode 100644 index 0000000..44b665d Binary files /dev/null and b/docs/ru/image-2.png differ diff --git a/docs/ru/image-3.png b/docs/ru/image-3.png new file mode 100644 index 0000000..20afc29 Binary files /dev/null and b/docs/ru/image-3.png differ diff --git a/docs/ru/image-4.png b/docs/ru/image-4.png new file mode 100644 index 0000000..6bc393f Binary files /dev/null and b/docs/ru/image-4.png differ diff --git a/docs/ru/image-5.png b/docs/ru/image-5.png new file mode 100644 index 0000000..e6346f8 Binary files /dev/null and b/docs/ru/image-5.png differ diff --git a/docs/ru/image-6.png b/docs/ru/image-6.png new file mode 100644 index 0000000..bad4710 Binary files /dev/null and b/docs/ru/image-6.png differ diff --git a/docs/ru/image-7.png b/docs/ru/image-7.png new file mode 100644 index 0000000..9c38147 Binary files /dev/null and b/docs/ru/image-7.png differ diff --git a/docs/ru/image-8.png b/docs/ru/image-8.png new file mode 100644 index 0000000..6d51451 Binary files /dev/null and b/docs/ru/image-8.png differ diff --git a/docs/ru/image-9.png b/docs/ru/image-9.png new file mode 100644 index 0000000..425c36b Binary files /dev/null and b/docs/ru/image-9.png differ diff --git a/docs/ru/image.png b/docs/ru/image.png new file mode 100644 index 0000000..9288236 Binary files /dev/null and b/docs/ru/image.png differ diff --git a/docs/ru/index.md b/docs/ru/index.md new file mode 100644 index 0000000..25fec24 --- /dev/null +++ b/docs/ru/index.md @@ -0,0 +1,7 @@ +# IT-Радио Docs RU +Пользовательская документация IT-Радио на русском + +## Оглавление +1. [Введение](./getting-started.md) +1. [Возможности IT-Радио](./itradio_func.md) +1. [Что такое AzuraCast?](./azuracast.md) \ No newline at end of file diff --git a/docs/ru/instruk.md b/docs/ru/instruk.md new file mode 100644 index 0000000..5f0301f --- /dev/null +++ b/docs/ru/instruk.md @@ -0,0 +1,6 @@ +# IT-Радио Docs RU +Инструкция по навигации в панели администратора проекта IT-Радио + +## Оглавление +1. [Добавление треков](./admin_song.md) +1. [Панель администратора Django](./azuracast.md) diff --git a/docs/ru/itradio_func.md b/docs/ru/itradio_func.md new file mode 100644 index 0000000..37f9563 --- /dev/null +++ b/docs/ru/itradio_func.md @@ -0,0 +1,13 @@ +# IT-Радио Docs RU +Пользовательская документация IT-Радио на русском + +### [Оглавление](./index.md) + +### Возможности IT-Радио +Проект IT-Радио предлагает следующий функционал +* Прослушивание _онлайн трансляции_ +* Добавление треков в _избранное_ +* Создание _плейлистов_ на свой вкус +* Прослушивание интересных _подкастов_ +* Просмотр новостей об __IT__ + diff --git a/server/proj/audio/admin.py b/server/proj/audio/admin.py index 77f564c..1de4243 100644 --- a/server/proj/audio/admin.py +++ b/server/proj/audio/admin.py @@ -1,9 +1,10 @@ from django.contrib import admin -from .models import Song, FavoriteSong, PlayList +from .models import Song, FavoriteSong, PlayList, Podkast @admin.register(Song) class SongAdmin(admin.ModelAdmin): list_display = ('slug', 'title', 'artist', 'genre', 'album') + exclude = ('azura_id',) readonly_fields = ['slug'] @admin.register(FavoriteSong) @@ -13,3 +14,7 @@ class FavoriteSongAdmin(admin.ModelAdmin): @admin.register(PlayList) class PlayListAdmin(admin.ModelAdmin): list_display = ('id', 'name', 'user', 'playlist_art') + +@admin.register(Podkast) +class PodkastAdmin(admin.ModelAdmin): + list_display = ('id', 'name', 'podkast_file', 'podkast_art') \ No newline at end of file diff --git a/server/proj/audio/models.py b/server/proj/audio/models.py index 35e7c17..c2d28d7 100644 --- a/server/proj/audio/models.py +++ b/server/proj/audio/models.py @@ -4,14 +4,13 @@ from django.template.defaultfilters import slugify class Song(models.Model): unique_id = models.CharField('ID трека для плеера', max_length=255, blank=True, null=True) + slug = models.SlugField('Слаг трека', blank=True, null=True) azura_id = models.CharField('ID трека с Азуры', max_length=255) title = models.CharField('Название трека', max_length=255) artist = models.CharField('Исполнитель', max_length=255) album = models.CharField('Альбом трека', blank=True, null=True, max_length=255) genre = models.CharField('Жанр трека', blank=True, null=True, max_length=50) art = models.CharField('Изображение трека', blank=True, null=True, max_length=255) - slug = models.SlugField('Слаг трека', blank=True, null=True) - def save(self, *args, **kwargs): if not self.id: @@ -44,7 +43,20 @@ class PlayList(models.Model): song = models.ManyToManyField(Song, blank=True, null=True) user = models.ForeignKey(MyUser, verbose_name='Пользователь', on_delete=models.CASCADE) playlist_art = models.FileField('Изображение плейлиста', blank=True, null=True, upload_to="playlist_images/") + def __str__(self): + return f"{self.name}" class Meta: verbose_name = 'Плейлисты' - verbose_name_plural = 'Плейлисты' \ No newline at end of file + verbose_name_plural = 'Плейлисты' + +class Podkast(models.Model): + name = models.CharField('Название подкаста', max_length=100) + podkast_file = models.FileField('Файл подкаста', upload_to="podkasts/") + podkast_art = models.FileField('Изображение подкаста', blank=True, null=True, upload_to="podkast_images/") + def __str__(self): + return f"{self.name}" + + class Meta: + verbose_name = 'Подкасты' + verbose_name_plural = 'Подкасты' \ No newline at end of file diff --git a/server/proj/audio/serializers.py b/server/proj/audio/serializers.py index 5377488..0f93569 100644 --- a/server/proj/audio/serializers.py +++ b/server/proj/audio/serializers.py @@ -1,5 +1,5 @@ from rest_framework import serializers -from .models import Song, FavoriteSong, PlayList +from .models import Song, FavoriteSong, PlayList, Podkast class SongSerializer(serializers.ModelSerializer): class Meta: @@ -17,14 +17,11 @@ class PlayListSerializer(serializers.ModelSerializer): instance.song.all(), many=True).data return rep - class FavoriteSongSerializer(serializers.ModelSerializer): - class Meta: model = FavoriteSong fields = ('id', 'song', 'user') - def to_representation(self, instance): rep = super().to_representation(instance) @@ -32,3 +29,8 @@ class FavoriteSongSerializer(serializers.ModelSerializer): instance.song).data return rep +class PodkastSerializer(serializers.ModelSerializer): + class Meta: + model = Podkast + fields = '__all__' + \ No newline at end of file diff --git a/server/proj/audio/views.py b/server/proj/audio/views.py index 2022710..d3a6c94 100644 --- a/server/proj/audio/views.py +++ b/server/proj/audio/views.py @@ -10,10 +10,39 @@ import requests from django.http import HttpResponse from .schemas import SongSchema, DeleteSongSchema, PlayListSchema -from .models import Song, FavoriteSong, PlayList -from .serializers import SongSerializer, FavoriteSongSerializer, PlayListSerializer +from .models import Song, FavoriteSong, PlayList, Podkast +from .serializers import SongSerializer, FavoriteSongSerializer, PlayListSerializer, PodkastSerializer from conf.settings.base import AZURACAST_URL, AZURACAST_API_KEY +def authorize_url(url): + file_url = url + API_KEY = AZURACAST_API_KEY + headers = { + "Authorization": f"Bearer {API_KEY}" + } + return requests.get(file_url, headers=headers) + + +class PodkastViewSet(GenericViewSet): + queryset = Podkast + serializer_class = PodkastSerializer + permission_classes = (IsAuthenticated, ) + def list(self, request): + queryset = self.get_queryset().objects.all() + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data, status=status.HTTP_200_OK) + + def retrieve(self, request, pk): + try: + queryset = self.get_queryset().objects.get(pk=pk) + serializer = self.get_serializer(queryset) + return Response(serializer.data, status=status.HTTP_200_OK) + except ObjectDoesNotExist: + return Response( + {'detail': 'Объекта не существует', 'error': {'PlayList': 'Объекта не существует'}}, + status=status.HTTP_404_NOT_FOUND) + + class PlayListViewSet(GenericViewSet): queryset = PlayList serializer_class = PlayListSerializer @@ -104,11 +133,7 @@ class PlayListViewSet(GenericViewSet): song = Song.objects.get(azura_id=request.data.get('azura_id')).pk except ObjectDoesNotExist: file_url = f"{AZURACAST_URL}api/station/it-radio/file/{request.data['azura_id']}" - API_KEY = AZURACAST_API_KEY - headers = { - "Authorization": f"Bearer {API_KEY}" - } - response = requests.get(file_url, headers=headers) + response = authorize_url(file_url) data = request.data file_play = f"{AZURACAST_URL}api/station/it-radio/file/{response.json()['unique_id']}/play" data.update(unique_id=file_play) @@ -169,11 +194,7 @@ class SongViewSet(GenericViewSet): try: song_obj = self.get_queryset().objects.get(azura_id=azura_id) file_url = song_obj.unique_id - API_KEY = AZURACAST_API_KEY - headers = { - "Authorization": f"Bearer {API_KEY}" - } - response = requests.get(file_url, headers=headers) + response = authorize_url(file_url) response.raise_for_status() file_response = HttpResponse(response.content, content_type='audio/mpeg') @@ -197,11 +218,7 @@ class SongViewSet(GenericViewSet): } except ObjectDoesNotExist: file_url = f"{AZURACAST_URL}api/station/it-radio/file/{request.data['azura_id']}" - API_KEY = AZURACAST_API_KEY - headers = { - "Authorization": f"Bearer {API_KEY}" - } - response = requests.get(file_url, headers=headers) + response = authorize_url(file_url) data = request.data file_play = f"{AZURACAST_URL}/api/station/it-radio/file/{response.json()['unique_id']}/play" data.update(unique_id=file_play) @@ -232,11 +249,7 @@ class SongViewSet(GenericViewSet): @action(detail=False, methods=['get']) def get_all_song(self, request): file_url = F"{AZURACAST_URL}api/station/1/files" - API_KEY = AZURACAST_API_KEY - headers = { - "Authorization": f"Bearer {API_KEY}" - } - response = requests.get(file_url, headers=headers) + response = authorize_url(file_url) data = [] for i in response.json(): i['azura_id'] = i.pop('song_id') @@ -246,11 +259,7 @@ class SongViewSet(GenericViewSet): @action(detail=False, methods=['get'], permission_classes=(AllowAny,)) def get_nowplaying(self, request): file_url = F"{AZURACAST_URL}api/nowplaying/it-radio" - API_KEY = AZURACAST_API_KEY - headers = { - "Authorization": f"Bearer {API_KEY}" - } - response = requests.get(file_url, headers=headers) + response = authorize_url(file_url) return Response(response.json(), status=status.HTTP_200_OK) diff --git a/server/proj/conf/settings/__pycache__/base.cpython-310.pyc b/server/proj/conf/settings/__pycache__/base.cpython-310.pyc index d2e9e63..dc54fe6 100644 Binary files a/server/proj/conf/settings/__pycache__/base.cpython-310.pyc and b/server/proj/conf/settings/__pycache__/base.cpython-310.pyc differ diff --git a/server/proj/conf/settings/base.py b/server/proj/conf/settings/base.py index de3e370..f94e04c 100644 --- a/server/proj/conf/settings/base.py +++ b/server/proj/conf/settings/base.py @@ -164,3 +164,18 @@ MIN_LEN_PASSWORD = 8 AZURACAST_URL = 'https://azuracast.itradio.team/' AZURACAST_API_KEY = config('AZURACAST_API_KEY') +""" ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS = 1 +ACCOUNT_USERNAME_MIN_LENGTH = 4 +LOGIN_REDIRECT_URL = "/" + +SITE_ID = 3 + +EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' +EMAIL_HOST = 'smtp.timeweb.ru' +EMAIL_PORT = 25 +EMAIL_HOST_USER = 'mail@flexidev.ru' +EMAIL_HOST_PASSWORD = 'gea(X7i(?=e@L/' +DEFAULT_FROM_EMAIL = EMAIL_HOST_USER +EMAIL_USE_SSL = True +EMAIL_USE_TLS = False +EMAIL_USE_SSL = False """ \ No newline at end of file