91 lines
2.3 KiB
TypeScript
91 lines
2.3 KiB
TypeScript
export interface SlskdRawFile {
|
|
filename: string
|
|
size: number
|
|
bitRate: number
|
|
length: number
|
|
}
|
|
|
|
export interface SlskdRawResponse {
|
|
username: string
|
|
files: SlskdRawFile[]
|
|
freeUploadSlots: number
|
|
speed: number
|
|
}
|
|
|
|
export interface DedupedFile {
|
|
displayName: string
|
|
filename: string
|
|
size: number
|
|
bitRate: number
|
|
length: number
|
|
bestPeer: {
|
|
username: string
|
|
freeSlots: number
|
|
speed: number
|
|
}
|
|
peerCount: number
|
|
}
|
|
|
|
function normalizeName(filename: string): string {
|
|
// Strip path separators (Windows backslash or Unix forward slash)
|
|
const basename = filename.replace(/^.*[\\\/]/, '')
|
|
// Strip extension
|
|
const noExt = basename.replace(/\.[^.]+$/, '')
|
|
return noExt.toLowerCase().trim()
|
|
}
|
|
|
|
function prettyName(filename: string): string {
|
|
return filename.replace(/^.*[\\\/]/, '').replace(/\.[^.]+$/, '')
|
|
}
|
|
|
|
export function extractBestFiles(responses: SlskdRawResponse[], limit = 30): DedupedFile[] {
|
|
const groups = new Map<string, { file: SlskdRawFile; peer: SlskdRawResponse; displayName: string }[]>()
|
|
|
|
for (const peer of responses) {
|
|
if (!peer.files?.length) continue
|
|
for (const file of peer.files) {
|
|
const key = normalizeName(file.filename)
|
|
if (!key) continue
|
|
const entry = { file, peer, displayName: prettyName(file.filename) }
|
|
const existing = groups.get(key)
|
|
if (existing) {
|
|
existing.push(entry)
|
|
} else {
|
|
groups.set(key, [entry])
|
|
}
|
|
}
|
|
}
|
|
|
|
const deduped: DedupedFile[] = []
|
|
|
|
for (const [, entries] of groups) {
|
|
// Pick best peer: prefer freeUploadSlots > 0, then highest speed
|
|
entries.sort((a, b) => {
|
|
const aFree = a.peer.freeUploadSlots > 0 ? 1 : 0
|
|
const bFree = b.peer.freeUploadSlots > 0 ? 1 : 0
|
|
if (aFree !== bFree) return bFree - aFree
|
|
return b.peer.speed - a.peer.speed
|
|
})
|
|
|
|
const best = entries[0]
|
|
deduped.push({
|
|
displayName: best.displayName,
|
|
filename: best.file.filename,
|
|
size: best.file.size,
|
|
bitRate: best.file.bitRate,
|
|
length: best.file.length,
|
|
bestPeer: {
|
|
username: best.peer.username,
|
|
freeSlots: best.peer.freeUploadSlots,
|
|
speed: best.peer.speed,
|
|
},
|
|
peerCount: entries.length,
|
|
})
|
|
}
|
|
|
|
// Sort by highest bitRate first
|
|
deduped.sort((a, b) => (b.bitRate || 0) - (a.bitRate || 0))
|
|
|
|
return deduped.slice(0, limit)
|
|
}
|