SABnzbd path mapping + ASIN-based request deletion

Add bidirectional path mapping and complete_dir-aware category sync to the SABnzbd integration. Introduces PathMapper usage, complete_dir extraction, calculateCategoryPath(), and ensureCategory() logic to choose empty/relative/absolute category paths; ensureCategory is invoked before adding NZBs. Update singleton factory to load download_dir and path-mapping config from DownloadClientManager and recreate the service when config is not loaded. Make DownloadClientManager pass path-mapping config into the SABnzbd service. Change request deletion to remove plex_library records by ASIN (deleteMany) with a fallback to exact title/author matches so availability checks and deletions are consistent. Update documentation and tests to reflect the new behavior and APIs.
This commit is contained in:
kikootwo
2026-02-03 12:20:44 -05:00
parent 11376b36a2
commit c559f8ebe9
12 changed files with 805 additions and 131 deletions
@@ -44,7 +44,8 @@ describe('IndexerConfigModal', () => {
priority: 25,
seedingTimeMinutes: 0,
rssEnabled: false,
categories: expect.arrayContaining([3030]),
audiobookCategories: expect.arrayContaining([3030]),
ebookCategories: expect.arrayContaining([7020]),
})
);
expect(onClose).toHaveBeenCalledTimes(1);
@@ -63,6 +64,7 @@ describe('IndexerConfigModal', () => {
/>
);
// Find the Audiobook toggle in the category tree and click it to deselect
const audiobookLabel = screen.getByText('Audiobook');
const audiobookRow = audiobookLabel.closest('div')?.parentElement;
if (!audiobookRow) {
@@ -72,7 +74,8 @@ describe('IndexerConfigModal', () => {
fireEvent.click(within(audiobookRow).getByRole('switch'));
fireEvent.click(screen.getByRole('button', { name: 'Add Indexer' }));
expect(screen.getByText('At least one category must be selected')).toBeInTheDocument();
// Component now shows specific error for audiobook categories
expect(screen.getByText('At least one audiobook category must be selected')).toBeInTheDocument();
expect(onSave).not.toHaveBeenCalled();
});