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
* 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> {
if (!this.cookie) {
@@ -378,57 +378,71 @@ export class QBittorrentService {
}
try {
// Try to create category first (idempotent - won't fail if exists)
await this.client.post(
'/torrents/createCategory',
new URLSearchParams({
category,
savePath: this.defaultSavePath,
}),
{
headers: {
Cookie: this.cookie,
'Content-Type': 'application/x-www-form-urlencoded',
},
}
);
// First, get all categories to check if it exists and what save path it has
const categoriesResponse = await this.client.get('/torrents/categories', {
headers: { Cookie: this.cookie },
});
console.log(`[qBittorrent] Category "${category}" created`);
} catch (error) {
// 409 = category already exists (expected, not an error)
if (axios.isAxiosError(error) && error.response?.status === 409) {
console.log(`[qBittorrent] Category "${category}" already exists`);
const categories = categoriesResponse.data;
const existingCategory = categories[category];
if (!existingCategory) {
// 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 {
// Unexpected error, but don't fail - editCategory below will handle it
console.warn(`[qBittorrent] Failed to create category:`, error);
// Category exists - check if save path needs updating
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) {
// 409 = category already has this save path (expected, not an error)
if (axios.isAxiosError(error) && error.response?.status === 409) {
console.log(`[qBittorrent] Category "${category}" already has save path: ${this.defaultSavePath}`);
// If we can't ensure the category, log error but don't throw
// Torrents can still be added with per-torrent savepath parameter
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 {
console.warn(`[qBittorrent] Failed to update category save path:`, error);
// Don't throw - torrents can still be added with per-torrent savepath parameter
console.error(`[qBittorrent] Failed to ensure category:`, error);
}
}
}