Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions __tests__/server/crossSeedWorker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,13 @@ vi.mock('../../src/server/db', () => ({
},
}))

vi.mock('../../src/server/utils/qbt', () => ({
loginToQbt: vi.fn(),
}))
vi.mock('../../src/server/utils/qbt', async (importOriginal) => {
const actual = await importOriginal<typeof import('../../src/server/utils/qbt')>()
return {
...actual,
loginToQbt: vi.fn(),
}
})

vi.mock('../../src/server/utils/crypto', () => ({
decrypt: vi.fn(() => 'apikey'),
Expand Down
24 changes: 12 additions & 12 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/server/routes/integrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Hono } from 'hono'
import { db, type Integration } from '../db'
import { encrypt, decrypt } from '../utils/crypto'
import { validateUrl } from '../utils/url'
import { loginToQbt } from '../utils/qbt'
import { loginToQbt, isTorrentAddSuccessful } from '../utils/qbt'
import { authMiddleware } from '../middleware/auth'
import { log } from '../utils/logger'

Expand Down Expand Up @@ -289,7 +289,7 @@ integrations.post('/:id/grab', async (c) => {
})

const addText = await addRes.text()
if (!addRes.ok || (addText.trim() !== 'Ok.' && addText.trim() !== 'Ok')) {
if (!addRes.ok || !isTorrentAddSuccessful(addText)) {
return c.json({ error: `Failed to add torrent: ${addText || `HTTP ${addRes.status}`}` }, 400)
}

Expand Down
4 changes: 2 additions & 2 deletions src/server/utils/crossSeedWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
CrossSeedDecisionType,
MatchMode,
} from '../db'
import { loginToQbt } from './qbt'
import { loginToQbt, isTorrentAddSuccessful } from './qbt'
import { fetchWithTls } from './fetch'
import { decrypt } from './crypto'
import { log } from './logger'
Expand Down Expand Up @@ -314,7 +314,7 @@ async function addTorrentToQbt(
body: formData,
})
const text = await res.text()
const apiSuccess = res.ok && text.trim().startsWith('Ok')
const apiSuccess = res.ok && isTorrentAddSuccessful(text)

if (!apiSuccess) {
log.error(`[CrossSeed] API returned failure: ${text}`)
Expand Down
27 changes: 27 additions & 0 deletions src/server/utils/qbt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,30 @@ export async function fetchInstanceTransferStats(
return null
}
}

export function isTorrentAddSuccessful(addText: string): boolean {
const trimmed = addText.trim()
if (trimmed === 'Ok.' || trimmed === 'Ok') {
return true
}
try {
const json = JSON.parse(trimmed)
if (json && typeof json === 'object') {
const successCount = json.success_count !== undefined ? Number(json.success_count) : 0
const pendingCount = json.pending_count !== undefined ? Number(json.pending_count) : 0
const failureCount = json.failure_count !== undefined ? Number(json.failure_count) : 0
const addedIds = Array.isArray(json.added_torrent_ids) ? json.added_torrent_ids : []

if (successCount > 0 || addedIds.length > 0 || pendingCount > 0) {
return true
}
if (failureCount > 0) {
return false
}
}
} catch {
// Not JSON, and not 'Ok'/'Ok.' -> treat as failed
}
return false
}

Loading