const fetch = require("node-fetch") const path = require("path") const cacheManager = require("./cache-manager") const apiInstance = "https://sponsor.ajay.app" async function internalGetSB(videoID) { // Check if data already exists const existingData = cacheManager.read("sb")[videoID] if (existingData) return existingData let data = [] // Otherwise, fetch let r try { r = await fetch(path.join(apiInstance, `/api/skipSegments?videoID=${videoID}&service=youtube`)) if (r.status == 400 || r.status == 404) throw new Error(r.statusText) } catch (e) { if (e instanceof fetch.FetchError) return new Error(`FetchError (${e.code})`) return e } let sbData = await r.json() if (!r.ok && !sbData && !sbData.randomTime) return new Error(`Status: ${r.status}`) if (sbData) data = sbData cacheManager.write("sb", videoID, data) return data } async function internalGetDeArrow(videoID) { // Check if data already exists const existingData = cacheManager.read("dearrow")[videoID] if (existingData && !existingData.loading) return existingData let data = {} // Otherwise, fetch let r try { r = await fetch(path.join(apiInstance, `/api/branding?videoID=${videoID}&service=youtube`)) } catch (e) { if (e instanceof fetch.FetchError) return new Error(`FetchError (${e.code})`) return e } let dearrowData = await r.json() if (!r.ok && !dearrowData && !dearrowData.randomTime) return new Error(`Status: ${r.status}`) if (dearrowData) { for (const title of dearrowData.titles) if (title.votes > 0 || title.locked) { // Use original title if (title.original) break data.title = title.title break } for (const thumbnail of dearrowData.thumbnails) if (thumbnail.votes > 0) { // Use original thumbnail if (thumbnail.original) break data.thumbnail = `api/v1/getThumbnail?videoID=${videoID}&time=${thumbnail.timestamp}` break } } cacheManager.write("dearrow", videoID, data) return data } module.exports = { getSB: async function(id) { let error = undefined let outData = undefined await internalGetSB(id) .then(data => { if (data.message) error = `SponsorBlock for ${id}: ${data}` else outData = data }) return{ error, data: outData } }, getDeArrow: async function(id) { let error = undefined let outData = undefined await internalGetDeArrow(id) .then(data => { if (data.message) error = `DeArrow for ${id}: ${data}` else outData = data }) return{ error, data: outData } }, applyToAllDeArrow: async function(videos) { let errors = {} let queue = videos.map(v => { return { id: v.videoId, tries: 0 } }) while (queue.length > 0) { let nextQueue = [] for (let i = 0; i < Math.min(queue.length, 3) && nextQueue.length < 3; i++) nextQueue.push(queue.shift()) await Promise.all(nextQueue.map(v => internalGetDeArrow(v.id) .then(data => { if (data.message) if (data.message.startsWith("FetchError")) if (v.tries < 3) { v.tries++ queue.unshift(v) } else errors[data.name] = `DeArrow for ${v.id}: ${data}` else errors[data.name] = `DeArrow for ${v.id}: ${data}` }))) } return { errors }; }, getAllDeArrowNoBlock: function(videos) { // Apply a "loading" value to each video before starting the operation let anyLoading = false for (const v of videos) { const oldData = cacheManager.read("dearrow")[v.videoId] if (!oldData) { cacheManager.write("dearrow", v.videoId, { loading: true }) v.dearrowData = { loading: true } anyLoading = true } else v.dearrowData = oldData } if (anyLoading) this.applyToAllDeArrow(videos) } }