first run at m3u profiles

This commit is contained in:
kappa118 2025-02-27 22:05:41 -05:00
parent eec6e06b1d
commit 53836fdac3
5 changed files with 115 additions and 8 deletions

View file

@ -1,6 +1,6 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .api_views import M3UAccountViewSet, M3UFilterViewSet, ServerGroupViewSet, RefreshM3UAPIView, RefreshSingleM3UAPIView, UserAgentViewSet
from .api_views import M3UAccountViewSet, M3UFilterViewSet, ServerGroupViewSet, RefreshM3UAPIView, RefreshSingleM3UAPIView, UserAgentViewSet, M3UAccountProfileViewSet
app_name = 'm3u'
@ -8,10 +8,13 @@ router = DefaultRouter()
router.register(r'accounts', M3UAccountViewSet, basename='m3u-account')
router.register(r'filters', M3UFilterViewSet, basename='m3u-filter')
router.register(r'server-groups', ServerGroupViewSet, basename='server-group')
router.register(r'profiles', M3UAccountViewSet, basename='m3u-account-profiles')
urlpatterns = [
path('refresh/', RefreshM3UAPIView.as_view(), name='m3u_refresh'),
path('refresh/<int:account_id>/', RefreshSingleM3UAPIView.as_view(), name='m3u_refresh_single'),
path('accounts/<int:m3u_account_id>/profiles/', M3UAccountProfileViewSet.as_view({'get': 'list', 'post': 'create'})),
path('accounts/<int:m3u_account_id>/profiles/<int:pk>/', M3UAccountProfileViewSet.as_view({'put': 'update', 'delete': 'destroy'})),
]
urlpatterns += router.urls

View file

@ -9,7 +9,7 @@ from django.http import JsonResponse
from django.core.cache import cache
# Import all models, including UserAgent.
from .models import M3UAccount, M3UFilter, ServerGroup
from .models import M3UAccount, M3UFilter, ServerGroup, M3UAccountProfile
from core.models import UserAgent
from core.serializers import UserAgentSerializer
# Import all serializers, including the UserAgentSerializer.
@ -17,6 +17,7 @@ from .serializers import (
M3UAccountSerializer,
M3UFilterSerializer,
ServerGroupSerializer,
M3UAccountProfileSerializer,
)
from .tasks import refresh_single_m3u_account, refresh_m3u_accounts
@ -68,3 +69,16 @@ class UserAgentViewSet(viewsets.ModelViewSet):
serializer_class = UserAgentSerializer
permission_classes = [IsAuthenticated]
class M3UAccountProfileViewSet(viewsets.ModelViewSet):
queryset = M3UAccountProfile.objects.all()
serializer_class = M3UAccountProfileSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
account_id = self.kwargs['account_id']
return M3UAccountProfile.objects.filter(account_id=account_id)
def perform_create(self, serializer):
account_id = self.kwargs['account_id']
account = M3UAccount.objects.get(id=account_id)
serializer.save(account=account)

View file

