Skip to content

Commit

Permalink
[69] Add global setting to delete unsynced series
Browse files Browse the repository at this point in the history
- Implements #468
- Adds a global setting called "Delete Un-Synced Series" to delete all series not in a Sync source
- Delay Sync task up to 5 minutes if started when a Sync is already running
- Add delete_unsynced_series field to Preference models
  • Loading branch information
CollinHeist committed Jul 5, 2024
1 parent 223ee38 commit c4ae227
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 26 deletions.
82 changes: 64 additions & 18 deletions app/internal/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@

from app.database.query import get_all_templates, get_interface
from app.dependencies import get_database, get_preferences
from app.internal.series import add_series
from app.internal.cards import delete_cards
from app.internal.series import add_series, delete_series
from app.models.card import Card
from app.models.connection import Connection
from app.models.loaded import Loaded
from app.models.series import Series
from app.models.sync import Sync
from app.schemas.series import NewSeries
Expand All @@ -24,27 +27,57 @@ def sync_all(*, log: Logger = log) -> None:
Schedule-able function to run all defined Syncs in the Database.
"""

# Wait if there is a Sync currently running
attempts = 5
while ((preferences := get_preferences()).currently_running_sync is not None
and attempts > 0):
log.debug('Sync is current running, waiting..')
sleep(60)
attempts -= 1

try:
# Get the Database
with next(get_database()) as db:
# Get and run all Syncs
for sync in db.query(Sync).all():
# Exit if there are no Syncs
if not (syncs := db.query(Sync).all()):
return None

# Run each Sync
for sync in syncs:
try:
run_sync(db, sync, log=log)
except HTTPException as e:
log.exception(f'{sync} Error Syncing - {e.detail}', e)
except HTTPException as exc:
log.exception(f'{sync} Error Syncing - {exc.detail}')
except OperationalError:
log.debug(f'Database is busy, sleeping..')
log.debug('Database is busy, sleeping..')
sleep(30)

# Remove un-synced Series if toggled
if preferences.delete_unsynced_series:
# Delete all Series which do not have an associated Sync
to_delete = db.query(Series)\
.filter(Series.sync_id.is_(None))\
.all()
for series in to_delete:
# Delete Cards and Loaded objects
delete_cards(
db,
db.query(Card).filter_by(series_id=series.id),
db.query(Loaded).filter_by(series_id=series.id),
commit=False,
log=log,
)
# Delete Series itself
delete_series(db, series, commit_changes=False, log=log)
db.commit()
except Exception as e:
log.exception(f'Failed to run all Syncs', e)
get_preferences().currently_running_sync = None
except Exception:
log.exception('Failed to run all Syncs')

preferences.currently_running_sync = None


def add_sync(
db: Session,
new_sync: Union[NewEmbySync, NewJellyfinSync, NewPlexSync,NewSonarrSync],
new_sync: Union[NewEmbySync, NewJellyfinSync,NewPlexSync,NewSonarrSync],
*,
log: Logger = log,
) -> Sync:
Expand Down Expand Up @@ -118,6 +151,7 @@ def run_sync(

# Process all Series returned by Sync
added: list[Series] = []
existing_series: set[Series] = set()
for series_info, lib_or_dir in all_series:
# Look for existing Series
existing = db.query(Series)\
Expand Down Expand Up @@ -153,6 +187,10 @@ def run_sync(

# If already exists in Database, update IDs and libraries then skip
if existing:
existing_series.add(existing)
# Assign Sync ID if one does not already exist
existing.sync_id = existing.sync_id or sync.id

# Add any new libraries
for new in libraries:
exists = any(
Expand All @@ -167,17 +205,25 @@ def run_sync(
# Update IDs
existing.update_from_series_info(series_info)
db.commit()
continue

# Create NewSeries for this entry
added.append(NewSeries(
name=series_info.name, year=series_info.year, libraries=libraries,
**series_info.ids, sync_id=sync.id, template_ids=sync.template_ids,
))
else:
added.append(NewSeries(
name=series_info.name, year=series_info.year,
libraries=libraries, **series_info.ids,
sync_id=sync.id, template_ids=sync.template_ids,
))

# Nothing added, log
if not added:
log.debug(f'{sync} No new Series synced')
log.info(f'{sync} No new Series synced')

# Clear the Sync ID of all Series which were not in the latest sync
if preferences.delete_unsynced_series:
for series in sync.series:
if series not in existing_series:
series.sync_id = None
log.debug(f'Series[{series.id}].sync_id = None')
db.commit()

# Process each newly added Series
preferences.currently_running_sync = None
Expand Down
4 changes: 3 additions & 1 deletion app/models/preferences.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class Preferences:
'imported_blueprints', 'colorblind_mode', 'library_unique_cards',
'invalid_connections', 'home_page_table_view', 'reduced_animations',
'currently_running_sync', 'interactive_card_previews',
'home_page_order', 'delete_unsynced_series',
)


Expand Down Expand Up @@ -186,6 +187,7 @@ def __initialize_defaults(self) -> None:
self.completely_delete_series = False
self.sync_specials = True
self.delete_missing_episodes = True
self.delete_unsynced_series = False
self.simplified_data_table = True
self.interactive_card_previews = True
self.remote_card_types = {}
Expand All @@ -197,7 +199,7 @@ def __initialize_defaults(self) -> None:
self.default_templates: list[int] = []
self.global_extras: dict[str, dict[str, str]] = {}

self.currently_running_sync = None
self.currently_running_sync: Optional[int] = None
self.invalid_connections: list[int] = []
self.use_emby = False
self.use_jellyfin = False
Expand Down
4 changes: 3 additions & 1 deletion app/schemas/preferences.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from pathlib import Path
from typing import Literal, Optional

from pydantic import DirectoryPath, PositiveInt, conint, constr, validator # pylint: disable=no-name-in-module
from pydantic import DirectoryPath, PositiveInt, conint, constr, validator

from app.schemas.base import (
Base, InterfaceType, ImageSource, UpdateBase, UNSPECIFIED
Expand Down Expand Up @@ -79,6 +79,7 @@ class UpdatePreferences(UpdateBase):
season_folder_format: str = UNSPECIFIED
sync_specials: bool = UNSPECIFIED
delete_missing_episodes: bool = UNSPECIFIED
delete_unsynced_series: bool = UNSPECIFIED
simplified_data_table: bool = UNSPECIFIED
default_card_type: CardTypeIdentifier = UNSPECIFIED
excluded_card_types: list[CardTypeIdentifier] = UNSPECIFIED
Expand Down Expand Up @@ -157,6 +158,7 @@ class Preferences(Base):
season_folder_format: str
sync_specials: bool
delete_missing_episodes: bool
delete_unsynced_series: bool
simplified_data_table: bool
is_docker: bool
default_card_type: CardTypeIdentifier
Expand Down
3 changes: 2 additions & 1 deletion app/templates/js/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,12 @@ function updateGlobalSettings() {
card_directory: $('input[name="card_directory"]').val(),
source_directory: $('input[name="source_directory"]').val(),
completely_delete_series: $('input[name="completely_delete_series"]').is(':checked'),
// Episode Data
// Series and Episode Data
episode_data_source: $('input[name="episode_data_source"]').val(),
image_source_priority: $('input[name="image_source_priority"]').val().split(','),
sync_specials: $('input[name="sync_specials"]').is(':checked'),
delete_missing_episodes: $('input[name="delete_missing_episodes"]').is(':checked'),
delete_unsynced_series: $('input[name="delete_unsynced_series"]').is(':checked'),
// Title Cards
default_card_type: $('input[name="default_card_type"]').val(),
excluded_card_types: parseListString($('input[name="excluded_card_types"]').val()),
Expand Down
25 changes: 21 additions & 4 deletions app/templates/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ <h3 class="ui horizontal divider header">

<!-- Episode Data Settings -->
<h3 class="ui horizontal divider header">
<i class="layer group icon"></i>
Episode Data
<i class="database icon"></i>
Series and Episode Data
</h3>
<section>
<div class="two fields">
Expand Down Expand Up @@ -143,6 +143,22 @@ <h3 class="ui horizontal divider header">
<p class="help">Whether to delete Episodes no longer present in the assigned Episode Data Source.</p>
</div>
</div>

<div class="two fields">
<div class="ui checkbox field">
<label>Delete Un-Synced Series</label>
{% if preferences.delete_unsynced_series %}
<input name="delete_unsynced_series" type="checkbox" checked="checked">
{% else %}
<input name="delete_unsynced_series" type="checkbox">
{% endif %}
<p class="help">
Whether to delete Series which no longer appear in any Syncs.
<br>
<b>This will delete all Title Cards and manually added Series (if they are not in any Sync).</b>
</p>
</div>
</div>
</section>

<!-- Title Card Settings -->
Expand Down Expand Up @@ -189,7 +205,7 @@ <h3 class="ui horizontal divider header">
<label>Watched Episode Style</label>
<div class="ui long compact selection dropdown">
<span class="default text">{{preferences.default_watched_style.title()}}</span>
<input name="default_watched_style" type="hidden">
<input name="default_watched_style" type="hidden" value="{{preferences.default_watched_style}}">
<i class="dropdown icon"></i>
<div class="menu">
<div class="header">Art Variations</div>
Expand All @@ -198,6 +214,7 @@ <h3 class="ui horizontal divider header">
<div class="item" data-value="art blur">Blurred Art</div>
<div class="item" data-value="art grayscale">Grayscale Art</div>
<div class="item" data-value="art blur grayscale">Blurred Grayscale Art</div>

<div class="header">Unique Variations</div>
<div class="divider"></div>
<div class="item" data-value="unique">Unique</div>
Expand All @@ -220,7 +237,7 @@ <h3 class="ui horizontal divider header">
<label>Unwatched Episode Style</label>
<div class="ui long compact selection dropdown">
<span class="default text">{{preferences.default_unwatched_style.title()}}</span>
<input name="default_unwatched_style" type="hidden">
<input name="default_unwatched_style" type="hidden" value="{{preferences.default_unwatched_style}}">
<i class="dropdown icon"></i>
<div class="menu">
<div class="header">Art</div>
Expand Down
2 changes: 1 addition & 1 deletion modules/ref/version_webui
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v2.0-alpha.10.1-webui68
v2.0-alpha.10.1-webui69

0 comments on commit c4ae227

Please sign in to comment.