mirror of
https://github.com/shadoll/moma.git
synced 2026-08-28 03:27:34 +00:00
added media catalog mode, impooved cache
This commit is contained in:
+74
-12
@@ -1,6 +1,7 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import Tree, Static, Footer, LoadingIndicator
|
||||
from textual.containers import Horizontal, Container, ScrollableContainer, Vertical
|
||||
from textual.widget import Widget
|
||||
from rich.markup import escape
|
||||
from pathlib import Path
|
||||
import threading
|
||||
@@ -9,11 +10,14 @@ import logging
|
||||
import os
|
||||
|
||||
from .constants import MEDIA_TYPES
|
||||
from .screens import OpenScreen, HelpScreen, RenameConfirmScreen
|
||||
from .screens import OpenScreen, HelpScreen, RenameConfirmScreen, SettingsScreen
|
||||
from .extractors.extractor import MediaExtractor
|
||||
from .formatters.media_formatter import MediaFormatter
|
||||
from .formatters.proposed_name_formatter import ProposedNameFormatter
|
||||
from .formatters.text_formatter import TextFormatter
|
||||
from .formatters.catalog_formatter import CatalogFormatter
|
||||
from .settings import Settings
|
||||
from .cache import Cache
|
||||
|
||||
|
||||
# Set up logging conditionally
|
||||
@@ -43,13 +47,17 @@ class RenamerApp(App):
|
||||
("f", "refresh", "Refresh"),
|
||||
("r", "rename", "Rename"),
|
||||
("p", "expand", "Toggle Tree"),
|
||||
("m", "toggle_mode", "Toggle Mode"),
|
||||
("h", "help", "Help"),
|
||||
("ctrl+s", "settings", "Settings"),
|
||||
]
|
||||
|
||||
def __init__(self, scan_dir):
|
||||
super().__init__()
|
||||
self.scan_dir = Path(scan_dir) if scan_dir else None
|
||||
self.tree_expanded = False
|
||||
self.settings = Settings()
|
||||
self.cache = Cache()
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
with Horizontal():
|
||||
@@ -60,7 +68,10 @@ class RenamerApp(App):
|
||||
yield LoadingIndicator(id="loading")
|
||||
with ScrollableContainer(id="details_container"):
|
||||
yield Static(
|
||||
"Select a file to view details", id="details", markup=True
|
||||
"Select a file to view details", id="details_technical", markup=True
|
||||
)
|
||||
yield Static(
|
||||
"", id="details_catalog", markup=False
|
||||
)
|
||||
yield Static("", id="proposed", markup=True)
|
||||
yield Footer()
|
||||
@@ -73,7 +84,7 @@ class RenamerApp(App):
|
||||
def scan_files(self):
|
||||
logging.info("scan_files called")
|
||||
if not self.scan_dir or not self.scan_dir.exists() or not self.scan_dir.is_dir():
|
||||
details = self.query_one("#details", Static)
|
||||
details = self.query_one("#details_technical", Static)
|
||||
details.update("Error: Directory does not exist or is not a directory")
|
||||
return
|
||||
tree = self.query_one("#file_tree", Tree)
|
||||
@@ -105,7 +116,11 @@ class RenamerApp(App):
|
||||
def _start_loading_animation(self):
|
||||
loading = self.query_one("#loading", LoadingIndicator)
|
||||
loading.display = True
|
||||
details = self.query_one("#details", Static)
|
||||
mode = self.settings.get("mode")
|
||||
if mode == "technical":
|
||||
details = self.query_one("#details_technical", Static)
|
||||
else:
|
||||
details = self.query_one("#details_catalog", Static)
|
||||
details.update("Retrieving media data")
|
||||
proposed = self.query_one("#proposed", Static)
|
||||
proposed.update("")
|
||||
@@ -119,7 +134,10 @@ class RenamerApp(App):
|
||||
if node.data and isinstance(node.data, Path):
|
||||
if node.data.is_dir():
|
||||
self._stop_loading_animation()
|
||||
details = self.query_one("#details", Static)
|
||||
details = self.query_one("#details_technical", Static)
|
||||
details.display = True
|
||||
details_catalog = self.query_one("#details_catalog", Static)
|
||||
details_catalog.display = False
|
||||
details.update("Directory")
|
||||
proposed = self.query_one("#proposed", Static)
|
||||
proposed.update("")
|
||||
@@ -133,12 +151,20 @@ class RenamerApp(App):
|
||||
time.sleep(1) # Minimum delay to show loading
|
||||
try:
|
||||
# Initialize extractors and formatters
|
||||
extractor = MediaExtractor(file_path)
|
||||
|
||||
extractor = MediaExtractor.create(file_path, self.cache, self.settings.get("cache_ttl_extractors"))
|
||||
|
||||
mode = self.settings.get("mode")
|
||||
if mode == "technical":
|
||||
formatter = MediaFormatter(extractor)
|
||||
full_info = formatter.file_info_panel()
|
||||
else: # catalog
|
||||
formatter = CatalogFormatter(extractor)
|
||||
full_info = formatter.format_catalog_info()
|
||||
|
||||
# Update UI
|
||||
self.call_later(
|
||||
self._update_details,
|
||||
MediaFormatter(extractor).file_info_panel(),
|
||||
full_info,
|
||||
ProposedNameFormatter(extractor).rename_line_formatted(file_path),
|
||||
)
|
||||
except Exception as e:
|
||||
@@ -150,9 +176,18 @@ class RenamerApp(App):
|
||||
|
||||
def _update_details(self, full_info: str, display_string: str):
|
||||
self._stop_loading_animation()
|
||||
details = self.query_one("#details", Static)
|
||||
details.update(full_info)
|
||||
|
||||
details_technical = self.query_one("#details_technical", Static)
|
||||
details_catalog = self.query_one("#details_catalog", Static)
|
||||
mode = self.settings.get("mode")
|
||||
if mode == "technical":
|
||||
details_technical.display = True
|
||||
details_catalog.display = False
|
||||
details_technical.update(full_info)
|
||||
else:
|
||||
details_technical.display = False
|
||||
details_catalog.display = True
|
||||
details_catalog.update(full_info)
|
||||
|
||||
proposed = self.query_one("#proposed", Static)
|
||||
proposed.update(display_string)
|
||||
|
||||
@@ -170,6 +205,11 @@ class RenamerApp(App):
|
||||
tree = self.query_one("#file_tree", Tree)
|
||||
node = tree.cursor_node
|
||||
if node and node.data and isinstance(node.data, Path) and node.data.is_file():
|
||||
# Clear cache for this file
|
||||
cache_key_base = str(node.data)
|
||||
# Invalidate all keys for this file (we can improve this later)
|
||||
for key in ["title", "year", "source", "extension", "video_tracks", "audio_tracks", "subtitle_tracks"]:
|
||||
self.cache.invalidate(f"{cache_key_base}_{key}")
|
||||
self._start_loading_animation()
|
||||
threading.Thread(
|
||||
target=self._extract_and_show_details, args=(node.data,)
|
||||
@@ -178,12 +218,29 @@ class RenamerApp(App):
|
||||
async def action_help(self):
|
||||
self.push_screen(HelpScreen())
|
||||
|
||||
async def action_settings(self):
|
||||
self.push_screen(SettingsScreen())
|
||||
|
||||
async def action_toggle_mode(self):
|
||||
current_mode = self.settings.get("mode")
|
||||
new_mode = "catalog" if current_mode == "technical" else "technical"
|
||||
self.settings.set("mode", new_mode)
|
||||
self.notify(f"Switched to {new_mode} mode", severity="information", timeout=2)
|
||||
# Refresh current file display if any
|
||||
tree = self.query_one("#file_tree", Tree)
|
||||
node = tree.cursor_node
|
||||
if node and node.data and isinstance(node.data, Path) and node.data.is_file():
|
||||
self._start_loading_animation()
|
||||
threading.Thread(
|
||||
target=self._extract_and_show_details, args=(node.data,)
|
||||
).start()
|
||||
|
||||
async def action_rename(self):
|
||||
tree = self.query_one("#file_tree", Tree)
|
||||
node = tree.cursor_node
|
||||
if node and node.data and isinstance(node.data, Path) and node.data.is_file():
|
||||
# Get the proposed name from the extractor
|
||||
extractor = MediaExtractor(node.data)
|
||||
extractor = MediaExtractor.create(node.data, self.cache, self.settings.get("cache_ttl_extractors"))
|
||||
proposed_formatter = ProposedNameFormatter(extractor)
|
||||
new_name = str(proposed_formatter)
|
||||
logging.info(f"Proposed new name: {new_name!r} for file: {node.data}")
|
||||
@@ -216,6 +273,11 @@ class RenamerApp(App):
|
||||
"""Update the tree node for a renamed file."""
|
||||
logging.info(f"update_renamed_file called with old_path={old_path}, new_path={new_path}")
|
||||
|
||||
# Clear cache for old file
|
||||
cache_key_base = str(old_path)
|
||||
for key in ["title", "year", "source", "extension", "video_tracks", "audio_tracks", "subtitle_tracks"]:
|
||||
self.cache.invalidate(f"{cache_key_base}_{key}")
|
||||
|
||||
tree = self.query_one("#file_tree", Tree)
|
||||
logging.info(f"Before update: cursor_node.data = {tree.cursor_node.data if tree.cursor_node else None}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user