Skip to content

Commit

Permalink
Periodically check for new releases
Browse files Browse the repository at this point in the history
- Part of #311.
- Check for new releases every 12 hours
- Add info message to the Settings page when a new release is available
  • Loading branch information
CollinHeist committed May 17, 2023
1 parent e2fa24d commit 0886816
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 7 deletions.
1 change: 1 addition & 0 deletions app/database/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from pickle import load
with file.open('rb') as fh:
PreferencesLocal = load(fh)
PreferencesLocal.current_version = Preferences.VERSION_FILE.read_text().strip()
else:
PreferencesLocal = Preferences()

Expand Down
40 changes: 40 additions & 0 deletions app/internal/availability.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from requests import get as req_get
from typing import Optional

from fastapi import HTTPException

from modules.Debug import log

def get_latest_version(raise_exc: bool = True) -> Optional[str]:
"""
Get the latest version of TitleCardMaker available.
Args:
raise_exc: Whether to raise an HTTPException if getting the
latest version fails for any reason.
Returns:
The string version number of the latest release. If unable to
determine, and raise_exc is False, then None is returned.
Raises:
HTTPException (500) if raise_exc is True and the version number
cannot be determined.
"""

try:
response = req_get(
'https://api.github.com/repos/CollinHeist/'
'TitleCardMaker/releases/latest'
)
assert response.ok
except Exception as e:
log.exception(f'Error checking for new release', e)
if raise_exc:
raise HTTPException(
status_code=500,
detail=f'Error checking for new release',
)
return None

return response.json().get('name', '').strip()
4 changes: 4 additions & 0 deletions app/models/preferences.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

class Preferences:

VERSION_FILE = TCM_ROOT / 'modules' / 'ref' / 'version'
DEFAULT_CARD_FILENAME_FORMAT = (
'{series_full_name} S{season_number:02}E{episode_number:02}'
)
Expand All @@ -33,6 +34,9 @@ class Preferences:


def __init__(self) -> None:
self.current_version = self.VERSION_FILE.read_text().strip()
self.available_version = None

self.asset_directory = Path(__file__).parent.parent / 'assets'
self.card_directory = TCM_ROOT / 'cards'
self.source_directory = TCM_ROOT / 'source'
Expand Down
13 changes: 12 additions & 1 deletion app/routers/availability.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from datetime import datetime, timedelta
from typing import Literal
from typing import Literal, Optional

from fastapi import APIRouter, Depends, HTTPException, Query
from requests import get as req_get
Expand All @@ -8,6 +8,7 @@
get_database, get_preferences, get_emby_interface, get_jellyfin_interface,
get_plex_interface, get_sonarr_interface,
)
from app.internal.availability import get_latest_version
import app.models as models
from app.models.template import OPERATIONS, ARGUMENT_KEYS
from app.schemas.card import CardType, LocalCardType, RemoteCardType
Expand Down Expand Up @@ -43,6 +44,16 @@ def _get_remote_cards() -> list[RemoteCardType]:
tags=['Availability'],
)


@availablility_router.get('/version', status_code=200)
def get_latest_available_version() -> Optional[str]:
"""
Get the latest version number for TitleCardMaker.
"""

return get_latest_version()


@availablility_router.get('/card-types', status_code=200, tags=['Title Cards'])
def get_all_available_card_types(
show_excluded: bool = Query(default=False),
Expand Down
2 changes: 1 addition & 1 deletion app/routers/cards.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ def create_card_for_episode(
series = get_series(db, episode.series_id, raise_exc=True)

# Set watch status of the Episode
_update_episode_watch_statuses(
update_episode_watch_statuses(
emby_interface, jellyfin_interface, plex_interface, series, [episode]
)

Expand Down
26 changes: 21 additions & 5 deletions app/routers/schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

from fastapi import APIRouter, Body, Depends, HTTPException

from app.dependencies import get_scheduler, get_preferences
from app.dependencies import get_scheduler
from app.internal.availability import get_latest_version
from app.internal.cards import create_all_title_cards
from app.internal.episodes import refresh_all_episode_data
from app.internal.series import load_all_media_servers
Expand Down Expand Up @@ -32,11 +33,13 @@
JOB_LOAD_MEDIA_SERVERS: str = 'LoadMediaServers'
JOB_REFRESH_EPISODE_DATA: str = 'RefreshEpisodeData'
JOB_SYNC_INTERFACES: str = 'SyncInterfaces'
# Internal Job ID's
INTERNAL_JOB_CHECK_FOR_NEW_RELEASE: str = 'CheckForNewRelease'

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

"""
Expand Down Expand Up @@ -88,12 +91,17 @@ def wrapped_translate_all_series():
translate_all_series()
_wrap_after(JOB_ADD_TRANSLATIONS)

def wrapped_get_latest_version():
_wrap_before(INTERNAL_JOB_CHECK_FOR_NEW_RELEASE)
get_latest_version()
_wrap_after(INTERNAL_JOB_CHECK_FOR_NEW_RELEASE)

"""
Dictionary of Job ID's to NewJob objects that contain the default Job
attributes for all major functions.
"""
BaseJobs = {
# TODO populate with actual function calls
# Public jobs
JOB_REFRESH_EPISODE_DATA: NewJob(
id=JOB_REFRESH_EPISODE_DATA,
function=wrapped_refresh_all_episode_data,
Expand Down Expand Up @@ -129,7 +137,15 @@ def wrapped_translate_all_series():
function=wrapped_download_all_series_logos,
seconds=60 * 60 * 24,
description='Download Logos for all Series',
)
),
# Internal (private) jobs
'CheckForNewRelease': NewJob(
id='CheckForNewRelease',
function=wrapped_get_latest_version,
seconds=60 * 60 * 12,
description='Check for a new release of TitleCardMaker',
internal=True,
),
}


Expand Down Expand Up @@ -188,7 +204,7 @@ def get_scheduled_tasks(
return [
_scheduled_task_from_job(job)
for job in scheduler.get_jobs()
if job.id in BaseJobs
if job.id in BaseJobs and not BaseJobs[job.id].internal
]


Expand Down
11 changes: 11 additions & 0 deletions app/routers/settings.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from pathlib import Path

from fastapi import APIRouter, Body, Depends

from app.dependencies import get_preferences, refresh_imagemagick_interface
Expand All @@ -14,6 +16,15 @@
)


@settings_router.get('/version', status_code=200)
def get_current_version(preferences = Depends(get_preferences)) -> str:
"""
Get the current version of TitleCardMaker.
"""

return preferences.version


@settings_router.patch('/update', status_code=200)
def update_global_settings(
update_preferences: UpdatePreferences = Body(...),
Expand Down
4 changes: 4 additions & 0 deletions app/schemas/schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ class NewJob(Base):
function: Callable[..., None] = Field(description='Function this Job will run')
seconds: PositiveInt = Field(description='How often (in seconds) to run this Job')
description: str = Field(description='Description of the Job')
internal: bool = Field(
default=False,
description='Whether this Job is internal and should not be exposed in API calls',
)
running: bool = Field(
default=False,
description='Whether this Job is currently running'
Expand Down

0 comments on commit 0886816

Please sign in to comment.