Commit b5c5706677ae

Vincent Demeester <vincent@sbr.pm>
2025-12-10 09:25:21
feat(arr): Add tracked state filtering to Lidarr queue management
- Enable identification of completed downloads with import failures - Support combining status and tracked state filters for precision - Improve queue troubleshooting with granular filtering options 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Vincent Demeester <vincent@sbr.pm>
1 parent d81c9f0
Changed files (3)
tools/arr/commands/lidarr_manage_queue.py
@@ -468,46 +468,63 @@ except Exception as e:
 
 
 def filter_queue_items(
-    items: List[Dict[str, Any]], filter_type: str = "all"
+    items: List[Dict[str, Any]],
+    filter_type: str = "all",
+    tracked_state: str = None
 ) -> List[Dict[str, Any]]:
     """
-    Filter queue items by type.
+    Filter queue items by type and tracked state.
 
     Args:
         items: List of queue items
         filter_type: Filter type - 'all', 'manual', 'warning',
                      'error', 'completed'
+        tracked_state: Specific tracked download state to filter by
+                      (e.g., 'importFailed', 'imported', 'importing')
 
     Returns:
         Filtered list of queue items
     """
-    if filter_type == "all":
-        return items
+    filtered = items if filter_type == "all" else []
 
-    filtered = []
-    for item in items:
-        status = item.get("status", "").lower()
-        error_message = item.get("errorMessage", "").lower()
-        tracked_download_status = item.get("trackedDownloadStatus", "")
-        tracked_download_status = tracked_download_status.lower()
+    # First apply the filter_type filter
+    if filter_type != "all":
+        for item in items:
+            status = item.get("status", "").lower()
+            error_message = item.get("errorMessage", "").lower()
+            tracked_download_status = item.get("trackedDownloadStatus", "")
+            tracked_download_status = tracked_download_status.lower()
 
-        if filter_type == "manual":
-            # Items that need manual import
-            if (
-                "warning" in status
-                or "manual" in tracked_download_status
-                or "manual import" in error_message
-            ):
-                filtered.append(item)
-        elif filter_type == "warning":
-            if "warning" in status:
-                filtered.append(item)
-        elif filter_type == "error":
-            if "error" in status or item.get("errorMessage"):
-                filtered.append(item)
-        elif filter_type == "completed":
-            if "completed" in status or "completed" in tracked_download_status:
-                filtered.append(item)
+            if filter_type == "manual":
+                # Items that need manual import
+                if (
+                    "warning" in status
+                    or "manual" in tracked_download_status
+                    or "manual import" in error_message
+                ):
+                    filtered.append(item)
+            elif filter_type == "warning":
+                if "warning" in status:
+                    filtered.append(item)
+            elif filter_type == "error":
+                if "error" in status or item.get("errorMessage"):
+                    filtered.append(item)
+            elif filter_type == "completed":
+                if (
+                    "completed" in status
+                    or "completed" in tracked_download_status
+                ):
+                    filtered.append(item)
+
+    # Then apply the tracked_state filter if specified
+    if tracked_state:
+        tracked_state_lower = tracked_state.lower()
+        filtered = [
+            item
+            for item in filtered
+            if item.get("trackedDownloadState", "").lower()
+            == tracked_state_lower
+        ]
 
     return filtered
 
