apilink / utils /streamDb.js
franyer24's picture
update logic
1ce7a42 verified
Raw
History Blame Contribute Delete
7.93 kB
const { getDb, scheduleSave } = require('./db')
const FRESH_DAYS = 30
const domainCache = new Map()
function parseUrl(url) {
try {
const u = new URL(url)
return { domain: u.origin, path: u.pathname + u.search + u.hash }
} catch {
return { domain: '', path: url }
}
}
async function getDomainId(db, domain) {
if (domainCache.has(domain)) return domainCache.get(domain)
const stmt = db.prepare(`SELECT id FROM domains WHERE domain = ?`)
stmt.bind([domain])
const found = stmt.step()
if (found) {
const id = stmt.getAsObject().id
stmt.free()
domainCache.set(domain, id)
return id
}
stmt.free()
try {
db.run(`INSERT INTO domains (domain) VALUES (?)`, [domain])
} catch {
const retryStmt = db.prepare(`SELECT id FROM domains WHERE domain = ?`)
retryStmt.bind([domain])
retryStmt.step()
const id = retryStmt.getAsObject().id
retryStmt.free()
domainCache.set(domain, id)
return id
}
const r = db.exec('SELECT last_insert_rowid()')
const id = r[0].values[0][0]
domainCache.set(domain, id)
return id
}
async function getFreshStreams(tmdbId, season, episode) {
const db = await getDb()
let where = `sr.tmdb_id = ? AND sr.is_alive = 1 AND sr.last_seen > datetime('now', '-' || ? || ' days')`
const params = [String(tmdbId), FRESH_DAYS]
if (season != null && episode != null) {
where += ` AND sr.season = ? AND sr.episode = ?`
params.push(season, episode)
} else {
where += ` AND sr.season IS NULL AND sr.episode IS NULL`
}
const stmt = db.prepare(`
SELECT sr.*, d.domain || sr.url_path as url
FROM stream_results sr
JOIN domains d ON d.id = sr.domain_id
WHERE ${where}
ORDER BY sr.quality DESC
`)
stmt.bind(params)
const results = []
while (stmt.step()) {
results.push(stmt.getAsObject())
}
stmt.free()
return results
}
async function getAllStreams(tmdbId, season, episode) {
const db = await getDb()
let where = `sr.tmdb_id = ? AND sr.is_alive = 1`
const params = [String(tmdbId)]
if (season != null && episode != null) {
where += ` AND sr.season = ? AND sr.episode = ?`
params.push(season, episode)
} else {
where += ` AND sr.season IS NULL AND sr.episode IS NULL`
}
const stmt = db.prepare(`
SELECT sr.*, d.domain || sr.url_path as url
FROM stream_results sr
JOIN domains d ON d.id = sr.domain_id
WHERE ${where}
ORDER BY sr.last_seen DESC
`)
stmt.bind(params)
const results = []
while (stmt.step()) {
results.push(stmt.getAsObject())
}
stmt.free()
return results
}
async function logStreams(tmdbId, provider, contentType, streams, title, season, episode) {
const db = await getDb()
const now = new Date().toISOString()
const seasonVal = season != null ? season : null
const episodeVal = episode != null ? episode : null
const checkStmt = db.prepare(`
SELECT sr.id FROM stream_results sr
JOIN domains d ON d.id = sr.domain_id
WHERE d.domain = ? AND sr.url_path = ? AND sr.provider = ? AND sr.tmdb_id = ?
AND sr.season IS ? AND sr.episode IS ?
`)
const insertStmt = db.prepare(`
INSERT INTO stream_results (domain_id, url_path, provider, content_type, tmdb_id, season, episode, title, quality, language, created_at, last_seen)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`)
const updateStmt = db.prepare(`
UPDATE stream_results SET last_seen = ?, is_alive = 1
WHERE domain_id = ? AND url_path = ? AND provider = ? AND tmdb_id = ?
AND season IS ? AND episode IS ?
`)
for (const s of streams) {
const { domain, path } = parseUrl(s.url)
const domainId = await getDomainId(db, domain)
checkStmt.bind([domain, path, provider, String(tmdbId), seasonVal, episodeVal])
const exists = checkStmt.step()
if (exists) {
updateStmt.run([now, domainId, path, provider, String(tmdbId), seasonVal, episodeVal])
} else {
insertStmt.run([
domainId,
path,
provider,
contentType,
String(tmdbId),
seasonVal,
episodeVal,
title || s.title || null,
s.quality || null,
s.language || null,
now,
now,
])
}
}
checkStmt.free()
insertStmt.free()
updateStmt.free()
scheduleSave()
}
async function markDead(url, provider) {
const db = await getDb()
const { domain, path } = parseUrl(url)
db.run(`
DELETE FROM stream_results
WHERE domain_id = (SELECT id FROM domains WHERE domain = ?)
AND url_path = ? AND provider = ?
`, [domain, path, provider || ''])
scheduleSave()
}
async function markAlive(url, provider) {
const db = await getDb()
const { domain, path } = parseUrl(url)
const now = new Date().toISOString()
db.run(`
UPDATE stream_results
SET is_alive = 1, last_seen = ?, last_validated = ?
WHERE domain_id = (SELECT id FROM domains WHERE domain = ?)
AND url_path = ? AND provider = ?
`, [now, now, domain, path, provider || ''])
scheduleSave()
}
async function getProvidersForTmdb(tmdbId) {
const db = await getDb()
const stmt = db.prepare(`
SELECT DISTINCT provider FROM stream_results
WHERE tmdb_id = ? AND is_alive = 1
`)
stmt.bind([String(tmdbId)])
const providers = []
while (stmt.step()) {
providers.push(stmt.getAsObject().provider)
}
stmt.free()
return providers
}
async function getStats() {
const db = await getDb()
const total = db.exec(`SELECT COUNT(*) FROM stream_results WHERE is_alive = 1`)
const totalStreams = total.length > 0 ? total[0].values[0][0] : 0
const totalDomains = db.exec(`SELECT COUNT(*) FROM domains`)
const domainsCount = totalDomains.length > 0 ? totalDomains[0].values[0][0] : 0
const byProvider = db.exec(`
SELECT provider,
COUNT(*) as total,
SUM(CASE WHEN is_alive = 1 THEN 1 ELSE 0 END) as alive,
MAX(last_seen) as last_seen
FROM stream_results
GROUP BY provider
ORDER BY total DESC
`)
const recentChecks = db.exec(`
SELECT COUNT(*) FROM stream_checks
WHERE checked_at > datetime('now', '-24 hours')
`)
const checks24h = recentChecks.length > 0 ? recentChecks[0].values[0][0] : 0
const stale = db.exec(`
SELECT COUNT(*) FROM stream_results
WHERE is_alive = 1 AND last_seen < datetime('now', '-${FRESH_DAYS} days')
`)
const staleCount = stale.length > 0 ? stale[0].values[0][0] : 0
const providers = []
if (byProvider.length > 0) {
for (const row of byProvider[0].values) {
providers.push({
provider: row[0],
total: row[1],
alive: row[2],
last_seen: row[3],
})
}
}
return {
totalStreams,
domainsCount,
staleCount,
checks24h,
providers,
}
}
async function logCheck(url, result) {
const db = await getDb()
db.run(`
INSERT INTO stream_checks (url, checked_at, response_ms, status_code, is_alive, error)
VALUES (?, datetime('now'), ?, ?, ?, ?)
`, [url, result.responseMs || null, result.statusCode || null, result.isAlive ? 1 : 0, result.error || null])
scheduleSave()
}
async function deleteStale(maxAgeDays) {
const db = await getDb()
db.run(`
DELETE FROM stream_results
WHERE last_seen < datetime('now', '-' || ? || ' days')
AND is_alive = 0
`, [maxAgeDays || 90])
scheduleSave()
}
async function pruneChecks(maxAgeDays) {
const db = await getDb()
db.run(`
DELETE FROM stream_checks
WHERE checked_at < datetime('now', '-' || ? || ' days')
`, [maxAgeDays || 90])
scheduleSave()
}
async function pruneOrphanDomains() {
const db = await getDb()
db.run(`
DELETE FROM domains
WHERE id NOT IN (SELECT DISTINCT domain_id FROM stream_results)
`)
scheduleSave()
}
module.exports = {
getFreshStreams,
getAllStreams,
logStreams,
markDead,
markAlive,
getProvidersForTmdb,
getStats,
logCheck,
deleteStale,
pruneChecks,
pruneOrphanDomains,
}