From 080d02cf465456d230528b0b9b2aef94f071595e Mon Sep 17 00:00:00 2001 From: Ayush Sehrawat <69469790+AyushSehrawat@users.noreply.github.com> Date: Sat, 3 Feb 2024 11:59:32 +0530 Subject: [PATCH] feat: frontend and backend improvements (#197) * rework in progress. * fix: correct limits for orionoid * fix: switch to comprehensions * fix: disable plex logging for id mismatches * time for sleep. rework still wip. * feat: parser works. needs more work. language needs a rewrite. disabled for now. * fix: overseerr bug on using external ids * fix: remove plex debug line for users * disable tvdb checks from listrr, overseerr, plex. needs reworked. * set torrentio to disabled by default. removed parse logs. raised torrentio limit slightly. * Set all default settings to disabled by default for onboarding * add extra logging attr. for debugging large groups of data. * feat: started status page rewrite * add dev branch builds with :dev tag (#165) * fix: listrr validation * rework in progress. * fix: correct limits for orionoid * fix: switch to comprehensions * fix: disable plex logging for id mismatches * time for sleep. rework still wip. * feat: parser works. needs more work. language needs a rewrite. disabled for now. * feat: frontend improvements (#158) * feat: added global debug for settings * feat: added dev to formDebug so it's always true in development but false in production * feat: added DEBUG & LOG to general settings * deps: switched svelte-sonner to shadcn customized toast component * refactor: renamed PlexDebridItem to IcebergItem and added changes made to /items * fix: fixed the wrong relative date in status page * docs: readme improvements * docs: readme improvements * docs: readme improvements * chore(deps): bump lucide-svelte from 0.303.0 to 0.307.0 in /frontend (#124) * refactor: componentized forms, soon will do same for fields too * Parse rewrite (#128) * Move parser to its own module * Add ORIGIN to env vars * Fix overseerr, watchlist, jackett validation. * Added more refined logic to parser module. * Set stage for testing * Add methods for individual checks * Update sort logic * Update default settings * Fix jackett. Begin to add title support for jackett. --------- Co-authored-by: Spoked Co-authored-by: Dreu LaVelle * feat: onboarding on the way ;), major refactoring of form related code * Simplified downloading logic and modified state matchine * fix typo in state machine and handle movie pathing correctly * Remove useless method * Temporary fix to test * Remove uncached stream hashes from item to avoid loop, some blacklisting logic could also be good * chore(deps-dev): bump @typescript-eslint/eslint-plugin in /frontend (#134) * chore(deps): bump lucide-svelte from 0.307.0 to 0.309.0 in /frontend (#133) * chore(deps-dev): bump @sveltejs/kit from 2.0.1 to 2.3.2 in /frontend (#132) * chore(deps): bump bits-ui from 0.13.0 to 0.14.0 in /frontend (#130) * chore(deps-dev): bump @sveltejs/adapter-node in /frontend (#138) * feat: some more onboarding and form improvements * Dev startup to disabling pickling * feat: Listrr Support Added (#136) * Start Listrr Feature * feat: Listrr ready for review. * small tweaks. rewrite coming later. --------- Co-authored-by: Spoked * Jackett rewrite (#139) * Add TorBox scraper * Add is_anime attribute to item * Rework Jackett to Keyword Queries. Added categories. Removed Torbox * Remove audio from parsing, it removed alot of good hits * fix movie scraping and modify response parsing logic to be more readable * fix: remove torbox module * remove audio from being parsed * remove more audio from parser * fix typo * fix: tidy audio and networks * small tweaks --------- Co-authored-by: Spoked Co-authored-by: Gaisberg * Avoid [None] if empty content service * fix: handle bad quality manually in parser (#145) Co-authored-by: Spoked * deps: updated deps due to security updateS * feat: added more onboarding steps, some bugs also introduced * chore(deps-dev): bump @sveltejs/kit from 2.3.2 to 2.4.2 in /frontend (#156) * chore(deps-dev): bump prettier from 3.1.1 to 3.2.4 in /frontend (#155) * chore(deps): bump lucide-svelte from 0.309.0 to 0.314.0 in /frontend (#154) * chore(deps): bump bits-ui from 0.14.0 to 0.15.1 in /frontend (#153) * feat: minor changes * feat: deps change * feat: deps change * feat: onboarding MVP done --------- Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Spoked <5782630+dreulavelle@users.noreply.github.com> Co-authored-by: Spoked Co-authored-by: Dreu LaVelle Co-authored-by: Gaisberg * feat: frontend improvements (#159) * feat: added global debug for settings * feat: added dev to formDebug so it's always true in development but false in production * feat: added DEBUG & LOG to general settings * deps: switched svelte-sonner to shadcn customized toast component * refactor: renamed PlexDebridItem to IcebergItem and added changes made to /items * fix: fixed the wrong relative date in status page * docs: readme improvements * docs: readme improvements * docs: readme improvements * chore(deps): bump lucide-svelte from 0.303.0 to 0.307.0 in /frontend (#124) * refactor: componentized forms, soon will do same for fields too * Parse rewrite (#128) * Move parser to its own module * Add ORIGIN to env vars * Fix overseerr, watchlist, jackett validation. * Added more refined logic to parser module. * Set stage for testing * Add methods for individual checks * Update sort logic * Update default settings * Fix jackett. Begin to add title support for jackett. --------- Co-authored-by: Spoked Co-authored-by: Dreu LaVelle * feat: onboarding on the way ;), major refactoring of form related code * Simplified downloading logic and modified state matchine * fix typo in state machine and handle movie pathing correctly * Remove useless method * Temporary fix to test * Remove uncached stream hashes from item to avoid loop, some blacklisting logic could also be good * chore(deps-dev): bump @typescript-eslint/eslint-plugin in /frontend (#134) * chore(deps): bump lucide-svelte from 0.307.0 to 0.309.0 in /frontend (#133) * chore(deps-dev): bump @sveltejs/kit from 2.0.1 to 2.3.2 in /frontend (#132) * chore(deps): bump bits-ui from 0.13.0 to 0.14.0 in /frontend (#130) * chore(deps-dev): bump @sveltejs/adapter-node in /frontend (#138) * feat: some more onboarding and form improvements * Dev startup to disabling pickling * feat: Listrr Support Added (#136) * Start Listrr Feature * feat: Listrr ready for review. * small tweaks. rewrite coming later. --------- Co-authored-by: Spoked * Jackett rewrite (#139) * Add TorBox scraper * Add is_anime attribute to item * Rework Jackett to Keyword Queries. Added categories. Removed Torbox * Remove audio from parsing, it removed alot of good hits * fix movie scraping and modify response parsing logic to be more readable * fix: remove torbox module * remove audio from being parsed * remove more audio from parser * fix typo * fix: tidy audio and networks * small tweaks --------- Co-authored-by: Spoked Co-authored-by: Gaisberg * Avoid [None] if empty content service * fix: handle bad quality manually in parser (#145) Co-authored-by: Spoked * deps: updated deps due to security updateS * feat: added more onboarding steps, some bugs also introduced * chore(deps-dev): bump @sveltejs/kit from 2.3.2 to 2.4.2 in /frontend (#156) * chore(deps-dev): bump prettier from 3.1.1 to 3.2.4 in /frontend (#155) * chore(deps): bump lucide-svelte from 0.309.0 to 0.314.0 in /frontend (#154) * chore(deps): bump bits-ui from 0.14.0 to 0.15.1 in /frontend (#153) * feat: minor changes * feat: deps change * feat: deps change * feat: onboarding MVP done * refactor: moved schemes into forms/helpers.ts and command menu improvements * refactor: switched to new font, changes made to all except status page * refactor: minor change, didn't get commit * fix: minor fix * feat: fixed git merge conflicts issue * feat: fixed status page font too --------- Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Spoked <5782630+dreulavelle@users.noreply.github.com> Co-authored-by: Spoked Co-authored-by: Dreu LaVelle Co-authored-by: Gaisberg * docs: minor improvements (#160) * docs: readme improvements * docs: readme improvements * docs: minor improvements (#161) * docs: readme improvements * docs: readme improvements * docs: readme improvements * docs: minor improvements (#162) * fix: correct parsing of external id's (#163) Co-authored-by: Dreu LaVelle * fix: overseerr bug on using external ids * fix: remove plex debug line for users * disable tvdb checks from listrr, overseerr, plex. needs reworked. * set torrentio to disabled by default. removed parse logs. raised torrentio limit slightly. * Set all default settings to disabled by default for onboarding * add extra logging attr. for debugging large groups of data. * fix: listrr validation * feat: rewrite of status page almost done * add verbose logging in plex to debug looping * revert debug logging for plex. figured out looping issue. * add back plex log. add boilerplate for trakt content service. wip * add back plex log. add boilerplate for trakt content service. wip * feat: status page improvements (#169) * Feat/better status page (#170) * feat: status page improvements * feat: status page improvements * feat: status page improvements * feat: new settings (#176) * added more validation and logging * changed name of test_items module * refactor: edited minor things in settings (#177) * remove tzdata * refactor: edited minor things in settings (#177) * remove tzdata * fix typo on parser * increase ratelimits on second_limiters * Fix/parser/add attribute (#179) * increase ratelimits on second_limiters * Iceberg works. All scrapers working together. Symlinking works. --------- Co-authored-by: Spoked * add extra attrs to extended api endpoint * feat: status page improvements (#182) * [DEV] feat: frontend improvements (#193) * feat: switched to new theme! refactor: componentized most of form except tag inputs * feat: completely refactored form and added many improvements in ui * fix: fixed the mobile select issue * feat: some more frontend improvements and bug fixes * fix: shows not being downloaded * fix: add check if data attr exists for orionoid * fix: validate on empty symlink path correctly * fix: added more validation to symlink paths * feat: added some checks to save settings (#196) * feat: improve symlink validation. fixed torrentio rate limits once and for all. * fix: remove extra debug line from orionoid. correct settings path in symlink. * fix: tweaked symlink validation. * fix: raise jackett ratelimit * fix: raise orionoid ratelimit * feat: add back second limiter with 1/5s per on Torrentio. This will help prolong requests more instead of bursting. --------- Co-authored-by: Spoked Co-authored-by: KingPin Co-authored-by: Dreu LaVelle Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Spoked <5782630+dreulavelle@users.noreply.github.com> Co-authored-by: Dreu LaVelle --- backend/program/__init__.py | 16 +-- backend/program/realdebrid.py | 6 +- backend/program/scrapers/jackett.py | 15 ++- backend/program/scrapers/orionoid.py | 27 ++--- backend/program/scrapers/torrentio.py | 15 +-- backend/program/symlink.py | 108 ++++++++++-------- frontend/src/lib/forms/helpers.ts | 4 + .../src/routes/settings/about/+page.svelte | 47 +++++++- .../routes/settings/content/+page.server.ts | 18 ++- .../routes/settings/general/+page.server.ts | 18 ++- .../settings/mediaserver/+page.server.ts | 18 ++- .../routes/settings/scrapers/+page.server.ts | 18 ++- 12 files changed, 213 insertions(+), 97 deletions(-) diff --git a/backend/program/__init__.py b/backend/program/__init__.py index 4a4ae483..0c906bc9 100644 --- a/backend/program/__init__.py +++ b/backend/program/__init__.py @@ -58,13 +58,9 @@ def validate(self): return all(service.initialized for service in self.core_manager.services) def stop(self): - try: - for service in self.core_manager.services: - if getattr(service, "running", False): - service.stop() - self.pickly.stop() - settings.save() - self.running = False - except Exception as e: - logger.error("Iceberg stopping with exception: %s", e) - pass \ No newline at end of file + for service in self.core_manager.services: + if getattr(service, "running", False): + service.stop() + self.pickly.stop() + settings.save() + self.running = False \ No newline at end of file diff --git a/backend/program/realdebrid.py b/backend/program/realdebrid.py index 60617dde..61fd13ff 100644 --- a/backend/program/realdebrid.py +++ b/backend/program/realdebrid.py @@ -130,9 +130,9 @@ def chunks(lst, n): "active_stream", {"hash": stream_hash, "files": wanted_files, "id": None}, ) - all_filenames = [file_info["filename"] for file_info in wanted_files.values()] - for file in all_filenames: - logger.debug(f"Found cached file {file} for {item.log_string}") + # all_filenames = [file_info["filename"] for file_info in wanted_files.values()] + # for file in all_filenames: + # logger.debug(f"Found cached file {file} for {item.log_string}") return True item.streams[stream_hash] = None return False diff --git a/backend/program/scrapers/jackett.py b/backend/program/scrapers/jackett.py index 1962d651..2ce2f6ad 100644 --- a/backend/program/scrapers/jackett.py +++ b/backend/program/scrapers/jackett.py @@ -25,7 +25,7 @@ def __init__(self, _): self.initialized = self.validate_settings() if not self.initialized and not self.api_key: return - self.minute_limiter = RateLimiter(max_calls=60, period=60, raise_on_limit=True) + self.minute_limiter = RateLimiter(max_calls=1000, period=3600, raise_on_limit=True) self.second_limiter = RateLimiter(max_calls=1, period=5) self.parse_logging = False logger.info("Jackett initialized!") @@ -45,7 +45,7 @@ def validate_settings(self) -> bool: except ReadTimeout: return True except Exception as e: - logger.exception("Jackett failed to initialize with API Key: %s", e) + logger.error("Jackett failed to initialize with API Key: %s", e) return False if self.settings.url: try: @@ -57,9 +57,10 @@ def validate_settings(self) -> bool: if not response.is_ok: return False except ReadTimeout: + logger.warn("Jackett connection timeout.") return True except Exception as e: - logger.exception("Jackett failed to initialize: %s", e) + logger.error("Jackett failed to initialize: %s", e) return False logger.info("Jackett is not configured and will not be used.") return False @@ -75,13 +76,11 @@ def run(self, item): logger.warn("Jackett rate limit hit for item: %s", item.log_string) return except RequestException as e: - self.minute_limiter.limit_hit() - logger.exception("Jackett request exception: %s", e, exc_info=True) + logger.debug("Jackett request exception: %s", e, exc_info=True) return except Exception as e: - self.minute_limiter.limit_hit() - # logger.debug("Jackett exception for item: %s - Exception: %s", item.log_string, e.args[0], exc_info=True) - # logger.debug("Exception details: %s", traceback.format_exc()) + logger.debug("Jackett exception for item: %s - Exception: %s", item.log_string, e.args[0], exc_info=True) + logger.debug("Exception details: %s", traceback.format_exc()) return def _scrape_item(self, item): diff --git a/backend/program/scrapers/orionoid.py b/backend/program/scrapers/orionoid.py index af858e1b..41665246 100644 --- a/backend/program/scrapers/orionoid.py +++ b/backend/program/scrapers/orionoid.py @@ -32,8 +32,8 @@ def __init__(self, _): self.orionoid_limit = 0 self.orionoid_remaining = 0 self.parse_logging = False - self.max_calls = 100 if not self.is_premium else 60 - self.period = 86400 if not self.is_premium else 60 + self.max_calls = 100 if not self.is_premium else 1000 + self.period = 86400 if not self.is_premium else 3600 self.minute_limiter = RateLimiter(max_calls=self.max_calls, period=self.period, raise_on_limit=True) self.second_limiter = RateLimiter(max_calls=1, period=5) logger.info("Orionoid initialized!") @@ -43,19 +43,20 @@ def validate_settings(self) -> bool: if not self.settings.enabled: logger.debug("Orionoid is set to disabled.") return False - if self.settings.api_key: - return True + if len(self.settings.api_key) != 32 or self.settings.api_key == "": + logger.error("Orionoid API Key is not valid or not set. Please check your settings.") + return False try: url = f"https://api.orionoid.com?keyapp={KEY_APP}&keyuser={self.settings.api_key}&mode=user&action=retrieve" response = get(url, retry_if_failed=False) - if response.is_ok: - return True - if not response.data.result.status == "success": - logger.error(f"Orionoid API Key is invalid. Status: {response.data.result.status}") - return False - if not response.is_ok: - logger.error(f"Orionoid Status Code: {response.status_code}, Reason: {response.reason}") - return False + if response.is_ok and hasattr(response.data, "result"): + if not response.data.result.status == "success": + logger.error(f"Orionoid API Key is invalid. Status: {response.data.result.status}") + return False + if not response.is_ok: + logger.error(f"Orionoid Status Code: {response.status_code}, Reason: {response.reason}") + return False + return True except Exception as e: logger.exception("Orionoid failed to initialize: %s", e) return False @@ -67,7 +68,7 @@ def check_premium(self) -> bool: """ url = f"https://api.orionoid.com?keyapp={KEY_APP}&keyuser={self.settings.api_key}&mode=user&action=retrieve" response = get(url, retry_if_failed=False) - if response.is_ok: + if response.is_ok and hasattr(response.data, "data"): active = True if response.data.data.status == "active" else False premium = response.data.data.subscription.package.premium debrid = response.data.data.service.realdebrid diff --git a/backend/program/scrapers/torrentio.py b/backend/program/scrapers/torrentio.py index 34d25b25..a47308f2 100644 --- a/backend/program/scrapers/torrentio.py +++ b/backend/program/scrapers/torrentio.py @@ -1,5 +1,4 @@ """ Torrentio scraper module """ -import os from typing import Optional from pydantic import BaseModel from requests import ConnectTimeout, ReadTimeout @@ -22,7 +21,7 @@ class Torrentio: def __init__(self, _): self.key = "torrentio" self.settings = TorrentioConfig(**settings_manager.get(f"scraping.{self.key}")) - self.minute_limiter = RateLimiter(max_calls=60, period=60, raise_on_limit=True) + self.minute_limiter = RateLimiter(max_calls=300, period=3600, raise_on_limit=True) self.second_limiter = RateLimiter(max_calls=1, period=5) self.initialized = self.validate_settings() if not self.initialized: @@ -57,26 +56,18 @@ def run(self, item): self._scrape_item(item) except RateLimitExceeded: self.minute_limiter.limit_hit() - logger.warn("Torrentio rate limit hit for item: %s", item.log_string) return except ConnectTimeout: - self.minute_limiter.limit_hit() logger.warn("Torrentio connection timeout for item: %s", item.log_string) return except ReadTimeout: - self.minute_limiter.limit_hit() + logger.warn("Torrentio read timeout for item: %s", item.log_string) return except RequestException as e: - self.minute_limiter.limit_hit() logger.warn("Torrentio request exception: %s", e) return - except AttributeError: - # TODO: will fix later - self.minute_limiter.limit_hit() - return except Exception as e: - self.minute_limiter.limit_hit() - logger.warn("Torrentio failed to scrape item: %s", e) + logger.warn("Torrentio exception thrown: %s", e) return def _scrape_item(self, item): diff --git a/backend/program/symlink.py b/backend/program/symlink.py index 96fc14c2..53cd153f 100644 --- a/backend/program/symlink.py +++ b/backend/program/symlink.py @@ -1,6 +1,7 @@ """Symlinking module""" import os from pathlib import Path +from typing import NamedTuple from pydantic import BaseModel from utils.settings import settings_manager as settings from utils.logger import logger @@ -10,6 +11,9 @@ class SymlinkConfig(BaseModel): host_path: Path container_path: Path +class Setting(NamedTuple): + key: str + value: str class Symlinker(): """ @@ -26,60 +30,74 @@ class Symlinker(): def __init__(self, _): self.key = "symlink" self.settings = SymlinkConfig(**settings.get(self.key)) - self.initialized = False - - if (self.settings.host_path / "__all__").exists(): - logger.debug("Detected Zurg host path. Using __all__ folder for host path.") - settings.set(self.key, self.settings.host_path) - self.settings.host_path = Path(self.settings.host_path) / "__all__" - elif (self.settings.host_path / "torrents").exists(): - logger.debug("Detected standard rclone host path. Using torrents folder for host path.") - settings.set(self.key, self.settings.host_path) - self.settings.host_path = Path(self.settings.host_path) / "torrents" - - self.library_path = self.settings.host_path.parent / "library" - - if not self.validate(): - logger.error("Symlink configuration is invalid. Please check the host and container paths.") - return - - self.initialize_library_paths() - - if not self.create_initial_folders(): - logger.error("Failed to create initial library folders.") + self.initialized = self.validate() + if not self.initialized: + logger.error("Symlink initialization failed due to invalid configuration.") return - - logger.info("Found rclone mount path: %s", self.settings.host_path) - logger.info("Symlinks will be placed in library path: %s", self.library_path) - logger.info("Plex will see the symlinks in: %s", self.settings.container_path.parent / "library") + logger.info("Rclone path symlinks are pointed to: %s", self.settings.host_path) + logger.info("Symlinks will be placed in: %s", self.library_path) logger.info("Symlink initialized!") self.initialized = True def validate(self): - if not self.settings.host_path or not self.settings.container_path: + """Validate paths and create the initial folders.""" + host_path = Path(self.settings.host_path) if self.settings.host_path else None + container_path = Path(self.settings.container_path) if self.settings.container_path else None + if not host_path or not container_path or host_path == Path('.') or container_path == Path('.'): + logger.error("Host or container path not provided, is empty, or is set to the current directory.") return False - host_path = Path(self.settings.host_path) - if not host_path.exists() or not host_path.is_dir(): - logger.error(f"Invalid host path: {self.settings.host_path}") + if not host_path.is_absolute(): + logger.error(f"Host path is not an absolute path: {host_path}") return False - return True - - def initialize_library_paths(self): - self.library_path_movies = self.library_path / "movies" - self.library_path_shows = self.library_path / "shows" - self.library_path_anime_movies = self.library_path / "anime_movies" - self.library_path_anime_shows = self.library_path / "anime_shows" + if not container_path.is_absolute(): + logger.error(f"Container path is not an absolute path: {container_path}") + return False + try: + if not host_path.is_dir(): + logger.error(f"Host path is not a directory or does not exist: {host_path}") + return False + if not container_path.is_dir(): + logger.error(f"Container path is not a directory or does not exist: {container_path}") + return False + if Path(self.settings.host_path / "__all__").exists() and Path(self.settings.host_path / "__all__").is_dir(): + logger.debug("Detected Zurg host path. Using __all__ folder for host path.") + self.settings.host_path = self.settings.host_path / "__all__" + elif Path(self.settings.host_path / "torrents").exists() and Path(self.settings.host_path / "torrents").is_dir(): + logger.debug("Detected standard rclone host path. Using torrents folder for host path.") + self.settings.host_path = self.settings.host_path / "torrents" + if not self.create_initial_folders(): + logger.error("Failed to create initial library folders.") + return False + return True + except FileNotFoundError as e: + logger.error(f"Path not found: {e}") + except PermissionError as e: + logger.error(f"Permission denied when accessing path: {e}") + except OSError as e: + logger.error(f"OS error when validating paths: {e}") + return False def create_initial_folders(self): - for library in [self.library_path_movies, - self.library_path_shows, - self.library_path_anime_movies, - self.library_path_anime_shows]: - try: - library.mkdir(parents=True, exist_ok=True) - except Exception as e: - logger.error("Failed to create directory %s: %s", library, e) - return False + """Create the initial library folders.""" + try: + self.library_path = self.settings.container_path.parent / "library" + self.library_path_movies = self.library_path / "movies" + self.library_path_shows = self.library_path / "shows" + self.library_path_anime_movies = self.library_path / "anime_movies" + self.library_path_anime_shows = self.library_path / "anime_shows" + folders = [self.library_path_movies, + self.library_path_shows, + self.library_path_anime_movies, + self.library_path_anime_shows] + for folder in folders: + if not folder.exists(): + folder.mkdir(parents=True, exist_ok=True) + except PermissionError as e: + logger.error(f"Permission denied when creating directory: {e}") + return False + except OSError as e: + logger.error(f"OS error when creating directory: {e}") + return False return True def run(self, item): diff --git a/frontend/src/lib/forms/helpers.ts b/frontend/src/lib/forms/helpers.ts index c089b4c4..492a18c3 100644 --- a/frontend/src/lib/forms/helpers.ts +++ b/frontend/src/lib/forms/helpers.ts @@ -3,6 +3,7 @@ import { z } from 'zod'; // General Settings ----------------------------------------------------------------------------------- export const generalSettingsToGet: string[] = ['debug', 'log', 'symlink', 'real_debrid']; +export const generalSettingsServices: string[] = ['symlink', 'real_debrid']; export const generalSettingsSchema = z.object({ debug: z.boolean().default(true), @@ -51,6 +52,7 @@ export function generalSettingsToSet(form: SuperValidated // Content Settings ----------------------------------------------------------------------------------- export const contentSettingsToGet: string[] = ['content']; +export const contentSettingsServices: string[] = ['content']; export const contentSettingsSchema = z.object({ overseerr_enabled: z.boolean().default(false), @@ -126,6 +128,7 @@ export function contentSettingsToSet(form: SuperValidated // Media Server Settings ----------------------------------------------------------------------------------- export const mediaServerSettingsToGet: string[] = ['plex']; +export const mediaServerSettingsServices: string[] = ['plex']; export const mediaServerSettingsSchema = z.object({ plex_token: z.string().optional().default(''), @@ -154,6 +157,7 @@ export function mediaServerSettingsToSet(form: SuperValidated @@ -49,6 +67,31 @@

About

Know what you're running.

+
+

+ {formatWords('Version')} +

+
+

+ {version} +

+ +
+
{#each Object.keys(aboutData) as key}
diff --git a/frontend/src/routes/settings/content/+page.server.ts b/frontend/src/routes/settings/content/+page.server.ts index b3bf7981..8ab7dbb2 100644 --- a/frontend/src/routes/settings/content/+page.server.ts +++ b/frontend/src/routes/settings/content/+page.server.ts @@ -1,10 +1,11 @@ import type { PageServerLoad, Actions } from './$types'; import { fail, error, redirect } from '@sveltejs/kit'; import { message, superValidate } from 'sveltekit-superforms/server'; -import { saveSettings } from '$lib/helpers'; +import { saveSettings, formatWords } from '$lib/helpers'; import { contentSettingsSchema, contentSettingsToGet, + contentSettingsServices, contentSettingsToPass, contentSettingsToSet } from '$lib/forms/helpers'; @@ -48,6 +49,21 @@ export const actions: Actions = { }); } + const data = await event.fetch('http://127.0.0.1:8080/services'); + const services = await data.json(); + const allServicesTrue: boolean = contentSettingsServices.every( + (service) => services.data[service] === true + ); + if (!allServicesTrue) { + return message( + form, + `${contentSettingsServices.map(formatWords).join(', ')} service(s) failed to initialize. Please check your settings.`, + { + status: 400 + } + ); + } + if (event.url.searchParams.get('onboarding') === 'true') { redirect(302, '/onboarding/4'); } diff --git a/frontend/src/routes/settings/general/+page.server.ts b/frontend/src/routes/settings/general/+page.server.ts index e4d81722..6e420686 100644 --- a/frontend/src/routes/settings/general/+page.server.ts +++ b/frontend/src/routes/settings/general/+page.server.ts @@ -1,10 +1,11 @@ import type { PageServerLoad, Actions } from './$types'; import { fail, error, redirect } from '@sveltejs/kit'; import { message, superValidate } from 'sveltekit-superforms/server'; -import { saveSettings } from '$lib/helpers'; +import { saveSettings, formatWords } from '$lib/helpers'; import { generalSettingsSchema, generalSettingsToGet, + generalSettingsServices, generalSettingsToPass, generalSettingsToSet } from '$lib/forms/helpers'; @@ -50,6 +51,21 @@ export const actions: Actions = { }); } + const data = await event.fetch('http://127.0.0.1:8080/services'); + const services = await data.json(); + const allServicesTrue: boolean = generalSettingsServices.every( + (service) => services.data[service] === true + ); + if (!allServicesTrue) { + return message( + form, + `${generalSettingsServices.map(formatWords).join(', ')} service(s) failed to initialize. Please check your settings.`, + { + status: 400 + } + ); + } + if (event.url.searchParams.get('onboarding') === 'true') { redirect(302, '/onboarding/2'); } diff --git a/frontend/src/routes/settings/mediaserver/+page.server.ts b/frontend/src/routes/settings/mediaserver/+page.server.ts index 16fbb396..a563079e 100644 --- a/frontend/src/routes/settings/mediaserver/+page.server.ts +++ b/frontend/src/routes/settings/mediaserver/+page.server.ts @@ -1,10 +1,11 @@ import type { PageServerLoad, Actions } from './$types'; import { fail, error, redirect } from '@sveltejs/kit'; import { message, superValidate } from 'sveltekit-superforms/server'; -import { saveSettings } from '$lib/helpers'; +import { saveSettings, formatWords } from '$lib/helpers'; import { mediaServerSettingsSchema, mediaServerSettingsToGet, + mediaServerSettingsServices, mediaServerSettingsToPass, mediaServerSettingsToSet } from '$lib/forms/helpers'; @@ -48,6 +49,21 @@ export const actions: Actions = { }); } + const data = await event.fetch('http://127.0.0.1:8080/services'); + const services = await data.json(); + const allServicesTrue: boolean = mediaServerSettingsServices.every( + (service) => services.data[service] === true + ); + if (!allServicesTrue) { + return message( + form, + `${mediaServerSettingsServices.map(formatWords).join(', ')} service(s) failed to initialize. Please check your settings.`, + { + status: 400 + } + ); + } + if (event.url.searchParams.get('onboarding') === 'true') { redirect(302, '/onboarding/3'); } diff --git a/frontend/src/routes/settings/scrapers/+page.server.ts b/frontend/src/routes/settings/scrapers/+page.server.ts index 216d8577..d3d73054 100644 --- a/frontend/src/routes/settings/scrapers/+page.server.ts +++ b/frontend/src/routes/settings/scrapers/+page.server.ts @@ -1,10 +1,11 @@ import type { PageServerLoad, Actions } from './$types'; import { fail, error, redirect } from '@sveltejs/kit'; import { message, superValidate } from 'sveltekit-superforms/server'; -import { saveSettings } from '$lib/helpers'; +import { saveSettings, formatWords } from '$lib/helpers'; import { scrapersSettingsSchema, scrapersSettingsToGet, + scrapersSettingsServices, scrapersSettingsToPass, scrapersSettingsToSet } from '$lib/forms/helpers'; @@ -48,6 +49,21 @@ export const actions: Actions = { }); } + const data = await event.fetch('http://127.0.0.1:8080/services'); + const services = await data.json(); + const allServicesTrue: boolean = scrapersSettingsServices.every( + (service) => services.data[service] === true + ); + if (!allServicesTrue) { + return message( + form, + `${scrapersSettingsServices.map(formatWords).join(', ')} service(s) failed to initialize. Please check your settings.`, + { + status: 400 + } + ); + } + if (event.url.searchParams.get('onboarding') === 'true') { redirect(302, '/?onboarding=true'); }