@@ -516,6 +533,7 @@ def run(
     url: str,
     api_key: str,
     filter_type: str,
+    tracked_state: str,
     remove_from_client: bool,
     blocklist: bool,
     skip_redownload: bool,
@@ -537,17 +555,31 @@ def run(
         print("\nQueue is empty!")
         return
 
-    # Filter items based on filter type
-    filtered_items = filter_queue_items(all_items, filter_type)
+    # Filter items based on filter type and tracked state
+    filtered_items = filter_queue_items(all_items, filter_type, tracked_state)
 
+    # Build filter description
+    filter_desc_parts = []
     if filter_type != "all":
+        filter_desc_parts.append(f"type: {filter_type}")
+    if tracked_state:
+        filter_desc_parts.append(f"tracked state: {tracked_state}")
+
+    if filter_desc_parts:
+        filter_desc = ", ".join(filter_desc_parts)
         print(
             f"Filtered to {len(filtered_items)} items "
-            f"(filter: {filter_type})"
+            f"({filter_desc})"
         )
 
     if not filtered_items:
-        print(f"\nNo items matching filter '{filter_type}'")
+        filter_msg = f"filter '{filter_type}'"
+        if tracked_state:
+            filter_msg = (
+                f"filters (type: {filter_type}, "
+                f"tracked state: {tracked_state})"
+            )
+        print(f"\nNo items matching {filter_msg}")
         return
 
     print_section_header("QUEUE ITEMS")
tools/arr/arr
@@ -223,12 +223,13 @@ def lidarr_update_metadata_profile(url, api_key, profile, artist, dry_run, no_co
 @click.argument("url")
 @click.argument("api_key")
 @click.option("--filter", "filter_type", default="all", type=click.Choice(["all", "manual", "warning", "error", "completed"]), help="Filter queue items by type (default: all)")
+@click.option("--tracked-state", default=None, help="Filter by tracked download state (e.g., importFailed, imported, importing)")
 @click.option("--remove-from-client/--keep-in-client", default=True, help="Remove from download client (default: yes)")
 @click.option("--blocklist/--no-blocklist", default=False, help="Add to blocklist (default: no)")
 @click.option("--skip-redownload/--allow-redownload", default=False, help="Skip automatic redownload (default: no)")
 @click.option("--dry-run", is_flag=True, help="Show what would be removed without making changes")
 @click.option("--no-confirm", "--yolo", is_flag=True, help="Skip interactive confirmation (use with caution)")
-def lidarr_manage_queue(url, api_key, filter_type, remove_from_client, blocklist, skip_redownload, dry_run, no_confirm):
+def lidarr_manage_queue(url, api_key, filter_type, tracked_state, remove_from_client, blocklist, skip_redownload, dry_run, no_confirm):
     """Manage Lidarr queue items with interactive selection.
 
     Browse and remove items from the Lidarr queue, with filtering options
@@ -244,6 +245,13 @@ def lidarr_manage_queue(url, api_key, filter_type, remove_from_client, blocklist
       - error: Items with errors
       - completed: Items that have completed downloading
 
+    Tracked states (use with --tracked-state):
+      - importFailed: Items where import failed
+      - imported: Successfully imported items
+      - importing: Currently importing items
+      - failedPending: Failed items pending retry
+      - (and other tracked download states from Lidarr)
+
     Examples:
         # View and manage all queue items
         arr lidarr manage-queue http://localhost:8686 your-api-key
@@ -252,6 +260,14 @@ def lidarr_manage_queue(url, api_key, filter_type, remove_from_client, blocklist
         arr lidarr manage-queue http://localhost:8686 your-api-key \\
             --filter manual
 
+        # Show completed items where import failed
+        arr lidarr manage-queue http://localhost:8686 your-api-key \\
+            --filter completed --tracked-state importFailed
+
+        # Show only items with specific tracked state
+        arr lidarr manage-queue http://localhost:8686 your-api-key \\
+            --tracked-state importing
+
         # Remove items with errors, add to blocklist
         arr lidarr manage-queue http://localhost:8686 your-api-key \\
             --filter error --blocklist
@@ -265,6 +281,7 @@ def lidarr_manage_queue(url, api_key, filter_type, remove_from_client, blocklist
         url,
         api_key,
         filter_type,
+        tracked_state,
         remove_from_client,
         blocklist,
         skip_redownload,
tools/arr/README.md
@@ -510,6 +510,12 @@ arr lidarr manage-queue http://localhost:8686 your-api-key --filter error
 # Show only completed items
 arr lidarr manage-queue http://localhost:8686 your-api-key --filter completed
 
+# Show completed items where import failed
+arr lidarr manage-queue http://localhost:8686 your-api-key --filter completed --tracked-state importFailed
+
+# Show only items with specific tracked state
+arr lidarr manage-queue http://localhost:8686 your-api-key --tracked-state importing
+
 # Remove items and add to blocklist
 arr lidarr manage-queue http://localhost:8686 your-api-key --blocklist
 
@@ -527,6 +533,15 @@ arr lidarr manage-queue http://localhost:8686 your-api-key --filter manual --dry
 - `error`: Items with errors
 - `completed`: Items that have completed downloading
 
+**Tracked states** (use with `--tracked-state`):
+- `importFailed`: Items where import has failed
+- `imported`: Successfully imported items
+- `importing`: Currently importing items
+- `failedPending`: Failed items pending retry
+- And other tracked download states from Lidarr API
+
+You can combine `--filter` and `--tracked-state` to narrow down results (e.g., completed items where import failed).
+
 **Removal options:**
 - `--remove-from-client` (default): Remove the item from your download client
 - `--keep-in-client`: Leave the item in your download client