Skip to content

Commit

Permalink
Periodically schedule episode translation
Browse files Browse the repository at this point in the history
- Major part of #311
- Add scheduled task to translate all series/episodes (defaults to every 6 hours)
- Wrap all scheduled tasks to log start/finish, as well as correctly set job running attributes
  • Loading branch information
CollinHeist committed May 2, 2023
1 parent 5815090 commit cf8d437
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 34 deletions.
13 changes: 4 additions & 9 deletions app/routers/cards.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,6 @@ def create_preview_card(
font = {
'color': CardClass.TITLE_COLOR,
'title_case': CardClass.DEFAULT_FONT_CASE,
# 'replacements': CardClass.FONT_REPLACEMENTS,
'file': CardClass.TITLE_FONT,
'size': 1.0,
}
Expand Down Expand Up @@ -480,14 +479,14 @@ def create_cards_for_series(
- series_id: ID of the Series to create Title Cards for.
"""

# Get this series, raise 404 if DNE
# Get this Series, raise 404 if DNE
series = get_series(db, series_id, raise_exc=True)

# Get all episodes for this series
# Get all Episodes for this Series
episodes = db.query(models.episode.Episode)\
.filter_by(series_id=series_id).all()

# Set watch statuses of the episodes
# Set watch statuses of the Episodes
_update_episode_watch_statuses(
emby_interface, jellyfin_interface, plex_interface, series, episodes
)
Expand Down Expand Up @@ -609,7 +608,7 @@ def get_episode_card(
@card_router.post('/key', tags=['Plex', 'Tautulli'], status_code=200)
def remake_card(
background_tasks: BackgroundTasks,
plex_rating_key: Optional[int] = Body(default=None),
plex_rating_key: int = Body(...),
# TODO other query options
preferences = Depends(get_preferences),
db = Depends(get_database),
Expand All @@ -622,10 +621,6 @@ def remake_card(
to remake the card of.
"""

# No key provided, exit
if plex_rating_key is None:
return None

# Key provided, no PlexInterface, raise 409
if plex_interface is None:
raise HTTPException(
Expand Down
67 changes: 48 additions & 19 deletions app/routers/schedule.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, Literal, Optional, Union
from typing import Any, Callable, Literal, Optional, Union

from apscheduler.events import EVENT_JOB_SUBMITTED, EVENT_JOB_EXECUTED, EVENT_JOB_ERROR
from fastapi import APIRouter, Body, Depends, Form, HTTPException
Expand All @@ -8,6 +8,7 @@
from app.routers.cards import create_all_title_cards
from app.routers.episodes import refresh_all_episode_data
from app.routers.sync import sync_all
from app.routers.translate import translate_all_series
from app.schemas.base import Base, UNSPECIFIED
from app.schemas.schedule import NewJob, ScheduledTask, UpdateInterval

Expand All @@ -32,42 +33,82 @@ def fake_func():
JOB_DOWNLOAD_SOURCE_IMAGES: str = 'DownloadSourceImages'
JOB_CREATE_TITLE_CARDS: str = 'CreateTitleCards'
JOB_LOAD_MEDIA_SERVERS: str = 'LoadMediaServers'
JOB_ADD_TRANSLATIONS: str = 'AddMissingTranslations'

TaskID = Literal[
JOB_REFRESH_EPISODE_DATA, JOB_SYNC_INTERFACES, JOB_DOWNLOAD_SOURCE_IMAGES,
JOB_CREATE_TITLE_CARDS, JOB_LOAD_MEDIA_SERVERS
JOB_CREATE_TITLE_CARDS, JOB_LOAD_MEDIA_SERVERS, JOB_ADD_TRANSLATIONS
]

def _wrap_before(job_id):
BaseJobs[job_id].running = True
log.debug(f'Task[{job_id}] Started execution')

def _wrap_after(job_id):
BaseJobs[job_id].running = False
log.debug(f'Task[{job_id}] Finished execution')

def wrapped_refresh_all_episode_data():
_wrap_before(JOB_REFRESH_EPISODE_DATA)
refresh_all_episode_data()
_wrap_after(JOB_REFRESH_EPISODE_DATA)

def wrapped_sync_all():
_wrap_before(JOB_SYNC_INTERFACES)
sync_all()
_wrap_after(JOB_SYNC_INTERFACES)

def wrapped_download_source_images():
_wrap_before(JOB_DOWNLOAD_SOURCE_IMAGES)
fake_func()
_wrap_after(JOB_DOWNLOAD_SOURCE_IMAGES)

def wrapped_create_all_title_cards():
_wrap_before(JOB_CREATE_TITLE_CARDS)
create_all_title_cards()
_wrap_after(JOB_CREATE_TITLE_CARDS)

def wrapped_translate_all_series():
_wrap_before(JOB_ADD_TRANSLATIONS)
translate_all_series()
_wrap_after(JOB_ADD_TRANSLATIONS)

BaseJobs = {
# TODO populate with actual function calls
JOB_REFRESH_EPISODE_DATA: NewJob(
id=JOB_REFRESH_EPISODE_DATA,
function=refresh_all_episode_data,
function=wrapped_refresh_all_episode_data,
seconds=60 * 60 * 6,
description='Look for new episodes and update all existing episodes',
), JOB_SYNC_INTERFACES: NewJob(
id=JOB_SYNC_INTERFACES,
function=sync_all,
function=wrapped_sync_all,
seconds=60 * 60 * 6,
description='Run all defined Syncs, adding any new Series',
), JOB_DOWNLOAD_SOURCE_IMAGES: NewJob(
id=JOB_DOWNLOAD_SOURCE_IMAGES,
function=fake_func,
function=wrapped_download_source_images,
seconds=60 * 60 * 4,
description='Download source images for Title Cards',
), JOB_CREATE_TITLE_CARDS: NewJob(
id=JOB_CREATE_TITLE_CARDS,
function=create_all_title_cards,
function=wrapped_create_all_title_cards,
seconds=60 * 60 * 6,
description='Create all missing or updated Title Cards',
), JOB_LOAD_MEDIA_SERVERS: NewJob(
id=JOB_LOAD_MEDIA_SERVERS,
function=fake_func,
seconds=60 * 60 * 4,
description='Load all Title Cards into Emby, Jellyfin, or Plex',
),
), JOB_ADD_TRANSLATIONS: NewJob(
id=JOB_ADD_TRANSLATIONS,
function=wrapped_translate_all_series,
seconds=60 * 60 * 4,
description='Search for and add all missing Episode translations',
)
}


# Initialize scheduler with starting jobs
def initialize_scheduler() -> None:
scheduler = get_scheduler()
Expand All @@ -82,18 +123,6 @@ def initialize_scheduler() -> None:
)
initialize_scheduler()

# Add listener to update running status of jobs
def job_started_listener(event):
log.debug(f'Task[{event.job_id}] Started execution')
BaseJobs.get(event.job_id).running = True

def job_finished_listener(event):
log.debug(f'Task[{event.job_id}] Finished execution')
BaseJobs.get(event.job_id).running = False

get_scheduler().add_listener(job_started_listener, EVENT_JOB_SUBMITTED)
get_scheduler().add_listener(job_finished_listener, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR)


def _scheduled_task_from_job(job) -> ScheduledTask:
"""
Expand Down
45 changes: 39 additions & 6 deletions app/routers/translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,41 @@
from app.routers.episodes import get_episode
from app.routers.series import get_series
from app.routers.templates import get_template
from app.schemas.base import UNSPECIFIED


def translate_all_series():
"""
Schedule-able function to add missing translations to all Series and
Episodes in the Database.
"""

try:
# Get the Database
with next(get_database()) as db:
# Get all Series
all_series = db.query(models.series.Series).all()
for series in all_series:
# TODO skip unmonitored Series
# Get the Series Template
try:
series_template = get_template(
db, series.template_id, raise_exc=True
)
except Exception as e:
log.warning(f'Skipping {series.as_series_info} - missing Template')
continue

# Get all Episodes of this Series
episodes = db.query(models.episode.Episode)\
.filter_by(series_id=series.id).all()

# Translate each Episode
for episode in episodes:
_translate_episode(
db, series, series_template,episode,get_tmdb_interface()
)
except Exception as e:
log.exception(f'Failed to add translations', e)


translation_router = APIRouter(
Expand Down Expand Up @@ -48,7 +82,7 @@ def _translate_episode(
except HTTPException:
log.warning(f'Episode[{episode.id}] Not translating {episode_info}'
f' - missing Template')
return
return None

# Get translations for this episode, exit if no translation
if episode_template is not None and episode_template.translations:
Expand All @@ -58,8 +92,7 @@ def _translate_episode(
elif series_template is not None and series_template.translations:
translations = series_template.translations
else:
log.debug(f'Episode[{episode.id}] Has no translation {episode_info}')
return
return None

# Look for and add each translation for this Episode
changed = False
Expand All @@ -69,7 +102,7 @@ def _translate_episode(

# Skip if this translation already exists
if data_key in episode.translations:
log.debug(f'Episode[{episode.id}] Already has "{data_key}" - skipping')
log.debug(f'{series.as_series_info} {episode_info} Already has "{data_key}" - skipping')
continue

# Get new translation from TMDb, add to Episode
Expand All @@ -78,7 +111,7 @@ def _translate_episode(
)
if translation is not None:
episode.translations[data_key] = translation
log.debug(f'Episode[{episode.id}] Translated {episode_info} {language_code} -> "{translation}" -> {data_key}')
log.debug(f'{series.as_series_info} {episode_info} Translated {episode_info} {language_code} -> "{translation}" -> {data_key}')
changed = True

# If any translations were added, commit updates to database
Expand Down

0 comments on commit cf8d437

Please sign in to comment.