Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix dev history #199

Merged
merged 1 commit into from
Feb 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 6 additions & 10 deletions backend/program/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,9 @@ def validate(self):
return all(service.initialized for service in self.core_manager.services)

def stop(self):
try:
for service in self.core_manager.services:
if getattr(service, "running", False):
service.stop()
self.pickly.stop()
settings.save()
self.running = False
except Exception as e:
logger.error("Iceberg stopping with exception: %s", e)
pass
for service in self.core_manager.services:
if getattr(service, "running", False):
service.stop()
self.pickly.stop()
settings.save()
self.running = False
6 changes: 3 additions & 3 deletions backend/program/realdebrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,9 @@ def chunks(lst, n):
"active_stream",
{"hash": stream_hash, "files": wanted_files, "id": None},
)
all_filenames = [file_info["filename"] for file_info in wanted_files.values()]
for file in all_filenames:
logger.debug(f"Found cached file {file} for {item.log_string}")
# all_filenames = [file_info["filename"] for file_info in wanted_files.values()]
# for file in all_filenames:
# logger.debug(f"Found cached file {file} for {item.log_string}")
return True
item.streams[stream_hash] = None
return False
Expand Down
15 changes: 7 additions & 8 deletions backend/program/scrapers/jackett.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def __init__(self, _):
self.initialized = self.validate_settings()
if not self.initialized and not self.api_key:
return
self.minute_limiter = RateLimiter(max_calls=60, period=60, raise_on_limit=True)
self.minute_limiter = RateLimiter(max_calls=1000, period=3600, raise_on_limit=True)
self.second_limiter = RateLimiter(max_calls=1, period=5)
self.parse_logging = False
logger.info("Jackett initialized!")
Expand All @@ -45,7 +45,7 @@ def validate_settings(self) -> bool:
except ReadTimeout:
return True
except Exception as e:
logger.exception("Jackett failed to initialize with API Key: %s", e)
logger.error("Jackett failed to initialize with API Key: %s", e)
return False
if self.settings.url:
try:
Expand All @@ -57,9 +57,10 @@ def validate_settings(self) -> bool:
if not response.is_ok:
return False
except ReadTimeout:
logger.warn("Jackett connection timeout.")
return True
except Exception as e:
logger.exception("Jackett failed to initialize: %s", e)
logger.error("Jackett failed to initialize: %s", e)
return False
logger.info("Jackett is not configured and will not be used.")
return False
Expand All @@ -75,13 +76,11 @@ def run(self, item):
logger.warn("Jackett rate limit hit for item: %s", item.log_string)
return
except RequestException as e:
self.minute_limiter.limit_hit()
logger.exception("Jackett request exception: %s", e, exc_info=True)
logger.debug("Jackett request exception: %s", e, exc_info=True)
return
except Exception as e:
self.minute_limiter.limit_hit()
# logger.debug("Jackett exception for item: %s - Exception: %s", item.log_string, e.args[0], exc_info=True)
# logger.debug("Exception details: %s", traceback.format_exc())
logger.debug("Jackett exception for item: %s - Exception: %s", item.log_string, e.args[0], exc_info=True)
logger.debug("Exception details: %s", traceback.format_exc())
return

