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
168 lines
5.9 KiB
Python
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)
|