diff --git a/src/videoag_common/objects/__init__.py b/src/videoag_common/objects/__init__.py index 6c95ffb30610b55254dafd836676c5b8b5eb10cd..11b0571ebd36767a02a9ac69e1075920e13f068e 100644 --- a/src/videoag_common/objects/__init__.py +++ b/src/videoag_common/objects/__init__.py @@ -1,7 +1,15 @@ from .course import Course, Lecture, Chapter from .site import Announcement, AnnouncementType, AnnouncementPageVisibility, Featured from .user import User -from .medium import PublishMedium, TargetMedium, TargetMediumType, PlainVideoTargetMedium, PlainAudioTargetMedium, SourceMedium +from .medium import ( + PublishMedium, + TargetMedium, + TargetMediumType, + PlainVideoTargetMedium, + PlainAudioTargetMedium, + ThumbnailTargetMedium, + SourceMedium +) from .view_permissions import ViewPermissions, ViewPermissionsType, EffectiveViewPermissions from .changelog import ( ChangelogEntry, diff --git a/src/videoag_common/objects/course.py b/src/videoag_common/objects/course.py index 9a32b14281e82249abaeb3938edf06239da5fccc..d31b9945ecf934e88134f006c27cf21fd154b650 100644 --- a/src/videoag_common/objects/course.py +++ b/src/videoag_common/objects/course.py @@ -254,15 +254,6 @@ class Lecture(DeletableApiObject, VisibilityApiObject, ApiViewPermissionsObject, def authentication_methods(self): return self.effective_view_permissions.get_authentication_methods() - @api_include_in_data( - type_id="string", - data_notes="URL to the lecture's thumbnail file (Maybe with a redirect)" - ) - def thumbnail_url(self) -> str: - # This is not too nice and only works from the API, but we need to access the API config - import api - return f"{api.config['API_BASE_URL']}/resources/lecture_thumbnail/{self.id}" - @property def effective_media_process_obj(self) -> MediaProcess: if self.media_process is not None: diff --git a/src/videoag_common/objects/medium.py b/src/videoag_common/objects/medium.py index 75e8b4a7b6e9c87737f0cfbdfbf415e3ce386857..0ab4ba12e1234a5c57d363015a884efc5dc4e97a 100644 --- a/src/videoag_common/objects/medium.py +++ b/src/videoag_common/objects/medium.py @@ -73,6 +73,7 @@ class SourceMedium(DeletableApiObject, Base): class TargetMediumType(Enum): PLAIN_VIDEO = "plain_video" PLAIN_AUDIO = "plain_audio" + THUMBNAIL = "thumbnail" _TARGET_MEDIUM_TYPE_ENUM = create_enum_type(TargetMediumType) @@ -114,6 +115,11 @@ class TargetMedium(DeletableApiObject, Base): primaryjoin=lambda: Job.id == TargetMedium.producer_job_id, lazy="raise_on_sql" ) + publish_medium: Mapped["PublishMedium"] = relationship( + primaryjoin=lambda: PublishMedium.target_medium_id == TargetMedium.id, + back_populates="target_medium", + lazy="raise_on_sql" + ) @api_include_in_data( type_id="string", @@ -169,6 +175,13 @@ class PlainAudioTargetMedium(TargetMedium): } +class ThumbnailTargetMedium(TargetMedium): + __tablename__ = None # Prevent our own base from adding a table name. This should be a single-table inheritance + __mapper_args__ = { + "polymorphic_identity": TargetMediumType.THUMBNAIL + } + + class PublishMedium(VisibilityApiObject, DeletableApiObject, Base): __api_data__ = ApiObjectClass( parent_relationship_config_ids=["lecture"] @@ -193,12 +206,21 @@ class PublishMedium(VisibilityApiObject, DeletableApiObject, Base): target_medium: Mapped["TargetMedium"] = api_mapped( relationship( primaryjoin=lambda: TargetMedium.id == PublishMedium.target_medium_id, + back_populates="publish_medium", lazy="raise_on_sql" ), ApiMany2OneRelationshipField( include_in_data=True ) ) + __table_args__ = ( + sql.Index( + "check_target_medium_unique", + target_medium_id, + unique=True, + postgresql_where="NOT deleted" + ), + ) class MediaProcessTemplate(ApiObject, Base):