diff --git a/app/models/episode.py b/app/models/episode.py index 0c53098a..d7739ff0 100755 --- a/app/models/episode.py +++ b/app/models/episode.py @@ -461,6 +461,24 @@ def get_source_file(self, style: Style) -> Path: ).resolve() + def get_mask_file(self) -> Path: + """ + Get the mask image associated with this Episode. + + Returns: + Path to the mask file for this Episode. + """ + + source_name = ( + self.source_file + or f's{self.season_number}e{self.episode_number}-mask.png' + ) + + return get_preferences().source_directory \ + / self.series.path_safe_name \ + / source_name + + @property def watched_statuses_flat(self) -> dict[str, bool]: """ @@ -525,14 +543,14 @@ def add_watched_status(self, current = self.watched_statuses.get(key) self.watched_statuses[key] = status.status if current != status.status: - log.trace(f'{self} Updating watched status ' + log.trace(f'{self} updating watched status ' f'({current} -> {status.status})') return current != status.status # Interface has no mappings, add self.watched_statuses[key] = status.status - log.trace(f'{self} Adding watched status') + log.trace(f'{self} adding watched status') return True diff --git a/app/routers/sources.py b/app/routers/sources.py index e359188a..60daf114 100755 --- a/app/routers/sources.py +++ b/app/routers/sources.py @@ -765,3 +765,69 @@ def delete_series_backdrop( file.unlink(missing_ok=True) request.state.log.debug(f'Deleted file ({file.resolve()})') + + +@source_router.put('/episode/{episode_id}/mask') +async def upload_episode_mask_image( + request: Request, + episode_id: int, + file: UploadFile, + db: Session = Depends(get_database), + ) -> None: + """ + + """ + + # Get contextual logger + log: Logger = request.state.log + + # Get Episode with this ID, raise 404 if DNE + episode = get_episode(db, episode_id, raise_exc=True) + + # Send error if no image content was provided + if not (uploaded_file := await file.read()): + raise HTTPException( + status_code=422, + detail='URL or file are required', + ) + + # If file already exists, warn about overwriting + if (mask_file := episode.get_mask_file()).exists(): + log.info(f'{episode} mask image "{mask_file}" exists - replacing') + + # Write content directly + mask_file.write_bytes(uploaded_file) + log.debug(f'Wrote {len(uploaded_file)} bytes to {mask_file}') + + # Delete associated Card and Loaded entry to initiate future reload + delete_cards( + db, + db.query(CardModel).filter_by(episode_id=episode_id), + db.query(LoadedModel).filter_by(episode_id=episode_id), + log=log, + ) + + +@source_router.delete('/episode/{episode_id}/mask') +def delete_episode_mask_image( + request: Request, + episode_id: int, + db: Session = Depends(get_database), + ) -> None: + """ + Delete the mask image for the given Episode. + + - episode_id: ID of the Episode whose mask file to delete. + """ + + # Get Episode with this ID, raise 404 if DNE + episode = get_episode(db, episode_id, raise_exc=True) + + if not (mask_file := episode.get_mask_file()).exists(): + raise HTTPException( + status_code=404, + detail='Episode does not have a mask image', + ) + + mask_file.unlink(missing_ok=True) + request.state.log.debug(f'Deleting {episode} "{mask_file}"') diff --git a/modules/ref/version_webui b/modules/ref/version_webui index 6093df5c..bc230c7a 100755 --- a/modules/ref/version_webui +++ b/modules/ref/version_webui @@ -1 +1 @@ -v2.0-alpha.13.0-webui9 \ No newline at end of file +v2.0-alpha.13.0-webui10 \ No newline at end of file