def _scrape_item(self, item):
Expand Down
27 changes: 14 additions & 13 deletions backend/program/scrapers/orionoid.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ def __init__(self, _):
self.orionoid_limit = 0
self.orionoid_remaining = 0
self.parse_logging = False
self.max_calls = 100 if not self.is_premium else 60
self.period = 86400 if not self.is_premium else 60
self.max_calls = 100 if not self.is_premium else 1000
self.period = 86400 if not self.is_premium else 3600
self.minute_limiter = RateLimiter(max_calls=self.max_calls, period=self.period, raise_on_limit=True)
self.second_limiter = RateLimiter(max_calls=1, period=5)
logger.info("Orionoid initialized!")
Expand All @@ -43,19 +43,20 @@ def validate_settings(self) -> bool:
if not self.settings.enabled:
logger.debug("Orionoid is set to disabled.")
return False
if self.settings.api_key:
return True
if len(self.settings.api_key) != 32 or self.settings.api_key == "":
logger.error("Orionoid API Key is not valid or not set. Please check your settings.")
return False
try:
url = f"https://api.orionoid.com?keyapp={KEY_APP}&keyuser={self.settings.api_key}&mode=user&action=retrieve"
response = get(url, retry_if_failed=False)
if response.is_ok:
return True
if not response.data.result.status == "success":
logger.error(f"Orionoid API Key is invalid. Status: {response.data.result.status}")
return False
if not response.is_ok:
logger.error(f"Orionoid Status Code: {response.status_code}, Reason: {response.reason}")
return False
if response.is_ok and hasattr(response.data, "result"):
if not response.data.result.status == "success":
logger.error(f"Orionoid API Key is invalid. Status: {response.data.result.status}")
return False
if not response.is_ok:
logger.error(f"Orionoid Status Code: {response.status_code}, Reason: {response.reason}")
return False
return True
except Exception as e:
logger.exception("Orionoid failed to initialize: %s", e)
return False
Expand All @@ -67,7 +68,7 @@ def check_premium(self) -> bool:
"""
url = f"https://api.orionoid.com?keyapp={KEY_APP}&keyuser={self.settings.api_key}&mode=user&action=retrieve"
response = get(url, retry_if_failed=False)
if response.is_ok:
if response.is_ok and hasattr(response.data, "data"):
active = True if response.data.data.status == "active" else False
premium = response.data.data.subscription.package.premium
debrid = response.data.data.service.realdebrid
Expand Down
15 changes: 3 additions & 12 deletions backend/program/scrapers/torrentio.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
""" Torrentio scraper module """
import os
from typing import Optional
from pydantic import BaseModel
from requests import ConnectTimeout, ReadTimeout
Expand All @@ -22,7 +21,7 @@ class Torrentio:
def __init__(self, _):
self.key = "torrentio"
self.settings = TorrentioConfig(**settings_manager.get(f"scraping.{self.key}"))
self.minute_limiter = RateLimiter(max_calls=60, period=60, raise_on_limit=True)
self.minute_limiter = RateLimiter(max_calls=300, period=3600, raise_on_limit=True)
self.second_limiter = RateLimiter(max_calls=1, period=5)
self.initialized = self.validate_settings()
if not self.initialized:
Expand Down Expand Up @@ -57,26 +56,18 @@ def run(self, item):
self._scrape_item(item)
except RateLimitExceeded:
self.minute_limiter.limit_hit()
logger.warn("Torrentio rate limit hit for item: %s", item.log_string)
return
except ConnectTimeout:
self.minute_limiter.limit_hit()
logger.warn("Torrentio connection timeout for item: %s", item.log_string)
return
except ReadTimeout:
self.minute_limiter.limit_hit()
logger.warn("Torrentio read timeout for item: %s", item.log_string)
return
except RequestException as e:
self.minute_limiter.limit_hit()
logger.warn("Torrentio request exception: %s", e)
return
except AttributeError:
# TODO: will fix later
self.minute_limiter.limit_hit()
return
except Exception as e:
self.minute_limiter.limit_hit()
logger.warn("Torrentio failed to scrape item: %s", e)
logger.warn("Torrentio exception thrown: %s", e)
return

def _scrape_item(self, item):
Expand Down
108 changes: 63 additions & 45 deletions backend/program/symlink.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Symlinking module"""
import os
from pathlib import Path
from typing import NamedTuple
from pydantic import BaseModel
from utils.settings import settings_manager as settings
from utils.logger import logger
Expand All @@ -10,6 +11,9 @@ class SymlinkConfig(BaseModel):
host_path: Path
container_path: Path

class Setting(NamedTuple):
key: str
value: str

