Files
EricRollei-Download_Tools/utils/persistent_settings.py
T
Eric Rollei 033f73d66c Add ModelMayhem, Wix, Portfolio handlers; fix Reddit video extraction; update dependencies
New Features:
- Add ModelMayhem handler with username-to-ID resolution and worksafe mode handling
- Add Wix handler for Wix-hosted websites
- Add Portfolio handler for generic portfolio sites
- Add persistent settings utility for saving node preferences

Reddit Improvements:
- Fix API credential loading in prefers_api() method
- Add external video host resolution (RedGifs, Gfycat, Imgur) using yt-dlp
- Add v.redd.it video URL resolution

Gallery-dl Config:
- Add RedGifs, Gfycat, Imgur format preferences
- Enable Reddit video downloads with comments=0, videos=true

Dependencies:
- Add asyncpraw for Reddit API
- Add atproto for Bluesky
- Add instaloader for Instagram
- Add aiohttp for async HTTP
- Add Pillow explicitly

Other:
- Update .gitignore to exclude node_settings.json
- Update example configs with new settings
2026-01-17 19:08:01 -08:00

168 lines
5.9 KiB
Python

"""
Persistent Settings Manager
Description: Manages persistent settings for download_tools nodes across reloads
Author: Eric Hiss (GitHub: EricRollei)
Contact: eric@historic.camera, eric@rollei.us
License: Dual License (Non-Commercial and Commercial Use)
Copyright (c) 2025 Eric Hiss. All rights reserved.
"""
import os
import json
from pathlib import Path
from typing import Optional, Any, Dict
class PersistentSettings:
"""
Manages persistent settings for download_tools nodes.
Settings are stored in a JSON file and persist across node/workflow reloads.
"""
_instance = None
_settings: Dict[str, Any] = {}
_settings_file: Path = None
def __new__(cls):
"""Singleton pattern to ensure only one instance exists."""
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._initialized = False
return cls._instance
def __init__(self):
"""Initialize the settings manager."""
if self._initialized:
return
# Determine settings file path
self._settings_file = Path(__file__).parent.parent / "configs" / "node_settings.json"
self._settings = self._load_settings()
self._initialized = True
print(f"PersistentSettings initialized from: {self._settings_file}")
def _load_settings(self) -> Dict[str, Any]:
"""Load settings from the JSON file."""
default_settings = {
"web_scraper": {
"auth_config_path": ""
},
"gallery_dl": {
"config_path": "",
"cookie_file": ""
},
"yt_dlp": {
"config_path": ""
}
}
try:
if self._settings_file.exists():
with open(self._settings_file, 'r', encoding='utf-8') as f:
loaded = json.load(f)
# Merge with defaults to ensure all keys exist
for key in default_settings:
if key not in loaded:
loaded[key] = default_settings[key]
elif isinstance(default_settings[key], dict):
for subkey in default_settings[key]:
if subkey not in loaded[key]:
loaded[key][subkey] = default_settings[key][subkey]
return loaded
else:
# Create the file with defaults
self._settings_file.parent.mkdir(parents=True, exist_ok=True)
with open(self._settings_file, 'w', encoding='utf-8') as f:
json.dump(default_settings, f, indent=4)
return default_settings
except Exception as e:
print(f"Error loading persistent settings: {e}")
return default_settings
def _save_settings(self) -> bool:
"""Save current settings to the JSON file."""
try:
self._settings_file.parent.mkdir(parents=True, exist_ok=True)
with open(self._settings_file, 'w', encoding='utf-8') as f:
json.dump(self._settings, f, indent=4)
return True
except Exception as e:
print(f"Error saving persistent settings: {e}")
return False
def get(self, node_type: str, key: str, default: Any = "") -> Any:
"""
Get a setting value for a specific node type.
Args:
node_type: The type of node ('web_scraper', 'gallery_dl', 'yt_dlp')
key: The setting key to retrieve
default: Default value if setting not found
Returns:
The setting value or default
"""
try:
if node_type in self._settings and key in self._settings[node_type]:
value = self._settings[node_type][key]
return value if value else default
return default
except Exception:
return default
def set(self, node_type: str, key: str, value: Any) -> bool:
"""
Set a setting value for a specific node type.
Args:
node_type: The type of node ('web_scraper', 'gallery_dl', 'yt_dlp')
key: The setting key to set
value: The value to store
Returns:
True if successful, False otherwise
"""
try:
if node_type not in self._settings:
self._settings[node_type] = {}
# Only update if value is non-empty (don't overwrite with empty values)
if value and str(value).strip():
self._settings[node_type][key] = str(value).strip()
return self._save_settings()
return True
except Exception as e:
print(f"Error setting persistent setting: {e}")
return False
def get_all(self, node_type: str) -> Dict[str, Any]:
"""Get all settings for a specific node type."""
return self._settings.get(node_type, {})
def reload(self) -> None:
"""Reload settings from file (useful if file was edited externally)."""
self._settings = self._load_settings()
# Global instance for easy access
_settings_manager: Optional[PersistentSettings] = None
def get_settings_manager() -> PersistentSettings:
"""Get the global settings manager instance."""
global _settings_manager
if _settings_manager is None:
_settings_manager = PersistentSettings()
return _settings_manager
def get_persistent_setting(node_type: str, key: str, default: Any = "") -> Any:
"""Convenience function to get a persistent setting."""
return get_settings_manager().get(node_type, key, default)
def set_persistent_setting(node_type: str, key: str, value: Any) -> bool:
"""Convenience function to set a persistent setting."""
return get_settings_manager().set(node_type, key, value)