From c107453c8ca106493d129f967a05a39b4b4a8f71 Mon Sep 17 00:00:00 2001 From: Ayush Sehrawat Date: Sat, 6 Jan 2024 12:12:52 +0530 Subject: [PATCH 01/39] feat: added global debug for settings --- frontend/src/routes/settings/+layout.svelte | 3 +++ frontend/src/routes/settings/content/+page.svelte | 11 ++++++++++- frontend/src/routes/settings/general/+page.svelte | 11 ++++++++++- frontend/src/routes/settings/mediaserver/+page.svelte | 5 ++++- frontend/src/routes/settings/scrapers/+page.svelte | 5 ++++- 5 files changed, 31 insertions(+), 4 deletions(-) diff --git a/frontend/src/routes/settings/+layout.svelte b/frontend/src/routes/settings/+layout.svelte index 920acb67..ee19171a 100644 --- a/frontend/src/routes/settings/+layout.svelte +++ b/frontend/src/routes/settings/+layout.svelte @@ -5,6 +5,9 @@ import { goto } from '$app/navigation'; import { page } from '$app/stores'; import clsx from 'clsx'; + import { setContext } from 'svelte'; + + setContext('formDebug', true); const settingsItems: NavItem[] = [ { diff --git a/frontend/src/routes/settings/content/+page.svelte b/frontend/src/routes/settings/content/+page.svelte index b31e3193..30d8cb33 100644 --- a/frontend/src/routes/settings/content/+page.svelte +++ b/frontend/src/routes/settings/content/+page.svelte @@ -12,6 +12,9 @@ import { Label } from '$lib/components/ui/label'; import * as Form from '$lib/components/ui/form'; import { contentSettingsSchema, type ContentSettingsSchema } from '$lib/schemas/setting'; + import { getContext } from 'svelte'; + + let formDebug: boolean = getContext('formDebug'); export let data: PageData; const contentForm = superForm(data.form); @@ -62,7 +65,13 @@

Content Settings

Configure content providers for Iceberg.

- +

diff --git a/frontend/src/routes/settings/general/+page.svelte b/frontend/src/routes/settings/general/+page.svelte index 2a6c6088..fde58224 100644 --- a/frontend/src/routes/settings/general/+page.svelte +++ b/frontend/src/routes/settings/general/+page.svelte @@ -10,6 +10,9 @@ import clsx from 'clsx'; import * as Form from '$lib/components/ui/form'; import { generalSettingsSchema, type GeneralSettingsSchema } from '$lib/schemas/setting'; + import { getContext } from 'svelte'; + + let formDebug: boolean = getContext('formDebug'); export let data: PageData; const generalForm = superForm(data.form); @@ -28,7 +31,13 @@ Configure global and default settings for Iceberg.

- +
diff --git a/frontend/src/routes/settings/mediaserver/+page.svelte b/frontend/src/routes/settings/mediaserver/+page.svelte index 6d0bc8ee..44c88a84 100644 --- a/frontend/src/routes/settings/mediaserver/+page.svelte +++ b/frontend/src/routes/settings/mediaserver/+page.svelte @@ -9,6 +9,9 @@ import clsx from 'clsx'; import * as Form from '$lib/components/ui/form'; import { mediaServerSettingsSchema, type MediaServerSettingsSchema } from '$lib/schemas/setting'; + import { getContext } from 'svelte'; + + let formDebug: boolean = getContext('formDebug'); export let data: PageData; const mediaServerForm = superForm(data.form); @@ -32,7 +35,7 @@ controlled form={mediaServerForm} let:config - debug={false} + debug={formDebug} >
diff --git a/frontend/src/routes/settings/scrapers/+page.svelte b/frontend/src/routes/settings/scrapers/+page.svelte index f3577dd7..572b7a14 100644 --- a/frontend/src/routes/settings/scrapers/+page.svelte +++ b/frontend/src/routes/settings/scrapers/+page.svelte @@ -10,6 +10,9 @@ import clsx from 'clsx'; import * as Form from '$lib/components/ui/form'; import { scrapersSettingsSchema, type ScrapersSettingsSchema } from '$lib/schemas/setting'; + import { getContext } from 'svelte'; + + let formDebug: boolean = getContext('formDebug'); export let data: PageData; const scrapersForm = superForm(data.form); @@ -28,7 +31,7 @@ Configure scraper settings for Iceberg.

- +
From c65c500fe993567f2457b2b3c47f89430ffca1fb Mon Sep 17 00:00:00 2001 From: Ayush Sehrawat Date: Sat, 6 Jan 2024 12:48:09 +0530 Subject: [PATCH 02/39] feat: added dev to formDebug so it's always true in development but false in production --- frontend/src/routes/settings/+layout.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/routes/settings/+layout.svelte b/frontend/src/routes/settings/+layout.svelte index ee19171a..2daecaa4 100644 --- a/frontend/src/routes/settings/+layout.svelte +++ b/frontend/src/routes/settings/+layout.svelte @@ -6,8 +6,9 @@ import { page } from '$app/stores'; import clsx from 'clsx'; import { setContext } from 'svelte'; + import { dev } from '$app/environment'; - setContext('formDebug', true); + setContext('formDebug', dev); const settingsItems: NavItem[] = [ { From b5280a7c2ba155933253d1650efcc7dcb283881f Mon Sep 17 00:00:00 2001 From: Ayush Sehrawat Date: Sat, 6 Jan 2024 13:00:59 +0530 Subject: [PATCH 03/39] feat: added DEBUG & LOG to general settings --- frontend/src/lib/schemas/setting.ts | 2 ++ .../routes/settings/general/+page.server.ts | 12 ++++++++- .../src/routes/settings/general/+page.svelte | 27 +++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/frontend/src/lib/schemas/setting.ts b/frontend/src/lib/schemas/setting.ts index ede90f53..ceab780e 100644 --- a/frontend/src/lib/schemas/setting.ts +++ b/frontend/src/lib/schemas/setting.ts @@ -1,6 +1,8 @@ import { z } from 'zod'; export const generalSettingsSchema = z.object({ + debug: z.boolean().default(true), + log: z.boolean().default(true), host_path: z.string().min(1), container_path: z.string().min(1), realdebrid_api_key: z.string().min(1), diff --git a/frontend/src/routes/settings/general/+page.server.ts b/frontend/src/routes/settings/general/+page.server.ts index 62d9912b..cccee017 100644 --- a/frontend/src/routes/settings/general/+page.server.ts +++ b/frontend/src/routes/settings/general/+page.server.ts @@ -7,7 +7,7 @@ import { saveSettings } from '$lib/helpers'; export const load: PageServerLoad = async ({ fetch }) => { async function getPartialSettings() { try { - const toGet = ['symlink', 'real_debrid']; + const toGet = ['debug', 'log', 'symlink', 'real_debrid']; const results = await fetch(`http://127.0.0.1:8080/settings/get/${toGet.join(',')}`); return await results.json(); } catch (e) { @@ -18,6 +18,8 @@ export const load: PageServerLoad = async ({ fetch }) => { let toPassToSchema: any = await getPartialSettings(); toPassToSchema = { + debug: toPassToSchema.data.debug, + log: toPassToSchema.data.log, host_path: toPassToSchema.data.symlink.host_path, container_path: toPassToSchema.data.symlink.container_path, realdebrid_api_key: toPassToSchema.data.real_debrid.api_key @@ -37,6 +39,14 @@ export const actions: Actions = { }); } const toSet = [ + { + key: 'debug', + value: form.data.debug + }, + { + key: 'log', + value: form.data.log + }, { key: 'symlink', value: { diff --git a/frontend/src/routes/settings/general/+page.svelte b/frontend/src/routes/settings/general/+page.svelte index fde58224..b91f4d2e 100644 --- a/frontend/src/routes/settings/general/+page.svelte +++ b/frontend/src/routes/settings/general/+page.svelte @@ -30,6 +30,9 @@

Configure global and default settings for Iceberg.

+

+ * These settings require a restart to take effect. +

+ + + + Debug * + + + + + + + + + + Log * + + + + + + Date: Sat, 6 Jan 2024 13:08:51 +0530 Subject: [PATCH 04/39] deps: switched svelte-sonner to shadcn customized toast component --- .../src/lib/components/ui/sonner/index.ts | 1 + .../lib/components/ui/sonner/sonner.svelte | 25 +++++++++++++++++++ frontend/src/routes/+layout.svelte | 2 +- 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 frontend/src/lib/components/ui/sonner/index.ts create mode 100644 frontend/src/lib/components/ui/sonner/sonner.svelte diff --git a/frontend/src/lib/components/ui/sonner/index.ts b/frontend/src/lib/components/ui/sonner/index.ts new file mode 100644 index 00000000..1ad9f4a2 --- /dev/null +++ b/frontend/src/lib/components/ui/sonner/index.ts @@ -0,0 +1 @@ +export { default as Toaster } from "./sonner.svelte"; diff --git a/frontend/src/lib/components/ui/sonner/sonner.svelte b/frontend/src/lib/components/ui/sonner/sonner.svelte new file mode 100644 index 00000000..0ddcf79c --- /dev/null +++ b/frontend/src/lib/components/ui/sonner/sonner.svelte @@ -0,0 +1,25 @@ + + + diff --git a/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte index f9b8afa8..df0c77d8 100644 --- a/frontend/src/routes/+layout.svelte +++ b/frontend/src/routes/+layout.svelte @@ -1,6 +1,6 @@ + + +
+
+

+ Content Providers +

+
+ +
+ + Overseerr +
+
+ + +
+ + Mdblist +
+
+ + +
+ + Plex Watchlists +
+
+
+
+ + {#if $form.overseerr_enabled} +
+ + + + Overseerr URL + + + + + +
+ +
+ + + + Overseerr API Key + + 0 + })} + spellcheck="false" + /> + + + +
+ {/if} + + {#if $form.plex_watchlist_enabled} +
+ + + + Plex RSS URL + + + + + +
+ +
+ + + + Plex RSS Update Interval + + + + + +
+ {/if} + + + + + {#if $form.mdblist_enabled} +
+ + + + Mdblist API Key + + 0 + })} + spellcheck="false" + /> + + + +
+ +
+ + + + Mdblist Update Interval + + + + + +
+ + {#if $mdblistListsErrors} + {$mdblistListsErrors} + {/if} + +
+ +
+ +
+ {#each $mdblistListsValues.filter((list) => list !== '') as list (list)} + + {/each} +
+
+
+ {/if} + + +
+ +
+
+
diff --git a/frontend/src/lib/forms/general-form.svelte b/frontend/src/lib/forms/general-form.svelte new file mode 100644 index 00000000..a1d4a96f --- /dev/null +++ b/frontend/src/lib/forms/general-form.svelte @@ -0,0 +1,100 @@ + + + +
+ + + + Debug * + + + + + + + + + + Log * + + + + + + + + + + Host Path + + + + + + + + + + Container Path + + + + + + + + + + Real Debrid API Key + + 0 + })} + spellcheck="false" + /> + + + + + +
+ +
+
+
diff --git a/frontend/src/lib/forms/media-server-form.svelte b/frontend/src/lib/forms/media-server-form.svelte new file mode 100644 index 00000000..dd1d55b8 --- /dev/null +++ b/frontend/src/lib/forms/media-server-form.svelte @@ -0,0 +1,70 @@ + + + +
+ + + + Plex URL + + + + + + + + + + Plex Token + + 0 + })} + spellcheck="false" + /> + + + + + +
+ +
+
+
diff --git a/frontend/src/lib/forms/scrapers-form.svelte b/frontend/src/lib/forms/scrapers-form.svelte new file mode 100644 index 00000000..8cf1065e --- /dev/null +++ b/frontend/src/lib/forms/scrapers-form.svelte @@ -0,0 +1,157 @@ + + + +
+ + + + Retry After 2 Times (hr) + + + + + + + + + + Retry After 5 Times (hr) + + + + + + + + + + Retry After 10 Times (hr) + + + + + + +
+

+ Scrapers Enabled +

+
+ +
+ + Torrentio +
+
+ + +
+ + Orionoid +
+
+ + +
+ + Jackett +
+
+
+
+ + {#if $form.torrentio_enabled} +
+ + + + Torrentio Filter + + + + + +
+ {/if} + + {#if $form.orionoid_enabled} +
+ + + + Orionoid API Key + + 0 + })} + spellcheck="false" + /> + + + +
+ {/if} + + {#if $form.jackett_enabled} +
+ + + + Jackett URL + + + + + +
+ {/if} + + +
+ +
+
+
diff --git a/frontend/src/routes/onboarding/+page.svelte b/frontend/src/routes/onboarding/+page.svelte new file mode 100644 index 00000000..8d063733 --- /dev/null +++ b/frontend/src/routes/onboarding/+page.svelte @@ -0,0 +1,5 @@ + + +hi \ No newline at end of file diff --git a/frontend/src/routes/settings/content/+page.svelte b/frontend/src/routes/settings/content/+page.svelte index 30d8cb33..f8cd06c8 100644 --- a/frontend/src/routes/settings/content/+page.svelte +++ b/frontend/src/routes/settings/content/+page.svelte @@ -1,263 +1,13 @@

Content Settings

Configure content providers for Iceberg.

- -
-
-

- Content Providers -

-
- -
- - Overseerr -
-
- - -
- - Mdblist -
-
- - -
- - Plex Watchlists -
-
-
-
- - {#if $form.overseerr_enabled} -
- - - - Overseerr URL - - - - - -
- -
- - - - Overseerr API Key - - 0 - })} - spellcheck="false" - /> - - - -
- {/if} - - {#if $form.plex_watchlist_enabled} -
- - - - Plex RSS URL - - - - - -
- -
- - - - Plex RSS Update Interval - - - - - -
- {/if} - - - - - {#if $form.mdblist_enabled} -
- - - - Mdblist API Key - - 0 - })} - spellcheck="false" - /> - - - -
- -
- - - - Mdblist Update Interval - - - - - -
- - {#if $mdblistListsErrors} - {$mdblistListsErrors} - {/if} - -
- -
- -
- {#each $mdblistListsValues.filter((list) => list !== '') as list (list)} - - {/each} -
-
-
- {/if} - - -
- -
-
-
+
diff --git a/frontend/src/routes/settings/general/+page.svelte b/frontend/src/routes/settings/general/+page.svelte index a9d9563d..e3cf9d8a 100644 --- a/frontend/src/routes/settings/general/+page.svelte +++ b/frontend/src/routes/settings/general/+page.svelte @@ -1,28 +1,9 @@
@@ -46,88 +27,5 @@ * These settings require a restart to take effect.

- -
- - - - Debug * - - - - - - - - - - Log * - - - - - - - - - - Host Path - - - - - - - - - - Container Path - - - - - - - - - - Real Debrid API Key - - 0 - })} - spellcheck="false" - /> - - - - - -
- -
-
-
+
diff --git a/frontend/src/routes/settings/mediaserver/+page.svelte b/frontend/src/routes/settings/mediaserver/+page.svelte index 44c88a84..0836e667 100644 --- a/frontend/src/routes/settings/mediaserver/+page.svelte +++ b/frontend/src/routes/settings/mediaserver/+page.svelte @@ -1,27 +1,7 @@
@@ -30,52 +10,5 @@ Configure media server settings for Iceberg.

- -
- - - - Plex URL - - - - - - - - - - Plex Token - - 0 - })} - spellcheck="false" - /> - - - - - -
- -
-
-
+
diff --git a/frontend/src/routes/settings/scrapers/+page.svelte b/frontend/src/routes/settings/scrapers/+page.svelte index 572b7a14..d96a2f69 100644 --- a/frontend/src/routes/settings/scrapers/+page.svelte +++ b/frontend/src/routes/settings/scrapers/+page.svelte @@ -1,164 +1,13 @@

Scraper Settings

-

- Configure scraper settings for Iceberg. -

- - -
- - - - Retry After 2 Times (hr) - - - - - - - - - - Retry After 5 Times (hr) - - - - - - - - - - Retry After 10 Times (hr) - - - - - - -
-

- Scrapers Enabled -

