diff --git a/src/routes/getSkipSegments.js b/src/routes/getSkipSegments.js index db6ce76..4bf6a6a 100644 --- a/src/routes/getSkipSegments.js +++ b/src/routes/getSkipSegments.js @@ -9,100 +9,100 @@ var getHash = require('../utils/getHash.js'); var getIP = require('../utils/getIP.js'); function cleanGetSegments(videoID, categories) { - let userHashedIP, shadowHiddenSegments; + let userHashedIP, shadowHiddenSegments; - let segments = []; + let segments = []; - try { - for (const category of categories) { - const categorySegments = db - .prepare( - 'all', - 'SELECT startTime, endTime, votes, UUID, shadowHidden FROM sponsorTimes WHERE videoID = ? and category = ? ORDER BY startTime', - [videoID, category] - ) - .filter(segment => { - if (segment.votes < -1) { - return false; //too untrustworthy, just ignore it - } + try { + for (const category of categories) { + const categorySegments = db + .prepare( + 'all', + 'SELECT startTime, endTime, votes, UUID, shadowHidden FROM sponsorTimes WHERE videoID = ? and category = ? ORDER BY startTime', + [videoID, category] + ) + .filter(segment => { + if (segment.votes < -1) { + return false; //too untrustworthy, just ignore it + } - //check if shadowHidden - //this means it is hidden to everyone but the original ip that submitted it - if (segment.shadowHidden != 1) { - return true; - } + //check if shadowHidden + //this means it is hidden to everyone but the original ip that submitted it + if (segment.shadowHidden != 1) { + return true; + } - if (shadowHiddenSegments === undefined) { - shadowHiddenSegments = privateDB.prepare('all', 'SELECT hashedIP FROM sponsorTimes WHERE videoID = ?', [videoID]); - } + if (shadowHiddenSegments === undefined) { + shadowHiddenSegments = privateDB.prepare('all', 'SELECT hashedIP FROM sponsorTimes WHERE videoID = ?', [videoID]); + } - //if this isn't their ip, don't send it to them - return shadowHiddenSegments.some(shadowHiddenSegment => { - if (userHashedIP === undefined) { - //hash the IP only if it's strictly necessary - userHashedIP = getHash(getIP(req) + config.globalSalt); - } - return shadowHiddenSegment.hashedIP === userHashedIP; - }); - }); + //if this isn't their ip, don't send it to them + return shadowHiddenSegments.some(shadowHiddenSegment => { + if (userHashedIP === undefined) { + //hash the IP only if it's strictly necessary + userHashedIP = getHash(getIP(req) + config.globalSalt); + } + return shadowHiddenSegment.hashedIP === userHashedIP; + }); + }); - chooseSegments(categorySegments).forEach(chosenSegment => { - segments.push({ - category, - segment: [chosenSegment.startTime, chosenSegment.endTime], - UUID: chosenSegment.UUID, - }); - }); + chooseSegments(categorySegments).forEach(chosenSegment => { + segments.push({ + category, + segment: [chosenSegment.startTime, chosenSegment.endTime], + UUID: chosenSegment.UUID, + }); + }); + } + + return segments; + } catch (err) { + if (err) { + logger.error(err); + return null; + } } - - return segments; - } catch (err) { - if (err) { - logger.error('j 2 Query failed'); - return undefined; - } - } } //gets a weighted random choice from the choices array based on their `votes` property. //amountOfChoices specifies the maximum amount of choices to return, 1 or more. //choices are unique function getWeightedRandomChoice(choices, amountOfChoices) { - //trivial case: no need to go through the whole process - if (amountOfChoices >= choices.length) { - return choices; - } - - //assign a weight to each choice - let totalWeight = 0; - choices = choices.map(choice => { - //The 3 makes -2 the minimum votes before being ignored completely - //https://www.desmos.com/calculator/c1duhfrmts - //this can be changed if this system increases in popularity. - const weight = Math.exp((choice.votes + 3), 0.85); - totalWeight += weight; - - return { ...choice, weight }; - }); - - //iterate and find amountOfChoices choices - const chosen = []; - while (amountOfChoices-- > 0) { - //weighted random draw of one element of choices - const randomNumber = Math.random() * totalWeight; - let stackWeight = choices[0].weight; - let i = 0; - while (stackWeight < randomNumber) { - stackWeight += choices[++i].weight; + //trivial case: no need to go through the whole process + if (amountOfChoices >= choices.length) { + return choices; } - //add it to the chosen ones and remove it from the choices before the next iteration - chosen.push(choices[i]); - totalWeight -= choices[i].weight; - choices.splice(i, 1); - } + //assign a weight to each choice + let totalWeight = 0; + choices = choices.map(choice => { + //The 3 makes -2 the minimum votes before being ignored completely + //https://www.desmos.com/calculator/c1duhfrmts + //this can be changed if this system increases in popularity. + const weight = Math.exp((choice.votes + 3), 0.85); + totalWeight += weight; - return chosen; + return { ...choice, weight }; + }); + + //iterate and find amountOfChoices choices + const chosen = []; + while (amountOfChoices-- > 0) { + //weighted random draw of one element of choices + const randomNumber = Math.random() * totalWeight; + let stackWeight = choices[0].weight; + let i = 0; + while (stackWeight < randomNumber) { + stackWeight += choices[++i].weight; + } + + //add it to the chosen ones and remove it from the choices before the next iteration + chosen.push(choices[i]); + totalWeight -= choices[i].weight; + choices.splice(i, 1); + } + + return chosen; } //This function will find segments that are contained inside of eachother, called similar segments @@ -110,34 +110,34 @@ function getWeightedRandomChoice(choices, amountOfChoices) { //This allows new less voted items to still sometimes appear to give them a chance at getting votes. //Segments with less than -1 votes are already ignored before this function is called function chooseSegments(segments) { - //Create groups of segments that are similar to eachother - //Segments must be sorted by their startTime so that we can build groups chronologically: - //1. As long as the segments' startTime fall inside the currentGroup, we keep adding them to that group - //2. If a segment starts after the end of the currentGroup (> cursor), no other segment will ever fall - // inside that group (because they're sorted) so we can create a new one - const similarSegmentsGroups = []; - let currentGroup; - let cursor = -1; //-1 to make sure that, even if the 1st segment starts at 0, a new group is created - segments.forEach(segment => { - if (segment.startTime > cursor) { - currentGroup = { segments: [], votes: 0 }; - similarSegmentsGroups.push(currentGroup); - } + //Create groups of segments that are similar to eachother + //Segments must be sorted by their startTime so that we can build groups chronologically: + //1. As long as the segments' startTime fall inside the currentGroup, we keep adding them to that group + //2. If a segment starts after the end of the currentGroup (> cursor), no other segment will ever fall + // inside that group (because they're sorted) so we can create a new one + const similarSegmentsGroups = []; + let currentGroup; + let cursor = -1; //-1 to make sure that, even if the 1st segment starts at 0, a new group is created + segments.forEach(segment => { + if (segment.startTime > cursor) { + currentGroup = { segments: [], votes: 0 }; + similarSegmentsGroups.push(currentGroup); + } - currentGroup.segments.push(segment); - //only if it is a positive vote, otherwise it is probably just a sponsor time with slightly wrong time - if (segment.votes > 0) { - currentGroup.votes += segment.votes; - } + currentGroup.segments.push(segment); + //only if it is a positive vote, otherwise it is probably just a sponsor time with slightly wrong time + if (segment.votes > 0) { + currentGroup.votes += segment.votes; + } - cursor = Math.max(cursor, segment.endTime); - }); + cursor = Math.max(cursor, segment.endTime); + }); - //if there are too many groups, find the best 8 - return getWeightedRandomChoice(similarSegmentsGroups, 8).map( - //randomly choose 1 good segment per group and return them - group => getWeightedRandomChoice(group.segments, 1)[0] - ); + //if there are too many groups, find the best 8 + return getWeightedRandomChoice(similarSegmentsGroups, 8).map( + //randomly choose 1 good segment per group and return them + group => getWeightedRandomChoice(group.segments, 1)[0] + ); } /** @@ -151,39 +151,39 @@ function chooseSegments(segments) { * @returns */ function handleGetSegments(req, res) { - const videoID = req.query.videoID; - // Default to sponsor - // If using params instead of JSON, only one category can be pulled - const categories = req.query.categories - ? JSON.parse(req.query.categories) - : req.query.category - ? [req.query.category] - : ['sponsor']; + const videoID = req.query.videoID; + // Default to sponsor + // If using params instead of JSON, only one category can be pulled + const categories = req.query.categories + ? JSON.parse(req.query.categories) + : req.query.category + ? [req.query.category] + : ['sponsor']; - let segments = cleanGetSegments(videoID, categories); + let segments = cleanGetSegments(videoID, categories); - if (segments === undefined) { - res.sendStatus(500); - return false; + if (segments === null || segments === undefined) { + res.sendStatus(500); + return false; } if (segments.length == 0) { - res.sendStatus(404); - return false; + res.sendStatus(404); + return false; } return segments; } module.exports = { - handleGetSegments, - cleanGetSegments, - endpoint: function (req, res) { - let segments = handleGetSegments(req, res); + handleGetSegments, + cleanGetSegments, + endpoint: function (req, res) { + let segments = handleGetSegments(req, res); - if (segments) { - //send result - res.send(segments); - } - }, + if (segments) { + //send result + res.send(segments); + } + }, };