mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2025-12-07 03:57:06 +03:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c132c5089 | ||
|
|
4e732b6367 | ||
|
|
3720681f84 | ||
|
|
2b16872936 | ||
|
|
dadbf8026e | ||
|
|
fd6071f8d6 | ||
|
|
1148803671 | ||
|
|
4379660b01 | ||
|
|
51efb9a5c1 | ||
|
|
abfbba2ad0 | ||
|
|
7e041e5b49 | ||
|
|
d7dec47de7 | ||
|
|
71527cc4b1 | ||
|
|
5fbe580c08 | ||
|
|
c59372dd62 | ||
|
|
ab0631ff63 | ||
|
|
db8c2e76e5 | ||
|
|
11c099c3dc | ||
|
|
9d0e479b90 | ||
|
|
cd36e2b64b | ||
|
|
930c0bc6a3 | ||
|
|
5a5118a7b0 | ||
|
|
5e3c8a3b15 | ||
|
|
a0e63e7326 | ||
|
|
27a6741d3e | ||
|
|
6850414a27 |
@@ -1,6 +1,6 @@
|
||||
# SponsorBlocker Server
|
||||
# SponsorBlock Server
|
||||
|
||||
SponsorBlocker is an extension that will skip over sponsored segments of YouTube videos. SponsorBlocker is a crowdsourced browser extension that let's anyone submit the start and end time's of sponsored segments of YouTube videos. Once one person submits this information, everyone else with this extension will skip right over the sponsored segment.
|
||||
SponsorBlock is an extension that will skip over sponsored segments of YouTube videos. SponsorBlock is a crowdsourced browser extension that let's anyone submit the start and end time's of sponsored segments of YouTube videos. Once one person submits this information, everyone else with this extension will skip right over the sponsored segment.
|
||||
|
||||
This is the server backend for it
|
||||
|
||||
@@ -14,4 +14,4 @@ Hopefully this project can be combined with projects like [this](https://github.
|
||||
|
||||
# Client
|
||||
|
||||
The client web browser extension is available here: https://github.com/ajayyy/SponsorBlocker
|
||||
The client web browser extension is available here: https://github.com/ajayyy/SponsorBlock
|
||||
|
||||
204
index.js
204
index.js
@@ -13,6 +13,8 @@ var crypto = require('crypto');
|
||||
//load database
|
||||
var sqlite3 = require('sqlite3').verbose();
|
||||
var db = new sqlite3.Database('./databases/sponsorTimes.db');
|
||||
//where the more sensitive data such as IP addresses are stored
|
||||
var privateDB = new sqlite3.Database('./databases/private.db');
|
||||
|
||||
// Create an HTTP service.
|
||||
http.createServer(app).listen(80);
|
||||
@@ -36,7 +38,7 @@ app.get('/api/getVideoSponsorTimes', function (req, res) {
|
||||
let votes = []
|
||||
let UUIDs = [];
|
||||
|
||||
db.prepare("SELECT startTime, endTime, votes, UUID FROM sponsorTimes WHERE videoID = ?").all(videoID, function(err, rows) {
|
||||
db.prepare("SELECT startTime, endTime, votes, UUID FROM sponsorTimes WHERE videoID = ? ORDER BY startTime").all(videoID, function(err, rows) {
|
||||
if (err) console.log(err);
|
||||
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
@@ -84,44 +86,82 @@ app.get('/api/postVideoSponsorTimes', function (req, res) {
|
||||
let endTime = req.query.endTime;
|
||||
let userID = req.query.userID;
|
||||
|
||||
if (typeof videoID != 'string' || startTime == undefined || endTime == undefined || userID == undefined) {
|
||||
//check if all correct inputs are here and the length is 1 second or more
|
||||
if (videoID == undefined || startTime == undefined || endTime == undefined || userID == undefined
|
||||
|| Math.abs(startTime - endTime) < 1) {
|
||||
//invalid request
|
||||
res.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
||||
//hash the userID
|
||||
userID = getHashedUserID(userID);
|
||||
|
||||
//x-forwarded-for if this server is behind a proxy
|
||||
let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
|
||||
|
||||
//hash the ip so no one can get it from the database
|
||||
let hashCreator = crypto.createHash('sha256');
|
||||
let hashedIP = hashCreator.update(ip + globalSalt).digest('hex');
|
||||
let hashedIP = ip + globalSalt;
|
||||
//hash it 5000 times, this makes it very hard to brute force
|
||||
for (let i = 0; i < 5000; i++) {
|
||||
let hashCreator = crypto.createHash('sha256');
|
||||
hashedIP = hashCreator.update(hashedIP).digest('hex');
|
||||
}
|
||||
|
||||
startTime = parseFloat(startTime);
|
||||
endTime = parseFloat(endTime);
|
||||
|
||||
let UUID = uuidv1();
|
||||
if (isNaN(startTime) || isNaN(endTime)) {
|
||||
//invalid request
|
||||
res.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
||||
if (startTime > endTime) {
|
||||
//time can't go backwards
|
||||
res.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
||||
//this can just be a hash of the data
|
||||
//it's better than generating an actual UUID like what was used before
|
||||
//also better for duplication checking
|
||||
let hashCreator = crypto.createHash('sha256');
|
||||
let UUID = hashCreator.update(videoID + startTime + endTime + userID).digest('hex');
|
||||
|
||||
//get current time
|
||||
let timeSubmitted = Date.now();
|
||||
|
||||
//check to see if the user has already submitted sponsors for this video
|
||||
db.prepare("SELECT UUID FROM sponsorTimes WHERE userID = ? and videoID = ?").all([userID, videoID], function(err, rows) {
|
||||
if (rows.length >= 4) {
|
||||
//too many sponsors for the same video from the same user
|
||||
let yesterday = timeSubmitted - 86400000;
|
||||
|
||||
//check to see if this ip has submitted too many sponsors today
|
||||
privateDB.prepare("SELECT COUNT(*) as count FROM sponsorTimes WHERE hashedIP = ? AND videoID = ? AND timeSubmitted > ?").get([hashedIP, videoID, yesterday], function(err, row) {
|
||||
if (row.count >= 10) {
|
||||
//too many sponsors for the same video from the same ip address
|
||||
res.sendStatus(429);
|
||||
} else {
|
||||
//check if this info has already been submitted first
|
||||
db.prepare("SELECT UUID FROM sponsorTimes WHERE startTime = ? and endTime = ? and videoID = ?").get([startTime, endTime, videoID], function(err, row) {
|
||||
if (err) console.log(err);
|
||||
|
||||
if (row == null) {
|
||||
//not a duplicate, execute query
|
||||
db.prepare("INSERT INTO sponsorTimes VALUES(?, ?, ?, ?, ?, ?, ?, ?)").run(videoID, startTime, endTime, 0, UUID, userID, hashedIP, timeSubmitted);
|
||||
|
||||
res.sendStatus(200);
|
||||
//check to see if the user has already submitted sponsors for this video
|
||||
db.prepare("SELECT COUNT(*) as count FROM sponsorTimes WHERE userID = ? and videoID = ?").get([userID, videoID], function(err, row) {
|
||||
if (row.count >= 4) {
|
||||
//too many sponsors for the same video from the same user
|
||||
res.sendStatus(429);
|
||||
} else {
|
||||
res.sendStatus(409);
|
||||
//check if this info has already been submitted first
|
||||
db.prepare("SELECT UUID FROM sponsorTimes WHERE startTime = ? and endTime = ? and videoID = ?").get([startTime, endTime, videoID], function(err, row) {
|
||||
if (err) console.log(err);
|
||||
|
||||
if (row == null) {
|
||||
//not a duplicate, execute query
|
||||
db.prepare("INSERT INTO sponsorTimes VALUES(?, ?, ?, ?, ?, ?, ?, ?)").run(videoID, startTime, endTime, 0, UUID, userID, timeSubmitted, 0);
|
||||
|
||||
//add to private db as well
|
||||
privateDB.prepare("INSERT INTO sponsorTimes VALUES(?, ?, ?)").run(videoID, hashedIP, timeSubmitted);
|
||||
|
||||
res.sendStatus(200);
|
||||
} else {
|
||||
res.sendStatus(409);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -140,32 +180,130 @@ app.get('/api/voteOnSponsorTime', function (req, res) {
|
||||
return;
|
||||
}
|
||||
|
||||
//-1 for downvote, 1 for upvote. Maybe more depending on reputation in the future
|
||||
let incrementAmount = 0;
|
||||
//hash the userID
|
||||
userID = getHashedUserID(userID + UUID);
|
||||
|
||||
//don't use userID for now, and just add the vote
|
||||
if (type == 1) {
|
||||
//upvote
|
||||
incrementAmount = 1;
|
||||
} else if (type == 0) {
|
||||
//downvote
|
||||
incrementAmount = -1;
|
||||
} else {
|
||||
//unrecongnised type of vote
|
||||
req.sendStatus(400);
|
||||
//x-forwarded-for if this server is behind a proxy
|
||||
let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
|
||||
|
||||
//hash the ip so no one can get it from the database
|
||||
let hashedIP = ip + globalSalt;
|
||||
//hash it 5000 times, this makes it very hard to brute force
|
||||
for (let i = 0; i < 5000; i++) {
|
||||
let hashCreator = crypto.createHash('sha256');
|
||||
hashedIP = hashCreator.update(hashedIP).digest('hex');
|
||||
}
|
||||
|
||||
//check if vote has already happened
|
||||
privateDB.prepare("SELECT type FROM votes WHERE userID = ? AND UUID = ?").get(userID, UUID, function(err, row) {
|
||||
if (err) console.log(err);
|
||||
|
||||
if (row != undefined && row.type == type) {
|
||||
//they have already done this exact vote
|
||||
res.status(405).send("Duplicate Vote");
|
||||
return;
|
||||
}
|
||||
|
||||
//-1 for downvote, 1 for upvote. Maybe more depending on reputation in the future
|
||||
let incrementAmount = 0;
|
||||
let oldIncrementAmount = 0;
|
||||
|
||||
if (type == 1) {
|
||||
//upvote
|
||||
incrementAmount = 1;
|
||||
} else if (type == 0) {
|
||||
//downvote
|
||||
incrementAmount = -1;
|
||||
} else {
|
||||
//unrecongnised type of vote
|
||||
res.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
if (row != undefined) {
|
||||
if (row.type == 1) {
|
||||
//upvote
|
||||
oldIncrementAmount = 1;
|
||||
} else if (row.type == 0) {
|
||||
//downvote
|
||||
oldIncrementAmount = -1;
|
||||
}
|
||||
}
|
||||
|
||||
//update the votes table
|
||||
if (row != undefined) {
|
||||
privateDB.prepare("UPDATE votes SET type = ? WHERE userID = ? AND UUID = ?").run(type, userID, UUID);
|
||||
} else {
|
||||
privateDB.prepare("INSERT INTO votes VALUES(?, ?, ?, ?)").run(UUID, userID, hashedIP, type);
|
||||
}
|
||||
|
||||
//update the vote count on this sponsorTime
|
||||
//oldIncrementAmount will be zero is row is null
|
||||
db.prepare("UPDATE sponsorTimes SET votes = votes + ? WHERE UUID = ?").run(incrementAmount - oldIncrementAmount, UUID);
|
||||
|
||||
//added to db
|
||||
res.sendStatus(200);
|
||||
});
|
||||
});
|
||||
|
||||
//Endpoint when a sponsorTime is used up
|
||||
app.get('/api/viewedVideoSponsorTime', function (req, res) {
|
||||
let UUID = req.query.UUID;
|
||||
|
||||
if (UUID == undefined) {
|
||||
//invalid request
|
||||
res.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
||||
db.prepare("UPDATE sponsorTimes SET votes = votes + ? WHERE UUID = ?").run(incrementAmount, UUID);
|
||||
//up the view count by one
|
||||
db.prepare("UPDATE sponsorTimes SET views = views + 1 WHERE UUID = ?").run(UUID);
|
||||
|
||||
//added to db
|
||||
res.sendStatus(200);
|
||||
});
|
||||
|
||||
//Gets all the views added up for one userID
|
||||
//Useful to see how much one user has contributed
|
||||
app.get('/api/getViewsForUser', function (req, res) {
|
||||
let userID = req.query.userID;
|
||||
|
||||
if (userID == undefined) {
|
||||
//invalid request
|
||||
res.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
||||
//hash the userID
|
||||
userID = getHashedUserID(userID);
|
||||
|
||||
//up the view count by one
|
||||
db.prepare("SELECT SUM(views) as viewCount FROM sponsorTimes WHERE userID = ?").get(userID, function(err, row) {
|
||||
if (err) console.log(err);
|
||||
|
||||
if (row.viewCount != null) {
|
||||
res.send({
|
||||
viewCount: row.viewCount
|
||||
});
|
||||
} else {
|
||||
res.sendStatus(404);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/database.db', function (req, res) {
|
||||
res.sendFile("./databases/sponsorTimes.db", { root: __dirname });
|
||||
});
|
||||
|
||||
function getHashedUserID(userID) {
|
||||
//hash the userID so no one can get it from the database
|
||||
let hashedUserID = userID;
|
||||
//hash it 5000 times, this makes it very hard to brute force
|
||||
for (let i = 0; i < 5000; i++) {
|
||||
let hashCreator = crypto.createHash('sha256');
|
||||
hashedUserID = hashCreator.update(hashedUserID).digest('hex');
|
||||
}
|
||||
|
||||
return hashedUserID;
|
||||
}
|
||||
|
||||
//This function will find sponsor times that are contained inside of eachother, called similar sponsor times
|
||||
//Only one similar time will be returned, randomly generated based on the sqrt of votes.
|
||||
|
||||
Reference in New Issue
Block a user