update refactoring and add new features

This commit is contained in:
2026-07-24 20:55:58 -07:00
parent 7f12bc7f40
commit 03fbbbcf28
69 changed files with 16332 additions and 17600 deletions
+10
View File
@@ -0,0 +1,10 @@
from CLI import MedeiaLexer
class DummyDocument:
def __init__(self, lines):
self.lines = list(lines)
lexer = MedeiaLexer()
doc = DummyDocument([r'C:\path\to\file'])
print(lexer.lex_document(doc)(0))
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env python3
import sys
import traceback
sys.path.insert(0, r'C:\Forgejo\Medios-Macina')
from tests.test_cli_parsing import *
failed = 0
tests = [(n, o) for n, o in globals().items() if n.startswith('test_') and callable(o)]
for n, o in tests:
try:
o()
print(n, 'OK')
except Exception as e:
print(n, 'FAIL', e)
traceback.print_exc()
failed += 1
if failed:
sys.exit(1)
print('All tests passed')
+10
View File
@@ -0,0 +1,10 @@
import importlib
import sys
import traceback
try:
importlib.import_module("CLI")
print("CLI imported OK")
except Exception:
traceback.print_exc()
sys.exit(1)
+23
View File
@@ -0,0 +1,23 @@
from pathlib import Path
p = Path('Store/registry.py')
lines = p.read_text(encoding='utf-8').splitlines()
stack = [0]
for i, line in enumerate(lines, start=1):
if not line.strip():
continue
leading = len(line) - len(line.lstrip(' '))
if leading > stack[-1]:
# increased indent -> push
stack.append(leading)
else:
# dedent: must match an existing level
if leading != stack[-1]:
if leading in stack:
# pop until match
while stack and stack[-1] != leading:
stack.pop()
else:
print(f"Line {i}: inconsistent indent {leading}, stack levels {stack}")
print(repr(line))
break
print('Done')
+19
View File
@@ -0,0 +1,19 @@
import re
from pathlib import Path
p = Path(r'c:\Forgejo\Medios-Macina\CLI.py')
s = p.read_text(encoding='utf-8')
pattern = re.compile(r'(?s)if False:\s*class _OldPipelineExecutor:.*?from rich\\.markdown import Markdown\\s*')
m = pattern.search(s)
print('found', bool(m))
if m:
print('start', m.start(), 'end', m.end())
print('snippet:', s[m.start():m.start()+120])
else:
# print a slice around the if False for debugging
i = s.find('if False:')
print('if False index', i)
print('around if False:', s[max(0,i-50):i+200])
j = s.find('from rich.markdown import Markdown', i)
print('next from rich index after if False', j)
if j!=-1:
print('around that:', s[j-50:j+80])
+12
View File
@@ -0,0 +1,12 @@
import traceback
try:
from PluginCore.backend_registry import BackendRegistry
s = BackendRegistry(config={}, suppress_debug=True)
print('INSTANCE TYPE:', type(s))
print('HAS is_available:', hasattr(s, 'is_available'))
if hasattr(s, 'is_available'):
print('is_available callable:', callable(getattr(s, 'is_available')))
print('DIR:', sorted([n for n in dir(s) if not n.startswith('__')]))
except Exception:
traceback.print_exc()
+35
View File
@@ -0,0 +1,35 @@
from pathlib import Path
p=Path('SYS/pipeline.py')
s=p.read_text(encoding='utf-8')
lines=s.splitlines()
stack=[]
for i,l in enumerate(lines,1):
stripped=l.strip()
# Skip commented lines
if stripped.startswith('#'):
continue
# compute indent as leading spaces (tabs are converted)
indent = len(l) - len(l.lstrip(' '))
if stripped.startswith('try:'):
stack.append((indent, i))
if stripped.startswith('except ') or stripped=='except:' or stripped.startswith('finally:'):
# find the most recent try with same indent
for idx in range(len(stack)-1, -1, -1):
if stack[idx][0] == indent:
stack.pop(idx)
break
else:
# no matching try at same indent
print(f"Found {stripped.split()[0]} at line {i} with no matching try at same indent")
print('Unmatched try count', len(stack))
if stack:
print('Unmatched try positions (indent, line):', stack)
for indent, lineno in stack:
start = max(1, lineno - 10)
end = min(len(lines), lineno + 10)
print(f"Context around line {lineno}:")
for i in range(start, end + 1):
print(f"{i:5d}: {lines[i-1]}")
else:
print("All try statements appear matched")
+9 -85
View File
@@ -8,7 +8,7 @@ running from a development checkout (by importing the top-level
from __future__ import annotations
from typing import Optional, List, Tuple
from typing import Optional, List
import importlib
import importlib.util
import os
@@ -64,91 +64,32 @@ def _ensure_repo_root_on_sys_path(pkg_file: Optional[Path] = None) -> Optional[P
return None
def _parse_mode_and_strip_args(args: List[str]) -> Tuple[Optional[str], List[str]]:
"""Parse --gui/--cli/--mode flags and return (mode, cleaned_args).
def _strip_mode_args(args: List[str]) -> List[str]:
"""Strip --gui/--cli/--mode flags from argument list.
The function removes any mode flags from the argument list so the selected
runner can receive the remaining arguments untouched.
Supported forms:
--gui, -g, --gui=true
--cli, -c, --cli=true
--mode=gui|cli
--mode gui|cli
Raises ValueError on conflicting or invalid flags.
The GUI/TUI mode has been discontinued. Any mode flags are silently
consumed so they don't interfere with remaining argument parsing.
"""
mode: Optional[str] = None
out: List[str] = []
i = 0
while i < len(args):
a = args[i]
la = a.lower()
# --gui / -g
if la in ("--gui", "-g"):
if mode and mode != "gui":
raise ValueError("Conflicting mode flags: found both 'gui' and 'cli'")
mode = "gui"
if la in ("--gui", "-g", "--cli", "-c"):
i += 1
continue
if la.startswith("--gui="):
val = la.split("=", 1)[1]
if val and val not in ("0", "false", "no", "off"):
if mode and mode != "gui":
raise ValueError(
"Conflicting mode flags: found both 'gui' and 'cli'"
)
mode = "gui"
if la.startswith("--gui=") or la.startswith("--cli="):
i += 1
continue
# --cli / -c
if la in ("--cli", "-c"):
if mode and mode != "cli":
raise ValueError("Conflicting mode flags: found both 'gui' and 'cli'")
mode = "cli"
i += 1
continue
if la.startswith("--cli="):
val = la.split("=", 1)[1]
if val and val not in ("0", "false", "no", "off"):
if mode and mode != "cli":
raise ValueError(
"Conflicting mode flags: found both 'gui' and 'cli'"
)
mode = "cli"
i += 1
continue
# --mode
if la.startswith("--mode="):
val = la.split("=", 1)[1]
val = val.lower()
if val not in ("gui", "cli"):
raise ValueError("--mode must be 'gui' or 'cli'")
if mode and mode != val:
raise ValueError("Conflicting mode flags: found both 'gui' and 'cli'")
mode = val
i += 1
continue
if la == "--mode":
if i + 1 >= len(args):
raise ValueError("--mode requires a value ('gui' or 'cli')")
val = args[i + 1].lower()
if val not in ("gui", "cli"):
raise ValueError("--mode must be 'gui' or 'cli'")
if mode and mode != val:
raise ValueError("Conflicting mode flags: found both 'gui' and 'cli'")
mode = val
i += 2
continue
# Not a mode flag; keep it
out.append(a)
i += 1
return mode, out
return out
def _run_cli(clean_args: List[str]) -> int:
@@ -221,15 +162,6 @@ def _run_cli(clean_args: List[str]) -> int:
return int(getattr(exc, "code", 0) or 0)
def _run_gui(clean_args: List[str]) -> int:
"""Report that the discontinued GUI/TUI mode is no longer available."""
_ = clean_args
print(
"Error: GUI/TUI mode has been discontinued and is no longer available.",
file=sys.stderr,
)
return 2
def main(argv: Optional[List[str]] = None) -> int:
"""Entry point for console_scripts.
@@ -239,11 +171,7 @@ def main(argv: Optional[List[str]] = None) -> int:
"""
args = list(argv) if argv is not None else list(sys.argv[1:])
try:
mode, clean_args = _parse_mode_and_strip_args(args)
except ValueError as exc:
print(f"Error parsing mode flags: {exc}", file=sys.stderr)
return 2
clean_args = _strip_mode_args(args)
# Early environment sanity check to detect urllib3/urllib3-future conflicts.
# When a broken urllib3 is detected we print an actionable message and
@@ -262,10 +190,6 @@ def main(argv: Optional[List[str]] = None) -> int:
# startup; we'll continue and let normal import errors surface.
pass
# If GUI requested, delegate directly (GUI may decide to honor any args itself)
if mode == "gui":
return _run_gui(clean_args)
# Support quoting a pipeline (or even a single full command) on the command line.
#
# - If the user provides a single argument that contains a pipe character,
+10
View File
@@ -0,0 +1,10 @@
import importlib
import traceback
try:
m = importlib.import_module('Provider.vimm')
print('Imported', m)
print('Vimm class:', getattr(m, 'Vimm', None))
except Exception as e:
print('Import failed:', e)
traceback.print_exc()
+6
View File
@@ -0,0 +1,6 @@
from pathlib import Path
p = Path('Store/registry.py')
for i, line in enumerate(p.read_text(encoding='utf-8').splitlines(), start=1):
leading = len(line) - len(line.lstrip(' '))
if leading > 20:
print(i, leading, repr(line))
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env python3
"""Format all tracked Python files using the repo's YAPF style.
This script intentionally formats only files tracked by git to avoid touching
files that are ignored (e.g., venv, site-packages).
"""
import subprocess
import sys
try:
out = subprocess.check_output(["git", "ls-files", "*.py"]).decode("utf-8")
except subprocess.CalledProcessError as exc:
print("Failed to get tracked files from git:", exc, file=sys.stderr)
sys.exit(1)
files = [f for f in (line.strip() for line in out.splitlines()) if f]
print(f"Formatting {len(files)} tracked python files...")
if not files:
print("No tracked python files found.")
sys.exit(0)
for path in files:
print(path)
subprocess.run([sys.executable, "-m", "yapf", "-i", "--style", ".style.yapf", path], check=False)
print("Done")
+11
View File
@@ -0,0 +1,11 @@
import pathlib
p = pathlib.Path('Store/registry.py')
with p.open('r', encoding='utf-8') as f:
lines = f.readlines()
for i in range(312, 328):
if i-1 < len(lines):
line = lines[i-1]
leading = line[:len(line)-len(line.lstrip('\t '))]
print(f"{i}: {repr(line.rstrip())} | leading={repr(leading)} len={len(leading)} chars={[ord(c) for c in leading]}")
else:
print(f"{i}: <EOF>")
+10
View File
@@ -0,0 +1,10 @@
from pathlib import Path
p = Path('Store/registry.py')
counts = {}
for i, line in enumerate(p.read_text(encoding='utf-8').splitlines(), start=1):
if not line.strip():
continue
leading = len(line) - len(line.lstrip(' '))
counts[leading] = counts.get(leading, 0) + 1
for k in sorted(counts.keys()):
print(k, counts[k])
+3
View File
@@ -0,0 +1,3 @@
from PluginCore.registry import list_plugins
print('All plugins:', list_plugins())
-197
View File
@@ -1,197 +0,0 @@
[build-system]
requires = ["setuptools>=65.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "medeia-macina"
version = "0.1.0"
description = "Comprehensive media management and search platform with support for local files, Hydrus database, torrents, books, and P2P networks"
requires-python = ">=3.9,<3.14"
license = {text = "MIT"}
authors = [
{name = "Your Name", email = "your.email@example.com"}
]
keywords = ["media", "search", "management", "hydrus", "download", "cli", "tui"]
classifiers = [
"Development Status :: 3 - Alpha",
"Environment :: Console",
"Intended Audience :: End Users/Desktop",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Multimedia",
"Topic :: Internet",
]
dependencies = [
# Core CLI and TUI frameworks
"typer>=0.9.0",
"prompt-toolkit>=3.0.0",
"textual>=0.30.0",
# Media processing and downloading
"yt-dlp[default]>=2023.11.0",
"yt-dlp-ejs", # EJS challenge solver scripts for YouTube JavaScript challenges
"requests>=2.31.0",
"charset-normalizer>=3.2.0",
"certifi>=2024.12.0",
"httpx>=0.25.0",
# Document and data handling
"pypdf>=3.0.0",
"mutagen>=1.46.0",
"cbor2>=4.0",
"zstandard>=0.23.0",
# Image and media support
"Pillow>=10.0.0",
"python-bidi>=0.4.2",
"ffmpeg-python>=0.2.0",
# Metadata extraction and processing
"musicbrainzngs>=0.7.0",
"lxml>=4.9.0",
# Advanced searching and libraries
# Optional Soulseek support installs aioslsk>=1.6.0 when [provider=soulseek] is configured.
"imdbinfo>=0.1.10",
# Encryption and security
"pycryptodome>=3.18.0",
# Data processing
"bencode3",
"tqdm>=4.66.0",
# Browser automation
"playwright>=1.40.0",
"paramiko>=3.5.0",
"scp>=0.15.0",
# Development and utilities
"python-dateutil>=2.8.0",
]
[project.optional-dependencies]
dev = [
# Testing
"pytest>=7.4.0",
"pytest-cov>=4.1.0",
"pytest-asyncio>=0.21.0",
# Code quality
"black>=23.11.0",
"flake8>=6.1.0",
"isort>=5.12.0",
"mypy>=1.7.0",
"pylint>=3.0.0",
# Documentation
"sphinx>=7.2.0",
"sphinx-rtd-theme>=1.3.0",
# Debugging and profiling
"ipython>=8.17.0",
"ipdb>=0.13.0",
"memory-profiler>=0.61.0",
# Version control and CI/CD helpers
"pre-commit>=3.5.0",
]
[project.scripts]
mm = "scripts.cli_entry:main"
medeia = "scripts.cli_entry:main"
[project.urls]
Homepage = "https://github.com/yourusername/medeia-macina"
Documentation = "https://medeia-macina.readthedocs.io"
Repository = "https://github.com/yourusername/medeia-macina.git"
Issues = "https://github.com/yourusername/medeia-macina/issues"
[tool.setuptools]
packages = [
"scripts",
"cmdlet",
"cmdnat",
"API",
"PluginCore",
"SYS",
]
package-dir = {"" = ".."}
[tool.setuptools.package-data]
scripts = ["*.py"]
[tool.black]
line-length = 100
target-version = ['py39', 'py310', 'py311', 'py312']
include = '\\.pyi?$'
extend-exclude = '''
/(
# directories
\.eggs
| \.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist
| __pycache__
)/
'''
[tool.isort]
profile = "black"
line_length = 100
target_version = ["py39", "py310", "py311", "py312"]
[tool.mypy]
python_version = "3.9"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = false
disallow_incomplete_defs = false
check_untyped_defs = false
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
[tool.pylint.messages_control]
disable = [
"C0330", "C0326", # Bad whitespace
"R0913", # Too many arguments
"R0914", # Too many local variables
]
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
addopts = "-v --cov=. --cov-report=html --cov-report=term-missing"
[tool.coverage.run]
branch = true
omit = [
"*/tests/*",
"*/__main__.py",
]
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"raise AssertionError",
"raise NotImplementedError",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
]
+3 -1
View File
@@ -14,6 +14,7 @@ charset-normalizer>=3.2.0
certifi>=2024.12.0
# Optional Telegram support installs telethon>=1.36.0 when [provider=telegram] is configured.
internetarchive>=4.1.0
yt-dlp-ejs
# Document and data handling
pypdf>=3.0.0
@@ -44,7 +45,8 @@ tqdm>=4.66.0
# Browser automation (for web scraping if needed)
playwright>=1.40.0
paramiko>=3.5.0
scp>=0.15.0
# Development and utilities
python-dateutil>=2.8.0