mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-01-23 10:45:27 +00:00
227 lines
9.3 KiB
Python
227 lines
9.3 KiB
Python
from django.db import models
|
|
from django.utils import timezone
|
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from apps.m3u.models import M3UAccount
|
|
from apps.channels.models import Logo
|
|
import uuid
|
|
|
|
|
|
class VODCategory(models.Model):
|
|
"""Categories for organizing VODs (e.g., Action, Comedy, Drama)"""
|
|
|
|
CATEGORY_TYPE_CHOICES = [
|
|
('movie', 'Movie'),
|
|
('series', 'Series'),
|
|
]
|
|
|
|
name = models.CharField(max_length=255)
|
|
category_type = models.CharField(
|
|
max_length=10,
|
|
choices=CATEGORY_TYPE_CHOICES,
|
|
default='movie',
|
|
help_text="Type of content this category contains"
|
|
)
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
|
|
class Meta:
|
|
verbose_name = 'VOD Category'
|
|
verbose_name_plural = 'VOD Categories'
|
|
ordering = ['name']
|
|
unique_together = [('name', 'category_type')]
|
|
|
|
def __str__(self):
|
|
return f"{self.name} ({self.get_category_type_display()})"
|
|
|
|
|
|
class Series(models.Model):
|
|
"""Series information for TV shows"""
|
|
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
|
|
name = models.CharField(max_length=255)
|
|
description = models.TextField(blank=True, null=True)
|
|
year = models.IntegerField(blank=True, null=True)
|
|
rating = models.CharField(max_length=10, blank=True, null=True)
|
|
genre = models.CharField(max_length=255, blank=True, null=True)
|
|
logo = models.ForeignKey(Logo, on_delete=models.SET_NULL, null=True, blank=True)
|
|
|
|
# Metadata IDs for deduplication
|
|
tmdb_id = models.CharField(max_length=50, blank=True, null=True, help_text="TMDB ID for metadata", db_index=True)
|
|
imdb_id = models.CharField(max_length=50, blank=True, null=True, help_text="IMDB ID for metadata", db_index=True)
|
|
|
|
# Additional metadata and properties
|
|
custom_properties = models.JSONField(blank=True, null=True, help_text='Additional metadata and properties for the series')
|
|
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
|
|
class Meta:
|
|
verbose_name = 'Series'
|
|
verbose_name_plural = 'Series'
|
|
ordering = ['name']
|
|
# Create unique constraint for deduplication
|
|
unique_together = [
|
|
('name', 'year', 'tmdb_id'),
|
|
('name', 'year', 'imdb_id'),
|
|
]
|
|
|
|
def __str__(self):
|
|
year_str = f" ({self.year})" if self.year else ""
|
|
return f"{self.name}{year_str}"
|
|
|
|
|
|
class Movie(models.Model):
|
|
"""Movie content"""
|
|
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
|
|
name = models.CharField(max_length=255)
|
|
description = models.TextField(blank=True, null=True)
|
|
year = models.IntegerField(blank=True, null=True)
|
|
rating = models.CharField(max_length=10, blank=True, null=True)
|
|
genre = models.CharField(max_length=255, blank=True, null=True)
|
|
duration = models.IntegerField(blank=True, null=True, help_text="Duration in minutes")
|
|
logo = models.ForeignKey(Logo, on_delete=models.SET_NULL, null=True, blank=True)
|
|
|
|
# Metadata IDs for deduplication
|
|
tmdb_id = models.CharField(max_length=50, blank=True, null=True, help_text="TMDB ID for metadata", db_index=True)
|
|
imdb_id = models.CharField(max_length=50, blank=True, null=True, help_text="IMDB ID for metadata", db_index=True)
|
|
|
|
# Additional metadata and properties
|
|
custom_properties = models.JSONField(blank=True, null=True, help_text='Additional metadata and properties for the movie')
|
|
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
|
|
class Meta:
|
|
verbose_name = 'Movie'
|
|
verbose_name_plural = 'Movies'
|
|
ordering = ['name']
|
|
# Create unique constraint for deduplication
|
|
unique_together = [
|
|
('name', 'year', 'tmdb_id'),
|
|
('name', 'year', 'imdb_id'),
|
|
]
|
|
|
|
def __str__(self):
|
|
year_str = f" ({self.year})" if self.year else ""
|
|
return f"{self.name}{year_str}"
|
|
|
|
|
|
class Episode(models.Model):
|
|
"""Episode content for TV series"""
|
|
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
|
|
name = models.CharField(max_length=255)
|
|
description = models.TextField(blank=True, null=True)
|
|
air_date = models.DateField(blank=True, null=True)
|
|
rating = models.CharField(max_length=10, blank=True, null=True)
|
|
duration = models.IntegerField(blank=True, null=True, help_text="Duration in minutes")
|
|
|
|
# Episode specific fields
|
|
series = models.ForeignKey(Series, on_delete=models.CASCADE, related_name='episodes')
|
|
season_number = models.IntegerField(blank=True, null=True)
|
|
episode_number = models.IntegerField(blank=True, null=True)
|
|
|
|
# Metadata IDs
|
|
tmdb_id = models.CharField(max_length=50, blank=True, null=True, help_text="TMDB ID for metadata", db_index=True)
|
|
imdb_id = models.CharField(max_length=50, blank=True, null=True, help_text="IMDB ID for metadata", db_index=True)
|
|
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
|
|
class Meta:
|
|
verbose_name = 'Episode'
|
|
verbose_name_plural = 'Episodes'
|
|
ordering = ['series__name', 'season_number', 'episode_number']
|
|
unique_together = [
|
|
('series', 'season_number', 'episode_number'),
|
|
]
|
|
|
|
def __str__(self):
|
|
season_ep = f"S{self.season_number or 0:02d}E{self.episode_number or 0:02d}"
|
|
return f"{self.series.name} - {season_ep} - {self.name}"
|
|
|
|
|
|
# New relation models to link M3U accounts with VOD content
|
|
|
|
class M3USeriesRelation(models.Model):
|
|
"""Links M3U accounts to Series with provider-specific information"""
|
|
m3u_account = models.ForeignKey(M3UAccount, on_delete=models.CASCADE, related_name='series_relations')
|
|
series = models.ForeignKey(Series, on_delete=models.CASCADE, related_name='m3u_relations')
|
|
category = models.ForeignKey(VODCategory, on_delete=models.SET_NULL, null=True, blank=True)
|
|
|
|
# Provider-specific fields - renamed to avoid clash with series ForeignKey
|
|
external_series_id = models.CharField(max_length=255, help_text="External series ID from M3U provider")
|
|
custom_properties = models.JSONField(blank=True, null=True, help_text="Provider-specific data")
|
|
|
|
# Timestamps
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
last_episode_refresh = models.DateTimeField(blank=True, null=True, help_text="Last time episodes were refreshed")
|
|
|
|
class Meta:
|
|
verbose_name = 'M3U Series Relation'
|
|
verbose_name_plural = 'M3U Series Relations'
|
|
unique_together = [('m3u_account', 'external_series_id')]
|
|
|
|
def __str__(self):
|
|
return f"{self.m3u_account.name} - {self.series.name}"
|
|
|
|
|
|
class M3UMovieRelation(models.Model):
|
|
"""Links M3U accounts to Movies with provider-specific information"""
|
|
m3u_account = models.ForeignKey(M3UAccount, on_delete=models.CASCADE, related_name='movie_relations')
|
|
movie = models.ForeignKey(Movie, on_delete=models.CASCADE, related_name='m3u_relations')
|
|
category = models.ForeignKey(VODCategory, on_delete=models.SET_NULL, null=True, blank=True)
|
|
|
|
# Streaming information (provider-specific)
|
|
url = models.URLField(max_length=2048)
|
|
stream_id = models.CharField(max_length=255, help_text="External stream ID from M3U provider")
|
|
container_extension = models.CharField(max_length=10, blank=True, null=True)
|
|
|
|
# Provider-specific data
|
|
custom_properties = models.JSONField(blank=True, null=True, help_text="Provider-specific data like quality, language, etc.")
|
|
|
|
# Timestamps
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
|
|
class Meta:
|
|
verbose_name = 'M3U Movie Relation'
|
|
verbose_name_plural = 'M3U Movie Relations'
|
|
unique_together = [('m3u_account', 'stream_id')]
|
|
|
|
def __str__(self):
|
|
return f"{self.m3u_account.name} - {self.movie.name}"
|
|
|
|
def get_stream_url(self):
|
|
"""Get the full stream URL for this movie from this provider"""
|
|
return self.url
|
|
|
|
|
|
class M3UEpisodeRelation(models.Model):
|
|
"""Links M3U accounts to Episodes with provider-specific information"""
|
|
m3u_account = models.ForeignKey(M3UAccount, on_delete=models.CASCADE, related_name='episode_relations')
|
|
episode = models.ForeignKey(Episode, on_delete=models.CASCADE, related_name='m3u_relations')
|
|
|
|
# Streaming information (provider-specific)
|
|
url = models.URLField(max_length=2048)
|
|
stream_id = models.CharField(max_length=255, help_text="External stream ID from M3U provider")
|
|
container_extension = models.CharField(max_length=10, blank=True, null=True)
|
|
|
|
# Provider-specific data
|
|
custom_properties = models.JSONField(blank=True, null=True, help_text="Provider-specific data like quality, language, etc.")
|
|
|
|
# Timestamps
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
|
|
class Meta:
|
|
verbose_name = 'M3U Episode Relation'
|
|
verbose_name_plural = 'M3U Episode Relations'
|
|
unique_together = [('m3u_account', 'stream_id')]
|
|
|
|
def __str__(self):
|
|
return f"{self.m3u_account.name} - {self.episode}"
|
|
|
|
def get_stream_url(self):
|
|
"""Get the full stream URL for this episode from this provider"""
|
|
return self.url
|