@ -0,0 +1,62 @@
# Generated by Django 5.1.6 on 2025-02-28 01:36
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('core', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='ServerGroup',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(help_text='Unique name for this server group.', max_length=100, unique=True)),
],
),
migrations.CreateModel(
name='M3UAccount',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(help_text='Unique name for this M3U account', max_length=255, unique=True)),
('server_url', models.URLField(blank=True, help_text='The base URL of the M3U server (optional if a file is uploaded)', null=True)),
('uploaded_file', models.FileField(blank=True, null=True, upload_to='m3u_uploads/')),
('max_streams', models.PositiveIntegerField(default=0, help_text='Maximum number of concurrent streams (0 for unlimited)')),
('is_active', models.BooleanField(default=True, help_text='Set to false to deactivate this M3U account')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='Time when this account was created')),
('updated_at', models.DateTimeField(auto_now=True, help_text='Time when this account was last updated')),
('user_agent', models.ForeignKey(blank=True, help_text='The User-Agent associated with this M3U account.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='m3u_accounts', to='core.useragent')),
('server_group', models.ForeignKey(blank=True, help_text='The server group this M3U account belongs to', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='m3u_accounts', to='m3u.servergroup')),
],
),
migrations.CreateModel(
name='M3UFilter',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('filter_type', models.CharField(choices=[('group', 'Group Title'), ('name', 'Stream Name')], default='group', help_text='Filter based on either group title or stream name.', max_length=50)),
('regex_pattern', models.CharField(help_text='A regex pattern to match streams or groups.', max_length=200)),
('exclude', models.BooleanField(default=True, help_text='If True, matching items are excluded; if False, only matches are included.')),
('m3u_account', models.ForeignKey(help_text='The M3U account this filter is applied to.', on_delete=django.db.models.deletion.CASCADE, related_name='filters', to='m3u.m3uaccount')),
],
),
migrations.CreateModel(
name='M3UAccountProfile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(help_text='Name for the M3U account profile', max_length=255)),
('max_streams', models.PositiveIntegerField(default=0, help_text='Maximum number of concurrent streams (0 for unlimited)')),
('search_pattern', models.CharField(max_length=255)),
('replace_pattern', models.CharField(max_length=255)),
('m3u_account_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='m3u.m3uaccount')),
],
options={
'constraints': [models.UniqueConstraint(fields=('m3u_account_id', 'name'), name='unique_account_name')],
},
),
]

View file

@ -134,7 +134,7 @@ class M3UFilter(models.Model):
# If no include filters exist, assume all non-excluded streams are valid
if not any(not f.exclude for f in filters):
return streams.exclude(id__in=[s.id for s in excluded_streams])
return streams.filter(id__in=[s.id for s in included_streams])
@ -148,3 +148,27 @@ class ServerGroup(models.Model):
def __str__(self):
return self.name
class M3UAccountProfile(models.Model):
name = models.CharField(
max_length=255,
help_text="Name for the M3U account profile"
)
m3u_account_id = models.ForeignKey(M3UAccount,
on_delete=models.CASCADE
)
max_streams = models.PositiveIntegerField(
default=0,
help_text="Maximum number of concurrent streams (0 for unlimited)"
)
search_pattern = models.CharField(
max_length=255,
)
replace_pattern = models.CharField(
max_length=255,
)
class Meta:
constraints = [
models.UniqueConstraint(fields=['m3u_account_id', 'name'], name='unique_account_name')
]

View file

@ -1,5 +1,5 @@
from rest_framework import serializers
from .models import M3UAccount, M3UFilter, ServerGroup
from .models import M3UAccount, M3UFilter, ServerGroup, M3UAccountProfile
from core.models import UserAgent
class M3UFilterSerializer(serializers.ModelSerializer):
@ -9,6 +9,12 @@ class M3UFilterSerializer(serializers.ModelSerializer):
model = M3UFilter
fields = ['id', 'filter_type', 'regex_pattern', 'exclude']
class M3UAccountProfileSerializer(serializers.ModelSerializer):
"""Serializer for M3U Account Profiles"""
class Meta:
model = M3UAccountProfile
fields = ['id', 'name', 'm3u_account_id', 'search_pattern', 'replace_pattern']
class M3UAccountSerializer(serializers.ModelSerializer):
"""Serializer for M3U Account"""
@ -18,20 +24,18 @@ class M3UAccountSerializer(serializers.ModelSerializer):
queryset=UserAgent.objects.all(),
required=True
)
profiles = M3UAccountProfileSerializer(many=True, read_only=True)
class Meta:
model = M3UAccount
fields = [
'id', 'name', 'server_url', 'uploaded_file', 'server_group',
'max_streams', 'is_active', 'created_at', 'updated_at', 'filters', 'user_agent'
'max_streams', 'is_active', 'created_at', 'updated_at', 'filters', 'user_agent', 'profiles'
]
class ServerGroupSerializer(serializers.ModelSerializer):
"""Serializer for Server Group"""
class Meta:
model = ServerGroup
fields = ['id', 'name']