From ca46f4c9ac8517c55e1b4456f3666fdf7f3b06ec Mon Sep 17 00:00:00 2001 From: Martijn van der Ven Date: Sat, 23 May 2020 16:53:45 +0200 Subject: [PATCH 1/6] Enable database upgrades to run --- src/databases/databases.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/databases/databases.js b/src/databases/databases.js index 7c3991f..d7008a0 100644 --- a/src/databases/databases.js +++ b/src/databases/databases.js @@ -49,11 +49,11 @@ function ugradeDB(db, prefix) { let versionCodeInfo = db.prepare("SELECT value FROM config WHERE key = ?").get("version"); let versionCode = versionCodeInfo ? versionCodeInfo.value : 0; - let path = config.schemaFolder + "/_upgrade_" + prefix + "_" + (versionCode + 1) + ".sql"; + let path = config.schemaFolder + "/_upgrade_" + prefix + "_" + (parseInt(versionCode) + 1) + ".sql"; while (fs.existsSync(path)) { db.exec(fs.readFileSync(path).toString()); versionCode = db.prepare("SELECT value FROM config WHERE key = ?").get("version").value; - path = config.schemaFolder + "/_upgrade_" + prefix + "_" + (versionCode + 1) + ".sql"; + path = config.schemaFolder + "/_upgrade_" + prefix + "_" + (parseInt(versionCode) + 1) + ".sql"; } } \ No newline at end of file From 803b1fa5050233c43b062c934cebd0a1365957ca Mon Sep 17 00:00:00 2001 From: Martijn van der Ven Date: Sat, 23 May 2020 16:54:21 +0200 Subject: [PATCH 2/6] Add initial version of querying by hash prefix --- databases/_upgrade_sponsorTimes_2.sql | 26 ++++++++++ src/app.js | 4 ++ src/databases/databases.js | 4 ++ src/routes/getSkipSegmentsByHash.js | 69 +++++++++++++++++++++++++++ 4 files changed, 103 insertions(+) create mode 100644 databases/_upgrade_sponsorTimes_2.sql create mode 100644 src/routes/getSkipSegmentsByHash.js diff --git a/databases/_upgrade_sponsorTimes_2.sql b/databases/_upgrade_sponsorTimes_2.sql new file mode 100644 index 0000000..5472499 --- /dev/null +++ b/databases/_upgrade_sponsorTimes_2.sql @@ -0,0 +1,26 @@ +BEGIN TRANSACTION; + +/* Add hash field */ +CREATE TABLE "sqlb_temp_table_1" ( + "videoID" TEXT NOT NULL, + "startTime" REAL NOT NULL, + "endTime" REAL NOT NULL, + "votes" INTEGER NOT NULL, + "incorrectVotes" INTEGER NOT NULL default '1', + "UUID" TEXT NOT NULL UNIQUE, + "userID" TEXT NOT NULL, + "timeSubmitted" INTEGER NOT NULL, + "views" INTEGER NOT NULL, + "category" TEXT NOT NULL DEFAULT "sponsor", + "shadowHidden" INTEGER NOT NULL, + "hashedVideoID" TEXT NOT NULL +); +INSERT INTO sqlb_temp_table_1 SELECT *, sha256(videoID) FROM sponsorTimes; + +DROP TABLE sponsorTimes; +ALTER TABLE sqlb_temp_table_1 RENAME TO "sponsorTimes"; + +/* Bump version in config */ +UPDATE config SET value = 2 WHERE key = "version"; + +COMMIT; \ No newline at end of file diff --git a/src/app.js b/src/app.js index 9561dc1..5595625 100644 --- a/src/app.js +++ b/src/app.js @@ -10,6 +10,7 @@ var loggerMiddleware = require('./middleware/logger.js'); // Routes var getSkipSegments = require('./routes/getSkipSegments.js').endpoint; var postSkipSegments = require('./routes/postSkipSegments.js'); +var getSkipSegmentsByHash = require('./routes/getSkipSegmentsByHash.js'); var voteOnSponsorTime = require('./routes/voteOnSponsorTime.js'); var viewedVideoSponsorTime = require('./routes/viewedVideoSponsorTime.js'); var setUsername = require('./routes/setUsername.js'); @@ -48,6 +49,9 @@ app.post('/api/postVideoSponsorTimes', oldSubmitSponsorTimes); app.get('/api/skipSegments', getSkipSegments); app.post('/api/skipSegments', postSkipSegments); +// add the privacy protecting skip segments functions +app.get('/api/skipSegments/:prefix', getSkipSegmentsByHash); + //voting endpoint app.get('/api/voteOnSponsorTime', voteOnSponsorTime); app.post('/api/voteOnSponsorTime', voteOnSponsorTime); diff --git a/src/databases/databases.js b/src/databases/databases.js index d7008a0..621ad8b 100644 --- a/src/databases/databases.js +++ b/src/databases/databases.js @@ -26,6 +26,10 @@ if (config.createDatabaseIfNotExist && !config.readOnly) { // Upgrade database if required if (!config.readOnly) { + // Register hashing function needed for running database upgrade + db.function("sha256", function (string) { + return require('crypto').createHash("sha256").update(string).digest("hex"); + }); ugradeDB(db, "sponsorTimes"); ugradeDB(privateDB, "private") } diff --git a/src/routes/getSkipSegmentsByHash.js b/src/routes/getSkipSegmentsByHash.js new file mode 100644 index 0000000..3672eed --- /dev/null +++ b/src/routes/getSkipSegmentsByHash.js @@ -0,0 +1,69 @@ +const config = require('../config.js'); +const { db, privateDB } = require('../databases/databases.js'); + +const getHash = require('../utils/getHash.js'); +const getIP = require('../utils/getIP.js'); + +/** + * @typedef {Object} Segment + * @property {string} videoID YouTube video ID the segment is meant for + * @property {number[]} segment Tuple of start and end times in seconds + * @property {string} category Category of content to skip + * @property {string} UUID Unique identifier for the specific segment + */ + +/** + * @param {string} prefix Lowercased hexadecimal hash prefix + * @param {string} hashedIP Custom hash of the visitor’s IP address + * @returns {Segment[]} + */ +function getSkipSegmentsByHash(prefix, hashedIP) { + /** + * @constant + * @type {Segment[]} + * @default + */ + const segments = []; + + const rows = db.prepare('SELECT videoID, startTime, endTime, UUID, category, shadowHidden FROM sponsorTimes WHERE votes >= -1 AND hashedVideoID LIKE ? ORDER BY startTime') + .all(prefix + '%'); + + const onlyForCurrentUser = privateDB.prepare('SELECT videoID FROM sponsorTimes WHERE hashedIP = ?').all(hashedIP).map(row => row.videoID); + + for (const row of rows) { + /** @TODO check if this logic does what is expected. */ + if (row.shadowHidden === 1 && onlyForCurrentUser.indexOf(row.videoID) === -1) { + // The current visitor’s IP did not submit for the current video. + // Do not send shadowHidden segments to them. + continue; + } + + segments.push({ + videoID: row.videoID, + segment: [row.startTime, row.endTime], + category: row.category, + UUID: row.UUID + }); + } + + return segments; +} + +const minimumPrefix = config.minimumPrefix || '3'; +const maximumPrefix = config.maximumPrefix || '32'; // Half the hash. +const prefixChecker = new RegExp('^[\\dA-F]{' + minimumPrefix + ',' + maximumPrefix + '}$', 'i'); + +module.exports = async function (req, res) { + if (!prefixChecker.test(req.params.prefix)) { + res.sendStatus(400).end(); // Exit early on faulty prefix + } + + const segments = getSkipSegmentsByHash( + req.params.prefix.toLowerCase(), + getHash(getIP(req) + config.globalSalt) + ); + + if (segments) { + res.send(segments) + } +} \ No newline at end of file From d8203dce77a5962076b93ab08fcc9213dc9d7d54 Mon Sep 17 00:00:00 2001 From: Martijn van der Ven Date: Sat, 23 May 2020 17:57:33 +0200 Subject: [PATCH 3/6] Make sure to always insert hashed video ID --- src/routes/postSkipSegments.js | 6 +++--- test/cases/getSavedTimeForUser.js | 4 ++-- test/cases/getSkipSegments.js | 17 +++++++++-------- test/cases/oldGetSponsorTime.js | 7 ++++--- test/cases/voteOnSponsorTime.js | 16 ++++++++-------- 5 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/routes/postSkipSegments.js b/src/routes/postSkipSegments.js index e178ee6..e13280f 100644 --- a/src/routes/postSkipSegments.js +++ b/src/routes/postSkipSegments.js @@ -232,9 +232,9 @@ module.exports = async function postSkipSegments(req, res) { try { db.prepare("INSERT INTO sponsorTimes " + - "(videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden)" + - "VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(videoID, segmentInfo.segment[0], - segmentInfo.segment[1], startingVotes, UUID, userID, timeSubmitted, 0, segmentInfo.category, shadowBanned); + "(videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden, hashedVideoID)" + + "VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(videoID, segmentInfo.segment[0], + segmentInfo.segment[1], startingVotes, UUID, userID, timeSubmitted, 0, segmentInfo.category, shadowBanned, getHash(videoID, 0)); //add to private db as well privateDB.prepare("INSERT INTO sponsorTimes VALUES(?, ?, ?)").run(videoID, hashedIP, timeSubmitted); diff --git a/test/cases/getSavedTimeForUser.js b/test/cases/getSavedTimeForUser.js index 5df8a48..242204d 100644 --- a/test/cases/getSavedTimeForUser.js +++ b/test/cases/getSavedTimeForUser.js @@ -5,8 +5,8 @@ var getHash = require('../../src/utils/getHash.js'); describe('getSavedTimeForUser', () => { before(() => { - let startOfQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden) VALUES"; - db.exec(startOfQuery + "('getSavedTimeForUser', 1, 11, 2, 'abc1239999', '" + getHash("testman") + "', 0, 50, 'sponsor', 0)"); + let startOfQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden, hashedVideoID) VALUES"; + db.exec(startOfQuery + "('getSavedTimeForUser', 1, 11, 2, 'abc1239999', '" + getHash("testman") + "', 0, 50, 'sponsor', 0, '" + getHash('getSavedTimeForUser', 0) + "')"); }); it('Should be able to get a 200', (done) => { diff --git a/test/cases/getSkipSegments.js b/test/cases/getSkipSegments.js index 5c15c63..db4698e 100644 --- a/test/cases/getSkipSegments.js +++ b/test/cases/getSkipSegments.js @@ -1,6 +1,7 @@ var request = require('request'); var db = require('../../src/databases/databases.js').db; var utils = require('../utils.js'); +var getHash = require('../../src/utils/getHash.js'); /* *CREATE TABLE IF NOT EXISTS "sponsorTimes" ( @@ -18,14 +19,14 @@ var utils = require('../utils.js'); describe('getSkipSegments', () => { before(() => { - let startOfQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden) VALUES"; - db.exec(startOfQuery + "('testtesttest', 1, 11, 2, '1-uuid-0', 'testman', 0, 50, 'sponsor', 0)"); - db.exec(startOfQuery + "('testtesttest', 20, 33, 2, '1-uuid-2', 'testman', 0, 50, 'intro', 0)"); - db.exec(startOfQuery + "('testtesttest,test', 1, 11, 2, '1-uuid-1', 'testman', 0, 50, 'sponsor', 0)"); - db.exec(startOfQuery + "('test3', 1, 11, 2, '1-uuid-4', 'testman', 0, 50, 'sponsor', 0)"); - db.exec(startOfQuery + "('test3', 7, 22, -3, '1-uuid-5', 'testman', 0, 50, 'sponsor', 0)"); - db.exec(startOfQuery + "('multiple', 1, 11, 2, '1-uuid-6', 'testman', 0, 50, 'intro', 0)"); - db.exec(startOfQuery + "('multiple', 20, 33, 2, '1-uuid-7', 'testman', 0, 50, 'intro', 0)"); + let startOfQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden, hashedVideoID) VALUES"; + db.exec(startOfQuery + "('testtesttest', 1, 11, 2, '1-uuid-0', 'testman', 0, 50, 'sponsor', 0, '" + getHash('testtesttest', 0) + "')"); + db.exec(startOfQuery + "('testtesttest', 20, 33, 2, '1-uuid-2', 'testman', 0, 50, 'intro', 0, '" + getHash('testtesttest', 0) + "')"); + db.exec(startOfQuery + "('testtesttest,test', 1, 11, 2, '1-uuid-1', 'testman', 0, 50, 'sponsor', 0, '" + getHash('testtesttest,test', 0) + "')"); + db.exec(startOfQuery + "('test3', 1, 11, 2, '1-uuid-4', 'testman', 0, 50, 'sponsor', 0, '" + getHash('test3', 0) + "')"); + db.exec(startOfQuery + "('test3', 7, 22, -3, '1-uuid-5', 'testman', 0, 50, 'sponsor', 0, '" + getHash('test3', 0) + "')"); + db.exec(startOfQuery + "('multiple', 1, 11, 2, '1-uuid-6', 'testman', 0, 50, 'intro', 0, '" + getHash('multiple', 0) + "')"); + db.exec(startOfQuery + "('multiple', 20, 33, 2, '1-uuid-7', 'testman', 0, 50, 'intro', 0, '" + getHash('multiple', 0) + "')"); }); diff --git a/test/cases/oldGetSponsorTime.js b/test/cases/oldGetSponsorTime.js index 569b109..8fe19d2 100644 --- a/test/cases/oldGetSponsorTime.js +++ b/test/cases/oldGetSponsorTime.js @@ -1,6 +1,7 @@ var request = require('request'); var db = require('../../src/databases/databases.js').db; var utils = require('../utils.js'); +var getHash = require('../../src/utils/getHash.js'); /* @@ -19,9 +20,9 @@ var utils = require('../utils.js'); describe('getVideoSponsorTime (Old get method)', () => { before(() => { - let startOfQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden) VALUES"; - db.exec(startOfQuery + "('old-testtesttest', 1, 11, 2, 'uuid-0', 'testman', 0, 50, 'sponsor', 0)"); - db.exec(startOfQuery + "('old-testtesttest,test', 1, 11, 2, 'uuid-1', 'testman', 0, 50, 'sponsor', 0)"); + let startOfQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden, hashedVideoID) VALUES"; + db.exec(startOfQuery + "('old-testtesttest', 1, 11, 2, 'uuid-0', 'testman', 0, 50, 'sponsor', 0, '" + getHash('old-testtesttest', 0) + "')"); + db.exec(startOfQuery + "('old-testtesttest,test', 1, 11, 2, 'uuid-1', 'testman', 0, 50, 'sponsor', 0, '" + getHash('old-testtesttest,test', 0) + "')"); }); it('Should be able to get a time', (done) => { diff --git a/test/cases/voteOnSponsorTime.js b/test/cases/voteOnSponsorTime.js index db618d2..437bbea 100644 --- a/test/cases/voteOnSponsorTime.js +++ b/test/cases/voteOnSponsorTime.js @@ -5,14 +5,14 @@ var getHash = require('../../src/utils/getHash.js') describe('voteOnSponsorTime', () => { before(() => { - let startOfQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden) VALUES"; - db.exec(startOfQuery + "('vote-testtesttest', 1, 11, 2, 'vote-uuid-0', 'testman', 0, 50, 'sponsor', 0)"); - db.exec(startOfQuery + "('vote-testtesttest', 20, 33, 10, 'vote-uuid-2', 'testman', 0, 50, 'intro', 0)"); - db.exec(startOfQuery + "('vote-testtesttest,test', 1, 11, 100, 'vote-uuid-3', 'testman', 0, 50, 'sponsor', 0)"); - db.exec(startOfQuery + "('vote-test3', 1, 11, 2, 'vote-uuid-4', 'testman', 0, 50, 'sponsor', 0)"); - db.exec(startOfQuery + "('vote-test3', 7, 22, -3, 'vote-uuid-5', 'testman', 0, 50, 'intro', 0)"); - db.exec(startOfQuery + "('vote-multiple', 1, 11, 2, 'vote-uuid-6', 'testman', 0, 50, 'intro', 0)"); - db.exec(startOfQuery + "('vote-multiple', 20, 33, 2, 'vote-uuid-7', 'testman', 0, 50, 'intro', 0)"); + let startOfQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden, hashedVideoID) VALUES"; + db.exec(startOfQuery + "('vote-testtesttest', 1, 11, 2, 'vote-uuid-0', 'testman', 0, 50, 'sponsor', 0, '" + getHash('vote-testtesttest', 0) + "')"); + db.exec(startOfQuery + "('vote-testtesttest', 20, 33, 10, 'vote-uuid-2', 'testman', 0, 50, 'intro', 0, '" + getHash('vote-testtesttest', 0) + "')"); + db.exec(startOfQuery + "('vote-testtesttest,test', 1, 11, 100, 'vote-uuid-3', 'testman', 0, 50, 'sponsor', 0, '" + getHash('vote-testtesttest,test', 0) + "')"); + db.exec(startOfQuery + "('vote-test3', 1, 11, 2, 'vote-uuid-4', 'testman', 0, 50, 'sponsor', 0, '" + getHash('vote-test3', 0) + "')"); + db.exec(startOfQuery + "('vote-test3', 7, 22, -3, 'vote-uuid-5', 'testman', 0, 50, 'intro', 0, '" + getHash('vote-test3', 0) + "')"); + db.exec(startOfQuery + "('vote-multiple', 1, 11, 2, 'vote-uuid-6', 'testman', 0, 50, 'intro', 0, '" + getHash('vote-multiple', 0) + "')"); + db.exec(startOfQuery + "('vote-multiple', 20, 33, 2, 'vote-uuid-7', 'testman', 0, 50, 'intro', 0, '" + getHash('vote-multiple', 0) + "')"); db.exec("INSERT INTO vipUsers (userID) VALUES ('" + getHash("VIPUser") + "')"); }); From c2510d302a3934a8c895c03b943403721b6df763 Mon Sep 17 00:00:00 2001 From: Martijn van der Ven Date: Sat, 23 May 2020 21:17:47 +0200 Subject: [PATCH 4/6] Limit overlapping segments to just one through weighted randomness --- src/routes/getSkipSegmentsByHash.js | 90 +++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 18 deletions(-) diff --git a/src/routes/getSkipSegmentsByHash.js b/src/routes/getSkipSegmentsByHash.js index 3672eed..8bb5fcc 100644 --- a/src/routes/getSkipSegmentsByHash.js +++ b/src/routes/getSkipSegmentsByHash.js @@ -12,24 +12,64 @@ const getIP = require('../utils/getIP.js'); * @property {string} UUID Unique identifier for the specific segment */ +/** + * @typedef {Object} Row + * @property {string} videoID + * @property {number} startTime + * @property {number} endTime + * @property {number} votes + * @property {string} UUID + * @property {string} category + * @property {number} shadowHidden + */ + +/** + * Input an array of database records and get only one back, weighed on votes. + * The logic is taken from getWeightedRandomChoice, just simplified input and output to not work on indices only. + * + * @param {Row[]} rows + * @returns {?Row} + */ +function pickWeightedRandomRow(rows) { + if (rows.length === 0) { + return null; + } else if (rows.length === 1) { + return rows[0]; + } + + const sqrtWeightsList = []; + let totalSqrtWeights = 0; + for (const row of rows) { + let sqrtVote = Math.sqrt((row.votes + 3) * 10); + sqrtWeightsList.push(sqrtVote); + totalSqrtWeights += sqrtVote; + } + + const randomNumber = Math.random(); + let currentVoteNumber = 0; + for (let i = 0; i < sqrtWeightsList.length; i++) { + if (randomNumber > currentVoteNumber / totalSqrtWeights && randomNumber < (currentVoteNumber + sqrtWeightsList[i]) / totalSqrtWeights) { + return rows[i]; + } + currentVoteNumber += sqrtWeightsList[i]; + } +} /** * @param {string} prefix Lowercased hexadecimal hash prefix * @param {string} hashedIP Custom hash of the visitor’s IP address - * @returns {Segment[]} + * @returns {Object.} */ function getSkipSegmentsByHash(prefix, hashedIP) { - /** - * @constant - * @type {Segment[]} - * @default - */ - const segments = []; - - const rows = db.prepare('SELECT videoID, startTime, endTime, UUID, category, shadowHidden FROM sponsorTimes WHERE votes >= -1 AND hashedVideoID LIKE ? ORDER BY startTime') + /** @type Row[] */ + const rows = db.prepare('SELECT videoID, startTime, endTime, votes, UUID, category, shadowHidden FROM sponsorTimes WHERE votes >= -1 AND hashedVideoID LIKE ? ORDER BY videoID, startTime') .all(prefix + '%'); - + /** @type {string[]} */ const onlyForCurrentUser = privateDB.prepare('SELECT videoID FROM sponsorTimes WHERE hashedIP = ?').all(hashedIP).map(row => row.videoID); + /** @type {Object.} */ + const rowGroupsPerVideo = {}; + let previousVideoID = null; + let previousEndTime = null; for (const row of rows) { /** @TODO check if this logic does what is expected. */ if (row.shadowHidden === 1 && onlyForCurrentUser.indexOf(row.videoID) === -1) { @@ -37,16 +77,30 @@ function getSkipSegmentsByHash(prefix, hashedIP) { // Do not send shadowHidden segments to them. continue; } - - segments.push({ - videoID: row.videoID, - segment: [row.startTime, row.endTime], - category: row.category, - UUID: row.UUID - }); + // Split up the rows per video and group overlapping segments together. + if (!(row.videoID in rowGroupsPerVideo)) { + rowGroupsPerVideo[row.videoID] = []; + } + if (previousVideoID === row.videoID && row.startTime <= previousEndTime) { + rowGroupsPerVideo[row.videoID][rowGroupsPerVideo[row.videoID].length - 1].push(row); + previousEndTime = Math.max(previousEndTime, row.endTime); + } else { + rowGroupsPerVideo[row.videoID].push([row]); + previousVideoID = row.videoID; + previousEndTime = row.endTime; + } } - return segments; + /** @type {Object.} */ + const output = {}; + for (const videoID in rowGroupsPerVideo) { + const pickedVideosForVideoID = []; + for (const group of rowGroupsPerVideo[videoID]) { + pickedVideosForVideoID.push(pickWeightedRandomRow(group)); + } + output[videoID] = pickedVideosForVideoID.map(row => ({ videoID: row.videoID, segment: [row.startTime, row.endTime], category: row.category, UUID: row.UUID })); + } + return output; } const minimumPrefix = config.minimumPrefix || '3'; From c7fd603933e7d33661c7d3ba7fb0486411b619b4 Mon Sep 17 00:00:00 2001 From: Martijn van der Ven Date: Sat, 23 May 2020 21:25:38 +0200 Subject: [PATCH 5/6] Respond Not Found when a prefix is empty --- src/routes/getSkipSegmentsByHash.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/routes/getSkipSegmentsByHash.js b/src/routes/getSkipSegmentsByHash.js index 8bb5fcc..1762ef3 100644 --- a/src/routes/getSkipSegmentsByHash.js +++ b/src/routes/getSkipSegmentsByHash.js @@ -117,7 +117,9 @@ module.exports = async function (req, res) { getHash(getIP(req) + config.globalSalt) ); - if (segments) { - res.send(segments) + if (Object.keys(segments).length > 0) { + res.send(segments); + } else { + res.sendStatus(404); // No skipable segments within this prefix } } \ No newline at end of file From 3167c24f75bcd9c390baff39c72a47130e2d16f8 Mon Sep 17 00:00:00 2001 From: Martijn van der Ven Date: Sat, 23 May 2020 21:27:21 +0200 Subject: [PATCH 6/6] Put hashedVideoID column before shadowHidden column --- databases/_upgrade_sponsorTimes_2.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/databases/_upgrade_sponsorTimes_2.sql b/databases/_upgrade_sponsorTimes_2.sql index 5472499..1c7270b 100644 --- a/databases/_upgrade_sponsorTimes_2.sql +++ b/databases/_upgrade_sponsorTimes_2.sql @@ -12,8 +12,8 @@ CREATE TABLE "sqlb_temp_table_1" ( "timeSubmitted" INTEGER NOT NULL, "views" INTEGER NOT NULL, "category" TEXT NOT NULL DEFAULT "sponsor", - "shadowHidden" INTEGER NOT NULL, - "hashedVideoID" TEXT NOT NULL + "hashedVideoID" TEXT NOT NULL, + "shadowHidden" INTEGER NOT NULL ); INSERT INTO sqlb_temp_table_1 SELECT *, sha256(videoID) FROM sponsorTimes;