141 lines
4.9 KiB
Python
141 lines
4.9 KiB
Python
"""
|
|
Podcast and Episode models for Podcastrr.
|
|
"""
|
|
from datetime import datetime
|
|
from app.models.database import db
|
|
|
|
class Podcast(db.Model):
|
|
"""
|
|
Model representing a podcast.
|
|
"""
|
|
__tablename__ = 'podcasts'
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
title = db.Column(db.String(255), nullable=False)
|
|
author = db.Column(db.String(255))
|
|
description = db.Column(db.Text)
|
|
image_url = db.Column(db.String(512))
|
|
feed_url = db.Column(db.String(512), nullable=False, unique=True)
|
|
external_id = db.Column(db.String(255), unique=True)
|
|
last_updated = db.Column(db.DateTime, default=datetime.utcnow)
|
|
last_checked = db.Column(db.DateTime, default=datetime.utcnow)
|
|
auto_download = db.Column(db.Boolean, default=False)
|
|
naming_format = db.Column(db.String(255), nullable=True) # If null, use global settings
|
|
episode_ordering = db.Column(db.String(20), default='absolute') # 'absolute' or 'season_episode'
|
|
tags = db.Column(db.String(512), nullable=True) # Comma-separated list of tags
|
|
|
|
# Relationships
|
|
episodes = db.relationship('Episode', backref='podcast', lazy='dynamic', cascade='all, delete-orphan')
|
|
|
|
def __repr__(self):
|
|
return f'<Podcast {self.title}>'
|
|
|
|
def to_dict(self):
|
|
"""
|
|
Convert podcast to dictionary for API responses.
|
|
"""
|
|
return {
|
|
'id': self.id,
|
|
'title': self.title,
|
|
'author': self.author,
|
|
'description': self.description,
|
|
'image_url': self.image_url,
|
|
'feed_url': self.feed_url,
|
|
'external_id': self.external_id,
|
|
'last_updated': self.last_updated.isoformat() if self.last_updated else None,
|
|
'last_checked': self.last_checked.isoformat() if self.last_checked else None,
|
|
'auto_download': self.auto_download,
|
|
'naming_format': self.naming_format,
|
|
'tags': self.tags.split(',') if self.tags else [],
|
|
'episode_count': self.episodes.count()
|
|
}
|
|
|
|
def get_tags(self):
|
|
"""
|
|
Get the list of tags for this podcast.
|
|
|
|
Returns:
|
|
list: List of tags.
|
|
"""
|
|
return [tag.strip() for tag in self.tags.split(',')] if self.tags else []
|
|
|
|
def add_tag(self, tag):
|
|
"""
|
|
Add a tag to this podcast.
|
|
|
|
Args:
|
|
tag (str): Tag to add.
|
|
"""
|
|
if not tag:
|
|
return
|
|
|
|
tags = self.get_tags()
|
|
if tag not in tags:
|
|
tags.append(tag)
|
|
self.tags = ','.join(tags)
|
|
|
|
def remove_tag(self, tag):
|
|
"""
|
|
Remove a tag from this podcast.
|
|
|
|
Args:
|
|
tag (str): Tag to remove.
|
|
"""
|
|
if not tag:
|
|
return
|
|
|
|
tags = self.get_tags()
|
|
if tag in tags:
|
|
tags.remove(tag)
|
|
self.tags = ','.join(tags) if tags else None
|
|
|
|
class Episode(db.Model):
|
|
"""
|
|
Model representing a podcast episode.
|
|
"""
|
|
__tablename__ = 'episodes'
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
podcast_id = db.Column(db.Integer, db.ForeignKey('podcasts.id'), nullable=False)
|
|
title = db.Column(db.String(255), nullable=False)
|
|
description = db.Column(db.Text)
|
|
audio_url = db.Column(db.String(512), nullable=False)
|
|
image_url = db.Column(db.String(512))
|
|
published_date = db.Column(db.DateTime)
|
|
duration = db.Column(db.Integer) # Duration in seconds
|
|
file_size = db.Column(db.Integer) # Size in bytes
|
|
season = db.Column(db.Integer, nullable=True) # Season number
|
|
episode_number = db.Column(db.String(50)) # Episode number within season or absolute
|
|
guid = db.Column(db.String(512), unique=True)
|
|
downloaded = db.Column(db.Boolean, default=False)
|
|
file_path = db.Column(db.String(512))
|
|
explicit = db.Column(db.Boolean, nullable=True) # Whether the episode is marked as explicit
|
|
download_error = db.Column(db.String(255), nullable=True) # Error message if download failed
|
|
status_code = db.Column(db.Integer, nullable=True) # HTTP status code from last download attempt
|
|
|
|
def __repr__(self):
|
|
return f'<Episode {self.title}>'
|
|
|
|
def to_dict(self):
|
|
"""
|
|
Convert episode to dictionary for API responses.
|
|
"""
|
|
return {
|
|
'id': self.id,
|
|
'podcast_id': self.podcast_id,
|
|
'title': self.title,
|
|
'description': self.description,
|
|
'audio_url': self.audio_url,
|
|
'image_url': self.image_url,
|
|
'published_date': self.published_date.isoformat() if self.published_date else None,
|
|
'duration': self.duration,
|
|
'file_size': self.file_size,
|
|
'season': self.season,
|
|
'episode_number': self.episode_number,
|
|
'guid': self.guid,
|
|
'downloaded': self.downloaded,
|
|
'file_path': self.file_path,
|
|
'explicit': self.explicit,
|
|
'download_error': self.download_error,
|
|
'status_code': self.status_code
|
|
}
|