Fix category save path not updating - check before create/edit

Root cause: We were blindly calling createCategory (409 if exists) then
editCategory (409 for unknown reason), but editCategory was failing and
the category kept its old save path, causing downloads to wrong location.

The 409 from editCategory doesn't mean 'already has this path', it means
the operation failed. Without checking first, we don't know why.

New approach:
1. GET /torrents/categories first to check current state
2. If category doesn't exist: create it with correct save path
3. If category exists with wrong path: edit to update save path
4. If category exists with correct path: skip (no API call needed)

Benefits:
- Avoids unnecessary 409 errors
- Only calls editCategory when actually needed
- Logs show exactly what's happening (create/update/skip)
- Handles both savePath and save_path response formats (v4.4.0+ compat)
- Better error logging with full response details

This ensures category save path is ACTUALLY updated when user changes
download_dir setting, fixing downloads going to wrong location.

References:
- qBittorrent API docs: https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)
- Issue #15969: save_path vs savePath inconsistency in API v2.8.4+
This commit is contained in:
Claude
2025-12-22 15:57:25 +00:00
committed by kikootwo
parent 0fa10941e1
commit 0222bca9f7
+61 -47
View File
@@ -370,7 +370,7 @@ export class QBittorrentService {
/** /**
* Ensure category exists in qBittorrent with correct save path * Ensure category exists in qBittorrent with correct save path
* Always updates the category's save path to match current config * Checks existing categories first, then creates or updates as needed
*/ */
private async ensureCategory(category: string): Promise<void> { private async ensureCategory(category: string): Promise<void> {
if (!this.cookie) { if (!this.cookie) {
@@ -378,57 +378,71 @@ export class QBittorrentService {
} }
try { try {
// Try to create category first (idempotent - won't fail if exists) // First, get all categories to check if it exists and what save path it has
await this.client.post( const categoriesResponse = await this.client.get('/torrents/categories', {
'/torrents/createCategory', headers: { Cookie: this.cookie },
new URLSearchParams({ });
category,
savePath: this.defaultSavePath,
}),
{
headers: {
Cookie: this.cookie,
'Content-Type': 'application/x-www-form-urlencoded',
},
}
);
console.log(`[qBittorrent] Category "${category}" created`); const categories = categoriesResponse.data;
} catch (error) { const existingCategory = categories[category];
// 409 = category already exists (expected, not an error)
if (axios.isAxiosError(error) && error.response?.status === 409) { if (!existingCategory) {
console.log(`[qBittorrent] Category "${category}" already exists`); // Category doesn't exist - create it
console.log(`[qBittorrent] Creating category "${category}" with save path: ${this.defaultSavePath}`);
await this.client.post(
'/torrents/createCategory',
new URLSearchParams({
category,
savePath: this.defaultSavePath,
}),
{
headers: {
Cookie: this.cookie,
'Content-Type': 'application/x-www-form-urlencoded',
},
}
);
console.log(`[qBittorrent] Category "${category}" created successfully`);
} else { } else {
// Unexpected error, but don't fail - editCategory below will handle it // Category exists - check if save path needs updating
console.warn(`[qBittorrent] Failed to create category:`, error); const currentSavePath = existingCategory.savePath || existingCategory.save_path;
if (currentSavePath !== this.defaultSavePath) {
console.log(`[qBittorrent] Updating category "${category}" save path from "${currentSavePath}" to "${this.defaultSavePath}"`);
await this.client.post(
'/torrents/editCategory',
new URLSearchParams({
category,
savePath: this.defaultSavePath,
}),
{
headers: {
Cookie: this.cookie,
'Content-Type': 'application/x-www-form-urlencoded',
},
}
);
console.log(`[qBittorrent] Category "${category}" save path updated successfully`);
} else {
console.log(`[qBittorrent] Category "${category}" already has correct save path: ${this.defaultSavePath}`);
}
} }
}
// Always update the category's save path to ensure it matches current config
// This handles cases where download_dir was changed after category was created
try {
await this.client.post(
'/torrents/editCategory',
new URLSearchParams({
category,
savePath: this.defaultSavePath,
}),
{
headers: {
Cookie: this.cookie,
'Content-Type': 'application/x-www-form-urlencoded',
},
}
);
console.log(`[qBittorrent] Category "${category}" save path updated to: ${this.defaultSavePath}`);
} catch (error) { } catch (error) {
// 409 = category already has this save path (expected, not an error) // If we can't ensure the category, log error but don't throw
if (axios.isAxiosError(error) && error.response?.status === 409) { // Torrents can still be added with per-torrent savepath parameter
console.log(`[qBittorrent] Category "${category}" already has save path: ${this.defaultSavePath}`); if (axios.isAxiosError(error)) {
console.error(`[qBittorrent] Failed to ensure category "${category}":`, {
status: error.response?.status,
statusText: error.response?.statusText,
data: error.response?.data,
requestedPath: this.defaultSavePath,
});
} else { } else {
console.warn(`[qBittorrent] Failed to update category save path:`, error); console.error(`[qBittorrent] Failed to ensure category:`, error);
// Don't throw - torrents can still be added with per-torrent savepath parameter
} }
} }
} }