Files
digiserver-v2/app/models/content.py
T
ske087 46602f1933 Sanitize codebase, reorganize docs, and add missing deploy files
Remove dead code identified in docs/SANITIZATION-REVIEW.md:
- app/blueprints/content_old.py and app/blueprints/playlist.py
- app/models/group.py, app/utils/nginx_config_reader.py
- orphaned templates (content_list, edit_content, upload_content,
  player_page) and the related group/Template references

Result: 6 blueprints, 82 routes, no dead modules or orphan templates.

Add files that deploy.sh and docker-entrypoint.sh already require but
which were never tracked:
- https_manager.py       (referenced by deploy.sh, migrate_network.sh,
                          docker-entrypoint.sh)
- Caddyfile.example      (seeded by deploy.sh; its absence aborts deploy)

Relocate generated Graphify artifacts from graphify-out/ to
docs/graphify-out/ (110 files, no content change) and archive the
superseded docs under docs/.

Ignore hygiene:
- ignore ad-hoc .env backups (.env.bak*) — they contain live secrets
- keep the pre-sanitization snapshots (docs/legacy code/,
  docs/old_code_documentation/) on disk but out of the repo

Fix .env.example: drop a duplicated config block, genericize the
hardcoded host IP, and document HOSTNAME_INTERNAL.
2026-09-11 12:18:34 +03:00

112 lines
4.3 KiB
Python

"""Content model for media files."""
from datetime import datetime
from typing import Optional, List
from app.extensions import db
class Content(db.Model):
"""Content model representing media files for display.
Attributes:
id: Primary key
filename: Original filename
content_type: Type of content (image, video, pdf, presentation, other)
duration: Default display duration in seconds
file_size: File size in bytes
description: Optional content description
uploaded_at: Upload timestamp
"""
__tablename__ = 'content'
id = db.Column(db.Integer, primary_key=True)
filename = db.Column(db.String(255), nullable=False, unique=True, index=True)
# The pristine original filename as uploaded. `filename` gets overwritten
# with an edited_media/... path when a player edits the content, so this
# column preserves the original so it can always be referenced/restored.
original_filename = db.Column(db.String(255), nullable=True, index=True)
content_type = db.Column(db.String(50), nullable=False, index=True)
duration = db.Column(db.Integer, default=10, nullable=True)
file_size = db.Column(db.BigInteger, nullable=True)
# For 'weblink' content this holds the web page URL to display on the player.
# NULL for file-based content (image/video/pdf).
url = db.Column(db.String(2048), nullable=True)
description = db.Column(db.Text, nullable=True)
uploaded_at = db.Column(db.DateTime, default=datetime.utcnow,
nullable=False, index=True)
# Relationships - many-to-many with playlists
playlists = db.relationship('Playlist', secondary='playlist_content',
back_populates='contents', lazy='dynamic')
def __repr__(self) -> str:
"""String representation of Content."""
return f'<Content {self.filename} (Type={self.content_type})>'
@property
def file_size_mb(self) -> float:
"""Get file size in megabytes."""
if self.file_size:
return round(self.file_size / (1024 * 1024), 2)
return 0.0
@property
def original_display_name(self) -> str:
"""Name of the original (unedited) file for display purposes."""
return self.original_filename or self.filename
@property
def original_media_path(self) -> str:
"""Path (relative to uploads/) where the original unedited file lives.
When a player edits content the original file is archived under
edited_media/<id>/original_<name>. If never edited, the original IS the
current file.
"""
if self.original_filename and self.original_filename != self.filename:
return f"edited_media/{self.id}/original_{self.original_filename}"
return self.filename
@property
def current_media_path(self) -> str:
"""Path (relative to uploads/) of the current/latest version to display."""
return self.filename
def is_image(self) -> bool:
"""Check if content is an image."""
return self.content_type == 'image'
def is_video(self) -> bool:
"""Check if content is a video."""
return self.content_type == 'video'
def is_pdf(self) -> bool:
"""Check if content is a PDF."""
return self.content_type == 'pdf'
def is_weblink(self) -> bool:
"""Check if content is a web link (URL) rather than an uploaded file."""
return self.content_type == 'weblink'
@property
def has_player_edits(self) -> bool:
"""Whether this content has been edited on a player."""
return self.edits.count() > 0
@property
def original_display_name(self) -> str:
"""Display name of the original (unedited) file."""
return self.original_filename or self.filename
@property
def original_media_path(self) -> str:
"""uploads/-relative path of the pristine original file.
After a player edit the original is archived at
edited_media/<id>/original_<name>; before any edit it is simply the
current file.
"""
if self.original_filename and self.filename != self.original_filename:
return f"edited_media/{self.id}/original_{self.original_filename}"
return self.filename