-
- -
- - Torrentio -
-
- - -
- - Orionoid -
-
- - -
- - Jackett -
-
-
-
- - {#if $form.torrentio_enabled} -
- - - - Torrentio Filter - - - - - -
- {/if} - - {#if $form.orionoid_enabled} -
- - - - Orionoid API Key - - 0 - })} - spellcheck="false" - /> - - - -
- {/if} - - {#if $form.jackett_enabled} -
- - - - Jackett URL - - - - - -
- {/if} +

Configure scraper settings for Iceberg.

- -
- -
-
-
+
From a7708f94c7b6329967c9b69f95f8626d2c20b85c Mon Sep 17 00:00:00 2001 From: Spoked <5782630+dreulavelle@users.noreply.github.com> Date: Sun, 14 Jan 2024 17:14:25 -0600 Subject: [PATCH 12/39] 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 --- backend/program/content/__init__.py | 2 +- backend/program/scrapers/__init__.py | 11 ++ backend/program/scrapers/jackett.py | 35 +++---- backend/program/scrapers/orionoid.py | 9 +- backend/program/scrapers/torrentio.py | 9 +- backend/utils/default_settings.json | 8 ++ backend/utils/parser.py | 144 ++++++++++++++++++++++++-- 7 files changed, 182 insertions(+), 36 deletions(-) diff --git a/backend/program/content/__init__.py b/backend/program/content/__init__.py index 61636e49..830da64b 100644 --- a/backend/program/content/__init__.py +++ b/backend/program/content/__init__.py @@ -13,7 +13,7 @@ def __init__(self, media_items): self.initialized = False self.key = "content" self.running = False - self.sm = ServiceManager(media_items, False, Mdblist, Overseerr, PlexWatchlist) + self.sm = ServiceManager(media_items, False, Overseerr, Mdblist, PlexWatchlist) if not self.validate(): logger.error("You have no content services enabled, please enable at least one!") return diff --git a/backend/program/scrapers/__init__.py b/backend/program/scrapers/__init__.py index 21a6020b..37734d3a 100644 --- a/backend/program/scrapers/__init__.py +++ b/backend/program/scrapers/__init__.py @@ -3,6 +3,7 @@ from utils.service_manager import ServiceManager from utils.settings import settings_manager as settings from utils.logger import logger +from utils.parser import parser from .torrentio import Torrentio from .orionoid import Orionoid from .jackett import Jackett @@ -55,3 +56,13 @@ def _needs_new_scrape(self, item) -> bool: > scrape_time or item.scraped_times == 0 ) + def _check_for_title_match(self, item, string) -> bool: + """Check if the title matches PTN title""" + parsed_title = parser.get_title(string) + if item.type == "movie": + return parsed_title == item.title + if item.type == "season": + return parsed_title == item.parent.title + if item.type == "episode": + return parsed_title == item.parent.parent.title + return False \ No newline at end of file diff --git a/backend/program/scrapers/jackett.py b/backend/program/scrapers/jackett.py index 33595dd1..85485e37 100644 --- a/backend/program/scrapers/jackett.py +++ b/backend/program/scrapers/jackett.py @@ -24,7 +24,7 @@ def __init__(self, _): if not self.initialized or not self.api_key: return self.minute_limiter = RateLimiter(max_calls=60, period=60, raise_on_limit=True) - self.second_limiter = RateLimiter(max_calls=1, period=1) + self.second_limiter = RateLimiter(max_calls=1, period=3) logger.info("Jackett initialized!") def validate_settings(self) -> bool: @@ -35,7 +35,7 @@ def validate_settings(self) -> bool: try: url = f"{self.settings.url}/api/v2.0/server/config" response = get(url=url, retry_if_failed=False, timeout=60) - if response.is_ok: + if response.is_ok and response.data.api_key is not None: self.api_key = response.data.api_key return True except ReadTimeout: @@ -79,22 +79,17 @@ def api_scrape(self, item): url = ( f"{self.settings.url}/api/v2.0/indexers/all/results/torznab?apikey={self.api_key}{query}" ) - try: + with self.second_limiter: response = get(url=url, retry_if_failed=False, timeout=60) - if response.is_ok: - data = {} - if not hasattr(response.data['rss']['channel'], "item"): - return {} - for stream in response.data['rss']['channel']['item']: - title = stream.get('title') - for attr in stream.get('torznab:attr', []): - if attr.get('@name') == 'infohash': - infohash = attr.get('@value') - if parser.parse(title) and infohash: - data[infohash] = {"name": title} - if len(data) > 0: - return data - return {} - except ReadTimeout: - logger.debug("Jackett timed out for %s", item.log_string) - return {} + if response.is_ok: + data = {} + for stream in response.data['rss']['channel']['item']: + title = stream.get('title') + for attr in stream.get('torznab:attr', []): + if attr.get('@name') == 'infohash': + infohash = attr.get('@value') + if parser.parse(title) and infohash: + data[infohash] = {"name": title} + if len(data) > 0: + return parser.sort_streams(data) + return {} diff --git a/backend/program/scrapers/orionoid.py b/backend/program/scrapers/orionoid.py index 3de6a629..98d0d02b 100644 --- a/backend/program/scrapers/orionoid.py +++ b/backend/program/scrapers/orionoid.py @@ -128,9 +128,8 @@ def api_scrape(self, item): data = {} for stream in response.data.data.streams: title = stream.file.name - infoHash = stream.file.hash - if parser.parse(title) and infoHash: - data[infoHash] = {"name": title} + if parser.parse(title) and stream.file.hash: + data[stream.file.hash] = {"name": title} if len(data) > 0: - return data - return {} + return parser.sort_streams(data) + return {} \ No newline at end of file diff --git a/backend/program/scrapers/torrentio.py b/backend/program/scrapers/torrentio.py index 291678cd..701e83c3 100644 --- a/backend/program/scrapers/torrentio.py +++ b/backend/program/scrapers/torrentio.py @@ -79,11 +79,14 @@ def api_scrape(self, item): response = get(f"{url}.json", retry_if_failed=False) if response.is_ok: data = {} + if len(response.data.streams) == 0: + return data for stream in response.data.streams: - if parser.parse(stream.title): + title = stream.title.split("\n👤")[0] + if parser.parse(title): data[stream.infoHash] = { - "name": stream.title.split("\n👤")[0], + "name": title, } if len(data) > 0: - return data + return parser.sort_streams(data) return {} diff --git a/backend/utils/default_settings.json b/backend/utils/default_settings.json index d6d4de7d..978bceb9 100644 --- a/backend/utils/default_settings.json +++ b/backend/utils/default_settings.json @@ -47,5 +47,13 @@ "enabled": false, "url": "http://localhost:9117" } + }, + "parser": { + "language": ["English"], + "include_4k": false, + "highest_quality": false, + "repack_proper": true, + "dual_audio": true, + "av1_audio": true } } diff --git a/backend/utils/parser.py b/backend/utils/parser.py index 14fc84bc..05a7b582 100644 --- a/backend/utils/parser.py +++ b/backend/utils/parser.py @@ -1,10 +1,58 @@ +import re import PTN +from typing import List +from pydantic import BaseModel +from utils.settings import settings_manager + + +class ParserConfig(BaseModel): + language: List[str] + include_4k: bool + highest_quality: bool + repack_proper: bool + dual_audio: bool # This sometimes doesnt work depending on if other audio is in the title + av1_audio: bool class Parser: + def __init__(self): + self.settings = ParserConfig(**settings_manager.get("parser")) + self.language = self.settings.language or ["English"] self.resolution = ["1080p", "720p"] - self.language = ["English"] + self.unwanted_codec = ["H.263", "Xvid"] # Bad for transcoding + self.unwanted_quality = ["Cam", "Telesync", "Telecine", "Screener", + "DVDSCR", "Workprint", "DVD-Rip", "TVRip", + "VODRip", "DVD-R", "DSRip", "BRRip"] + self.quality = [None, "Blu-ray", "WEB-DL", "WEBRip", "HDRip", + "HDTVRip", "BDRip", "Pay-Per-View Rip"] + self.audio = [None, "AAC", "AAC 2.0", "AAC 5.1", "FLAC", "AVC", "Custom"] + self.network = ["Apple TV+", "Amazon Studios", "Netflix", + "Nickelodeon", "YouTube Premium", "Disney Plus", + "DisneyNOW", "HBO Max", "HBO", "Hulu Networks", + "DC Universe", "Adult Swim", "Comedy Central", + "Peacock", "AMC", "PBS", "Crunchyroll", + "Syndication", "Hallmark", "BBC", "VICE", + "MSNBC", "Crave"] # Will probably be used later in `Versions` + self.validate_settings() + + def validate_settings(self): + if self.settings.highest_quality: + self.resolution = ["UHD", "2160p", "4K", "1080p", "720p"] + self.audio += ["Dolby TrueHD", "Dolby Atmos", + "Dolby Digital EX", "Dolby Digital Plus", + "Dolby Digital 5.1", "Dolby Digital 7.1", + "Dolby Digital Plus 5.1", "Dolby Digital Plus 7.1" + "DTS-HD MA", "DTS-HD MA", "DTS-HD", "DTS-HD MA 5.1" + "DTS-EX", "DTS:X", "DTS", "5.1", "7.1"] + elif self.settings.include_4k: + self.resolution = ["2160p", "4K", "1080p", "720p"] + else: + self.resolution = ["1080p", "720p"] + if self.settings.dual_audio: + self.audio += ["Dual"] + if not self.settings.av1_audio: + self.unwanted_codec += ["AV1"] # Not all devices support this codec def _parse(self, string): parse = PTN.parse(string) @@ -19,39 +67,121 @@ def _parse(self, string): else: episodes.append(int(episode)) + title = parse.get("title") season = parse.get("season") - + audio = parse.get("audio") + codec = parse.get("codec") resolution = parse.get("resolution") quality = parse.get("quality") + subtitles = parse.get("subtitles") language = parse.get("language") + hdr = parse.get("hdr") + upscaled = parse.get("upscaled") + remastered = parse.get("remastered") + proper = parse.get("proper") + repack = parse.get("repack") + remux = parse.get("remux") if not language: language = "English" extended = parse.get("extended") return { - "episodes": episodes or [], + "title": title, "resolution": resolution or [], "quality": quality or [], + "season": season, + "episodes": episodes or [], + "codec": codec or [], + "audio": audio or [], + "hdr": hdr or False, + "upscaled": upscaled or False, + "remastered": remastered or False, + "proper": proper or False, + "repack": repack or False, + "subtitles": True if subtitles == "Available" else False, "language": language or [], + "remux": remux or False, "extended": extended, - "season": season, } - def episodes(self, string): + def episodes(self, string) -> List[int]: parse = self._parse(string) return parse["episodes"] - def episodes_in_season(self, season, string): + def episodes_in_season(self, season, string) -> List[int]: parse = self._parse(string) if parse["season"] == season: return parse["episodes"] return [] - def parse(self, string): + def _is_4k(self, string) -> bool: + """Check if content is `4k`.""" + if self.settings.include_4k: + parsed = self._parse(string) + return parsed.get("resolution", False) in ["2160p", "4K"] + + def _is_highest_quality(self, string) -> bool: + """Check if content is `highest quality`.""" + if self.settings.highest_quality: + parsed = self._parse(string) + return any([ + parsed.get("hdr", False), + parsed.get("remux", False), + parsed.get("audio", False) in self.audio, + parsed.get("resolution", False) in ["UHD", "2160p", "4K"], + parsed.get("upscaled", False) + ]) + + def _is_repack_or_proper(self, string) -> bool: + """Check if content is `repack` or `proper`.""" + if self.settings.repack_proper: + parsed = self._parse(string) + return any([ + parsed.get("proper", False), + parsed.get("repack", False), + ]) + + def _is_dual_audio(self, string) -> bool: + """Check if content is `dual audio`.""" + if self.settings.dual_audio: + parsed = self._parse(string) + return parsed.get("audio") == "Dual" or \ + re.search(r"((dual.audio)|(english|eng)\W+(dub|audio))", string, flags=re.IGNORECASE) is not None + + def _is_network(self, string) -> bool: + """Check if content is from a `network`.""" + parsed = self._parse(string) + return parsed.get("network", False) in self.network + + def sort_streams(self, streams: dict) -> dict: + """Sorts streams based on user preferences.""" + def sorting_key(item): + _, stream = item + title = stream['name'] + return ( + self._is_dual_audio(title), + self._is_repack_or_proper(title), + self._is_highest_quality(title), + self._is_4k(title), + self._is_network(title) + ) + sorted_streams = sorted(streams.items(), key=sorting_key, reverse=True) + return dict(sorted_streams) + + def parse(self, string) -> bool: + """Parse the given string and return True if it matches the user settings.""" parse = self._parse(string) return ( parse["resolution"] in self.resolution and parse["language"] in self.language + and parse["audio"] in self.audio + and not parse["quality"] in self.unwanted_quality + and not parse["codec"] in self.unwanted_codec ) + def get_title(self, string) -> str: + """Get the `title` from the given string.""" + parse = self._parse(string) + return parse["title"] + parser = Parser() \ No newline at end of file From 1d8df593badadeb62864d610aae669bad022e565 Mon Sep 17 00:00:00 2001 From: Ayush Sehrawat Date: Mon, 15 Jan 2024 15:20:27 +0530 Subject: [PATCH 13/39] feat: onboarding on the way ;), major refactoring of form related code --- frontend/package.json | 1 + frontend/pnpm-lock.yaml | 63 +++++++ frontend/src/hooks.server.ts | 22 +++ frontend/src/lib/forms/content-form.svelte | 3 + frontend/src/lib/forms/general-form.svelte | 3 + frontend/src/lib/forms/helpers.ts | 155 ++++++++++++++++++ .../src/lib/forms/media-server-form.svelte | 3 + frontend/src/lib/forms/scrapers-form.svelte | 3 + frontend/src/routes/+layout.svelte | 12 +- frontend/src/routes/onboarding/+page.svelte | 55 ++++++- .../src/routes/onboarding/1/+page.server.ts | 11 ++ frontend/src/routes/onboarding/1/+page.svelte | 21 +++ .../routes/settings/content/+page.server.ts | 45 +---- .../routes/settings/general/+page.server.ts | 39 +---- .../settings/mediaserver/+page.server.ts | 22 +-- .../routes/settings/scrapers/+page.server.ts | 42 +---- 16 files changed, 367 insertions(+), 133 deletions(-) create mode 100644 frontend/src/hooks.server.ts create mode 100644 frontend/src/lib/forms/helpers.ts create mode 100644 frontend/src/routes/onboarding/1/+page.server.ts create mode 100644 frontend/src/routes/onboarding/1/+page.svelte diff --git a/frontend/package.json b/frontend/package.json index 37e7d394..3ba92d46 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -46,6 +46,7 @@ "lucide-svelte": "^0.307.0", "luxon": "^3.4.4", "mode-watcher": "^0.1.2", + "motion": "^10.17.0", "nprogress": "^0.2.0", "svelte-sonner": "^0.3.6", "sveltekit-superforms": "^1.13.1", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 46e1fa6c..c5640399 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -26,6 +26,9 @@ dependencies: mode-watcher: specifier: ^0.1.2 version: 0.1.2(svelte@4.2.8) + motion: + specifier: ^10.17.0 + version: 10.17.0 nprogress: specifier: ^0.2.0 version: 0.2.0 @@ -463,6 +466,53 @@ packages: svelte: 4.2.8 dev: false + /@motionone/animation@10.17.0: + resolution: {integrity: sha512-ANfIN9+iq1kGgsZxs+Nz96uiNcPLGTXwfNo2Xz/fcJXniPYpaz/Uyrfa+7I5BPLxCP82sh7quVDudf1GABqHbg==} + dependencies: + '@motionone/easing': 10.17.0 + '@motionone/types': 10.17.0 + '@motionone/utils': 10.17.0 + tslib: 2.6.2 + dev: false + + /@motionone/dom@10.17.0: + resolution: {integrity: sha512-cMm33swRlCX/qOPHWGbIlCl0K9Uwi6X5RiL8Ma6OrlJ/TP7Q+Np5GE4xcZkFptysFjMTi4zcZzpnNQGQ5D6M0Q==} + dependencies: + '@motionone/animation': 10.17.0 + '@motionone/generators': 10.17.0 + '@motionone/types': 10.17.0 + '@motionone/utils': 10.17.0 + hey-listen: 1.0.8 + tslib: 2.6.2 + dev: false + + /@motionone/easing@10.17.0: + resolution: {integrity: sha512-Bxe2wSuLu/qxqW4rBFS5m9tMLOw+QBh8v5A7Z5k4Ul4sTj5jAOfZG5R0bn5ywmk+Fs92Ij1feZ5pmC4TeXA8Tg==} + dependencies: + '@motionone/utils': 10.17.0 + tslib: 2.6.2 + dev: false + + /@motionone/generators@10.17.0: + resolution: {integrity: sha512-T6Uo5bDHrZWhIfxG/2Aut7qyWQyJIWehk6OB4qNvr/jwA/SRmixwbd7SOrxZi1z5rH3LIeFFBKK1xHnSbGPZSQ==} + dependencies: + '@motionone/types': 10.17.0 + '@motionone/utils': 10.17.0 + tslib: 2.6.2 + dev: false + + /@motionone/types@10.17.0: + resolution: {integrity: sha512-EgeeqOZVdRUTEHq95Z3t8Rsirc7chN5xFAPMYFobx8TPubkEfRSm5xihmMUkbaR2ErKJTUw3347QDPTHIW12IA==} + dev: false + + /@motionone/utils@10.17.0: + resolution: {integrity: sha512-bGwrki4896apMWIj9yp5rAS2m0xyhxblg6gTB/leWDPt+pb410W8lYWsxyurX+DH+gO1zsQsfx2su/c1/LtTpg==} + dependencies: + '@motionone/types': 10.17.0 + hey-listen: 1.0.8 + tslib: 2.6.2 + dev: false + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1695,6 +1745,10 @@ packages: dependencies: function-bind: 1.1.2 + /hey-listen@1.0.8: + resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==} + dev: false + /human-signals@5.0.0: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} @@ -1988,6 +2042,15 @@ packages: svelte: 4.2.8 dev: false + /motion@10.17.0: + resolution: {integrity: sha512-yBHYkrnJRiomuo88YQzG/v+nzFXKNlKw/Hh7uy8AV7nrYHnE07O6PEECShGbFmZvLrAOGc9qKzEDYmspDYjNWw==} + dependencies: + '@motionone/animation': 10.17.0 + '@motionone/dom': 10.17.0 + '@motionone/types': 10.17.0 + '@motionone/utils': 10.17.0 + dev: false + /mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} diff --git a/frontend/src/hooks.server.ts b/frontend/src/hooks.server.ts new file mode 100644 index 00000000..04d404ab --- /dev/null +++ b/frontend/src/hooks.server.ts @@ -0,0 +1,22 @@ +import type { Handle } from '@sveltejs/kit'; +import { redirect, error } from '@sveltejs/kit'; +import { sequence } from '@sveltejs/kit/hooks'; + +const onboarding: Handle = async ({ event, resolve }) => { + if (!event.url.pathname.startsWith('/onboarding')) { + const res = await event.fetch('http://127.0.0.1:8080/services'); + const data = await res.json(); + if (!data.success || !data.data) { + error(500, 'API Error'); + } + const toCheck = ['content', 'scraping', 'plex', 'real_debrid', 'symlink']; + const allServicesTrue: boolean = toCheck.every((service) => data.data[service] === true); + if (!allServicesTrue) { + redirect(302, '/onboarding'); + } + } + + return resolve(event); +}; + +export const handle = sequence(onboarding); diff --git a/frontend/src/lib/forms/content-form.svelte b/frontend/src/lib/forms/content-form.svelte index 48f1b42d..55dbfdd4 100644 --- a/frontend/src/lib/forms/content-form.svelte +++ b/frontend/src/lib/forms/content-form.svelte @@ -59,9 +59,12 @@ $mdblistListsValues = ['']; } } + + export let actionUrl: string = "?/default" ) { + return [ + { + key: 'debug', + value: form.data.debug + }, + { + key: 'log', + value: form.data.log + }, + { + key: 'symlink', + value: { + host_path: form.data.host_path, + container_path: form.data.container_path + } + }, + { + key: 'real_debrid', + value: { + api_key: form.data.realdebrid_api_key + } + } + ]; +} + +// Content Settings ----------------------------------------------------------------------------------- +export const contentSettingsToGet: string[] = ['content']; + +export function contentSettingsToPass(data:any) { + return { + overseerr_enabled: data.data.content.overseerr.enabled, + overseerr_url: data.data.content.overseerr?.url || '', + overseerr_api_key: data.data.content.overseerr?.api_key || '', + mdblist_enabled: data.data.content.mdblist.enabled, + mdblist_api_key: data.data.content.mdblist?.api_key || '', + mdblist_update_interval: data.data.content.mdblist?.update_interval || 80, + mdblist_lists: data.data.content.mdblist?.lists || [''], + plex_watchlist_enabled: data.data.content.plex_watchlist.enabled, + plex_watchlist_rss: data.data.content.plex_watchlist?.rss || '', + plex_watchlist_update_interval: + data.data.content.plex_watchlist?.update_interval || 80 + } +} + +export function contentSettingsToSet(form: SuperValidated) { + return [ + { + key: 'content', + value: { + overseerr: { + enabled: form.data.overseerr_enabled, + url: form.data.overseerr_url, + api_key: form.data.overseerr_api_key + }, + mdblist: { + enabled: form.data.mdblist_enabled, + api_key: form.data.mdblist_api_key, + update_interval: form.data.mdblist_update_interval, + lists: form.data.mdblist_lists + }, + plex_watchlist: { + enabled: form.data.plex_watchlist_enabled, + rss: form.data.plex_watchlist_rss, + update_interval: form.data.plex_watchlist_update_interval + } + } + } + ]; +} + +// Media Server Settings ----------------------------------------------------------------------------------- +export const mediaServerSettingsToGet: string[] = ['plex']; + +export function mediaServerSettingsToPass(data: any) { + return { + plex_token: data.data.plex.token, + plex_url: data.data.plex.url + } +} + +export function mediaServerSettingsToSet(form: SuperValidated) { + return [ + { + key: 'plex', + value: { + token: form.data.plex_token, + url: form.data.plex_url + } + } + ]; +} + +// Scrapers Settings ----------------------------------------------------------------------------------- +export const scrapersSettingsToGet: string[] = ['scraping']; + +export function scrapersSettingsToPass(data: any) { + return { + after_2: data.data.scraping.after_2, + after_5: data.data.scraping.after_5, + after_10: data.data.scraping.after_10, + torrentio_enabled: data.data.scraping.torrentio.enabled, + orionoid_enabled: data.data.scraping.orionoid.enabled, + jackett_enabled: data.data.scraping.jackett.enabled, + torrentio_filter: data.data.scraping.torrentio?.filter || '', + orionoid_api_key: data.data.scraping.orionoid?.api_key || '', + jackett_url: data.data.scraping.jackett?.url || '' + } +} + +export function scrapersSettingsToSet(form: SuperValidated) { + return [ + { + key: 'scraping', + value: { + after_2: form.data.after_2, + after_5: form.data.after_5, + after_10: form.data.after_10, + torrentio: { + enabled: form.data.torrentio_enabled, + filter: form.data.torrentio_filter + }, + orionoid: { + enabled: form.data.orionoid_enabled, + api_key: form.data.orionoid_api_key + }, + jackett: { + enabled: form.data.jackett_enabled, + url: form.data.jackett_url + } + } + } + ]; +} diff --git a/frontend/src/lib/forms/media-server-form.svelte b/frontend/src/lib/forms/media-server-form.svelte index dd1d55b8..b244d3e0 100644 --- a/frontend/src/lib/forms/media-server-form.svelte +++ b/frontend/src/lib/forms/media-server-form.svelte @@ -22,9 +22,12 @@ } else if ($message) { toast.error($message); } + + export let actionUrl: string = "?/default" import { ModeWatcher } from 'mode-watcher'; - import { Toaster } from "$lib/components/ui/sonner"; + import { Toaster } from '$lib/components/ui/sonner'; import '../app.pcss'; import { onMount } from 'svelte'; import { afterNavigate, beforeNavigate, goto } from '$app/navigation'; @@ -8,6 +8,7 @@ import Header from '$lib/components/header.svelte'; import * as Command from '$lib/components/ui/command'; import { Settings, CircleDashed, SlidersHorizontal, Info, Layers, Tv } from 'lucide-svelte'; + import { page } from '$app/stores'; beforeNavigate(() => { NProgress.start(); @@ -38,12 +39,17 @@
-
+ {#if !$page.url.pathname.startsWith('/onboarding')} +
+ {/if}
- + No results found. diff --git a/frontend/src/routes/onboarding/+page.svelte b/frontend/src/routes/onboarding/+page.svelte index 8d063733..d2c8bb84 100644 --- a/frontend/src/routes/onboarding/+page.svelte +++ b/frontend/src/routes/onboarding/+page.svelte @@ -1,5 +1,56 @@ -hi \ No newline at end of file +
+
+

Welcome to Iceberg!

+

+ Before you can start using Iceberg, you need to configure some services first. +

+ +
+
diff --git a/frontend/src/routes/onboarding/1/+page.server.ts b/frontend/src/routes/onboarding/1/+page.server.ts new file mode 100644 index 00000000..d8467125 --- /dev/null +++ b/frontend/src/routes/onboarding/1/+page.server.ts @@ -0,0 +1,11 @@ +import type { PageServerLoad, Actions } from './$types'; +import { fail, error } from '@sveltejs/kit'; +import { message, superValidate } from 'sveltekit-superforms/server'; +import { generalSettingsSchema } from '$lib/schemas/setting'; +import { saveSettings } from '$lib/helpers'; + +export const load: PageServerLoad = async () => { + const form = await superValidate(generalSettingsSchema); + + return { form }; +}; diff --git a/frontend/src/routes/onboarding/1/+page.svelte b/frontend/src/routes/onboarding/1/+page.svelte new file mode 100644 index 00000000..22f1fc49 --- /dev/null +++ b/frontend/src/routes/onboarding/1/+page.svelte @@ -0,0 +1,21 @@ + + +
+
+

Step 1

+

Let's get started by configuring your general settings.

+ Fields marked with * require restart of backend services. +
+
+ + +
+
diff --git a/frontend/src/routes/settings/content/+page.server.ts b/frontend/src/routes/settings/content/+page.server.ts index 93d59729..dd929d8d 100644 --- a/frontend/src/routes/settings/content/+page.server.ts +++ b/frontend/src/routes/settings/content/+page.server.ts @@ -3,12 +3,12 @@ import { fail, error } from '@sveltejs/kit'; import { message, superValidate } from 'sveltekit-superforms/server'; import { contentSettingsSchema } from '$lib/schemas/setting'; import { saveSettings } from '$lib/helpers'; +import { contentSettingsToGet, contentSettingsToPass, contentSettingsToSet } from '$lib/forms/helpers'; export const load: PageServerLoad = async ({ fetch }) => { async function getPartialSettings() { try { - const toGet = ['content']; - const results = await fetch(`http://127.0.0.1:8080/settings/get/${toGet.join(',')}`); + const results = await fetch(`http://127.0.0.1:8080/settings/get/${contentSettingsToGet.join(',')}`); return await results.json(); } catch (e) { console.error(e); @@ -16,23 +16,10 @@ export const load: PageServerLoad = async ({ fetch }) => { } } - let toPassToSchema: any = await getPartialSettings(); - toPassToSchema = { - overseerr_enabled: toPassToSchema.data.content.overseerr.enabled, - overseerr_url: toPassToSchema.data.content.overseerr?.url || '', - overseerr_api_key: toPassToSchema.data.content.overseerr?.api_key || '', - mdblist_enabled: toPassToSchema.data.content.mdblist.enabled, - mdblist_api_key: toPassToSchema.data.content.mdblist?.api_key || '', - mdblist_update_interval: toPassToSchema.data.content.mdblist?.update_interval || 80, - mdblist_lists: toPassToSchema.data.content.mdblist?.lists || [''], - plex_watchlist_enabled: toPassToSchema.data.content.plex_watchlist.enabled, - plex_watchlist_rss: toPassToSchema.data.content.plex_watchlist?.rss || '', - plex_watchlist_update_interval: - toPassToSchema.data.content.plex_watchlist?.update_interval || 80 - }; + let data: any = await getPartialSettings(); + const toPassToSchema = contentSettingsToPass(data); const form = await superValidate(toPassToSchema, contentSettingsSchema); - return { form }; }; @@ -44,29 +31,7 @@ export const actions: Actions = { form }); } - const toSet = [ - { - key: 'content', - value: { - overseerr: { - enabled: form.data.overseerr_enabled, - url: form.data.overseerr_url, - api_key: form.data.overseerr_api_key - }, - mdblist: { - enabled: form.data.mdblist_enabled, - api_key: form.data.mdblist_api_key, - update_interval: form.data.mdblist_update_interval, - lists: form.data.mdblist_lists - }, - plex_watchlist: { - enabled: form.data.plex_watchlist_enabled, - rss: form.data.plex_watchlist_rss, - update_interval: form.data.plex_watchlist_update_interval - } - } - } - ]; + const toSet = contentSettingsToSet(form); try { const data = await saveSettings(event.fetch, toSet); diff --git a/frontend/src/routes/settings/general/+page.server.ts b/frontend/src/routes/settings/general/+page.server.ts index cccee017..2a90e635 100644 --- a/frontend/src/routes/settings/general/+page.server.ts +++ b/frontend/src/routes/settings/general/+page.server.ts @@ -3,12 +3,12 @@ import { fail, error } from '@sveltejs/kit'; import { message, superValidate } from 'sveltekit-superforms/server'; import { generalSettingsSchema } from '$lib/schemas/setting'; import { saveSettings } from '$lib/helpers'; +import { generalSettingsToGet, generalSettingsToPass, generalSettingsToSet } from '$lib/forms/helpers'; export const load: PageServerLoad = async ({ fetch }) => { async function getPartialSettings() { try { - const toGet = ['debug', 'log', 'symlink', 'real_debrid']; - const results = await fetch(`http://127.0.0.1:8080/settings/get/${toGet.join(',')}`); + const results = await fetch(`http://127.0.0.1:8080/settings/get/${generalSettingsToGet.join(',')}`); return await results.json(); } catch (e) { console.error(e); @@ -16,17 +16,10 @@ export const load: PageServerLoad = async ({ fetch }) => { } } - let toPassToSchema: any = await getPartialSettings(); - toPassToSchema = { - debug: toPassToSchema.data.debug, - log: toPassToSchema.data.log, - host_path: toPassToSchema.data.symlink.host_path, - container_path: toPassToSchema.data.symlink.container_path, - realdebrid_api_key: toPassToSchema.data.real_debrid.api_key - }; + let data: any = await getPartialSettings(); + let toPassToSchema = generalSettingsToPass(data); const form = await superValidate(toPassToSchema, generalSettingsSchema); - return { form }; }; @@ -38,29 +31,7 @@ export const actions: Actions = { form }); } - const toSet = [ - { - key: 'debug', - value: form.data.debug - }, - { - key: 'log', - value: form.data.log - }, - { - key: 'symlink', - value: { - host_path: form.data.host_path, - container_path: form.data.container_path - } - }, - { - key: 'real_debrid', - value: { - api_key: form.data.realdebrid_api_key - } - } - ]; + const toSet = generalSettingsToSet(form); try { const data = await saveSettings(event.fetch, toSet); diff --git a/frontend/src/routes/settings/mediaserver/+page.server.ts b/frontend/src/routes/settings/mediaserver/+page.server.ts index e4a33d31..bd78353f 100644 --- a/frontend/src/routes/settings/mediaserver/+page.server.ts +++ b/frontend/src/routes/settings/mediaserver/+page.server.ts @@ -3,12 +3,12 @@ import { fail, error } from '@sveltejs/kit'; import { message, superValidate } from 'sveltekit-superforms/server'; import { mediaServerSettingsSchema } from '$lib/schemas/setting'; import { saveSettings } from '$lib/helpers'; +import { mediaServerSettingsToGet, mediaServerSettingsToPass, mediaServerSettingsToSet } from '$lib/forms/helpers'; export const load: PageServerLoad = async ({ fetch }) => { async function getPartialSettings() { try { - const toGet = ['plex']; - const results = await fetch(`http://127.0.0.1:8080/settings/get/${toGet.join(',')}`); + const results = await fetch(`http://127.0.0.1:8080/settings/get/${mediaServerSettingsToGet.join(',')}`); return await results.json(); } catch (e) { console.error(e); @@ -16,14 +16,10 @@ export const load: PageServerLoad = async ({ fetch }) => { } } - let toPassToSchema: any = await getPartialSettings(); - toPassToSchema = { - plex_token: toPassToSchema.data.plex.token, - plex_url: toPassToSchema.data.plex.url - }; + let data: any = await getPartialSettings(); + let toPassToSchema = mediaServerSettingsToPass(data); const form = await superValidate(toPassToSchema, mediaServerSettingsSchema); - return { form }; }; @@ -35,15 +31,7 @@ export const actions: Actions = { form }); } - const toSet = [ - { - key: 'plex', - value: { - token: form.data.plex_token, - url: form.data.plex_url - } - } - ]; + const toSet = mediaServerSettingsToSet(form); try { const data = await saveSettings(event.fetch, toSet); diff --git a/frontend/src/routes/settings/scrapers/+page.server.ts b/frontend/src/routes/settings/scrapers/+page.server.ts index 834d9892..b1300489 100644 --- a/frontend/src/routes/settings/scrapers/+page.server.ts +++ b/frontend/src/routes/settings/scrapers/+page.server.ts @@ -3,12 +3,12 @@ import { fail, error } from '@sveltejs/kit'; import { message, superValidate } from 'sveltekit-superforms/server'; import { scrapersSettingsSchema } from '$lib/schemas/setting'; import { saveSettings } from '$lib/helpers'; +import { scrapersSettingsToGet, scrapersSettingsToPass, scrapersSettingsToSet } from '$lib/forms/helpers'; export const load: PageServerLoad = async ({ fetch }) => { async function getPartialSettings() { try { - const toGet = ['scraping']; - const results = await fetch(`http://127.0.0.1:8080/settings/get/${toGet.join(',')}`); + const results = await fetch(`http://127.0.0.1:8080/settings/get/${scrapersSettingsToGet.join(',')}`); return await results.json(); } catch (e) { console.error(e); @@ -16,21 +16,10 @@ export const load: PageServerLoad = async ({ fetch }) => { } } - let toPassToSchema: any = await getPartialSettings(); - toPassToSchema = { - after_2: toPassToSchema.data.scraping.after_2, - after_5: toPassToSchema.data.scraping.after_5, - after_10: toPassToSchema.data.scraping.after_10, - torrentio_enabled: toPassToSchema.data.scraping.torrentio.enabled, - orionoid_enabled: toPassToSchema.data.scraping.orionoid.enabled, - jackett_enabled: toPassToSchema.data.scraping.jackett.enabled, - torrentio_filter: toPassToSchema.data.scraping.torrentio?.filter || '', - orionoid_api_key: toPassToSchema.data.scraping.orionoid?.api_key || '', - jackett_url: toPassToSchema.data.scraping.jackett?.url || '' - }; + let data: any = await getPartialSettings(); + let toPassToSchema = scrapersSettingsToPass(data); const form = await superValidate(toPassToSchema, scrapersSettingsSchema); - return { form }; }; @@ -42,28 +31,7 @@ export const actions: Actions = { form }); } - const toSet = [ - { - key: 'scraping', - value: { - after_2: form.data.after_2, - after_5: form.data.after_5, - after_10: form.data.after_10, - torrentio: { - enabled: form.data.torrentio_enabled, - filter: form.data.torrentio_filter - }, - orionoid: { - enabled: form.data.orionoid_enabled, - api_key: form.data.orionoid_api_key - }, - jackett: { - enabled: form.data.jackett_enabled, - url: form.data.jackett_url, - } - } - } - ]; + const toSet = scrapersSettingsToSet(form); try { const data = await saveSettings(event.fetch, toSet); From a36a1d0aa9a7fc568c05fa599049f7f8ed261f2a Mon Sep 17 00:00:00 2001 From: Gaisberg Date: Sat, 6 Jan 2024 17:02:21 +0200 Subject: [PATCH 14/39] Simplified downloading logic and modified state matchine --- backend/program/media/item.py | 4 +- backend/program/media/state.py | 5 +- backend/program/realdebrid.py | 109 ++++++++------------------------- 3 files changed, 29 insertions(+), 89 deletions(-) diff --git a/backend/program/media/item.py b/backend/program/media/item.py index 627a4638..b75a8515 100644 --- a/backend/program/media/item.py +++ b/backend/program/media/item.py @@ -211,9 +211,7 @@ def _determine_state(self): return Symlink() if all(episode.file and episode.folder for episode in self.episodes): return Download() - if self.is_scraped() or any( - episode.state == Scrape for episode in self.episodes - ): + if self.is_scraped(): return Scrape() if any(episode.state == Content for episode in self.episodes): return Content() diff --git a/backend/program/media/state.py b/backend/program/media/state.py index e51fa5e4..0477a80e 100644 --- a/backend/program/media/state.py +++ b/backend/program/media/state.py @@ -24,6 +24,9 @@ def perform_action(self, modules): scraper = next(module for module in modules if module.key == "scraping") if self.context.type in ["movie", "season", "episode"]: scraper.run(self.context) + if self.context.state == Content and self.context.type == "season": + for episode in self.context.episodes: + episode.state.perform_action(modules) if self.context.type == "show": for season in self.context.seasons: if season.aired_at: @@ -40,7 +43,7 @@ def perform_action(self, modules): debrid.run(self.context) if self.context.type == "show": for season in self.context.seasons: - if season.aired_at: + if season.aired_at and self.context.state == Scrape: season.state.perform_action(modules) else: for episode in season.episodes: diff --git a/backend/program/realdebrid.py b/backend/program/realdebrid.py index 3a98fa5d..862ae189 100644 --- a/backend/program/realdebrid.py +++ b/backend/program/realdebrid.py @@ -30,9 +30,7 @@ def __init__(self, _): self.auth_headers = {"Authorization": f"Bearer {self.settings.api_key}"} self.running = False if not self._validate_settings(): - logger.error( - "Realdebrid settings incorrect or not premium!" - ) + logger.error("Realdebrid settings incorrect or not premium!") return logger.info("Real Debrid initialized!") self.initialized = True @@ -59,33 +57,26 @@ def download(self, item): def _download(self, item): """Download movie from real-debrid.com""" downloaded = 0 - self._check_stream_availability(item) - if self._determine_best_stream(item): + if self.is_cached(item): if not self._is_downloaded(item): downloaded = self._download_item(item) - self._update_torrent_info(item) self._set_file_paths(item) return downloaded def _is_downloaded(self, item): - if not item.get("active_stream", None): - return False torrents = self.get_torrents() for torrent in torrents: if torrent.hash == item.active_stream.get("hash"): item.set("active_stream.id", torrent.id) + self.set_active_files(item) logger.debug("Torrent for %s already downloaded", item.log_string) return True return False - def _update_torrent_info(self, item): - info = self.get_torrent_info(item.get("active_stream")["id"]) - item.active_stream["name"] = info.filename - def _download_item(self, item): request_id = self.add_magnet(item) - - time.sleep(0.3) + item.set("active_stream.id", request_id) + self.set_active_files(item) self.select_files(request_id, item) item.set("active_stream.id", request_id) logger.debug("Downloaded %s", item.log_string) @@ -96,31 +87,24 @@ def _get_torrent_info(self, request_id): if not data["id"] in self._torrents.keys(): self._torrents[data["id"]] = data - def _determine_best_stream(self, item) -> bool: - """Returns true if season stream found for episode""" - for hash, stream in item.streams.items(): - if stream.get("cached"): - item.set("active_stream", stream) - item.set("active_stream.hash", hash) - break + def set_active_files(self, item): + info = self.get_torrent_info(item.get("active_stream")["id"]) + item.active_stream["name"] = info.filename - if item.get("active_stream", None): - logger.debug("Found cached release for %s", item.log_string) - return True - else: - logger.debug("No cached release found for %s", item.log_string) - item.set("streams", {}) - return False + for file in info.files: + extension = os.path.splitext(file.path)[1] + if extension in WANTED_FORMATS: + filename = os.path.basename(file.path) + item.active_stream["files"][str(file.id)] = {"filename": filename} - def _check_stream_availability(self, item): + def is_cached(self, item): if len(item.streams) == 0: return - # Split the streams into chunks of 5 - # The api call is slow and we don't want to wait for it for too long def chunks(lst, n): for i in range(0, len(lst), n): - yield lst[i:i + n] + yield lst[i : i + n] + stream_chunks = list(chunks(list(item.streams), 5)) for stream_chunk in stream_chunks: @@ -130,63 +114,17 @@ def chunks(lst, n): additional_headers=self.auth_headers, response_type=dict, ) - cached = False for stream_hash, provider_list in response.data.items(): if len(provider_list) == 0: continue for containers in provider_list.values(): - for container in containers: - wanted_files = { - file_id: file - for file_id, file in container.items() - if os.path.splitext(file["filename"])[1] in WANTED_FORMATS - } - if len(wanted_files) >= 1: - cached = False - if item.type == "season": - episodes = [] - for file in wanted_files.values(): - episodes += parser.episodes_in_season( - item.number, file["filename"] - ) - if len(episodes) >= len(item.episodes): - cached = True - if item.type == "movie": - if len(wanted_files) == 1: - cached = True - if item.type == "episode": - for file in wanted_files.values(): - episodes = parser.episodes_in_season( - item.parent.number, file["filename"] - ) - if item.number in episodes: - cached = True - break - item.streams[stream_hash]["files"] = wanted_files - item.streams[stream_hash]["cached"] = cached - if cached: - return - - def _real_episode_count(self, files): - def count_episodes(episode_numbers): - count = 0 - for episode in episode_numbers: - if "-" in episode: - start, end = map(int, episode.split("-")) - count += end - start + 1 - else: - count += 1 - return count - - total_count = 0 - for file in files.values(): - episode_numbers = re.findall( - r"E(\d{1,2}(?:-\d{1,2})?)", - file["filename"], - re.IGNORECASE, - ) - total_count += count_episodes(episode_numbers) - return total_count + if len(containers) > 0: + item.set( + "active_stream", + {"hash": stream_hash, "files": {}, "id": None}, + ) + return True + return False def _set_file_paths(self, item): if item.type == "movie": @@ -218,6 +156,7 @@ def _handle_episode_paths(self, episode): if episode.number == episode_number: episode.set("folder", episode.active_stream.get("name")) episode.set("file", file["filename"]) + return def add_magnet(self, item) -> str: """Add magnet link to real-debrid.com""" From 9ce7c9c0d6dafb384a4c75c9ab08b9e8a4d96810 Mon Sep 17 00:00:00 2001 From: Gaisberg Date: Sat, 6 Jan 2024 17:43:30 +0200 Subject: [PATCH 15/39] fix typo in state machine and handle movie pathing correctly --- backend/program/media/state.py | 2 +- backend/program/realdebrid.py | 34 ++++++++++++++++++++++------------ 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/backend/program/media/state.py b/backend/program/media/state.py index 0477a80e..5826c014 100644 --- a/backend/program/media/state.py +++ b/backend/program/media/state.py @@ -43,7 +43,7 @@ def perform_action(self, modules): debrid.run(self.context) if self.context.type == "show": for season in self.context.seasons: - if season.aired_at and self.context.state == Scrape: + if season.aired_at and season.state == Scrape: season.state.perform_action(modules) else: for episode in season.episodes: diff --git a/backend/program/realdebrid.py b/backend/program/realdebrid.py index 862ae189..8b8db153 100644 --- a/backend/program/realdebrid.py +++ b/backend/program/realdebrid.py @@ -1,7 +1,6 @@ """Realdebrid module""" import os -import re -import time +from pathlib import Path from typing import Optional from pydantic import BaseModel from requests import ConnectTimeout @@ -118,12 +117,14 @@ def chunks(lst, n): if len(provider_list) == 0: continue for containers in provider_list.values(): - if len(containers) > 0: - item.set( - "active_stream", - {"hash": stream_hash, "files": {}, "id": None}, - ) - return True + for container in containers: + for file in container.values(): + if Path(file["filename"]).suffix in WANTED_FORMATS: + item.set( + "active_stream", + {"hash": stream_hash, "files": {}, "id": None}, + ) + return True return False def _set_file_paths(self, item): @@ -135,11 +136,20 @@ def _set_file_paths(self, item): self._handle_episode_paths(item) def _handle_movie_paths(self, item): + def is_wanted(file: str, item): + if Path(file).stem == item.active_stream["name"]: + item.set("file", file) + return True + item.set("folder", item.active_stream.get("name")) - item.set( - "file", - next(iter(item.active_stream["files"].values())).get("filename"), - ) + for file in item.active_stream["files"].values(): + if type(file) == dict: + for sub_file in file.values(): + if is_wanted(sub_file, item): + return + else: + if is_wanted(file, item): + return def _handle_season_paths(self, season): for file in season.active_stream["files"].values(): From 06eeeab0ca0c700d0d60f17b4afa60b584e9c272 Mon Sep 17 00:00:00 2001 From: Gaisberg Date: Sat, 6 Jan 2024 17:53:20 +0200 Subject: [PATCH 16/39] Remove useless method --- backend/program/realdebrid.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/backend/program/realdebrid.py b/backend/program/realdebrid.py index 8b8db153..64f78326 100644 --- a/backend/program/realdebrid.py +++ b/backend/program/realdebrid.py @@ -50,10 +50,6 @@ def run(self, item): self.download(item) def download(self, item): - """Download given media items from real-debrid.com""" - self._download(item) - - def _download(self, item): """Download movie from real-debrid.com""" downloaded = 0 if self.is_cached(item): From 8b57ca71c68511e053fdabf1b90287c847aaa95a Mon Sep 17 00:00:00 2001 From: Gaisberg Date: Mon, 8 Jan 2024 20:47:53 +0200 Subject: [PATCH 17/39] Temporary fix to test --- backend/program/media/item.py | 8 ----- backend/program/realdebrid.py | 64 +++++++++++++++++------------------ backend/program/symlink.py | 13 +++++-- 3 files changed, 42 insertions(+), 43 deletions(-) diff --git a/backend/program/media/item.py b/backend/program/media/item.py index b75a8515..71f845ac 100644 --- a/backend/program/media/item.py +++ b/backend/program/media/item.py @@ -70,11 +70,6 @@ def _determine_state(self): return Content() return Unknown() - def is_cached(self): - if self.streams: - return any(stream.get("cached", None) for stream in self.streams.values()) - return False - def is_scraped(self): return len(self.streams) > 0 @@ -116,9 +111,6 @@ def to_extended_dict(self): dict["network"] = (self.network if hasattr(self, "network") else None,) return dict - def is_not_cached(self): - return not self.is_cached() - def __iter__(self): for attr, _ in vars(self).items(): yield attr diff --git a/backend/program/realdebrid.py b/backend/program/realdebrid.py index 64f78326..9525778d 100644 --- a/backend/program/realdebrid.py +++ b/backend/program/realdebrid.py @@ -1,6 +1,7 @@ """Realdebrid module""" import os from pathlib import Path +import time from typing import Optional from pydantic import BaseModel from requests import ConnectTimeout @@ -55,6 +56,8 @@ def download(self, item): if self.is_cached(item): if not self._is_downloaded(item): downloaded = self._download_item(item) + else: + downloaded = True self._set_file_paths(item) return downloaded @@ -62,6 +65,11 @@ def _is_downloaded(self, item): torrents = self.get_torrents() for torrent in torrents: if torrent.hash == item.active_stream.get("hash"): + info = self.get_torrent_info(torrent.id) + if item.type == "episode": + if not any(file for file in info.files if file.selected == 1 and item.number in parser.episodes_in_season(item.parent.number, Path(file.path).name)): + return False + item.set("active_stream.id", torrent.id) self.set_active_files(item) logger.debug("Torrent for %s already downloaded", item.log_string) @@ -72,6 +80,7 @@ def _download_item(self, item): request_id = self.add_magnet(item) item.set("active_stream.id", request_id) self.set_active_files(item) + time.sleep(0.5) self.select_files(request_id, item) item.set("active_stream.id", request_id) logger.debug("Downloaded %s", item.log_string) @@ -84,14 +93,9 @@ def _get_torrent_info(self, request_id): def set_active_files(self, item): info = self.get_torrent_info(item.get("active_stream")["id"]) + item.active_stream["alternative_name"] = info.original_filename item.active_stream["name"] = info.filename - for file in info.files: - extension = os.path.splitext(file.path)[1] - if extension in WANTED_FORMATS: - filename = os.path.basename(file.path) - item.active_stream["files"][str(file.id)] = {"filename": filename} - def is_cached(self, item): if len(item.streams) == 0: return @@ -114,13 +118,19 @@ def chunks(lst, n): continue for containers in provider_list.values(): for container in containers: - for file in container.values(): - if Path(file["filename"]).suffix in WANTED_FORMATS: - item.set( - "active_stream", - {"hash": stream_hash, "files": {}, "id": None}, - ) - return True + wanted_files = {} + if item.type == "movie" and all(file["filesize"] > 200000 for file in container.values()): + wanted_files = container + if item.type == "season" and all(any(episode.number in parser.episodes_in_season(item.number, file["filename"]) for file in container.values()) for episode in item.episodes): + wanted_files = container + if item.type == "episode" and any(item.number in parser.episodes_in_season(item.parent.number, episode["filename"]) for episode in container.values()): + wanted_files = container + if len(wanted_files) > 0 and all(item for item in wanted_files.values() if Path(item["filename"]).suffix in WANTED_FORMATS): + item.set( + "active_stream", + {"hash": stream_hash, "files": wanted_files, "id": None}, + ) + return True return False def _set_file_paths(self, item): @@ -132,20 +142,9 @@ def _set_file_paths(self, item): self._handle_episode_paths(item) def _handle_movie_paths(self, item): - def is_wanted(file: str, item): - if Path(file).stem == item.active_stream["name"]: - item.set("file", file) - return True - item.set("folder", item.active_stream.get("name")) - for file in item.active_stream["files"].values(): - if type(file) == dict: - for sub_file in file.values(): - if is_wanted(sub_file, item): - return - else: - if is_wanted(file, item): - return + item.set("alternative_folder", item.active_stream.get("alternative_name")) + item.set("file", next(file for file in item.active_stream.get("files").values())["filename"]) def _handle_season_paths(self, season): for file in season.active_stream["files"].values(): @@ -154,15 +153,16 @@ def _handle_season_paths(self, season): season.episodes[episode - 1].set( "folder", season.active_stream.get("name") ) + season.episodes[episode - 1].set( + "alternative_folder", season.active_stream.get("alternative_name") + ) season.episodes[episode - 1].set("file", file["filename"]) def _handle_episode_paths(self, episode): - for file in episode.active_stream["files"].values(): - for episode_number in parser.episodes(file["filename"]): - if episode.number == episode_number: - episode.set("folder", episode.active_stream.get("name")) - episode.set("file", file["filename"]) - return + file = next(file for file in episode.active_stream.get("files").values() if episode.number in parser.episodes_in_season(episode.parent.number, file["filename"])) + episode.set("folder", episode.active_stream.get("name")) + episode.set("alternative_folder", episode.active_stream.get("alternative_name")) + episode.set("file", file["filename"]) def add_magnet(self, item) -> str: """Add magnet link to real-debrid.com""" diff --git a/backend/program/symlink.py b/backend/program/symlink.py index b3716f61..62e5d167 100644 --- a/backend/program/symlink.py +++ b/backend/program/symlink.py @@ -77,9 +77,16 @@ def _determine_file_name(self, item): return filename def _run(self, item): - if os.path.exists( - os.path.join(self.settings.host_path, item.folder, item.file) - ): + found = False + if os.path.exists(os.path.join(self.settings.host_path, item.folder, item.file)): + found = True + elif os.path.exists(os.path.join(self.settings.host_path, item.alternative_folder, item.file)): + item.set("folder", item.alternative_folder) + found = True + elif os.path.exists(os.path.join(self.settings.host_path, item.file, item.file)): + item.set("folder", item.file) + found = True + if found: self._symlink(item) def _symlink(self, item): From 69434fc4ede55111ba73bc820ad05d867e832cfa Mon Sep 17 00:00:00 2001 From: Gaisberg Date: Mon, 15 Jan 2024 00:09:12 +0200 Subject: [PATCH 18/39] Remove uncached stream hashes from item to avoid loop, some blacklisting logic could also be good --- backend/program/realdebrid.py | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/program/realdebrid.py b/backend/program/realdebrid.py index 9525778d..fa033bd6 100644 --- a/backend/program/realdebrid.py +++ b/backend/program/realdebrid.py @@ -131,6 +131,7 @@ def chunks(lst, n): {"hash": stream_hash, "files": wanted_files, "id": None}, ) return True + item.streams[stream_hash] = None return False def _set_file_paths(self, item): From 15a4d96a5b0b63966b2a4368523fd5d5e27383b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 09:09:20 +0000 Subject: [PATCH 19/39] chore(deps-dev): bump @typescript-eslint/eslint-plugin in /frontend (#134) --- frontend/package.json | 2 +- frontend/pnpm-lock.yaml | 77 ++++++++++++++++++++++++++++++++--------- 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 3ba92d46..33aa2039 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,7 +19,7 @@ "@sveltejs/vite-plugin-svelte": "^3.0.0", "@types/luxon": "^3.3.7", "@types/nprogress": "^0.2.3", - "@typescript-eslint/eslint-plugin": "^6.17.0", + "@typescript-eslint/eslint-plugin": "^6.19.0", "@typescript-eslint/parser": "^6.17.0", "autoprefixer": "^10.4.14", "eslint": "^8.28.0", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index c5640399..7a6f3e7a 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -68,8 +68,8 @@ devDependencies: specifier: ^0.2.3 version: 0.2.3 '@typescript-eslint/eslint-plugin': - specifier: ^6.17.0 - version: 6.17.0(@typescript-eslint/parser@6.17.0)(eslint@8.56.0)(typescript@5.3.3) + specifier: ^6.19.0 + version: 6.19.0(@typescript-eslint/parser@6.17.0)(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/parser': specifier: ^6.17.0 version: 6.17.0(eslint@8.56.0)(typescript@5.3.3) @@ -809,8 +809,8 @@ packages: resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} dev: true - /@typescript-eslint/eslint-plugin@6.17.0(@typescript-eslint/parser@6.17.0)(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-Vih/4xLXmY7V490dGwBQJTpIZxH4ZFH6eCVmQ4RFkB+wmaCTDAx4dtgoWwMNGKLkqRY1L6rPqzEbjorRnDo4rQ==} + /@typescript-eslint/eslint-plugin@6.19.0(@typescript-eslint/parser@6.17.0)(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-DUCUkQNklCQYnrBSSikjVChdc84/vMPDQSgJTHBZ64G9bA9w0Crc0rd2diujKbTdp6w2J47qkeHQLoi0rpLCdg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -822,10 +822,10 @@ packages: dependencies: '@eslint-community/regexpp': 4.10.0 '@typescript-eslint/parser': 6.17.0(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/scope-manager': 6.17.0 - '@typescript-eslint/type-utils': 6.17.0(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/utils': 6.17.0(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 6.17.0 + '@typescript-eslint/scope-manager': 6.19.0 + '@typescript-eslint/type-utils': 6.19.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/utils': 6.19.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.19.0 debug: 4.3.4 eslint: 8.56.0 graphemer: 1.4.0 @@ -867,8 +867,16 @@ packages: '@typescript-eslint/visitor-keys': 6.17.0 dev: true - /@typescript-eslint/type-utils@6.17.0(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-hDXcWmnbtn4P2B37ka3nil3yi3VCQO2QEB9gBiHJmQp5wmyQWqnjA85+ZcE8c4FqnaB6lBwMrPkgd4aBYz3iNg==} + /@typescript-eslint/scope-manager@6.19.0: + resolution: {integrity: sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.19.0 + '@typescript-eslint/visitor-keys': 6.19.0 + dev: true + + /@typescript-eslint/type-utils@6.19.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-mcvS6WSWbjiSxKCwBcXtOM5pRkPQ6kcDds/juxcy/727IQr3xMEcwr/YLHW2A2+Fp5ql6khjbKBzOyjuPqGi/w==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -877,8 +885,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.17.0(typescript@5.3.3) - '@typescript-eslint/utils': 6.17.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/typescript-estree': 6.19.0(typescript@5.3.3) + '@typescript-eslint/utils': 6.19.0(eslint@8.56.0)(typescript@5.3.3) debug: 4.3.4 eslint: 8.56.0 ts-api-utils: 1.0.3(typescript@5.3.3) @@ -892,6 +900,11 @@ packages: engines: {node: ^16.0.0 || >=18.0.0} dev: true + /@typescript-eslint/types@6.19.0: + resolution: {integrity: sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==} + engines: {node: ^16.0.0 || >=18.0.0} + dev: true + /@typescript-eslint/typescript-estree@6.17.0(typescript@5.3.3): resolution: {integrity: sha512-gVQe+SLdNPfjlJn5VNGhlOhrXz4cajwFd5kAgWtZ9dCZf4XJf8xmgCTLIqec7aha3JwgLI2CK6GY1043FRxZwg==} engines: {node: ^16.0.0 || >=18.0.0} @@ -914,8 +927,30 @@ packages: - supports-color dev: true - /@typescript-eslint/utils@6.17.0(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-LofsSPjN/ITNkzV47hxas2JCsNCEnGhVvocfyOcLzT9c/tSZE7SfhS/iWtzP1lKNOEfLhRTZz6xqI8N2RzweSQ==} + /@typescript-eslint/typescript-estree@6.19.0(typescript@5.3.3): + resolution: {integrity: sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 6.19.0 + '@typescript-eslint/visitor-keys': 6.19.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.3 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@6.19.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-QR41YXySiuN++/dC9UArYOg4X86OAYP83OWTewpVx5ct1IZhjjgTLocj7QNxGhWoTqknsgpl7L+hGygCO+sdYw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -923,9 +958,9 @@ packages: '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.6 - '@typescript-eslint/scope-manager': 6.17.0 - '@typescript-eslint/types': 6.17.0 - '@typescript-eslint/typescript-estree': 6.17.0(typescript@5.3.3) + '@typescript-eslint/scope-manager': 6.19.0 + '@typescript-eslint/types': 6.19.0 + '@typescript-eslint/typescript-estree': 6.19.0(typescript@5.3.3) eslint: 8.56.0 semver: 7.5.4 transitivePeerDependencies: @@ -941,6 +976,14 @@ packages: eslint-visitor-keys: 3.4.3 dev: true + /@typescript-eslint/visitor-keys@6.19.0: + resolution: {integrity: sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.19.0 + eslint-visitor-keys: 3.4.3 + dev: true + /@ungap/structured-clone@1.2.0: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: true From 8855390286b231d21f8ca8d82f7aaddda41d25bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 09:09:51 +0000 Subject: [PATCH 20/39] chore(deps): bump lucide-svelte from 0.307.0 to 0.309.0 in /frontend (#133) --- frontend/package.json | 2 +- frontend/pnpm-lock.yaml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 33aa2039..3aceb95b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -43,7 +43,7 @@ "clsx": "^2.0.0", "cmdk-sv": "^0.0.12", "formsnap": "^0.4.2", - "lucide-svelte": "^0.307.0", + "lucide-svelte": "^0.309.0", "luxon": "^3.4.4", "mode-watcher": "^0.1.2", "motion": "^10.17.0", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 7a6f3e7a..bd3a7c3f 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -18,8 +18,8 @@ dependencies: specifier: ^0.4.2 version: 0.4.2(svelte@4.2.8)(sveltekit-superforms@1.13.1)(zod@3.22.4) lucide-svelte: - specifier: ^0.307.0 - version: 0.307.0(svelte@4.2.8) + specifier: ^0.309.0 + version: 0.309.0(svelte@4.2.8) luxon: specifier: ^3.4.4 version: 3.4.4 @@ -1984,8 +1984,8 @@ packages: yallist: 4.0.0 dev: true - /lucide-svelte@0.307.0(svelte@4.2.8): - resolution: {integrity: sha512-9iGONh7UHEbA3xCTJlT5PYv9GuWrerX4429XWSoLTIjF9UnNCuOmYAu8sA72FmjehrwdJFbvHB+yT/HmCadL4A==} + /lucide-svelte@0.309.0(svelte@4.2.8): + resolution: {integrity: sha512-+t85+5Y696FVDoJHiiMhJGalv+UiWUX46gMudOQfYrVGjsyC2MGSZRNAGAHkdykA4RJSh/wUtSgnTgCmd0Swvw==} peerDependencies: svelte: '>=3 <5' dependencies: From 8d8d5c42b08cc1bc5101f1b7558182035c36119e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 09:15:03 +0000 Subject: [PATCH 21/39] chore(deps-dev): bump @sveltejs/kit from 2.0.1 to 2.3.2 in /frontend (#132) --- frontend/package.json | 2 +- frontend/pnpm-lock.yaml | 44 ++++++++++++++++++++--------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 3aceb95b..a68d583d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,7 +15,7 @@ "devDependencies": { "@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-node": "^2.0.0", - "@sveltejs/kit": "^2.0.0", + "@sveltejs/kit": "^2.3.2", "@sveltejs/vite-plugin-svelte": "^3.0.0", "@types/luxon": "^3.3.7", "@types/nprogress": "^0.2.3", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index bd3a7c3f..d43117d3 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -37,7 +37,7 @@ dependencies: version: 0.3.6(svelte@4.2.8) sveltekit-superforms: specifier: ^1.13.1 - version: 1.13.1(@sveltejs/kit@2.0.1)(svelte@4.2.8)(zod@3.22.4) + version: 1.13.1(@sveltejs/kit@2.3.2)(svelte@4.2.8)(zod@3.22.4) tailwind-merge: specifier: ^2.2.0 version: 2.2.0 @@ -51,13 +51,13 @@ dependencies: devDependencies: '@sveltejs/adapter-auto': specifier: ^3.0.0 - version: 3.0.0(@sveltejs/kit@2.0.1) + version: 3.0.0(@sveltejs/kit@2.3.2) '@sveltejs/adapter-node': specifier: ^2.0.0 - version: 2.0.0(@sveltejs/kit@2.0.1) + version: 2.0.0(@sveltejs/kit@2.3.2) '@sveltejs/kit': - specifier: ^2.0.0 - version: 2.0.1(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10) + specifier: ^2.3.2 + version: 2.3.2(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10) '@sveltejs/vite-plugin-svelte': specifier: ^3.0.0 version: 3.0.1(svelte@4.2.8)(vite@5.0.10) @@ -693,16 +693,16 @@ packages: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} dev: true - /@sveltejs/adapter-auto@3.0.0(@sveltejs/kit@2.0.1): + /@sveltejs/adapter-auto@3.0.0(@sveltejs/kit@2.3.2): resolution: {integrity: sha512-UNWSs/rOReBRfI/xFwSO2WYF1a7PT74SrWOHJmSNLY3Lq+zbH0uuvnlP+TmrTUBvOTkou3WJDjL6lK3n6aOUgQ==} peerDependencies: '@sveltejs/kit': ^2.0.0 dependencies: - '@sveltejs/kit': 2.0.1(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10) + '@sveltejs/kit': 2.3.2(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10) import-meta-resolve: 4.0.0 dev: true - /@sveltejs/adapter-node@2.0.0(@sveltejs/kit@2.0.1): + /@sveltejs/adapter-node@2.0.0(@sveltejs/kit@2.3.2): resolution: {integrity: sha512-js/FBTnwDhr111wCY7b+B30LD8uTUgegRVGG2q6xwzgU8qmj+8/V5E6f/kNmDbyBJH45V6tr8RTKsLvpNScK4g==} peerDependencies: '@sveltejs/kit': ^2.0.0 @@ -710,12 +710,12 @@ packages: '@rollup/plugin-commonjs': 25.0.7(rollup@4.9.1) '@rollup/plugin-json': 6.1.0(rollup@4.9.1) '@rollup/plugin-node-resolve': 15.2.3(rollup@4.9.1) - '@sveltejs/kit': 2.0.1(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10) + '@sveltejs/kit': 2.3.2(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10) rollup: 4.9.1 dev: true - /@sveltejs/kit@2.0.1(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10): - resolution: {integrity: sha512-Pu/YJ5pPy8UgC1LlrlGpL7UK25CKganlLqqO7oYdFGt7eSa+Y2ouJomwiGHatV3uHvVOPGEkcb4O4QtyZQDuGw==} + /@sveltejs/kit@2.3.2(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10): + resolution: {integrity: sha512-AzGWV1TyUSkBuciy06E5NegXndIEgTthDtllv80qynEJFh8bZD62ZxLajiQLOsKGqRDilEQyshDARQxjIqiaqg==} engines: {node: '>=18.13'} hasBin: true requiresBuild: true @@ -729,12 +729,13 @@ packages: cookie: 0.6.0 devalue: 4.3.2 esm-env: 1.0.0 + import-meta-resolve: 4.0.0 kleur: 4.1.5 magic-string: 0.30.5 - mrmime: 1.0.1 + mrmime: 2.0.0 sade: 1.8.1 set-cookie-parser: 2.6.0 - sirv: 2.0.3 + sirv: 2.0.4 svelte: 4.2.8 tiny-glob: 0.2.9 vite: 5.0.10 @@ -1670,7 +1671,7 @@ packages: zod: ^3.22.2 dependencies: svelte: 4.2.8 - sveltekit-superforms: 1.13.1(@sveltejs/kit@2.0.1)(svelte@4.2.8)(zod@3.22.4) + sveltekit-superforms: 1.13.1(@sveltejs/kit@2.3.2)(svelte@4.2.8)(zod@3.22.4) zod: 3.22.4 dev: false @@ -1812,7 +1813,6 @@ packages: /import-meta-resolve@4.0.0: resolution: {integrity: sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==} - dev: true /imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} @@ -2098,8 +2098,8 @@ packages: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} - /mrmime@1.0.1: - resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} + /mrmime@2.0.0: + resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} engines: {node: '>=10'} /ms@2.1.2: @@ -2582,12 +2582,12 @@ packages: engines: {node: '>=14'} dev: true - /sirv@2.0.3: - resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==} + /sirv@2.0.4: + resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==} engines: {node: '>= 10'} dependencies: '@polka/url': 1.0.0-next.24 - mrmime: 1.0.1 + mrmime: 2.0.0 totalist: 3.0.1 /slash@3.0.0: @@ -2798,14 +2798,14 @@ packages: magic-string: 0.30.5 periscopic: 3.1.0 - /sveltekit-superforms@1.13.1(@sveltejs/kit@2.0.1)(svelte@4.2.8)(zod@3.22.4): + /sveltekit-superforms@1.13.1(@sveltejs/kit@2.3.2)(svelte@4.2.8)(zod@3.22.4): resolution: {integrity: sha512-aWHAV6t6+7g37IGFNLRtbRIoqfC3a56Hf4/tTUNFZfwO2mOQGfeMb49WWBLdZZTrFRPGngfNRUxQzeoO3GspnQ==} peerDependencies: '@sveltejs/kit': 1.x || 2.x svelte: 3.x || 4.x zod: 3.x dependencies: - '@sveltejs/kit': 2.0.1(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10) + '@sveltejs/kit': 2.3.2(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10) svelte: 4.2.8 zod: 3.22.4 dev: false From eb81df8953c09d5a3ffe3b0c862b803c654c17de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 09:22:32 +0000 Subject: [PATCH 22/39] chore(deps): bump bits-ui from 0.13.0 to 0.14.0 in /frontend (#130) --- frontend/package.json | 2 +- frontend/pnpm-lock.yaml | 24 +++++++++--------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index a68d583d..023decc9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -39,7 +39,7 @@ }, "type": "module", "dependencies": { - "bits-ui": "^0.13.0", + "bits-ui": "^0.14.0", "clsx": "^2.0.0", "cmdk-sv": "^0.0.12", "formsnap": "^0.4.2", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index d43117d3..78778860 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -6,8 +6,8 @@ settings: dependencies: bits-ui: - specifier: ^0.13.0 - version: 0.13.0(svelte@4.2.8) + specifier: ^0.14.0 + version: 0.14.0(svelte@4.2.8) clsx: specifier: ^2.0.0 version: 2.0.0 @@ -394,12 +394,6 @@ packages: resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} dev: true - /@internationalized/date@3.5.0: - resolution: {integrity: sha512-nw0Q+oRkizBWMioseI8+2TeUPEyopJVz5YxoYVzR0W1v+2YytiYah7s/ot35F149q/xAg4F1gT/6eTd+tsUpFQ==} - dependencies: - '@swc/helpers': 0.5.3 - dev: false - /@internationalized/date@3.5.1: resolution: {integrity: sha512-LUQIfwU9e+Fmutc/DpRTGXSdgYZLBegi4wygCWDSVmUdLTaMHsQyASDiJtREwanwKuQLq0hY76fCJ9J/9I2xOQ==} dependencies: @@ -445,15 +439,15 @@ packages: dependencies: '@floating-ui/core': 1.5.2 '@floating-ui/dom': 1.5.3 - '@internationalized/date': 3.5.0 + '@internationalized/date': 3.5.1 dequal: 2.0.3 focus-trap: 7.5.4 nanoid: 4.0.2 svelte: 4.2.8 dev: false - /@melt-ui/svelte@0.67.0(svelte@4.2.8): - resolution: {integrity: sha512-fd9PsDE6sKbeyExagqH0nOpZEnDqyr2efbkjfmCRRYXVW5vlDEOPaSB+mg4Tjch121102sFH1Od+MlXwmeHy3A==} + /@melt-ui/svelte@0.68.0(svelte@4.2.8): + resolution: {integrity: sha512-/QvA98hnYEodZtHJ71+ocum/WWp30hVNt3F8uiZKnNYwZDaiQYjlyR9AaGKYcZLCe6R68op1mfCzc0kTzJilyA==} peerDependencies: svelte: '>=3 <5' dependencies: @@ -462,7 +456,7 @@ packages: '@internationalized/date': 3.5.1 dequal: 2.0.3 focus-trap: 7.5.4 - nanoid: 4.0.2 + nanoid: 5.0.4 svelte: 4.2.8 dev: false @@ -1130,13 +1124,13 @@ packages: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} - /bits-ui@0.13.0(svelte@4.2.8): - resolution: {integrity: sha512-XMvGKhJQMvSWqaan0eaIx1uAVcFBpImgO6xf+XTb7UhqdzbH0//6be4DeR1nRUpIU70XoU1B7i3lMPrTWg37ng==} + /bits-ui@0.14.0(svelte@4.2.8): + resolution: {integrity: sha512-S1LNwp/Sge1Ro1g0iJ+msIUeJYmoNhXBFShnLDOUOKQe3JG1wd027KxZnVd7db6UWr1IqlJdd4/bbB7NYXeYDw==} peerDependencies: svelte: ^4.0.0 dependencies: '@internationalized/date': 3.5.1 - '@melt-ui/svelte': 0.67.0(svelte@4.2.8) + '@melt-ui/svelte': 0.68.0(svelte@4.2.8) nanoid: 5.0.4 svelte: 4.2.8 dev: false From 8db126d638fde10bb12068b7e56ef685ba66daaa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 09:23:10 +0000 Subject: [PATCH 23/39] chore(deps-dev): bump @sveltejs/adapter-node in /frontend (#138) --- frontend/package.json | 2 +- frontend/pnpm-lock.yaml | 165 +++++++++++++++++++++++++++++++++++----- 2 files changed, 147 insertions(+), 20 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 023decc9..60ad8618 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@sveltejs/adapter-auto": "^3.0.0", - "@sveltejs/adapter-node": "^2.0.0", + "@sveltejs/adapter-node": "^3.0.1", "@sveltejs/kit": "^2.3.2", "@sveltejs/vite-plugin-svelte": "^3.0.0", "@types/luxon": "^3.3.7", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 78778860..5bc09b09 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -53,8 +53,8 @@ devDependencies: specifier: ^3.0.0 version: 3.0.0(@sveltejs/kit@2.3.2) '@sveltejs/adapter-node': - specifier: ^2.0.0 - version: 2.0.0(@sveltejs/kit@2.3.2) + specifier: ^3.0.1 + version: 3.0.1(@sveltejs/kit@2.3.2) '@sveltejs/kit': specifier: ^2.3.2 version: 2.3.2(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10) @@ -528,7 +528,7 @@ packages: /@polka/url@1.0.0-next.24: resolution: {integrity: sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==} - /@rollup/plugin-commonjs@25.0.7(rollup@4.9.1): + /@rollup/plugin-commonjs@25.0.7(rollup@4.9.5): resolution: {integrity: sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ==} engines: {node: '>=14.0.0'} peerDependencies: @@ -537,16 +537,16 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.9.1) + '@rollup/pluginutils': 5.1.0(rollup@4.9.5) commondir: 1.0.1 estree-walker: 2.0.2 glob: 8.1.0 is-reference: 1.2.1 magic-string: 0.30.5 - rollup: 4.9.1 + rollup: 4.9.5 dev: true - /@rollup/plugin-json@6.1.0(rollup@4.9.1): + /@rollup/plugin-json@6.1.0(rollup@4.9.5): resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -555,11 +555,11 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.9.1) - rollup: 4.9.1 + '@rollup/pluginutils': 5.1.0(rollup@4.9.5) + rollup: 4.9.5 dev: true - /@rollup/plugin-node-resolve@15.2.3(rollup@4.9.1): + /@rollup/plugin-node-resolve@15.2.3(rollup@4.9.5): resolution: {integrity: sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==} engines: {node: '>=14.0.0'} peerDependencies: @@ -568,16 +568,16 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.9.1) + '@rollup/pluginutils': 5.1.0(rollup@4.9.5) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-builtin-module: 3.2.1 is-module: 1.0.0 resolve: 1.22.8 - rollup: 4.9.1 + rollup: 4.9.5 dev: true - /@rollup/pluginutils@5.1.0(rollup@4.9.1): + /@rollup/pluginutils@5.1.0(rollup@4.9.5): resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} engines: {node: '>=14.0.0'} peerDependencies: @@ -589,7 +589,7 @@ packages: '@types/estree': 1.0.5 estree-walker: 2.0.2 picomatch: 2.3.1 - rollup: 4.9.1 + rollup: 4.9.5 dev: true /@rollup/rollup-android-arm-eabi@4.9.1: @@ -599,6 +599,14 @@ packages: requiresBuild: true optional: true + /@rollup/rollup-android-arm-eabi@4.9.5: + resolution: {integrity: sha512-idWaG8xeSRCfRq9KpRysDHJ/rEHBEXcHuJ82XY0yYFIWnLMjZv9vF/7DOq8djQ2n3Lk6+3qfSH8AqlmHlmi1MA==} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-android-arm64@4.9.1: resolution: {integrity: sha512-Jto9Fl3YQ9OLsTDWtLFPtaIMSL2kwGyGoVCmPC8Gxvym9TCZm4Sie+cVeblPO66YZsYH8MhBKDMGZ2NDxuk/XQ==} cpu: [arm64] @@ -606,6 +614,14 @@ packages: requiresBuild: true optional: true + /@rollup/rollup-android-arm64@4.9.5: + resolution: {integrity: sha512-f14d7uhAMtsCGjAYwZGv6TwuS3IFaM4ZnGMUn3aCBgkcHAYErhV1Ad97WzBvS2o0aaDv4mVz+syiN0ElMyfBPg==} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-darwin-arm64@4.9.1: resolution: {integrity: sha512-LtYcLNM+bhsaKAIGwVkh5IOWhaZhjTfNOkGzGqdHvhiCUVuJDalvDxEdSnhFzAn+g23wgsycmZk1vbnaibZwwA==} cpu: [arm64] @@ -613,6 +629,14 @@ packages: requiresBuild: true optional: true + /@rollup/rollup-darwin-arm64@4.9.5: + resolution: {integrity: sha512-ndoXeLx455FffL68OIUrVr89Xu1WLzAG4n65R8roDlCoYiQcGGg6MALvs2Ap9zs7AHg8mpHtMpwC8jBBjZrT/w==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-darwin-x64@4.9.1: resolution: {integrity: sha512-KyP/byeXu9V+etKO6Lw3E4tW4QdcnzDG/ake031mg42lob5tN+5qfr+lkcT/SGZaH2PdW4Z1NX9GHEkZ8xV7og==} cpu: [x64] @@ -620,6 +644,14 @@ packages: requiresBuild: true optional: true + /@rollup/rollup-darwin-x64@4.9.5: + resolution: {integrity: sha512-UmElV1OY2m/1KEEqTlIjieKfVwRg0Zwg4PLgNf0s3glAHXBN99KLpw5A5lrSYCa1Kp63czTpVll2MAqbZYIHoA==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-arm-gnueabihf@4.9.1: resolution: {integrity: sha512-Yqz/Doumf3QTKplwGNrCHe/B2p9xqDghBZSlAY0/hU6ikuDVQuOUIpDP/YcmoT+447tsZTmirmjgG3znvSCR0Q==} cpu: [arm] @@ -627,6 +659,14 @@ packages: requiresBuild: true optional: true + /@rollup/rollup-linux-arm-gnueabihf@4.9.5: + resolution: {integrity: sha512-Q0LcU61v92tQB6ae+udZvOyZ0wfpGojtAKrrpAaIqmJ7+psq4cMIhT/9lfV6UQIpeItnq/2QDROhNLo00lOD1g==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-arm64-gnu@4.9.1: resolution: {integrity: sha512-u3XkZVvxcvlAOlQJ3UsD1rFvLWqu4Ef/Ggl40WAVCuogf4S1nJPHh5RTgqYFpCOvuGJ7H5yGHabjFKEZGExk5Q==} cpu: [arm64] @@ -634,6 +674,14 @@ packages: requiresBuild: true optional: true + /@rollup/rollup-linux-arm64-gnu@4.9.5: + resolution: {integrity: sha512-dkRscpM+RrR2Ee3eOQmRWFjmV/payHEOrjyq1VZegRUa5OrZJ2MAxBNs05bZuY0YCtpqETDy1Ix4i/hRqX98cA==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-arm64-musl@4.9.1: resolution: {integrity: sha512-0XSYN/rfWShW+i+qjZ0phc6vZ7UWI8XWNz4E/l+6edFt+FxoEghrJHjX1EY/kcUGCnZzYYRCl31SNdfOi450Aw==} cpu: [arm64] @@ -641,6 +689,14 @@ packages: requiresBuild: true optional: true + /@rollup/rollup-linux-arm64-musl@4.9.5: + resolution: {integrity: sha512-QaKFVOzzST2xzY4MAmiDmURagWLFh+zZtttuEnuNn19AiZ0T3fhPyjPPGwLNdiDT82ZE91hnfJsUiDwF9DClIQ==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-riscv64-gnu@4.9.1: resolution: {integrity: sha512-LmYIO65oZVfFt9t6cpYkbC4d5lKHLYv5B4CSHRpnANq0VZUQXGcCPXHzbCXCz4RQnx7jvlYB1ISVNCE/omz5cw==} cpu: [riscv64] @@ -648,6 +704,14 @@ packages: requiresBuild: true optional: true + /@rollup/rollup-linux-riscv64-gnu@4.9.5: + resolution: {integrity: sha512-HeGqmRJuyVg6/X6MpE2ur7GbymBPS8Np0S/vQFHDmocfORT+Zt76qu+69NUoxXzGqVP1pzaY6QIi0FJWLC3OPA==} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-x64-gnu@4.9.1: resolution: {integrity: sha512-kr8rEPQ6ns/Lmr/hiw8sEVj9aa07gh1/tQF2Y5HrNCCEPiCBGnBUt9tVusrcBBiJfIt1yNaXN6r1CCmpbFEDpg==} cpu: [x64] @@ -655,6 +719,14 @@ packages: requiresBuild: true optional: true + /@rollup/rollup-linux-x64-gnu@4.9.5: + resolution: {integrity: sha512-Dq1bqBdLaZ1Gb/l2e5/+o3B18+8TI9ANlA1SkejZqDgdU/jK/ThYaMPMJpVMMXy2uRHvGKbkz9vheVGdq3cJfA==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-x64-musl@4.9.1: resolution: {integrity: sha512-t4QSR7gN+OEZLG0MiCgPqMWZGwmeHhsM4AkegJ0Kiy6TnJ9vZ8dEIwHw1LcZKhbHxTY32hp9eVCMdR3/I8MGRw==} cpu: [x64] @@ -662,6 +734,14 @@ packages: requiresBuild: true optional: true + /@rollup/rollup-linux-x64-musl@4.9.5: + resolution: {integrity: sha512-ezyFUOwldYpj7AbkwyW9AJ203peub81CaAIVvckdkyH8EvhEIoKzaMFJj0G4qYJ5sw3BpqhFrsCc30t54HV8vg==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-win32-arm64-msvc@4.9.1: resolution: {integrity: sha512-7XI4ZCBN34cb+BH557FJPmh0kmNz2c25SCQeT9OiFWEgf8+dL6ZwJ8f9RnUIit+j01u07Yvrsuu1rZGxJCc51g==} cpu: [arm64] @@ -669,6 +749,14 @@ packages: requiresBuild: true optional: true + /@rollup/rollup-win32-arm64-msvc@4.9.5: + resolution: {integrity: sha512-aHSsMnUw+0UETB0Hlv7B/ZHOGY5bQdwMKJSzGfDfvyhnpmVxLMGnQPGNE9wgqkLUs3+gbG1Qx02S2LLfJ5GaRQ==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-win32-ia32-msvc@4.9.1: resolution: {integrity: sha512-yE5c2j1lSWOH5jp+Q0qNL3Mdhr8WuqCNVjc6BxbVfS5cAS6zRmdiw7ktb8GNpDCEUJphILY6KACoFoRtKoqNQg==} cpu: [ia32] @@ -676,6 +764,14 @@ packages: requiresBuild: true optional: true + /@rollup/rollup-win32-ia32-msvc@4.9.5: + resolution: {integrity: sha512-AiqiLkb9KSf7Lj/o1U3SEP9Zn+5NuVKgFdRIZkvd4N0+bYrTOovVd0+LmYCPQGbocT4kvFyK+LXCDiXPBF3fyA==} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-win32-x64-msvc@4.9.1: resolution: {integrity: sha512-PyJsSsafjmIhVgaI1Zdj7m8BB8mMckFah/xbpplObyHfiXzKcI5UOUXRyOdHW7nz4DpMCuzLnF7v5IWHenCwYA==} cpu: [x64] @@ -683,6 +779,14 @@ packages: requiresBuild: true optional: true + /@rollup/rollup-win32-x64-msvc@4.9.5: + resolution: {integrity: sha512-1q+mykKE3Vot1kaFJIDoUFv5TuW+QQVaf2FmTT9krg86pQrGStOSJJ0Zil7CFagyxDuouTepzt5Y5TVzyajOdQ==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@sinclair/typebox@0.27.8: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} dev: true @@ -696,16 +800,16 @@ packages: import-meta-resolve: 4.0.0 dev: true - /@sveltejs/adapter-node@2.0.0(@sveltejs/kit@2.3.2): - resolution: {integrity: sha512-js/FBTnwDhr111wCY7b+B30LD8uTUgegRVGG2q6xwzgU8qmj+8/V5E6f/kNmDbyBJH45V6tr8RTKsLvpNScK4g==} + /@sveltejs/adapter-node@3.0.1(@sveltejs/kit@2.3.2): + resolution: {integrity: sha512-w1NE6K60WQ7JcNa4IyrhzoyH6MIuGdBL9Bd3EaRNcSYdvCG4mOzXI6kLI3kUhLI8krKGoQeOxLhchC5xLnqo6Q==} peerDependencies: '@sveltejs/kit': ^2.0.0 dependencies: - '@rollup/plugin-commonjs': 25.0.7(rollup@4.9.1) - '@rollup/plugin-json': 6.1.0(rollup@4.9.1) - '@rollup/plugin-node-resolve': 15.2.3(rollup@4.9.1) + '@rollup/plugin-commonjs': 25.0.7(rollup@4.9.5) + '@rollup/plugin-json': 6.1.0(rollup@4.9.5) + '@rollup/plugin-node-resolve': 15.2.3(rollup@4.9.5) '@sveltejs/kit': 2.3.2(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10) - rollup: 4.9.1 + rollup: 4.9.5 dev: true /@sveltejs/kit@2.3.2(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10): @@ -2524,6 +2628,29 @@ packages: '@rollup/rollup-win32-x64-msvc': 4.9.1 fsevents: 2.3.3 + /rollup@4.9.5: + resolution: {integrity: sha512-E4vQW0H/mbNMw2yLSqJyjtkHY9dslf/p0zuT1xehNRqUTBOFMqEjguDvqhXr7N7r/4ttb2jr4T41d3dncmIgbQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + dependencies: + '@types/estree': 1.0.5 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.9.5 + '@rollup/rollup-android-arm64': 4.9.5 + '@rollup/rollup-darwin-arm64': 4.9.5 + '@rollup/rollup-darwin-x64': 4.9.5 + '@rollup/rollup-linux-arm-gnueabihf': 4.9.5 + '@rollup/rollup-linux-arm64-gnu': 4.9.5 + '@rollup/rollup-linux-arm64-musl': 4.9.5 + '@rollup/rollup-linux-riscv64-gnu': 4.9.5 + '@rollup/rollup-linux-x64-gnu': 4.9.5 + '@rollup/rollup-linux-x64-musl': 4.9.5 + '@rollup/rollup-win32-arm64-msvc': 4.9.5 + '@rollup/rollup-win32-ia32-msvc': 4.9.5 + '@rollup/rollup-win32-x64-msvc': 4.9.5 + fsevents: 2.3.3 + dev: true + /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: From b0f2a6ca5c0ed30a932b8cece28ff3df0366a200 Mon Sep 17 00:00:00 2001 From: Ayush Sehrawat Date: Wed, 17 Jan 2024 16:25:58 +0530 Subject: [PATCH 24/39] feat: some more onboarding and form improvements --- frontend/src/hooks.server.ts | 2 +- frontend/src/lib/forms/content-form.svelte | 28 +++++++++++++------ frontend/src/lib/forms/general-form.svelte | 24 +++++++++++----- .../src/lib/forms/media-server-form.svelte | 10 +++++-- frontend/src/lib/forms/scrapers-form.svelte | 26 ++++++++++++----- frontend/src/routes/+layout.svelte | 4 +++ frontend/src/routes/onboarding/+layout.svelte | 8 ++++++ .../src/routes/onboarding/1/+page.server.ts | 23 +++++++++++++-- frontend/src/routes/onboarding/1/+page.svelte | 2 +- frontend/src/routes/settings/+layout.svelte | 4 --- .../routes/settings/general/+page.server.ts | 8 +++++- 11 files changed, 105 insertions(+), 34 deletions(-) create mode 100644 frontend/src/routes/onboarding/+layout.svelte diff --git a/frontend/src/hooks.server.ts b/frontend/src/hooks.server.ts index 04d404ab..3ffdb5c6 100644 --- a/frontend/src/hooks.server.ts +++ b/frontend/src/hooks.server.ts @@ -3,7 +3,7 @@ import { redirect, error } from '@sveltejs/kit'; import { sequence } from '@sveltejs/kit/hooks'; const onboarding: Handle = async ({ event, resolve }) => { - if (!event.url.pathname.startsWith('/onboarding')) { + if (!event.url.pathname.startsWith('/onboarding') && event.request.method === 'GET') { const res = await event.fetch('http://127.0.0.1:8080/services'); const data = await res.json(); if (!data.success || !data.data) { diff --git a/frontend/src/lib/forms/content-form.svelte b/frontend/src/lib/forms/content-form.svelte index 55dbfdd4..bd9cb3ee 100644 --- a/frontend/src/lib/forms/content-form.svelte +++ b/frontend/src/lib/forms/content-form.svelte @@ -18,7 +18,7 @@ export let data: SuperValidated; const contentForm = superForm(data); - const { form, message, delayed } = contentForm; + const { form, message, delayed, errors } = contentForm; const { values: mdblistListsValues, errors: mdblistListsErrors } = arrayProxy( contentForm, @@ -60,7 +60,7 @@ } } - export let actionUrl: string = "?/default" + export let actionUrl: string = '?/default';
- + {#if $errors.overseerr_url} + + {/if}
@@ -130,7 +132,9 @@ spellcheck="false" />
- + {#if $errors.overseerr_api_key} + + {/if}
{/if} @@ -146,7 +150,9 @@ - + {#if $errors.plex_watchlist_rss} + + {/if}
@@ -160,7 +166,9 @@
- + {#if $errors.plex_watchlist_update_interval} + + {/if}
{/if} @@ -196,7 +204,9 @@ spellcheck="false" /> - + {#if $errors.mdblist_api_key} + + {/if}
@@ -210,7 +220,9 @@ - + {#if $errors.mdblist_update_interval} + + {/if}
diff --git a/frontend/src/lib/forms/general-form.svelte b/frontend/src/lib/forms/general-form.svelte index 4c2a0bd0..97f04273 100644 --- a/frontend/src/lib/forms/general-form.svelte +++ b/frontend/src/lib/forms/general-form.svelte @@ -15,7 +15,7 @@ export let data: SuperValidated; const generalForm = superForm(data); - const { form, message, delayed } = generalForm; + const { form, message, delayed, errors } = generalForm; $: if ($message && $page.status === 200) { toast.success($message); @@ -23,7 +23,7 @@ toast.error($message); } - export let actionUrl: string = "?/default" + export let actionUrl: string = '?/default'; - + {#if $errors.debug} + + {/if} @@ -52,7 +54,9 @@ - + {#if $errors.log} + + {/if} @@ -62,7 +66,9 @@ - + {#if $errors.host_path} + + {/if} @@ -72,7 +78,9 @@ - + {#if $errors.container_path} + + {/if} @@ -87,7 +95,9 @@ spellcheck="false" /> - + {#if $errors.realdebrid_api_key} + + {/if} diff --git a/frontend/src/lib/forms/media-server-form.svelte b/frontend/src/lib/forms/media-server-form.svelte index b244d3e0..12b71232 100644 --- a/frontend/src/lib/forms/media-server-form.svelte +++ b/frontend/src/lib/forms/media-server-form.svelte @@ -15,7 +15,7 @@ export let data: SuperValidated; const mediaServerForm = superForm(data); - const { form, message, delayed } = mediaServerForm; + const { form, message, delayed, errors } = mediaServerForm; $: if ($message && $page.status === 200) { toast.success($message); @@ -42,7 +42,9 @@ - + {#if $errors.plex_url} + + {/if} @@ -57,7 +59,9 @@ spellcheck="false" /> - + {#if $errors.plex_token} + + {/if} diff --git a/frontend/src/lib/forms/scrapers-form.svelte b/frontend/src/lib/forms/scrapers-form.svelte index 703004fb..d2bc7d22 100644 --- a/frontend/src/lib/forms/scrapers-form.svelte +++ b/frontend/src/lib/forms/scrapers-form.svelte @@ -16,7 +16,7 @@ export let data: SuperValidated; const scrapersForm = superForm(data); - const { form, message, delayed } = scrapersForm; + const { form, message, delayed, errors } = scrapersForm; $: if ($message && $page.status === 200) { toast.success($message); @@ -43,7 +43,9 @@ - + {#if $errors.after_2} + + {/if} @@ -53,7 +55,9 @@ - + {#if $errors.after_5} + + {/if} @@ -63,7 +67,9 @@ - + {#if $errors.after_10} + + {/if}
@@ -105,7 +111,9 @@ - + {#if $errors.torrentio_filter} + + {/if}
{/if} @@ -126,7 +134,9 @@ spellcheck="false" /> - + {#if $errors.orionoid_api_key} + + {/if} {/if} @@ -142,7 +152,9 @@ - + {#if $errors.jackett_url} + + {/if} {/if} diff --git a/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte index 5261dcd1..f672084e 100644 --- a/frontend/src/routes/+layout.svelte +++ b/frontend/src/routes/+layout.svelte @@ -9,6 +9,10 @@ import * as Command from '$lib/components/ui/command'; import { Settings, CircleDashed, SlidersHorizontal, Info, Layers, Tv } from 'lucide-svelte'; import { page } from '$app/stores'; + import { setContext } from 'svelte'; + import { dev } from '$app/environment'; + + setContext('formDebug', dev); beforeNavigate(() => { NProgress.start(); diff --git a/frontend/src/routes/onboarding/+layout.svelte b/frontend/src/routes/onboarding/+layout.svelte new file mode 100644 index 00000000..15518de9 --- /dev/null +++ b/frontend/src/routes/onboarding/+layout.svelte @@ -0,0 +1,8 @@ + + + + Onboarding | Iceberg + + + diff --git a/frontend/src/routes/onboarding/1/+page.server.ts b/frontend/src/routes/onboarding/1/+page.server.ts index d8467125..1edbc2c3 100644 --- a/frontend/src/routes/onboarding/1/+page.server.ts +++ b/frontend/src/routes/onboarding/1/+page.server.ts @@ -3,9 +3,28 @@ import { fail, error } from '@sveltejs/kit'; import { message, superValidate } from 'sveltekit-superforms/server'; import { generalSettingsSchema } from '$lib/schemas/setting'; import { saveSettings } from '$lib/helpers'; +import { + generalSettingsToGet, + generalSettingsToPass, + generalSettingsToSet +} from '$lib/forms/helpers'; -export const load: PageServerLoad = async () => { - const form = await superValidate(generalSettingsSchema); +export const load: PageServerLoad = async ({ fetch }) => { + async function getPartialSettings() { + try { + const results = await fetch( + `http://127.0.0.1:8080/settings/get/${generalSettingsToGet.join(',')}` + ); + return await results.json(); + } catch (e) { + console.error(e); + error(503, 'Unable to fetch settings data. API is down.'); + } + } + let data: any = await getPartialSettings(); + let toPassToSchema = generalSettingsToPass(data); + + const form = await superValidate(toPassToSchema, generalSettingsSchema, { errors: false }); return { form }; }; diff --git a/frontend/src/routes/onboarding/1/+page.svelte b/frontend/src/routes/onboarding/1/+page.svelte index 22f1fc49..498e1744 100644 --- a/frontend/src/routes/onboarding/1/+page.svelte +++ b/frontend/src/routes/onboarding/1/+page.svelte @@ -16,6 +16,6 @@
- +
diff --git a/frontend/src/routes/settings/+layout.svelte b/frontend/src/routes/settings/+layout.svelte index 2daecaa4..920acb67 100644 --- a/frontend/src/routes/settings/+layout.svelte +++ b/frontend/src/routes/settings/+layout.svelte @@ -5,10 +5,6 @@ import { goto } from '$app/navigation'; import { page } from '$app/stores'; import clsx from 'clsx'; - import { setContext } from 'svelte'; - import { dev } from '$app/environment'; - - setContext('formDebug', dev); const settingsItems: NavItem[] = [ { diff --git a/frontend/src/routes/settings/general/+page.server.ts b/frontend/src/routes/settings/general/+page.server.ts index 2a90e635..4ea82c01 100644 --- a/frontend/src/routes/settings/general/+page.server.ts +++ b/frontend/src/routes/settings/general/+page.server.ts @@ -1,5 +1,5 @@ import type { PageServerLoad, Actions } from './$types'; -import { fail, error } from '@sveltejs/kit'; +import { fail, error, redirect } from '@sveltejs/kit'; import { message, superValidate } from 'sveltekit-superforms/server'; import { generalSettingsSchema } from '$lib/schemas/setting'; import { saveSettings } from '$lib/helpers'; @@ -26,6 +26,8 @@ export const load: PageServerLoad = async ({ fetch }) => { export const actions: Actions = { default: async (event) => { const form = await superValidate(event, generalSettingsSchema); + console.log(event.url.searchParams) + if (!form.valid) { return fail(400, { form @@ -42,6 +44,10 @@ export const actions: Actions = { }); } + if (event.url.searchParams.get('onboarding') === 'true') { + redirect(302, '/onboarding/2'); + } + return message(form, 'Settings saved!'); } }; From 7ac4b88899268b98162aa7cc6c233f2b013ea297 Mon Sep 17 00:00:00 2001 From: Gaisberg Date: Wed, 17 Jan 2024 10:06:47 +0200 Subject: [PATCH 25/39] Dev startup to disabling pickling --- backend/main.py | 6 +++++- backend/program/__init__.py | 8 +++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/backend/main.py b/backend/main.py index f78ced1f..3103d10c 100644 --- a/backend/main.py +++ b/backend/main.py @@ -9,7 +9,11 @@ import threading import time import uvicorn +import argparse +parser = argparse.ArgumentParser() +parser.add_argument('--dev', action='store_true', help='Enable development mode') +args = parser.parse_args() class Server(uvicorn.Server): def install_signal_handlers(self): @@ -29,7 +33,7 @@ def run_in_thread(self): sys.exit(0) app = FastAPI() -app.program = Program() +app.program = Program(args) app.add_middleware( CORSMiddleware, diff --git a/backend/program/__init__.py b/backend/program/__init__.py index d4ce400f..ae415b63 100644 --- a/backend/program/__init__.py +++ b/backend/program/__init__.py @@ -18,9 +18,10 @@ class Program(threading.Thread): """Program class""" - def __init__(self): + def __init__(self, args): super().__init__(name="Iceberg") self.running = False + self.startup_args = args def start(self): logger.info("Iceberg v%s starting!", settings.get("version")) @@ -29,8 +30,9 @@ def start(self): self.data_path = get_data_path() if not os.path.exists(self.data_path): os.mkdir(self.data_path) - self.pickly = Pickly(self.media_items, self.data_path) - self.pickly.start() + if not self.startup_args.dev: + self.pickly = Pickly(self.media_items, self.data_path) + self.pickly.start() self.core_manager = ServiceManager(self.media_items, True, Content, Plex, Scraping, Debrid, Symlinker) if self.validate(): logger.info("Iceberg started!") From 6953784e799dc59713fe93a7add892139e5997b9 Mon Sep 17 00:00:00 2001 From: Spoked <5782630+dreulavelle@users.noreply.github.com> Date: Wed, 17 Jan 2024 18:29:38 -0600 Subject: [PATCH 26/39] feat: Listrr Support Added (#136) * Start Listrr Feature * feat: Listrr ready for review. * small tweaks. rewrite coming later. --------- Co-authored-by: Spoked --- backend/program/content/__init__.py | 5 +- backend/program/content/listrr.py | 107 ++++++++++++++++++++++++++ backend/program/scrapers/torrentio.py | 4 +- backend/program/updaters/trakt.py | 15 +++- backend/utils/default_settings.json | 7 ++ 5 files changed, 131 insertions(+), 7 deletions(-) create mode 100644 backend/program/content/listrr.py diff --git a/backend/program/content/__init__.py b/backend/program/content/__init__.py index 830da64b..da9c49e5 100644 --- a/backend/program/content/__init__.py +++ b/backend/program/content/__init__.py @@ -1,10 +1,11 @@ import threading import time from utils.logger import logger +from utils.service_manager import ServiceManager from .mdblist import Mdblist from .overseerr import Overseerr from .plex_watchlist import PlexWatchlist -from utils.service_manager import ServiceManager +from .listrr import Listrr class Content(threading.Thread): @@ -13,7 +14,7 @@ def __init__(self, media_items): self.initialized = False self.key = "content" self.running = False - self.sm = ServiceManager(media_items, False, Overseerr, Mdblist, PlexWatchlist) + self.sm = ServiceManager(media_items, False, Overseerr, PlexWatchlist, Listrr, Mdblist) if not self.validate(): logger.error("You have no content services enabled, please enable at least one!") return diff --git a/backend/program/content/listrr.py b/backend/program/content/listrr.py new file mode 100644 index 00000000..b60b269a --- /dev/null +++ b/backend/program/content/listrr.py @@ -0,0 +1,107 @@ +"""Mdblist content module""" +from time import time +from typing import Optional +from pydantic import BaseModel +from utils.settings import settings_manager +from utils.logger import logger +from utils.request import get, ping +from requests.exceptions import HTTPError +from program.media.container import MediaItemContainer +from program.updaters.trakt import Updater as Trakt, get_imdbid_from_tmdb, get_imdbid_from_tvdb + + +class ListrrConfig(BaseModel): + enabled: bool + movie_lists: Optional[list] + show_lists: Optional[list] + api_key: Optional[str] + update_interval: int # in seconds + + +class Listrr: + """Content class for Listrr""" + + def __init__(self, media_items: MediaItemContainer): + self.key = "listrr" + self.url = "https://listrr.pro/api" + self.settings = ListrrConfig(**settings_manager.get(f"content.{self.key}")) + self.headers = {"X-Api-Key": self.settings.api_key} + self.initialized = self.validate_settings() + if not self.initialized: + return + self.media_items = media_items + self.updater = Trakt() + self.next_run_time = 0 + logger.info("Listrr initialized!") + + def validate_settings(self) -> bool: + if not self.settings.enabled: + logger.debug("Listrr is set to disabled.") + return False + if self.settings.api_key == "" or len(self.settings.api_key) != 64: + logger.error("Listrr api key is not set.") + return False + try: + response = ping("https://listrr.pro/", additional_headers=self.headers) + return response.ok + except Exception: + logger.error("Listrr url is not reachable.") + return False + + def run(self): + """Fetch media from Listrr and add them to media_items attribute.""" + if time() < self.next_run_time: + return + self.next_run_time = time() + self.settings.update_interval + movie_items = self._get_items_from_Listrr("Movies", self.settings.movie_lists) + show_items = self._get_items_from_Listrr("Shows", self.settings.show_lists) + items = list(set(movie_items + show_items)) + container = self.updater.create_items(items) + for item in container: + item.set("requested_by", "Listrr") + added_items = self.media_items.extend(container) + length = len(added_items) + if length >= 1 and length <= 5: + for item in added_items: + logger.info("Added %s", item.log_string) + elif length > 5: + logger.info("Added %s items", length) + + def _get_items_from_Listrr(self, content_type, content_lists): + """Fetch unique IMDb IDs from Listrr for a given type and list of content.""" + unique_ids = set() + for list_id in content_lists: + page = 1 + total_pages = 1 + while page <= total_pages: + if list_id == "": + break + try: + response = get( + self.url + f"/List/{content_type}/{list_id}/ReleaseDate/Descending/{page}", + additional_headers=self.headers, + ) + if response.is_ok: + total_pages = response.data.pages + for item in response.data.items: + imdb_id = item.imDbId + if imdb_id: + unique_ids.add(imdb_id) + elif content_type == "Shows" and item.tvDbId: + imdb_id = get_imdbid_from_tvdb(item.tvDbId) + if imdb_id: + unique_ids.add(imdb_id) + elif content_type == "Movies" and item.tmDbId: + imdb_id = get_imdbid_from_tmdb(item.tmDbId) + if imdb_id: + unique_ids.add(imdb_id) + else: + break + except HTTPError as e: + if e.response.status_code in [400, 404, 429, 500]: + break + except Exception as e: + logger.error(f"An error occurred: {e}") + break + page += 1 + return list(unique_ids) diff --git a/backend/program/scrapers/torrentio.py b/backend/program/scrapers/torrentio.py index 701e83c3..97195852 100644 --- a/backend/program/scrapers/torrentio.py +++ b/backend/program/scrapers/torrentio.py @@ -20,7 +20,7 @@ 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.second_limiter = RateLimiter(max_calls=1, period=1) + self.second_limiter = RateLimiter(max_calls=1, period=3) self.initialized = self.validate_settings() if not self.initialized: return @@ -76,7 +76,7 @@ def api_scrape(self, item): if identifier: url += f"{identifier}" with self.second_limiter: - response = get(f"{url}.json", retry_if_failed=False) + response = get(f"{url}.json", retry_if_failed=False, timeout=30) if response.is_ok: data = {} if len(response.data.streams) == 0: diff --git a/backend/program/updaters/trakt.py b/backend/program/updaters/trakt.py index 406145da..34119c00 100644 --- a/backend/program/updaters/trakt.py +++ b/backend/program/updaters/trakt.py @@ -1,7 +1,7 @@ """Trakt updater module""" -from datetime import datetime import math import concurrent.futures +from datetime import datetime from os import path from utils.logger import get_data_path, logger from utils.request import get @@ -117,7 +117,6 @@ def _map_item_from_data(data, item_type): # API METHODS - def get_show(imdb_id: str): """Wrapper for trakt.tv API show method""" url = f"https://api.trakt.tv/shows/{imdb_id}/seasons?extended=episodes,full" @@ -130,7 +129,6 @@ def get_show(imdb_id: str): return response.data return [] - def create_item_from_imdb_id(imdb_id: str): """Wrapper for trakt.tv API search method""" url = f"https://api.trakt.tv/search/imdb/{imdb_id}?extended=full" @@ -159,3 +157,14 @@ def get_imdbid_from_tvdb(tvdb_id: str) -> str: if response.is_ok and len(response.data) > 0: return response.data[0].show.ids.imdb return None + +def get_imdbid_from_tmdb(tmdb_id: str) -> str: + """Get IMDb ID from TMDB ID in Trakt""" + url = f"https://api.trakt.tv/search/tmdb/{tmdb_id}?extended=full" + response = get( + url, + additional_headers={"trakt-api-version": "2", "trakt-api-key": CLIENT_ID}, + ) + if response.is_ok and len(response.data) > 0: + return response.data[0].movie.ids.imdb + return None \ No newline at end of file diff --git a/backend/utils/default_settings.json b/backend/utils/default_settings.json index 978bceb9..513fb90b 100644 --- a/backend/utils/default_settings.json +++ b/backend/utils/default_settings.json @@ -25,6 +25,13 @@ "api_key": "", "update_interval": 80 }, + "listrr": { + "enabled": false, + "movie_lists": [""], + "show_lists": [""], + "api_key": "", + "update_interval": 80 + }, "overseerr": { "enabled": true, "url": "http://localhost:5055", From ca99757a0255efb677454ff74d4b91a74af2c7ca Mon Sep 17 00:00:00 2001 From: Spoked <5782630+dreulavelle@users.noreply.github.com> Date: Wed, 17 Jan 2024 19:16:58 -0600 Subject: [PATCH 27/39] 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 --- backend/program/content/overseerr.py | 7 +++- backend/program/scrapers/__init__.py | 10 ----- backend/program/scrapers/jackett.py | 59 ++++++++++++++-------------- backend/program/updaters/trakt.py | 7 +++- backend/utils/default_settings.json | 6 +-- backend/utils/parser.py | 54 ++++++++++++------------- requirements.txt | 3 +- 7 files changed, 72 insertions(+), 74 deletions(-) diff --git a/backend/program/content/overseerr.py b/backend/program/content/overseerr.py index 2ae12cc0..cc959352 100644 --- a/backend/program/content/overseerr.py +++ b/backend/program/content/overseerr.py @@ -5,7 +5,7 @@ from utils.logger import logger from utils.request import get, ping from program.media.container import MediaItemContainer -from program.updaters.trakt import Updater as Trakt +from program.updaters.trakt import Updater as Trakt, get_imdbid_from_tvdb class OverseerrConfig(BaseModel): @@ -102,6 +102,11 @@ def get_imdb_id(self, overseerr_item): imdb_id = response.data.externalIds.imdbId if imdb_id: return imdb_id + if not imdb_id: + # I've seen a case where no imdbId was returned but a tvdbId was + imdb_id = get_imdbid_from_tvdb(response.data.externalIds.tvdbId) + if imdb_id: + return imdb_id self.not_found_ids.append(f"{id_extension}{external_id}") title = getattr(response.data, "title", None) or getattr( response.data, "originalName", None diff --git a/backend/program/scrapers/__init__.py b/backend/program/scrapers/__init__.py index 37734d3a..ae87ac97 100644 --- a/backend/program/scrapers/__init__.py +++ b/backend/program/scrapers/__init__.py @@ -56,13 +56,3 @@ def _needs_new_scrape(self, item) -> bool: > scrape_time or item.scraped_times == 0 ) - def _check_for_title_match(self, item, string) -> bool: - """Check if the title matches PTN title""" - parsed_title = parser.get_title(string) - if item.type == "movie": - return parsed_title == item.title - if item.type == "season": - return parsed_title == item.parent.title - if item.type == "episode": - return parsed_title == item.parent.parent.title - return False \ No newline at end of file diff --git a/backend/program/scrapers/jackett.py b/backend/program/scrapers/jackett.py index 85485e37..90063eb5 100644 --- a/backend/program/scrapers/jackett.py +++ b/backend/program/scrapers/jackett.py @@ -28,8 +28,9 @@ def __init__(self, _): logger.info("Jackett initialized!") def validate_settings(self) -> bool: - """Validate the Jackett settings.""" + """Validate Jackett settings.""" if not self.settings.enabled: + logger.debug("Jackett is set to disabled.") return False if self.settings.url: try: @@ -46,8 +47,7 @@ def validate_settings(self) -> bool: return False def run(self, item): - """Scrape the Jackett API for the given media items - and update the object with scraped streams""" + """Scrape Jackett for the given media items""" try: self._scrape_item(item) except RequestException: @@ -67,29 +67,30 @@ def _scrape_item(self, item): logger.debug("Could not find streams for %s", item.log_string) def api_scrape(self, item): - """Wrapper for torrentio scrape method""" - query = "" - if item.type == "movie": - query = f"&t=movie&imdbid={item.imdb_id}" - if item.type == "season": - query = f"&t=tv-search&imdbid={item.parent.imdb_id}&season={item.number}" - if item.type == "episode": - query = f"&t=tv-search&imdbid={item.parent.parent.imdb_id}&season={item.parent.number}&ep={item.number}" - - url = ( - f"{self.settings.url}/api/v2.0/indexers/all/results/torznab?apikey={self.api_key}{query}" - ) - with self.second_limiter: - response = get(url=url, retry_if_failed=False, timeout=60) - if response.is_ok: - data = {} - for stream in response.data['rss']['channel']['item']: - title = stream.get('title') - for attr in stream.get('torznab:attr', []): - if attr.get('@name') == 'infohash': - infohash = attr.get('@value') - if parser.parse(title) and infohash: - data[infohash] = {"name": title} - if len(data) > 0: - return parser.sort_streams(data) - return {} + """Wrapper for `Jackett` scrape method""" + # https://github.com/Jackett/Jackett/wiki/Jackett-Categories + with self.minute_limiter: + query = "" + if item.type == "movie": + query = f"&cat=2010,2020,2030,2040,2045,2050,2080&t=movie&q={item.title} {item.aired_at.year}" + if item.type == "season": + query = f"&cat=5010,5020,5030,5040,5045,5050,5060,5070,5080&t=tvsearch&q={item.parent.title}&season={item.number}" + if item.type == "episode": + query = f"&cat=5010,5020,5030,5040,5045,5050,5060,5070,5080&t=tvsearch&q={item.parent.parent.title}&season={item.parent.number}&ep={item.number}" + url = (f"{self.settings.url}/api/v2.0/indexers/!status:failing,test:passed/results/torznab?apikey={self.api_key}{query}") + with self.second_limiter: + response = get(url=url, retry_if_failed=False, timeout=60) + if response.is_ok: + data = {} + for stream in response.data['rss']['channel'].get('item', []): + title = stream.get('title') + if parser.check_for_title_match(item, title): + if parser.parse(title): + attr = stream.get('torznab:attr', []) + infohash_attr = next((a for a in attr if a.get('@name') == 'infohash'), None) + if infohash_attr: + infohash = infohash_attr.get('@value') + data[infohash] = {"name": title} + if len(data) > 0: + return parser.sort_streams(data) + return {} diff --git a/backend/program/updaters/trakt.py b/backend/program/updaters/trakt.py index 34119c00..10a61b2d 100644 --- a/backend/program/updaters/trakt.py +++ b/backend/program/updaters/trakt.py @@ -19,7 +19,6 @@ def __init__(self): self.pkl_file = path.join(get_data_path(), "trakt_data.pkl") self.ids = [] - def create_items(self, imdb_ids): """Update media items to state where they can start downloading""" self.trakt_data.load(self.pkl_file) @@ -85,11 +84,13 @@ def _map_item_from_data(data, item_type): if getattr(data, "released", None): released_at = data.released formatted_aired_at = datetime.strptime(released_at, "%Y-%m-%d") + is_anime = "anime" in getattr(data, "genres", []) item = { "title": getattr(data, "title", None), # 'Game of Thrones' "year": getattr(data, "year", None), # 2011 "status": getattr(data, "status", None), # 'ended', 'released', 'returning series' "aired_at": formatted_aired_at, # datetime.datetime(2011, 4, 17, 0, 0) + "is_anime": is_anime, # True" "imdb_id": getattr(data.ids, "imdb", None), # 'tt0496424' "tvdb_id": getattr(data.ids, "tvdb", None), # 79488 "tmdb_id": getattr(data.ids, "tmdb", None), # 1399 @@ -155,7 +156,9 @@ def get_imdbid_from_tvdb(tvdb_id: str) -> str: additional_headers={"trakt-api-version": "2", "trakt-api-key": CLIENT_ID}, ) if response.is_ok and len(response.data) > 0: - return response.data[0].show.ids.imdb + # noticing there are multiple results for some TVDB IDs + # TODO: Need to check item.type and compare to the resulting types.. + return response.data[0].show.ids.imdb return None def get_imdbid_from_tmdb(tmdb_id: str) -> str: diff --git a/backend/utils/default_settings.json b/backend/utils/default_settings.json index 513fb90b..2f13f05f 100644 --- a/backend/utils/default_settings.json +++ b/backend/utils/default_settings.json @@ -1,5 +1,5 @@ { - "version": "0.4.0", + "version": "0.4.3", "debug": true, "log": true, "symlink": { @@ -59,8 +59,6 @@ "language": ["English"], "include_4k": false, "highest_quality": false, - "repack_proper": true, - "dual_audio": true, - "av1_audio": true + "repack_proper": true } } diff --git a/backend/utils/parser.py b/backend/utils/parser.py index 05a7b582..3047510c 100644 --- a/backend/utils/parser.py +++ b/backend/utils/parser.py @@ -3,6 +3,7 @@ from typing import List from pydantic import BaseModel from utils.settings import settings_manager +from thefuzz import fuzz class ParserConfig(BaseModel): @@ -10,8 +11,6 @@ class ParserConfig(BaseModel): include_4k: bool highest_quality: bool repack_proper: bool - dual_audio: bool # This sometimes doesnt work depending on if other audio is in the title - av1_audio: bool class Parser: @@ -26,33 +25,15 @@ def __init__(self): "VODRip", "DVD-R", "DSRip", "BRRip"] self.quality = [None, "Blu-ray", "WEB-DL", "WEBRip", "HDRip", "HDTVRip", "BDRip", "Pay-Per-View Rip"] - self.audio = [None, "AAC", "AAC 2.0", "AAC 5.1", "FLAC", "AVC", "Custom"] - self.network = ["Apple TV+", "Amazon Studios", "Netflix", - "Nickelodeon", "YouTube Premium", "Disney Plus", - "DisneyNOW", "HBO Max", "HBO", "Hulu Networks", - "DC Universe", "Adult Swim", "Comedy Central", - "Peacock", "AMC", "PBS", "Crunchyroll", - "Syndication", "Hallmark", "BBC", "VICE", - "MSNBC", "Crave"] # Will probably be used later in `Versions` self.validate_settings() def validate_settings(self): if self.settings.highest_quality: self.resolution = ["UHD", "2160p", "4K", "1080p", "720p"] - self.audio += ["Dolby TrueHD", "Dolby Atmos", - "Dolby Digital EX", "Dolby Digital Plus", - "Dolby Digital 5.1", "Dolby Digital 7.1", - "Dolby Digital Plus 5.1", "Dolby Digital Plus 7.1" - "DTS-HD MA", "DTS-HD MA", "DTS-HD", "DTS-HD MA 5.1" - "DTS-EX", "DTS:X", "DTS", "5.1", "7.1"] elif self.settings.include_4k: self.resolution = ["2160p", "4K", "1080p", "720p"] else: self.resolution = ["1080p", "720p"] - if self.settings.dual_audio: - self.audio += ["Dual"] - if not self.settings.av1_audio: - self.unwanted_codec += ["AV1"] # Not all devices support this codec def _parse(self, string): parse = PTN.parse(string) @@ -127,7 +108,6 @@ def _is_highest_quality(self, string) -> bool: return any([ parsed.get("hdr", False), parsed.get("remux", False), - parsed.get("audio", False) in self.audio, parsed.get("resolution", False) in ["UHD", "2160p", "4K"], parsed.get("upscaled", False) ]) @@ -143,15 +123,21 @@ def _is_repack_or_proper(self, string) -> bool: def _is_dual_audio(self, string) -> bool: """Check if content is `dual audio`.""" - if self.settings.dual_audio: - parsed = self._parse(string) - return parsed.get("audio") == "Dual" or \ - re.search(r"((dual.audio)|(english|eng)\W+(dub|audio))", string, flags=re.IGNORECASE) is not None + parsed = self._parse(string) + return parsed.get("audio") == "Dual" or \ + re.search(r"((dual.audio)|(english|eng)\W+(dub|audio))", string, flags=re.IGNORECASE) is not None def _is_network(self, string) -> bool: """Check if content is from a `network`.""" parsed = self._parse(string) - return parsed.get("network", False) in self.network + network = ["Apple TV+", "Amazon Studios", "Netflix", + "Nickelodeon", "YouTube Premium", "Disney Plus", + "DisneyNOW", "HBO Max", "HBO", "Hulu Networks", + "DC Universe", "Adult Swim", "Comedy Central", + "Peacock", "AMC", "PBS", "Crunchyroll", + "Syndication", "Hallmark", "BBC", "VICE", + "MSNBC", "Crave"] # Will probably be used later in `Versions` + return (parsed.get("network", False)) in network def sort_streams(self, streams: dict) -> dict: """Sorts streams based on user preferences.""" @@ -174,7 +160,6 @@ def parse(self, string) -> bool: return ( parse["resolution"] in self.resolution and parse["language"] in self.language - and parse["audio"] in self.audio and not parse["quality"] in self.unwanted_quality and not parse["codec"] in self.unwanted_codec ) @@ -184,4 +169,19 @@ def get_title(self, string) -> str: parse = self._parse(string) return parse["title"] + def check_for_title_match(self, item, string, threshold=94) -> bool: + """Check if the title matches PTN title using fuzzy matching.""" + # TODO1: remove special chars from parsed_title and target_title. Could improve matching. + # TODO2: We should be checking aliases as well for titles. Anime only probably? + parsed_title = self.get_title(string) + if item.type == "movie": + target_title = item.title + elif item.type == "season": + target_title = item.parent.title + elif item.type == "episode": + target_title = item.parent.parent.title + else: + return False + return fuzz.ratio(parsed_title.lower(), target_title.lower()) >= threshold + parser = Parser() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index f19ab6e4..7c090cdf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,4 +7,5 @@ pathos pydantic fastapi uvicorn[standard] -parse-torrent-title \ No newline at end of file +parse-torrent-title +thefuzz \ No newline at end of file From fbdd7638cc9db51f8db31bd1874c0c270491e35f Mon Sep 17 00:00:00 2001 From: Gaisberg Date: Thu, 18 Jan 2024 21:00:50 +0200 Subject: [PATCH 28/39] Avoid [None] if empty content service --- backend/program/content/mdblist.py | 2 +- backend/program/content/overseerr.py | 2 +- backend/program/content/plex_watchlist.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/program/content/mdblist.py b/backend/program/content/mdblist.py index e58fd67a..d6896429 100644 --- a/backend/program/content/mdblist.py +++ b/backend/program/content/mdblist.py @@ -55,7 +55,7 @@ def run(self): items += self._get_items_from_list( list_id, self.settings.api_key ) - new_items = [item for item in items if item not in self.media_items] + new_items = [item for item in items if item not in self.media_items] or [] container = self.updater.create_items(new_items) for item in container: item.set("requested_by", "Mdblist") diff --git a/backend/program/content/overseerr.py b/backend/program/content/overseerr.py index cc959352..a33c00e9 100644 --- a/backend/program/content/overseerr.py +++ b/backend/program/content/overseerr.py @@ -51,7 +51,7 @@ def run(self): """Fetch media from overseerr and add them to media_items attribute if they are not already there""" items = self._get_items_from_overseerr(10000) - new_items = [item for item in items if item not in self.media_items] + new_items = [item for item in items if item not in self.media_items] or [] container = self.updater.create_items(new_items) for item in container: item.set("requested_by", "Overseerr") diff --git a/backend/program/content/plex_watchlist.py b/backend/program/content/plex_watchlist.py index e07c5793..9abcbf67 100644 --- a/backend/program/content/plex_watchlist.py +++ b/backend/program/content/plex_watchlist.py @@ -51,7 +51,7 @@ def run(self): """Fetch media from Plex Watchlist and add them to media_items attribute if they are not already there""" items = self._create_unique_list() - new_items = [item for item in items if item not in self.media_items] + new_items = [item for item in items if item not in self.media_items] or [] container = self.updater.create_items(new_items) for item in container: item.set("requested_by", "Plex Watchlist") From 2426ba193cc0103f6870a0e36cdac7ec455220a3 Mon Sep 17 00:00:00 2001 From: Spoked <5782630+dreulavelle@users.noreply.github.com> Date: Sat, 20 Jan 2024 03:51:15 -0600 Subject: [PATCH 29/39] fix: handle bad quality manually in parser (#145) Co-authored-by: Spoked --- backend/utils/parser.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/backend/utils/parser.py b/backend/utils/parser.py index 3047510c..0e48331d 100644 --- a/backend/utils/parser.py +++ b/backend/utils/parser.py @@ -20,9 +20,6 @@ def __init__(self): self.language = self.settings.language or ["English"] self.resolution = ["1080p", "720p"] self.unwanted_codec = ["H.263", "Xvid"] # Bad for transcoding - self.unwanted_quality = ["Cam", "Telesync", "Telecine", "Screener", - "DVDSCR", "Workprint", "DVD-Rip", "TVRip", - "VODRip", "DVD-R", "DSRip", "BRRip"] self.quality = [None, "Blu-ray", "WEB-DL", "WEBRip", "HDRip", "HDTVRip", "BDRip", "Pay-Per-View Rip"] self.validate_settings() @@ -139,6 +136,22 @@ def _is_network(self, string) -> bool: "MSNBC", "Crave"] # Will probably be used later in `Versions` return (parsed.get("network", False)) in network + def _is_unwanted_quality(string) -> bool: + """Check if string has an `unwanted` quality.""" + patterns = [ + re.compile(r"(?:HD)?CAM(?:-?Rip)?", re.IGNORECASE), + re.compile(r"(?:HD)?TS|TELESYNC|PDVD|PreDVDRip", re.IGNORECASE), + re.compile(r"(?:HD)?TC|TELECINE", re.IGNORECASE), + re.compile(r"WEB[ -]?Cap", re.IGNORECASE), + re.compile(r"WP|WORKPRINT", re.IGNORECASE), + re.compile(r"(?:DVD)?SCR(?:EENER)?|BDSCR", re.IGNORECASE), + re.compile(r"DVD-?(?:Rip|Mux)", re.IGNORECASE), + re.compile(r"DVDR|DVD-Full|Full-rip", re.IGNORECASE), + re.compile(r"D?TVRip|DVBRip", re.IGNORECASE), + re.compile(r"VODR(?:ip)?", re.IGNORECASE) + ] + return any(pattern.search(string) for pattern in patterns) + def sort_streams(self, streams: dict) -> dict: """Sorts streams based on user preferences.""" def sorting_key(item): @@ -161,7 +174,7 @@ def parse(self, string) -> bool: parse["resolution"] in self.resolution and parse["language"] in self.language and not parse["quality"] in self.unwanted_quality - and not parse["codec"] in self.unwanted_codec + and not self._is_unwanted_quality(string) ) def get_title(self, string) -> str: From 5c07c1fbb36d874fa1da4703191595b29487590c Mon Sep 17 00:00:00 2001 From: Ayush Sehrawat Date: Sun, 21 Jan 2024 12:55:09 +0530 Subject: [PATCH 30/39] deps: updated deps due to security updateS --- frontend/package.json | 2 +- frontend/pnpm-lock.yaml | 173 ++++++---------------------------------- 2 files changed, 25 insertions(+), 150 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 60ad8618..920b66fc 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -34,7 +34,7 @@ "tailwindcss": "^3.4.0", "tslib": "^2.4.1", "typescript": "^5.0.0", - "vite": "^5.0.0", + "vite": "^5.0.12", "vitest": "^1.1.0" }, "type": "module", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 5bc09b09..4ca8d62e 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -57,10 +57,10 @@ devDependencies: version: 3.0.1(@sveltejs/kit@2.3.2) '@sveltejs/kit': specifier: ^2.3.2 - version: 2.3.2(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10) + version: 2.3.2(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.12) '@sveltejs/vite-plugin-svelte': specifier: ^3.0.0 - version: 3.0.1(svelte@4.2.8)(vite@5.0.10) + version: 3.0.1(svelte@4.2.8)(vite@5.0.12) '@types/luxon': specifier: ^3.3.7 version: 3.3.7 @@ -113,8 +113,8 @@ devDependencies: specifier: ^5.0.0 version: 5.3.3 vite: - specifier: ^5.0.0 - version: 5.0.10 + specifier: ^5.0.12 + version: 5.0.12 vitest: specifier: ^1.1.0 version: 1.1.0 @@ -592,26 +592,11 @@ packages: rollup: 4.9.5 dev: true - /@rollup/rollup-android-arm-eabi@4.9.1: - resolution: {integrity: sha512-6vMdBZqtq1dVQ4CWdhFwhKZL6E4L1dV6jUjuBvsavvNJSppzi6dLBbuV+3+IyUREaj9ZFvQefnQm28v4OCXlig==} - cpu: [arm] - os: [android] - requiresBuild: true - optional: true - /@rollup/rollup-android-arm-eabi@4.9.5: resolution: {integrity: sha512-idWaG8xeSRCfRq9KpRysDHJ/rEHBEXcHuJ82XY0yYFIWnLMjZv9vF/7DOq8djQ2n3Lk6+3qfSH8AqlmHlmi1MA==} cpu: [arm] os: [android] requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-android-arm64@4.9.1: - resolution: {integrity: sha512-Jto9Fl3YQ9OLsTDWtLFPtaIMSL2kwGyGoVCmPC8Gxvym9TCZm4Sie+cVeblPO66YZsYH8MhBKDMGZ2NDxuk/XQ==} - cpu: [arm64] - os: [android] - requiresBuild: true optional: true /@rollup/rollup-android-arm64@4.9.5: @@ -619,14 +604,6 @@ packages: cpu: [arm64] os: [android] requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-darwin-arm64@4.9.1: - resolution: {integrity: sha512-LtYcLNM+bhsaKAIGwVkh5IOWhaZhjTfNOkGzGqdHvhiCUVuJDalvDxEdSnhFzAn+g23wgsycmZk1vbnaibZwwA==} - cpu: [arm64] - os: [darwin] - requiresBuild: true optional: true /@rollup/rollup-darwin-arm64@4.9.5: @@ -634,14 +611,6 @@ packages: cpu: [arm64] os: [darwin] requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-darwin-x64@4.9.1: - resolution: {integrity: sha512-KyP/byeXu9V+etKO6Lw3E4tW4QdcnzDG/ake031mg42lob5tN+5qfr+lkcT/SGZaH2PdW4Z1NX9GHEkZ8xV7og==} - cpu: [x64] - os: [darwin] - requiresBuild: true optional: true /@rollup/rollup-darwin-x64@4.9.5: @@ -649,14 +618,6 @@ packages: cpu: [x64] os: [darwin] requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-arm-gnueabihf@4.9.1: - resolution: {integrity: sha512-Yqz/Doumf3QTKplwGNrCHe/B2p9xqDghBZSlAY0/hU6ikuDVQuOUIpDP/YcmoT+447tsZTmirmjgG3znvSCR0Q==} - cpu: [arm] - os: [linux] - requiresBuild: true optional: true /@rollup/rollup-linux-arm-gnueabihf@4.9.5: @@ -664,14 +625,6 @@ packages: cpu: [arm] os: [linux] requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-arm64-gnu@4.9.1: - resolution: {integrity: sha512-u3XkZVvxcvlAOlQJ3UsD1rFvLWqu4Ef/Ggl40WAVCuogf4S1nJPHh5RTgqYFpCOvuGJ7H5yGHabjFKEZGExk5Q==} - cpu: [arm64] - os: [linux] - requiresBuild: true optional: true /@rollup/rollup-linux-arm64-gnu@4.9.5: @@ -679,14 +632,6 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-arm64-musl@4.9.1: - resolution: {integrity: sha512-0XSYN/rfWShW+i+qjZ0phc6vZ7UWI8XWNz4E/l+6edFt+FxoEghrJHjX1EY/kcUGCnZzYYRCl31SNdfOi450Aw==} - cpu: [arm64] - os: [linux] - requiresBuild: true optional: true /@rollup/rollup-linux-arm64-musl@4.9.5: @@ -694,14 +639,6 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-riscv64-gnu@4.9.1: - resolution: {integrity: sha512-LmYIO65oZVfFt9t6cpYkbC4d5lKHLYv5B4CSHRpnANq0VZUQXGcCPXHzbCXCz4RQnx7jvlYB1ISVNCE/omz5cw==} - cpu: [riscv64] - os: [linux] - requiresBuild: true optional: true /@rollup/rollup-linux-riscv64-gnu@4.9.5: @@ -709,14 +646,6 @@ packages: cpu: [riscv64] os: [linux] requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-x64-gnu@4.9.1: - resolution: {integrity: sha512-kr8rEPQ6ns/Lmr/hiw8sEVj9aa07gh1/tQF2Y5HrNCCEPiCBGnBUt9tVusrcBBiJfIt1yNaXN6r1CCmpbFEDpg==} - cpu: [x64] - os: [linux] - requiresBuild: true optional: true /@rollup/rollup-linux-x64-gnu@4.9.5: @@ -724,14 +653,6 @@ packages: cpu: [x64] os: [linux] requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-linux-x64-musl@4.9.1: - resolution: {integrity: sha512-t4QSR7gN+OEZLG0MiCgPqMWZGwmeHhsM4AkegJ0Kiy6TnJ9vZ8dEIwHw1LcZKhbHxTY32hp9eVCMdR3/I8MGRw==} - cpu: [x64] - os: [linux] - requiresBuild: true optional: true /@rollup/rollup-linux-x64-musl@4.9.5: @@ -739,14 +660,6 @@ packages: cpu: [x64] os: [linux] requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-win32-arm64-msvc@4.9.1: - resolution: {integrity: sha512-7XI4ZCBN34cb+BH557FJPmh0kmNz2c25SCQeT9OiFWEgf8+dL6ZwJ8f9RnUIit+j01u07Yvrsuu1rZGxJCc51g==} - cpu: [arm64] - os: [win32] - requiresBuild: true optional: true /@rollup/rollup-win32-arm64-msvc@4.9.5: @@ -754,14 +667,6 @@ packages: cpu: [arm64] os: [win32] requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-win32-ia32-msvc@4.9.1: - resolution: {integrity: sha512-yE5c2j1lSWOH5jp+Q0qNL3Mdhr8WuqCNVjc6BxbVfS5cAS6zRmdiw7ktb8GNpDCEUJphILY6KACoFoRtKoqNQg==} - cpu: [ia32] - os: [win32] - requiresBuild: true optional: true /@rollup/rollup-win32-ia32-msvc@4.9.5: @@ -769,14 +674,6 @@ packages: cpu: [ia32] os: [win32] requiresBuild: true - dev: true - optional: true - - /@rollup/rollup-win32-x64-msvc@4.9.1: - resolution: {integrity: sha512-PyJsSsafjmIhVgaI1Zdj7m8BB8mMckFah/xbpplObyHfiXzKcI5UOUXRyOdHW7nz4DpMCuzLnF7v5IWHenCwYA==} - cpu: [x64] - os: [win32] - requiresBuild: true optional: true /@rollup/rollup-win32-x64-msvc@4.9.5: @@ -784,7 +681,6 @@ packages: cpu: [x64] os: [win32] requiresBuild: true - dev: true optional: true /@sinclair/typebox@0.27.8: @@ -796,7 +692,7 @@ packages: peerDependencies: '@sveltejs/kit': ^2.0.0 dependencies: - '@sveltejs/kit': 2.3.2(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10) + '@sveltejs/kit': 2.3.2(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.12) import-meta-resolve: 4.0.0 dev: true @@ -808,11 +704,11 @@ packages: '@rollup/plugin-commonjs': 25.0.7(rollup@4.9.5) '@rollup/plugin-json': 6.1.0(rollup@4.9.5) '@rollup/plugin-node-resolve': 15.2.3(rollup@4.9.5) - '@sveltejs/kit': 2.3.2(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10) + '@sveltejs/kit': 2.3.2(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.12) rollup: 4.9.5 dev: true - /@sveltejs/kit@2.3.2(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10): + /@sveltejs/kit@2.3.2(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.12): resolution: {integrity: sha512-AzGWV1TyUSkBuciy06E5NegXndIEgTthDtllv80qynEJFh8bZD62ZxLajiQLOsKGqRDilEQyshDARQxjIqiaqg==} engines: {node: '>=18.13'} hasBin: true @@ -822,7 +718,7 @@ packages: svelte: ^4.0.0 || ^5.0.0-next.0 vite: ^5.0.3 dependencies: - '@sveltejs/vite-plugin-svelte': 3.0.1(svelte@4.2.8)(vite@5.0.10) + '@sveltejs/vite-plugin-svelte': 3.0.1(svelte@4.2.8)(vite@5.0.12) '@types/cookie': 0.6.0 cookie: 0.6.0 devalue: 4.3.2 @@ -836,9 +732,9 @@ packages: sirv: 2.0.4 svelte: 4.2.8 tiny-glob: 0.2.9 - vite: 5.0.10 + vite: 5.0.12 - /@sveltejs/vite-plugin-svelte-inspector@2.0.0(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10): + /@sveltejs/vite-plugin-svelte-inspector@2.0.0(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.12): resolution: {integrity: sha512-gjr9ZFg1BSlIpfZ4PRewigrvYmHWbDrq2uvvPB1AmTWKuM+dI1JXQSUu2pIrYLb/QncyiIGkFDFKTwJ0XqQZZg==} engines: {node: ^18.0.0 || >=20} peerDependencies: @@ -846,29 +742,29 @@ packages: svelte: ^4.0.0 || ^5.0.0-next.0 vite: ^5.0.0 dependencies: - '@sveltejs/vite-plugin-svelte': 3.0.1(svelte@4.2.8)(vite@5.0.10) + '@sveltejs/vite-plugin-svelte': 3.0.1(svelte@4.2.8)(vite@5.0.12) debug: 4.3.4 svelte: 4.2.8 - vite: 5.0.10 + vite: 5.0.12 transitivePeerDependencies: - supports-color - /@sveltejs/vite-plugin-svelte@3.0.1(svelte@4.2.8)(vite@5.0.10): + /@sveltejs/vite-plugin-svelte@3.0.1(svelte@4.2.8)(vite@5.0.12): resolution: {integrity: sha512-CGURX6Ps+TkOovK6xV+Y2rn8JKa8ZPUHPZ/NKgCxAmgBrXReavzFl8aOSCj3kQ1xqT7yGJj53hjcV/gqwDAaWA==} engines: {node: ^18.0.0 || >=20} peerDependencies: svelte: ^4.0.0 || ^5.0.0-next.0 vite: ^5.0.0 dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 2.0.0(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10) + '@sveltejs/vite-plugin-svelte-inspector': 2.0.0(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.12) debug: 4.3.4 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.5 svelte: 4.2.8 svelte-hmr: 0.15.3(svelte@4.2.8) - vite: 5.0.10 - vitefu: 0.2.5(vite@5.0.10) + vite: 5.0.12 + vitefu: 0.2.5(vite@5.0.12) transitivePeerDependencies: - supports-color @@ -2608,26 +2504,6 @@ packages: glob: 7.2.3 dev: true - /rollup@4.9.1: - resolution: {integrity: sha512-pgPO9DWzLoW/vIhlSoDByCzcpX92bKEorbgXuZrqxByte3JFk2xSW2JEeAcyLc9Ru9pqcNNW+Ob7ntsk2oT/Xw==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.9.1 - '@rollup/rollup-android-arm64': 4.9.1 - '@rollup/rollup-darwin-arm64': 4.9.1 - '@rollup/rollup-darwin-x64': 4.9.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.9.1 - '@rollup/rollup-linux-arm64-gnu': 4.9.1 - '@rollup/rollup-linux-arm64-musl': 4.9.1 - '@rollup/rollup-linux-riscv64-gnu': 4.9.1 - '@rollup/rollup-linux-x64-gnu': 4.9.1 - '@rollup/rollup-linux-x64-musl': 4.9.1 - '@rollup/rollup-win32-arm64-msvc': 4.9.1 - '@rollup/rollup-win32-ia32-msvc': 4.9.1 - '@rollup/rollup-win32-x64-msvc': 4.9.1 - fsevents: 2.3.3 - /rollup@4.9.5: resolution: {integrity: sha512-E4vQW0H/mbNMw2yLSqJyjtkHY9dslf/p0zuT1xehNRqUTBOFMqEjguDvqhXr7N7r/4ttb2jr4T41d3dncmIgbQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -2649,7 +2525,6 @@ packages: '@rollup/rollup-win32-ia32-msvc': 4.9.5 '@rollup/rollup-win32-x64-msvc': 4.9.5 fsevents: 2.3.3 - dev: true /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -2926,7 +2801,7 @@ packages: svelte: 3.x || 4.x zod: 3.x dependencies: - '@sveltejs/kit': 2.3.2(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10) + '@sveltejs/kit': 2.3.2(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.12) svelte: 4.2.8 zod: 3.22.4 dev: false @@ -3101,7 +2976,7 @@ packages: debug: 4.3.4 pathe: 1.1.1 picocolors: 1.0.0 - vite: 5.0.10 + vite: 5.0.12 transitivePeerDependencies: - '@types/node' - less @@ -3113,8 +2988,8 @@ packages: - terser dev: true - /vite@5.0.10: - resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==} + /vite@5.0.12: + resolution: {integrity: sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -3143,11 +3018,11 @@ packages: dependencies: esbuild: 0.19.9 postcss: 8.4.32 - rollup: 4.9.1 + rollup: 4.9.5 optionalDependencies: fsevents: 2.3.3 - /vitefu@0.2.5(vite@5.0.10): + /vitefu@0.2.5(vite@5.0.12): resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} peerDependencies: vite: ^3.0.0 || ^4.0.0 || ^5.0.0 @@ -3155,7 +3030,7 @@ packages: vite: optional: true dependencies: - vite: 5.0.10 + vite: 5.0.12 /vitest@1.1.0: resolution: {integrity: sha512-oDFiCrw7dd3Jf06HoMtSRARivvyjHJaTxikFxuqJjO76U436PqlVw1uLn7a8OSPrhSfMGVaRakKpA2lePdw79A==} @@ -3200,7 +3075,7 @@ packages: strip-literal: 1.3.0 tinybench: 2.5.1 tinypool: 0.8.1 - vite: 5.0.10 + vite: 5.0.12 vite-node: 1.1.0 why-is-node-running: 2.2.2 transitivePeerDependencies: From be210628c28bbf3661bde5202d8ba94e20c30bff Mon Sep 17 00:00:00 2001 From: Ayush Sehrawat Date: Sun, 21 Jan 2024 17:20:23 +0530 Subject: [PATCH 31/39] feat: added more onboarding steps, some bugs also introduced --- frontend/package.json | 2 + frontend/pnpm-lock.yaml | 15 +++ frontend/src/lib/forms/content-form.svelte | 2 +- frontend/src/lib/forms/general-form.svelte | 2 +- .../src/lib/forms/media-server-form.svelte | 100 +++++++++++++++++- frontend/src/lib/forms/scrapers-form.svelte | 2 +- frontend/src/routes/onboarding/+page.svelte | 4 + .../src/routes/onboarding/2/+page.server.ts | 30 ++++++ frontend/src/routes/onboarding/2/+page.svelte | 21 ++++ .../src/routes/onboarding/3/+page.server.ts | 30 ++++++ frontend/src/routes/onboarding/3/+page.svelte | 21 ++++ .../routes/settings/content/+page.server.ts | 16 ++- .../settings/mediaserver/+page.server.ts | 16 ++- 13 files changed, 248 insertions(+), 13 deletions(-) create mode 100644 frontend/src/routes/onboarding/2/+page.server.ts create mode 100644 frontend/src/routes/onboarding/2/+page.svelte create mode 100644 frontend/src/routes/onboarding/3/+page.server.ts create mode 100644 frontend/src/routes/onboarding/3/+page.svelte diff --git a/frontend/package.json b/frontend/package.json index 920b66fc..6fc8f6f6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,6 +19,7 @@ "@sveltejs/vite-plugin-svelte": "^3.0.0", "@types/luxon": "^3.3.7", "@types/nprogress": "^0.2.3", + "@types/uuid": "^9.0.7", "@typescript-eslint/eslint-plugin": "^6.19.0", "@typescript-eslint/parser": "^6.17.0", "autoprefixer": "^10.4.14", @@ -52,6 +53,7 @@ "sveltekit-superforms": "^1.13.1", "tailwind-merge": "^2.2.0", "tailwind-variants": "^0.1.18", + "uuid": "^9.0.1", "zod": "^3.22.4" } } \ No newline at end of file diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 4ca8d62e..45417f3f 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -44,6 +44,9 @@ dependencies: tailwind-variants: specifier: ^0.1.18 version: 0.1.19(tailwindcss@3.4.0) + uuid: + specifier: ^9.0.1 + version: 9.0.1 zod: specifier: ^3.22.4 version: 3.22.4 @@ -67,6 +70,9 @@ devDependencies: '@types/nprogress': specifier: ^0.2.3 version: 0.2.3 + '@types/uuid': + specifier: ^9.0.7 + version: 9.0.7 '@typescript-eslint/eslint-plugin': specifier: ^6.19.0 version: 6.19.0(@typescript-eslint/parser@6.17.0)(eslint@8.56.0)(typescript@5.3.3) @@ -804,6 +810,10 @@ packages: resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} dev: true + /@types/uuid@9.0.7: + resolution: {integrity: sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==} + dev: true + /@typescript-eslint/eslint-plugin@6.19.0(@typescript-eslint/parser@6.17.0)(eslint@8.56.0)(typescript@5.3.3): resolution: {integrity: sha512-DUCUkQNklCQYnrBSSikjVChdc84/vMPDQSgJTHBZ64G9bA9w0Crc0rd2diujKbTdp6w2J47qkeHQLoi0rpLCdg==} engines: {node: ^16.0.0 || >=18.0.0} @@ -2967,6 +2977,11 @@ packages: /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + /uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + dev: false + /vite-node@1.1.0: resolution: {integrity: sha512-jV48DDUxGLEBdHCQvxL1mEh7+naVy+nhUUUaPAZLd3FJgXuxQiewHcfeZebbJ6onDqNGkP4r3MhQ342PRlG81Q==} engines: {node: ^18.0.0 || >=20.0.0} diff --git a/frontend/src/lib/forms/content-form.svelte b/frontend/src/lib/forms/content-form.svelte index bd9cb3ee..51c0c907 100644 --- a/frontend/src/lib/forms/content-form.svelte +++ b/frontend/src/lib/forms/content-form.svelte @@ -266,7 +266,7 @@ {#if $delayed} {/if} - Save changes + Save changes and continue diff --git a/frontend/src/lib/forms/general-form.svelte b/frontend/src/lib/forms/general-form.svelte index 97f04273..0ae31bbf 100644 --- a/frontend/src/lib/forms/general-form.svelte +++ b/frontend/src/lib/forms/general-form.svelte @@ -106,7 +106,7 @@ {#if $delayed} {/if} - Save changes + Save changes and continue diff --git a/frontend/src/lib/forms/media-server-form.svelte b/frontend/src/lib/forms/media-server-form.svelte index 12b71232..ec4d26fa 100644 --- a/frontend/src/lib/forms/media-server-form.svelte +++ b/frontend/src/lib/forms/media-server-form.svelte @@ -10,6 +10,7 @@ import { mediaServerSettingsSchema, type MediaServerSettingsSchema } from '$lib/schemas/setting'; import { getContext } from 'svelte'; import type { SuperValidated } from 'sveltekit-superforms'; + import { v4 as uuidv4 } from 'uuid'; let formDebug: boolean = getContext('formDebug'); @@ -23,7 +24,80 @@ toast.error($message); } - export let actionUrl: string = "?/default" + export let actionUrl: string = '?/default'; + + let ongoingAuth: boolean = false; + let clientIdentifier: string; + let genClientIdentifier = () => { + clientIdentifier = uuidv4(); + return clientIdentifier; + }; + let appName = 'Iceberg'; + let plexId: string; + let plexCode: string; + let pollingInterval: any; + + async function genPlexPin() { + let data = await fetch('https://plex.tv/api/v2/pins?strong=true', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-Plex-Product': appName, + code: plexCode, + 'X-Plex-Client-Identifier': genClientIdentifier() + } + }); + + return await data.json(); + } + + async function pollPlexPin() { + let data = await fetch(`https://plex.tv/api/v2/pins/${plexId}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-Plex-Product': appName, + 'X-Plex-Client-Identifier': clientIdentifier + } + }); + + let json = await data.json(); + if ('errors' in json) { + toast.error(json.errors[0].message); + ongoingAuth = false; + clearInterval(pollingInterval); + } + + if (json.authToken) { + $form.plex_token = json.authToken; + clearInterval(pollingInterval); + ongoingAuth = false; + } + } + + async function startLogin(): Promise { + ongoingAuth = true; + try { + const pin = await genPlexPin(); + if ('errors' in pin) { + toast.error(pin.errors[0].message); + ongoingAuth = false; + return; + } + plexId = pin.id; + plexCode = pin.code; + + window.open( + `https://app.plex.tv/auth#?clientID=${clientIdentifier}&code=${plexCode}&context%5Bdevice%5D%5Bproduct%5D=${appName}` + ); + + pollingInterval = setInterval(pollPlexPin, 2000); + } catch (e) { + console.error(e); + } + } Plex Token - 0 })} spellcheck="false" - /> + /> --> + + {#if $errors.plex_token} @@ -70,7 +162,7 @@ {#if $delayed} {/if} - Save changes + Save changes and continue diff --git a/frontend/src/lib/forms/scrapers-form.svelte b/frontend/src/lib/forms/scrapers-form.svelte index d2bc7d22..05e9f88f 100644 --- a/frontend/src/lib/forms/scrapers-form.svelte +++ b/frontend/src/lib/forms/scrapers-form.svelte @@ -165,7 +165,7 @@ {#if $delayed} {/if} - Save changes + Save changes and continue diff --git a/frontend/src/routes/onboarding/+page.svelte b/frontend/src/routes/onboarding/+page.svelte index d2c8bb84..c614532b 100644 --- a/frontend/src/routes/onboarding/+page.svelte +++ b/frontend/src/routes/onboarding/+page.svelte @@ -3,6 +3,7 @@ import { onMount } from 'svelte'; import { Button } from '$lib/components/ui/button'; import Rocket from 'lucide-svelte/icons/rocket'; + import Mountain from 'lucide-svelte/icons/mountain'; let rootElement: HTMLElement; let inView = false; @@ -41,6 +42,9 @@ class="flex flex-col p-8 md:px-24 lg:px-32 overflow-x-hidden h-svh w-full" >
+
+ +

Welcome to Iceberg!

Before you can start using Iceberg, you need to configure some services first. diff --git a/frontend/src/routes/onboarding/2/+page.server.ts b/frontend/src/routes/onboarding/2/+page.server.ts new file mode 100644 index 00000000..46571291 --- /dev/null +++ b/frontend/src/routes/onboarding/2/+page.server.ts @@ -0,0 +1,30 @@ +import type { PageServerLoad, Actions } from './$types'; +import { fail, error } from '@sveltejs/kit'; +import { message, superValidate } from 'sveltekit-superforms/server'; +import { mediaServerSettingsSchema } from '$lib/schemas/setting'; +import { saveSettings } from '$lib/helpers'; +import { + mediaServerSettingsToGet, + mediaServerSettingsToPass, + mediaServerSettingsToSet +} from '$lib/forms/helpers'; + +export const load: PageServerLoad = async ({ fetch }) => { + async function getPartialSettings() { + try { + const results = await fetch( + `http://127.0.0.1:8080/settings/get/${mediaServerSettingsToGet.join(',')}` + ); + return await results.json(); + } catch (e) { + console.error(e); + error(503, 'Unable to fetch settings data. API is down.'); + } + } + + let data: any = await getPartialSettings(); + let toPassToSchema = mediaServerSettingsToPass(data); + + const form = await superValidate(toPassToSchema, mediaServerSettingsSchema, { errors: false }); + return { form }; +}; diff --git a/frontend/src/routes/onboarding/2/+page.svelte b/frontend/src/routes/onboarding/2/+page.svelte new file mode 100644 index 00000000..8c12c855 --- /dev/null +++ b/frontend/src/routes/onboarding/2/+page.svelte @@ -0,0 +1,21 @@ + + +

+
+

Step 2

+

Time to configure your media server.

+ Fields marked with * require restart of backend services. +
+
+ + +
+
diff --git a/frontend/src/routes/onboarding/3/+page.server.ts b/frontend/src/routes/onboarding/3/+page.server.ts new file mode 100644 index 00000000..b46ca034 --- /dev/null +++ b/frontend/src/routes/onboarding/3/+page.server.ts @@ -0,0 +1,30 @@ +import type { PageServerLoad, Actions } from './$types'; +import { fail, error } from '@sveltejs/kit'; +import { message, superValidate } from 'sveltekit-superforms/server'; +import { contentSettingsSchema } from '$lib/schemas/setting'; +import { saveSettings } from '$lib/helpers'; +import { + contentSettingsToGet, + contentSettingsToPass, + contentSettingsToSet +} from '$lib/forms/helpers'; + +export const load: PageServerLoad = async ({ fetch }) => { + async function getPartialSettings() { + try { + const results = await fetch( + `http://127.0.0.1:8080/settings/get/${contentSettingsToGet.join(',')}` + ); + return await results.json(); + } catch (e) { + console.error(e); + error(503, 'Unable to fetch settings data. API is down.'); + } + } + + let data: any = await getPartialSettings(); + let toPassToSchema = contentSettingsToPass(data); + + const form = await superValidate(toPassToSchema, contentSettingsSchema, { errors: false }); + return { form }; +}; diff --git a/frontend/src/routes/onboarding/3/+page.svelte b/frontend/src/routes/onboarding/3/+page.svelte new file mode 100644 index 00000000..fef7e1ea --- /dev/null +++ b/frontend/src/routes/onboarding/3/+page.svelte @@ -0,0 +1,21 @@ + + +
+
+

Step 3

+

Services to request content from.

+ Fields marked with * require restart of backend services. +
+
+ + +
+
diff --git a/frontend/src/routes/settings/content/+page.server.ts b/frontend/src/routes/settings/content/+page.server.ts index dd929d8d..d768203d 100644 --- a/frontend/src/routes/settings/content/+page.server.ts +++ b/frontend/src/routes/settings/content/+page.server.ts @@ -1,14 +1,20 @@ import type { PageServerLoad, Actions } from './$types'; -import { fail, error } from '@sveltejs/kit'; +import { fail, error, redirect } from '@sveltejs/kit'; import { message, superValidate } from 'sveltekit-superforms/server'; import { contentSettingsSchema } from '$lib/schemas/setting'; import { saveSettings } from '$lib/helpers'; -import { contentSettingsToGet, contentSettingsToPass, contentSettingsToSet } from '$lib/forms/helpers'; +import { + contentSettingsToGet, + contentSettingsToPass, + contentSettingsToSet +} from '$lib/forms/helpers'; export const load: PageServerLoad = async ({ fetch }) => { async function getPartialSettings() { try { - const results = await fetch(`http://127.0.0.1:8080/settings/get/${contentSettingsToGet.join(',')}`); + const results = await fetch( + `http://127.0.0.1:8080/settings/get/${contentSettingsToGet.join(',')}` + ); return await results.json(); } catch (e) { console.error(e); @@ -42,6 +48,10 @@ export const actions: Actions = { }); } + if (event.url.searchParams.get('onboarding') === 'true') { + redirect(302, '/?onboarding=true'); + } + return message(form, 'Settings saved!'); } }; diff --git a/frontend/src/routes/settings/mediaserver/+page.server.ts b/frontend/src/routes/settings/mediaserver/+page.server.ts index bd78353f..0b2b4f54 100644 --- a/frontend/src/routes/settings/mediaserver/+page.server.ts +++ b/frontend/src/routes/settings/mediaserver/+page.server.ts @@ -1,14 +1,20 @@ import type { PageServerLoad, Actions } from './$types'; -import { fail, error } from '@sveltejs/kit'; +import { fail, error, redirect } from '@sveltejs/kit'; import { message, superValidate } from 'sveltekit-superforms/server'; import { mediaServerSettingsSchema } from '$lib/schemas/setting'; import { saveSettings } from '$lib/helpers'; -import { mediaServerSettingsToGet, mediaServerSettingsToPass, mediaServerSettingsToSet } from '$lib/forms/helpers'; +import { + mediaServerSettingsToGet, + mediaServerSettingsToPass, + mediaServerSettingsToSet +} from '$lib/forms/helpers'; export const load: PageServerLoad = async ({ fetch }) => { async function getPartialSettings() { try { - const results = await fetch(`http://127.0.0.1:8080/settings/get/${mediaServerSettingsToGet.join(',')}`); + const results = await fetch( + `http://127.0.0.1:8080/settings/get/${mediaServerSettingsToGet.join(',')}` + ); return await results.json(); } catch (e) { console.error(e); @@ -42,6 +48,10 @@ export const actions: Actions = { }); } + if (event.url.searchParams.get('onboarding') === 'true') { + redirect(302, '/onboarding/3'); + } + return message(form, 'Settings saved!'); } }; From 495b9cd68e04ec2dcbb9561f2e433f2d0f481592 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 09:07:02 +0000 Subject: [PATCH 32/39] chore(deps-dev): bump @sveltejs/kit from 2.3.2 to 2.4.2 in /frontend (#156) --- frontend/package.json | 2 +- frontend/pnpm-lock.yaml | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 6fc8f6f6..e687d74f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,7 +15,7 @@ "devDependencies": { "@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-node": "^3.0.1", - "@sveltejs/kit": "^2.3.2", + "@sveltejs/kit": "^2.4.2", "@sveltejs/vite-plugin-svelte": "^3.0.0", "@types/luxon": "^3.3.7", "@types/nprogress": "^0.2.3", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 45417f3f..8b80876b 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -37,7 +37,7 @@ dependencies: version: 0.3.6(svelte@4.2.8) sveltekit-superforms: specifier: ^1.13.1 - version: 1.13.1(@sveltejs/kit@2.3.2)(svelte@4.2.8)(zod@3.22.4) + version: 1.13.1(@sveltejs/kit@2.4.2)(svelte@4.2.8)(zod@3.22.4) tailwind-merge: specifier: ^2.2.0 version: 2.2.0 @@ -54,10 +54,10 @@ dependencies: devDependencies: '@sveltejs/adapter-auto': specifier: ^3.0.0 - version: 3.0.0(@sveltejs/kit@2.3.2) + version: 3.0.0(@sveltejs/kit@2.4.2) '@sveltejs/adapter-node': specifier: ^3.0.1 - version: 3.0.1(@sveltejs/kit@2.3.2) + version: 3.0.1(@sveltejs/kit@2.4.2) '@sveltejs/kit': specifier: ^2.3.2 version: 2.3.2(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.12) @@ -693,7 +693,7 @@ packages: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} dev: true - /@sveltejs/adapter-auto@3.0.0(@sveltejs/kit@2.3.2): + /@sveltejs/adapter-auto@3.0.0(@sveltejs/kit@2.4.2): resolution: {integrity: sha512-UNWSs/rOReBRfI/xFwSO2WYF1a7PT74SrWOHJmSNLY3Lq+zbH0uuvnlP+TmrTUBvOTkou3WJDjL6lK3n6aOUgQ==} peerDependencies: '@sveltejs/kit': ^2.0.0 @@ -702,7 +702,7 @@ packages: import-meta-resolve: 4.0.0 dev: true - /@sveltejs/adapter-node@3.0.1(@sveltejs/kit@2.3.2): + /@sveltejs/adapter-node@3.0.1(@sveltejs/kit@2.4.2): resolution: {integrity: sha512-w1NE6K60WQ7JcNa4IyrhzoyH6MIuGdBL9Bd3EaRNcSYdvCG4mOzXI6kLI3kUhLI8krKGoQeOxLhchC5xLnqo6Q==} peerDependencies: '@sveltejs/kit': ^2.0.0 @@ -1675,7 +1675,7 @@ packages: zod: ^3.22.2 dependencies: svelte: 4.2.8 - sveltekit-superforms: 1.13.1(@sveltejs/kit@2.3.2)(svelte@4.2.8)(zod@3.22.4) + sveltekit-superforms: 1.13.1(@sveltejs/kit@2.4.2)(svelte@4.2.8)(zod@3.22.4) zod: 3.22.4 dev: false @@ -2804,7 +2804,7 @@ packages: magic-string: 0.30.5 periscopic: 3.1.0 - /sveltekit-superforms@1.13.1(@sveltejs/kit@2.3.2)(svelte@4.2.8)(zod@3.22.4): + /sveltekit-superforms@1.13.1(@sveltejs/kit@2.4.2)(svelte@4.2.8)(zod@3.22.4): resolution: {integrity: sha512-aWHAV6t6+7g37IGFNLRtbRIoqfC3a56Hf4/tTUNFZfwO2mOQGfeMb49WWBLdZZTrFRPGngfNRUxQzeoO3GspnQ==} peerDependencies: '@sveltejs/kit': 1.x || 2.x From 9d3cbc50e9ddc5a0863d7b65b9fd53eb3bf1aa83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 09:08:18 +0000 Subject: [PATCH 33/39] chore(deps-dev): bump prettier from 3.1.1 to 3.2.4 in /frontend (#155) --- frontend/package.json | 2 +- frontend/pnpm-lock.yaml | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index e687d74f..d7686d62 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -28,7 +28,7 @@ "eslint-plugin-svelte": "^2.35.1", "postcss": "^8.4.24", "postcss-load-config": "^4.0.1", - "prettier": "^3.0.0", + "prettier": "^3.2.4", "prettier-plugin-tailwindcss": "^0.5.9", "svelte": "^4.2.8", "svelte-check": "^3.6.0", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 8b80876b..cf4a3adb 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -98,11 +98,11 @@ devDependencies: specifier: ^4.0.1 version: 4.0.2(postcss@8.4.32) prettier: - specifier: ^3.0.0 - version: 3.1.1 + specifier: ^3.2.4 + version: 3.2.4 prettier-plugin-tailwindcss: specifier: ^0.5.9 - version: 0.5.9(prettier@3.1.1) + version: 0.5.9(prettier@3.2.4) svelte: specifier: ^4.2.8 version: 4.2.8 @@ -2389,7 +2389,7 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /prettier-plugin-tailwindcss@0.5.9(prettier@3.1.1): + /prettier-plugin-tailwindcss@0.5.9(prettier@3.2.4): resolution: {integrity: sha512-9x3t1s2Cjbut2QiP+O0mDqV3gLXTe2CgRlQDgucopVkUdw26sQi53p/q4qvGxMLBDfk/dcTV57Aa/zYwz9l8Ew==} engines: {node: '>=14.21.3'} peerDependencies: @@ -2438,11 +2438,11 @@ packages: prettier-plugin-twig-melody: optional: true dependencies: - prettier: 3.1.1 + prettier: 3.2.4 dev: true - /prettier@3.1.1: - resolution: {integrity: sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==} + /prettier@3.2.4: + resolution: {integrity: sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==} engines: {node: '>=14'} hasBin: true dev: true From ea88e41a716332fa5d28cdfced8ad4fa2768c4cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 09:08:49 +0000 Subject: [PATCH 34/39] chore(deps): bump lucide-svelte from 0.309.0 to 0.314.0 in /frontend (#154) --- frontend/package.json | 2 +- frontend/pnpm-lock.yaml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index d7686d62..e0d464cf 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -44,7 +44,7 @@ "clsx": "^2.0.0", "cmdk-sv": "^0.0.12", "formsnap": "^0.4.2", - "lucide-svelte": "^0.309.0", + "lucide-svelte": "^0.314.0", "luxon": "^3.4.4", "mode-watcher": "^0.1.2", "motion": "^10.17.0", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index cf4a3adb..1cb4d37c 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -18,8 +18,8 @@ dependencies: specifier: ^0.4.2 version: 0.4.2(svelte@4.2.8)(sveltekit-superforms@1.13.1)(zod@3.22.4) lucide-svelte: - specifier: ^0.309.0 - version: 0.309.0(svelte@4.2.8) + specifier: ^0.314.0 + version: 0.314.0(svelte@4.2.8) luxon: specifier: ^3.4.4 version: 3.4.4 @@ -1988,8 +1988,8 @@ packages: yallist: 4.0.0 dev: true - /lucide-svelte@0.309.0(svelte@4.2.8): - resolution: {integrity: sha512-+t85+5Y696FVDoJHiiMhJGalv+UiWUX46gMudOQfYrVGjsyC2MGSZRNAGAHkdykA4RJSh/wUtSgnTgCmd0Swvw==} + /lucide-svelte@0.314.0(svelte@4.2.8): + resolution: {integrity: sha512-w7oPy6aU7ybjopT9k65S1S5y/XgFVL6i6Qm0MT9pSjvORfWEdaI7cvonx5D2w3+Ty8+0jVRm4k8U/txhEpwLOg==} peerDependencies: svelte: '>=3 <5' dependencies: From 1b91e138b2b2b4151994f5cf1e3a615a661ec238 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 09:10:04 +0000 Subject: [PATCH 35/39] chore(deps): bump bits-ui from 0.14.0 to 0.15.1 in /frontend (#153) --- frontend/package.json | 2 +- frontend/pnpm-lock.yaml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index e0d464cf..e38dd7cb 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -40,7 +40,7 @@ }, "type": "module", "dependencies": { - "bits-ui": "^0.14.0", + "bits-ui": "^0.15.1", "clsx": "^2.0.0", "cmdk-sv": "^0.0.12", "formsnap": "^0.4.2", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 1cb4d37c..48955f74 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -6,8 +6,8 @@ settings: dependencies: bits-ui: - specifier: ^0.14.0 - version: 0.14.0(svelte@4.2.8) + specifier: ^0.15.1 + version: 0.15.1(svelte@4.2.8) clsx: specifier: ^2.0.0 version: 2.0.0 @@ -1134,8 +1134,8 @@ packages: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} - /bits-ui@0.14.0(svelte@4.2.8): - resolution: {integrity: sha512-S1LNwp/Sge1Ro1g0iJ+msIUeJYmoNhXBFShnLDOUOKQe3JG1wd027KxZnVd7db6UWr1IqlJdd4/bbB7NYXeYDw==} + /bits-ui@0.15.1(svelte@4.2.8): + resolution: {integrity: sha512-1Np8bT6W6SC2tKESfm0CySW+7+xU5S0GuUZqIxC41atZE3WIRiRlzXEYHxW88w6UaLFzZ51ns4E7pchkdV5XCQ==} peerDependencies: svelte: ^4.0.0 dependencies: From 2134814d7d2be5f7e08bb0546899581fdd6f5d88 Mon Sep 17 00:00:00 2001 From: Ayush Sehrawat Date: Tue, 23 Jan 2024 14:40:51 +0530 Subject: [PATCH 36/39] feat: minor changes --- frontend/package.json | 2 + frontend/pnpm-lock.yaml | 41 +++++++++++++++---- frontend/src/lib/forms/helpers.ts | 14 ++++++- .../src/lib/forms/media-server-form.svelte | 2 +- frontend/src/lib/schemas/setting.ts | 7 +++- frontend/svelte.config.js | 8 +--- 6 files changed, 57 insertions(+), 17 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index e38dd7cb..c98b6207 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,6 +13,8 @@ "format": "prettier --write ." }, "devDependencies": { + "@melt-ui/pp": "^0.3.0", + "@melt-ui/svelte": "^0.70.0", "@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-node": "^3.0.1", "@sveltejs/kit": "^2.4.2", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 48955f74..7be198d7 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -52,6 +52,12 @@ dependencies: version: 3.22.4 devDependencies: + '@melt-ui/pp': + specifier: ^0.3.0 + version: 0.3.0(@melt-ui/svelte@0.70.0)(svelte@4.2.8) + '@melt-ui/svelte': + specifier: ^0.70.0 + version: 0.70.0(svelte@4.2.8) '@sveltejs/adapter-auto': specifier: ^3.0.0 version: 3.0.0(@sveltejs/kit@2.4.2) @@ -367,18 +373,15 @@ packages: resolution: {integrity: sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A==} dependencies: '@floating-ui/utils': 0.1.6 - dev: false /@floating-ui/dom@1.5.3: resolution: {integrity: sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==} dependencies: '@floating-ui/core': 1.5.2 '@floating-ui/utils': 0.1.6 - dev: false /@floating-ui/utils@0.1.6: resolution: {integrity: sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==} - dev: false /@humanwhocodes/config-array@0.11.13: resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} @@ -404,7 +407,6 @@ packages: resolution: {integrity: sha512-LUQIfwU9e+Fmutc/DpRTGXSdgYZLBegi4wygCWDSVmUdLTaMHsQyASDiJtREwanwKuQLq0hY76fCJ9J/9I2xOQ==} dependencies: '@swc/helpers': 0.5.3 - dev: false /@jest/schemas@29.6.3: resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} @@ -438,6 +440,19 @@ packages: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 + /@melt-ui/pp@0.3.0(@melt-ui/svelte@0.70.0)(svelte@4.2.8): + resolution: {integrity: sha512-b07Bdh8l2KcwKVCXOY+SoBw1dk9eWvQfMSi6SoacpRVyVmmfpi0kV4oGt3HYF0tUCB3sEmVicxse50ZzZxEzEA==} + engines: {pnpm: '>=8.6.3'} + peerDependencies: + '@melt-ui/svelte': '>= 0.29.0' + svelte: ^3.55.0 || ^4.0.0 || ^5.0.0-next.1 + dependencies: + '@melt-ui/svelte': 0.70.0(svelte@4.2.8) + estree-walker: 3.0.3 + magic-string: 0.30.5 + svelte: 4.2.8 + dev: true + /@melt-ui/svelte@0.61.2(svelte@4.2.8): resolution: {integrity: sha512-BHkD9G31zQBToA4euDRBgTQRvWxT9scufOVCXgDO6HKTvyxFspbWT2bgiSFqAK4BbAGDn9Ao36Q8F9O71KN4OQ==} peerDependencies: @@ -466,6 +481,20 @@ packages: svelte: 4.2.8 dev: false + /@melt-ui/svelte@0.70.0(svelte@4.2.8): + resolution: {integrity: sha512-ni14892MHJMAxSl2cz1pcgfnLR7fee1nNDJmx47hV19ewxSs8eQ8iguPrfx1ONtgjbp2YYVZhlpERi7szd30cA==} + peerDependencies: + svelte: '>=3 <5' + dependencies: + '@floating-ui/core': 1.5.2 + '@floating-ui/dom': 1.5.3 + '@internationalized/date': 3.5.1 + dequal: 2.0.3 + focus-trap: 7.5.4 + nanoid: 5.0.4 + svelte: 4.2.8 + dev: true + /@motionone/animation@10.17.0: resolution: {integrity: sha512-ANfIN9+iq1kGgsZxs+Nz96uiNcPLGTXwfNo2Xz/fcJXniPYpaz/Uyrfa+7I5BPLxCP82sh7quVDudf1GABqHbg==} dependencies: @@ -778,7 +807,6 @@ packages: resolution: {integrity: sha512-FaruWX6KdudYloq1AHD/4nU+UsMTdNE8CKyrseXWEcgjDAbvkwJg2QGPAnfIJLIWsjZOSPLOAykK6fuYp4vp4A==} dependencies: tslib: 2.6.2 - dev: false /@types/cookie@0.6.0: resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} @@ -1665,7 +1693,6 @@ packages: resolution: {integrity: sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==} dependencies: tabbable: 6.2.0 - dev: false /formsnap@0.4.2(svelte@4.2.8)(sveltekit-superforms@1.13.1)(zod@3.22.4): resolution: {integrity: sha512-iUhGDUcjUW9tCOYLZ1rbNR1wkjtEbrXDxhUnl7+zPKjP5K8ikDfhZWH1cquKdUcHJoSE7M/Rruvg0fNQMjSNtA==} @@ -2131,7 +2158,6 @@ packages: resolution: {integrity: sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==} engines: {node: ^18 || >=20} hasBin: true - dev: false /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -2818,7 +2844,6 @@ packages: /tabbable@6.2.0: resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} - dev: false /tailwind-merge@1.14.0: resolution: {integrity: sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==} diff --git a/frontend/src/lib/forms/helpers.ts b/frontend/src/lib/forms/helpers.ts index 108958ee..05d165a0 100644 --- a/frontend/src/lib/forms/helpers.ts +++ b/frontend/src/lib/forms/helpers.ts @@ -60,7 +60,12 @@ export function contentSettingsToPass(data:any) { plex_watchlist_enabled: data.data.content.plex_watchlist.enabled, plex_watchlist_rss: data.data.content.plex_watchlist?.rss || '', plex_watchlist_update_interval: - data.data.content.plex_watchlist?.update_interval || 80 + data.data.content.plex_watchlist?.update_interval || 80, + listrr_enabled: data.data.content.listrr.enabled, + listrr_api_key: data.data.content.listrr?.api_key || '', + listrr_update_interval: data.data.content.listrr?.update_interval || 80, + listrr_movie_lists: data.data.content.listrr?.movie_lists || [''], + listrr_show_lists: data.data.content.listrr?.show_lists || [''] } } @@ -84,6 +89,13 @@ export function contentSettingsToSet(form: SuperValidated enabled: form.data.plex_watchlist_enabled, rss: form.data.plex_watchlist_rss, update_interval: form.data.plex_watchlist_update_interval + }, + listrr: { + enabled: form.data.listrr_enabled, + api_key: form.data.listrr_api_key, + update_interval: form.data.listrr_update_interval, + movie_lists: form.data.listrr_movie_lists, + show_lists: form.data.listrr_show_lists } } } diff --git a/frontend/src/lib/forms/media-server-form.svelte b/frontend/src/lib/forms/media-server-form.svelte index ec4d26fa..1d200bbf 100644 --- a/frontend/src/lib/forms/media-server-form.svelte +++ b/frontend/src/lib/forms/media-server-form.svelte @@ -132,7 +132,7 @@ })} spellcheck="false" /> --> - +
+ + +
+ + Listrr +
+
@@ -235,12 +295,17 @@ class="text-base md:text-lg font-semibold w-48 min-w-48 text-muted-foreground" for="mdblist_lists">Mdblist Lists -
+ { + addToList(event, 'mdblist'); + }} + class="w-full flex flex-col gap-4 items-start" + >
{#each $mdblistListsValues.filter((list) => list !== '') as list (list)} @@ -249,7 +314,149 @@ in:fly={{ y: 10, duration: 200 }} out:fly={{ y: -10, duration: 200 }} class="flex items-center gap-2 py-1 px-6 text-sm bg-slate-200 dark:bg-slate-800 rounded-md" - on:click={() => removeFromMdblistLists(list)} + on:click={() => removeFromList(list, 'mdblist')} + > +

{list}

+ + + {/each} +
+
+ + {/if} + + + + + + {#if $form.listrr_enabled} +
+ + + + Listrr API Key + + 0 + })} + spellcheck="false" + /> + + {#if $errors.listrr_api_key} + + {/if} + +
+ +
+ + + + Listrr Update Interval + + + + {#if $errors.listrr_update_interval} + + {/if} + +
+ + {#if $listrrMovieListsErrors} + {$listrrMovieListsErrors} + {/if} + {#if $listrrShowListsErrors} + {$listrrShowListsErrors} + {/if} + +
+ +
{ + addToList(event, 'listrr_movie'); + }} + class="w-full flex flex-col gap-4 items-start" + > + +
+ {#each $listrrMovieListsValues.filter((list) => list !== '') as list (list)} + + {/each} +
+
+
+ +
+ +
{ + addToList(event, 'listrr_show'); + }} + class="w-full flex flex-col gap-4 items-start" + > + +
+ {#each $listrrShowListsValues.filter((list) => list !== '') as list (list)} +