class Symlinker():
"""
Expand All @@ -26,60 +30,74 @@ class Symlinker():
def __init__(self, _):
self.key = "symlink"
self.settings = SymlinkConfig(**settings.get(self.key))
self.initialized = False

if (self.settings.host_path / "__all__").exists():
logger.debug("Detected Zurg host path. Using __all__ folder for host path.")
settings.set(self.key, self.settings.host_path)
self.settings.host_path = Path(self.settings.host_path) / "__all__"
elif (self.settings.host_path / "torrents").exists():
logger.debug("Detected standard rclone host path. Using torrents folder for host path.")
settings.set(self.key, self.settings.host_path)
self.settings.host_path = Path(self.settings.host_path) / "torrents"

self.library_path = self.settings.host_path.parent / "library"

if not self.validate():
logger.error("Symlink configuration is invalid. Please check the host and container paths.")
return

self.initialize_library_paths()

if not self.create_initial_folders():
logger.error("Failed to create initial library folders.")
self.initialized = self.validate()
if not self.initialized:
logger.error("Symlink initialization failed due to invalid configuration.")
return

logger.info("Found rclone mount path: %s", self.settings.host_path)
logger.info("Symlinks will be placed in library path: %s", self.library_path)
logger.info("Plex will see the symlinks in: %s", self.settings.container_path.parent / "library")
logger.info("Rclone path symlinks are pointed to: %s", self.settings.host_path)
logger.info("Symlinks will be placed in: %s", self.library_path)
logger.info("Symlink initialized!")
self.initialized = True

def validate(self):
if not self.settings.host_path or not self.settings.container_path:
"""Validate paths and create the initial folders."""
host_path = Path(self.settings.host_path) if self.settings.host_path else None
container_path = Path(self.settings.container_path) if self.settings.container_path else None
if not host_path or not container_path or host_path == Path('.') or container_path == Path('.'):
logger.error("Host or container path not provided, is empty, or is set to the current directory.")
return False
host_path = Path(self.settings.host_path)
if not host_path.exists() or not host_path.is_dir():
logger.error(f"Invalid host path: {self.settings.host_path}")
if not host_path.is_absolute():
logger.error(f"Host path is not an absolute path: {host_path}")
return False
return True

def initialize_library_paths(self):
self.library_path_movies = self.library_path / "movies"
self.library_path_shows = self.library_path / "shows"
self.library_path_anime_movies = self.library_path / "anime_movies"
self.library_path_anime_shows = self.library_path / "anime_shows"
if not container_path.is_absolute():
logger.error(f"Container path is not an absolute path: {container_path}")
return False
try:
if not host_path.is_dir():
logger.error(f"Host path is not a directory or does not exist: {host_path}")
return False
if not container_path.is_dir():
logger.error(f"Container path is not a directory or does not exist: {container_path}")
return False
if Path(self.settings.host_path / "__all__").exists() and Path(self.settings.host_path / "__all__").is_dir():
logger.debug("Detected Zurg host path. Using __all__ folder for host path.")
self.settings.host_path = self.settings.host_path / "__all__"
elif Path(self.settings.host_path / "torrents").exists() and Path(self.settings.host_path / "torrents").is_dir():
logger.debug("Detected standard rclone host path. Using torrents folder for host path.")
self.settings.host_path = self.settings.host_path / "torrents"
if not self.create_initial_folders():
logger.error("Failed to create initial library folders.")
return False
return True
except FileNotFoundError as e:
logger.error(f"Path not found: {e}")
except PermissionError as e:
logger.error(f"Permission denied when accessing path: {e}")
except OSError as e:
logger.error(f"OS error when validating paths: {e}")
return False

def create_initial_folders(self):
for library in [self.library_path_movies,
self.library_path_shows,
self.library_path_anime_movies,
self.library_path_anime_shows]:
try:
library.mkdir(parents=True, exist_ok=True)
except Exception as e:
logger.error("Failed to create directory %s: %s", library, e)
return False
"""Create the initial library folders."""
try:
self.library_path = self.settings.container_path.parent / "library"
self.library_path_movies = self.library_path / "movies"
self.library_path_shows = self.library_path / "shows"
self.library_path_anime_movies = self.library_path / "anime_movies"
self.library_path_anime_shows = self.library_path / "anime_shows"
folders = [self.library_path_movies,
self.library_path_shows,
self.library_path_anime_movies,
self.library_path_anime_shows]
for folder in folders:
if not folder.exists():
folder.mkdir(parents=True, exist_ok=True)
except PermissionError as e:
logger.error(f"Permission denied when creating directory: {e}")
return False
except OSError as e:
logger.error(f"OS error when creating directory: {e}")
return False
return True

def run(self, item):
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/lib/forms/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { z } from 'zod';

// General Settings -----------------------------------------------------------------------------------
export const generalSettingsToGet: string[] = ['debug', 'log', 'symlink', 'real_debrid'];
export const generalSettingsServices: string[] = ['symlink', 'real_debrid'];

export const generalSettingsSchema = z.object({
debug: z.boolean().default(true),
Expand Down Expand Up @@ -51,6 +52,7 @@ export function generalSettingsToSet(form: SuperValidated<GeneralSettingsSchema>

// Content Settings -----------------------------------------------------------------------------------
export const contentSettingsToGet: string[] = ['content'];
export const contentSettingsServices: string[] = ['content'];

export const contentSettingsSchema = z.object({
overseerr_enabled: z.boolean().default(false),
Expand Down Expand Up @@ -126,6 +128,7 @@ export function contentSettingsToSet(form: SuperValidated<ContentSettingsSchema>

// Media Server Settings -----------------------------------------------------------------------------------
export const mediaServerSettingsToGet: string[] = ['plex'];
export const mediaServerSettingsServices: string[] = ['plex'];

export const mediaServerSettingsSchema = z.object({
plex_token: z.string().optional().default(''),
Expand Down Expand Up @@ -154,6 +157,7 @@ export function mediaServerSettingsToSet(form: SuperValidated<MediaServerSetting

// Scrapers Settings -----------------------------------------------------------------------------------
export const scrapersSettingsToGet: string[] = ['scraping'];
export const scrapersSettingsServices: string[] = ['scraping'];

export const scrapersSettingsSchema = z.object({
after_2: z.number().nonnegative().default(0.5),
Expand Down
Loading