This commit is contained in:
2026-01-09 01:22:06 -08:00
parent 89ac3bb7e8
commit 1deddfda5c
10 changed files with 1004 additions and 179 deletions

View File

@@ -205,36 +205,71 @@ class SharedArgs:
def get_store_choices(config: Optional[Dict[str, Any]] = None) -> List[str]:
"""Get list of available store backend names.
This method dynamically discovers all configured storage backends
instead of using a static list. Should be called when building
autocomplete choices or validating store names.
This method returns the cached list of available backends from the most
recent startup check. Stores that failed to initialize are filtered out.
Users must restart to refresh the list if stores are enabled/disabled.
Args:
config: Optional config dict. If not provided, will try to load from config module.
config: Ignored (kept for compatibility); uses cached startup result.
Returns:
List of backend names (e.g., ['default', 'test', 'home', 'work'])
Only includes backends that successfully initialized at startup.
Example:
SharedArgs.STORE.choices = SharedArgs.get_store_choices(config)
"""
# Use the cached startup check result if available
if hasattr(SharedArgs, "_cached_available_stores"):
return SharedArgs._cached_available_stores or []
# Fallback to configured names if cache doesn't exist yet
# (This shouldn't happen in normal operation, but provides a safe fallback)
try:
# Use the non-instantiating helper so autocomplete doesn't trigger backend init.
from Store.registry import list_configured_backend_names
# If no config provided, try to load it
if config is None:
try:
from SYS.config import load_config
config = load_config()
except Exception:
return []
return list_configured_backend_names(config)
return list_configured_backend_names(config) or []
except Exception:
# Fallback to empty list if FileStorage isn't available
return []
@staticmethod
def _refresh_store_choices_cache(config: Optional[Dict[str, Any]] = None) -> None:
"""Refresh the cached store choices list. Should be called once at startup.
This performs the actual StoreRegistry initialization check and caches the result.
Subsequent calls to get_store_choices() will use this cache.
Args:
config: Config dict. If not provided, will try to load from config module.
"""
try:
if config is None:
try:
from SYS.config import load_config
config = load_config()
except Exception:
SharedArgs._cached_available_stores = []
return
# Initialize registry once to filter disabled stores
from Store.registry import Store as StoreRegistry
try:
registry = StoreRegistry(config=config, suppress_debug=True)
available = registry.list_backends()
SharedArgs._cached_available_stores = available or []
except Exception:
# If registry creation fails, fallback to configured names
from Store.registry import list_configured_backend_names
SharedArgs._cached_available_stores = list_configured_backend_names(config) or []
except Exception:
SharedArgs._cached_available_stores = []
LOCATION = CmdletArg(
"location",
type="enum",

View File

@@ -321,12 +321,10 @@ class Add_File(Cmdlet):
is_storage_backend_location = False
if location:
try:
# Use a config-only check to avoid instantiating backends (which may perform network checks).
from Store.registry import list_configured_backend_names
is_storage_backend_location = location in (
list_configured_backend_names(config) or []
)
# Check against the cached startup list of available backends
from cmdlet._shared import SharedArgs
available_backends = SharedArgs.get_store_choices(config)
is_storage_backend_location = location in available_backends
except Exception:
is_storage_backend_location = False
@@ -546,7 +544,10 @@ class Add_File(Cmdlet):
# Update pipe_obj with resolved path
pipe_obj.path = str(media_path)
if not self._validate_source(media_path):
# When using -path (filesystem export), allow all file types.
# When using -store (backend), restrict to SUPPORTED_MEDIA_EXTENSIONS.
allow_all_files = not (location and is_storage_backend_location)
if not self._validate_source(media_path, allow_all_extensions=allow_all_files):
failures += 1
continue
@@ -1193,8 +1194,15 @@ class Add_File(Cmdlet):
return files_info
@staticmethod
def _validate_source(media_path: Optional[Path]) -> bool:
"""Validate that the source file exists and is supported."""
@staticmethod
def _validate_source(media_path: Optional[Path], allow_all_extensions: bool = False) -> bool:
"""Validate that the source file exists and is supported.
Args:
media_path: Path to the file to validate
allow_all_extensions: If True, skip file type filtering (used for -path exports).
If False, only allow SUPPORTED_MEDIA_EXTENSIONS (used for -store).
"""
if media_path is None:
return False
@@ -1214,11 +1222,12 @@ class Add_File(Cmdlet):
log(f"File not found: {media_path}")
return False
# Validate file type
file_extension = media_path.suffix.lower()
if file_extension not in SUPPORTED_MEDIA_EXTENSIONS:
log(f"❌ Unsupported file type: {file_extension}", file=sys.stderr)
return False
# Validate file type: only when adding to -store backend, not for -path exports
if not allow_all_extensions:
file_extension = media_path.suffix.lower()
if file_extension not in SUPPORTED_MEDIA_EXTENSIONS:
log(f"❌ Unsupported file type: {file_extension}", file=sys.stderr)
return False
return True