diff --git a/backend/src/services/tautulli.ts b/backend/src/services/tautulli.ts index 574ba60..2811ec5 100644 --- a/backend/src/services/tautulli.ts +++ b/backend/src/services/tautulli.ts @@ -21,25 +21,38 @@ export async function backfillUserHistory(user: { u.email?.toLowerCase() === user.plexUsername.toLowerCase() || u.friendly_name?.toLowerCase() === user.plexUsername.toLowerCase(), ); - if (!tautulliUser) return; + if (!tautulliUser) { + console.log(`Tautulli user not found for ${user.plexUsername}`); + return; + } + + const afterTimestamp = Math.floor( + (Date.now() - 30 * 24 * 60 * 60 * 1000) / 1000, + ); - const startDate = (() => { - const d = new Date(); - d.setDate(d.getDate() - 30); - return d.toISOString().split("T")[0]; - })(); - const afterTimestamp = Math.floor(new Date(startDate).getTime() / 1000); const historyResponse = await axios.get(`${TAUTULLI_URL}/api/v2`, { params: { apikey: TAUTULLI_API_KEY, cmd: "get_history", user_id: tautulliUser.user_id, - after: afterTimestamp, + grouping: 0, length: 1000, }, }); - const historyData = historyResponse.data?.response?.data?.data || []; - if (!Array.isArray(historyData) || historyData.length === 0) return; + const allHistoryData = historyResponse.data?.response?.data?.data || []; + const historyData = allHistoryData.filter((item: any) => { + if (!item.started) return false; + return item.started >= afterTimestamp; + }); + + console.log( + `Backfill ${user.plexUsername}: ${allHistoryData.length} total, ${historyData.length} in last 30d`, + ); + + if (!Array.isArray(historyData) || historyData.length === 0) { + console.log(`No recent history for ${user.plexUsername}`); + return; + } const settings = await prisma.systemSettings.findFirst(); const creditsPerMinute = settings?.creditsPerMinute || 2; @@ -49,22 +62,54 @@ export async function backfillUserHistory(user: { let totalCredits = 0; let totalMinutes = 0; for (const item of historyData) { - const sessionId = item.reference_id?.toString() || item.id?.toString(); - if (!sessionId) continue; - if (await prisma.watchEvent.findUnique({ where: { sessionId } })) + const sessionId = + item.id?.toString() || + item.reference_id?.toString() || + `${user.id}_${item.started}`; + if (!sessionId) { + console.log("Skip: no sessionId", item.title); continue; + } + if (await prisma.watchEvent.findUnique({ where: { sessionId } })) { + console.log("Skip existing:", sessionId); + continue; + } + const percentComplete = item.percent_complete || 0; - const watchDurationSeconds = - item.duration || - (item.stopped && item.started ? item.stopped - item.started : 0); - const watchDurationMinutes = Math.floor(watchDurationSeconds / 60); - if ( - percentComplete < minWatchPercent || - watchDurationMinutes < minWatchMinutes || - watchDurationMinutes > 600 - ) + const pausedCounter = item.paused_counter || 0; + + if (!item.started || !item.stopped) { + console.log("Skip missing timestamps:", item.title); continue; + } + + const watchDurationSeconds = item.stopped - item.started - pausedCounter; + const watchDurationMinutes = Math.floor(watchDurationSeconds / 60); + + if ( + watchDurationSeconds < 0 || + watchDurationMinutes > 600 || + percentComplete < minWatchPercent || + watchDurationMinutes < minWatchMinutes + ) { + console.log( + "Skip:", + item.title, + percentComplete + "%", + watchDurationMinutes + "min", + ); + continue; + } + const creditsEarned = watchDurationMinutes * creditsPerMinute; + + console.log( + "Credit:", + item.title, + watchDurationMinutes + "min", + creditsEarned + "$COOP", + ); + const watchEvent = await prisma.watchEvent.create({ data: { userId: user.id, @@ -73,7 +118,7 @@ export async function backfillUserHistory(user: { contentType: item.media_type || "movie", title: item.title || item.full_title || "Unknown", grandparentTitle: item.grandparent_title || null, - duration: item.stopped - item.started, + duration: watchDurationSeconds, percentComplete: Math.floor(percentComplete), creditsEarned, isProcessed: true, @@ -93,6 +138,11 @@ export async function backfillUserHistory(user: { totalCredits += creditsEarned; totalMinutes += watchDurationMinutes; } + + console.log( + `Backfill done ${user.plexUsername}: ${totalCredits} credits, ${totalMinutes} min`, + ); + if (totalCredits > 0) await prisma.user.update({ where: { id: user.id },