This commit is contained in:
nose
2025-12-11 12:47:30 -08:00
parent 6b05dc5552
commit 65d12411a2
92 changed files with 17447 additions and 14308 deletions

View File

@@ -233,8 +233,8 @@ class DownloadModal(ModalScreen):
self.screenshot_checkbox.value = False
self.playlist_merge_checkbox.value = False
# Initialize PDF playlist URLs (set by _handle_pdf_playlist)
self.pdf_urls = []
# Initialize PDF playlist url (set by _handle_pdf_playlist)
self.pdf_url = []
self.is_pdf_playlist = False
# Hide playlist by default (show format select)
@@ -288,10 +288,10 @@ class DownloadModal(ModalScreen):
# Launch the background worker with PDF playlist info
self._submit_worker(url, tags, source, download_enabled, playlist_selection, merge_enabled,
is_pdf_playlist=self.is_pdf_playlist, pdf_urls=self.pdf_urls if self.is_pdf_playlist else [])
is_pdf_playlist=self.is_pdf_playlist, pdf_url=self.pdf_url if self.is_pdf_playlist else [])
@work(thread=True)
def _submit_worker(self, url: str, tags: list, source: str, download_enabled: bool, playlist_selection: str = "", merge_enabled: bool = False, is_pdf_playlist: bool = False, pdf_urls: Optional[list] = None) -> None:
def _submit_worker(self, url: str, tags: list, source: str, download_enabled: bool, playlist_selection: str = "", merge_enabled: bool = False, is_pdf_playlist: bool = False, pdf_url: Optional[list] = None) -> None:
"""Background worker to execute the cmdlet pipeline.
Args:
@@ -302,10 +302,10 @@ class DownloadModal(ModalScreen):
playlist_selection: Playlist track selection (e.g., "1-3", "all", "merge")
merge_enabled: Whether to merge playlist files after download
is_pdf_playlist: Whether this is a PDF pseudo-playlist
pdf_urls: List of PDF URLs if is_pdf_playlist is True
pdf_url: List of PDF url if is_pdf_playlist is True
"""
if pdf_urls is None:
pdf_urls = []
if pdf_url is None:
pdf_url = []
# Initialize worker to None so outer exception handler can check it
worker = None
@@ -340,9 +340,9 @@ class DownloadModal(ModalScreen):
worker.log_step("Download initiated")
# Handle PDF playlist specially
if is_pdf_playlist and pdf_urls:
logger.info(f"Processing PDF playlist with {len(pdf_urls)} PDFs")
self._handle_pdf_playlist_download(pdf_urls, tags, playlist_selection, merge_enabled)
if is_pdf_playlist and pdf_url:
logger.info(f"Processing PDF playlist with {len(pdf_url)} PDFs")
self._handle_pdf_playlist_download(pdf_url, tags, playlist_selection, merge_enabled)
self.app.call_from_thread(self._hide_progress)
self.app.call_from_thread(self.dismiss)
return
@@ -690,7 +690,7 @@ class DownloadModal(ModalScreen):
'media_kind': 'audio',
'hash_hex': None,
'hash': None,
'known_urls': [],
'url': [],
'title': filepath_obj.stem
})()
files_to_merge.append(file_result)
@@ -934,8 +934,8 @@ class DownloadModal(ModalScreen):
"""Scrape metadata from URL(s) in URL textarea - wipes tags and source.
This is triggered by Ctrl+T when URL textarea is focused.
Supports single URL or multiple URLs (newline/comma-separated).
For multiple PDF URLs, creates pseudo-playlist for merge workflow.
Supports single URL or multiple url (newline/comma-separated).
For multiple PDF url, creates pseudo-playlist for merge workflow.
"""
try:
text = self.paragraph_textarea.text.strip()
@@ -943,29 +943,29 @@ class DownloadModal(ModalScreen):
logger.warning("No URL to scrape metadata from")
return
# Parse multiple URLs (newline or comma-separated)
urls = []
# Parse multiple url (newline or comma-separated)
url = []
for line in text.split('\n'):
line = line.strip()
if line:
# Handle comma-separated URLs within a line
# Handle comma-separated url within a line
for url in line.split(','):
url = url.strip()
if url:
urls.append(url)
url.append(url)
# Check if multiple URLs provided
if len(urls) > 1:
logger.info(f"Detected {len(urls)} URLs - checking for PDF pseudo-playlist")
# Check if all URLs appear to be PDFs
all_pdfs = all(url.endswith('.pdf') or 'pdf' in url.lower() for url in urls)
# Check if multiple url provided
if len(url) > 1:
logger.info(f"Detected {len(url)} url - checking for PDF pseudo-playlist")
# Check if all url appear to be PDFs
all_pdfs = all(url.endswith('.pdf') or 'pdf' in url.lower() for url in url)
if all_pdfs:
logger.info(f"All URLs are PDFs - creating pseudo-playlist")
self._handle_pdf_playlist(urls)
logger.info(f"All url are PDFs - creating pseudo-playlist")
self._handle_pdf_playlist(url)
return
# Single URL - proceed with normal metadata scraping
url = urls[0] if urls else text.strip()
url = url[0] if url else text.strip()
logger.info(f"Scraping fresh metadata from: {url}")
# Check if tags are already provided in textarea
@@ -1044,21 +1044,21 @@ class DownloadModal(ModalScreen):
)
def _handle_pdf_playlist(self, pdf_urls: list) -> None:
"""Handle multiple PDF URLs as a pseudo-playlist.
def _handle_pdf_playlist(self, pdf_url: list) -> None:
"""Handle multiple PDF url as a pseudo-playlist.
Creates a playlist-like structure with PDF metadata for merge workflow.
Extracts title from URL or uses default naming.
Args:
pdf_urls: List of PDF URLs to process
pdf_url: List of PDF url to process
"""
try:
logger.info(f"Creating PDF pseudo-playlist with {len(pdf_urls)} items")
logger.info(f"Creating PDF pseudo-playlist with {len(pdf_url)} items")
# Create playlist items from PDF URLs
# Create playlist items from PDF url
playlist_items = []
for idx, url in enumerate(pdf_urls, 1):
for idx, url in enumerate(pdf_url, 1):
# Extract filename from URL for display
try:
# Get filename from URL path
@@ -1083,15 +1083,15 @@ class DownloadModal(ModalScreen):
# Build minimal metadata structure for UI population
metadata = {
'title': f'{len(pdf_urls)} PDF Documents',
'title': f'{len(pdf_url)} PDF Documents',
'tags': [],
'formats': [('pdf', 'pdf')], # Default format is PDF
'playlist_items': playlist_items,
'is_pdf_playlist': True # Mark as PDF pseudo-playlist
}
# Store URLs for later use during merge
self.pdf_urls = pdf_urls
# Store url for later use during merge
self.pdf_url = pdf_url
self.is_pdf_playlist = True
# Populate the modal with metadata
@@ -1099,7 +1099,7 @@ class DownloadModal(ModalScreen):
self._populate_from_metadata(metadata, wipe_tags_and_source=True)
self.app.notify(
f"Loaded {len(pdf_urls)} PDFs as playlist",
f"Loaded {len(pdf_url)} PDFs as playlist",
title="PDF Playlist",
severity="information",
timeout=3
@@ -1115,11 +1115,11 @@ class DownloadModal(ModalScreen):
)
def _handle_pdf_playlist_download(self, pdf_urls: list, tags: list, selection: str, merge_enabled: bool) -> None:
def _handle_pdf_playlist_download(self, pdf_url: list, tags: list, selection: str, merge_enabled: bool) -> None:
"""Download and merge PDF playlist.
Args:
pdf_urls: List of PDF URLs to download
pdf_url: List of PDF url to download
tags: Tags to apply to the merged PDF
selection: Selection string like "1-3" or "1,3,5"
merge_enabled: Whether to merge the PDFs
@@ -1141,7 +1141,7 @@ class DownloadModal(ModalScreen):
# Create temporary list of playlist items for selection parsing
# We need this because _parse_playlist_selection uses self.playlist_items
temp_items = []
for url in pdf_urls:
for url in pdf_url:
temp_items.append({'title': url})
self.playlist_items = temp_items
@@ -1149,20 +1149,20 @@ class DownloadModal(ModalScreen):
selected_indices = self._parse_playlist_selection(selection)
if not selected_indices:
# No valid selection, use all
selected_indices = list(range(len(pdf_urls)))
selected_indices = list(range(len(pdf_url)))
selected_urls = [pdf_urls[i] for i in selected_indices]
selected_url = [pdf_url[i] for i in selected_indices]
logger.info(f"Downloading {len(selected_urls)} selected PDFs for merge")
logger.info(f"Downloading {len(selected_url)} selected PDFs for merge")
# Download PDFs to temporary directory
temp_dir = Path.home() / ".downlow_temp_pdfs"
temp_dir.mkdir(exist_ok=True)
downloaded_files = []
for idx, url in enumerate(selected_urls, 1):
for idx, url in enumerate(selected_url, 1):
try:
logger.info(f"Downloading PDF {idx}/{len(selected_urls)}: {url}")
logger.info(f"Downloading PDF {idx}/{len(selected_url)}: {url}")
response = requests.get(url, timeout=30)
response.raise_for_status()
@@ -1619,7 +1619,7 @@ class DownloadModal(ModalScreen):
)
return
else:
success_msg = "download-data completed successfully"
success_msg = "download-data completed successfully"
logger.info(success_msg)
if worker:
worker.append_stdout(f"{success_msg}\n")
@@ -1670,7 +1670,7 @@ class DownloadModal(ModalScreen):
worker.append_stdout(f"{warning_msg}\n")
else:
if worker:
worker.append_stdout("Tags applied successfully\n")
worker.append_stdout("Tags applied successfully\n")
except Exception as e:
error_msg = f"❌ Tagging error: {e}"
logger.error(error_msg, exc_info=True)
@@ -1684,7 +1684,7 @@ class DownloadModal(ModalScreen):
worker.append_stdout(f"{warning_msg}\n")
else:
if worker:
worker.append_stdout("Download complete (no tags to apply)\n")
worker.append_stdout("Download complete (no tags to apply)\n")
def _show_format_select(self) -> None:
"""Show format select (always visible for single files)."""
@@ -1770,9 +1770,9 @@ class DownloadModal(ModalScreen):
# Namespaces to exclude (metadata-only, not user-facing)
excluded_namespaces = {
'hash', # Hash values (internal)
'known_url', # URLs (internal)
'url', # url (internal)
'relationship', # Internal relationships
'url', # URLs (internal)
'url', # url (internal)
}
# Add all other tags