invidious backend finished. Queueing youtube videos no longer requires

API KEY, does not directly connect to or run any youtube code/servers.
Age Restricted videos are now fully queable.
This commit is contained in:
rainbownapkin 2022-07-12 06:21:48 +00:00
parent f3306f2263
commit e171415b30
5 changed files with 213 additions and 58 deletions

View file

@ -112,6 +112,11 @@ io:
# https.domain are included implicitly).
allowed-origins: []
#pull info from invidious
invidious-backend: true
#invidious source, defaults to vid.puffyan.us, not affiliated, simply a well known US based instance
#invidious-source: 'vid.puffyan.us'
# YouTube v3 API key
# 1. Go to https://console.developers.google.com/, create a new "project" (or choose an existing one)
# 2. Make sure the YouTube Data v3 API is "enabled" for your project: https://console.developers.google.com/apis/library/youtube.googleapis.com

View file

@ -1098,8 +1098,6 @@ PlaylistModule.prototype._addItem = function (media, data, user, cb) {
}
console.log("pre media subload");
console.log(data);
if (data.subtitle && (media.type === "cu" || media.type === "fi")) {

View file

@ -65,6 +65,8 @@ var defaults = {
"allowed-origins": []
}
},
"invidious-backend": true,
"invidious-source": 'vid.puffyan.us',
"youtube-v3-key": "",
"channel-blacklist": [],
"channel-path": "r",

View file

@ -1,3 +1,42 @@
/*
fore.st is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
fore.st is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with fore.st. If not, see < http://www.gnu.org/licenses/ >.
(C) 2022- by rainbownapkin, <ourforest@420blaze.it>
Original cytube license:
MIT License
Copyright (c) 2013-2022 Calvin Montgomery
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
const https = require("https");
const Media = require("./media");
const CustomEmbedFilter = require("./customembed").filter;
@ -57,78 +96,175 @@ function convertMedia(media) {
media.meta);
}
function getBlocked(reg){
var regionlist = ["AD","AE","AF","AG","AI","AL","AM","AO","AQ","AR","AS","AT","AU","AW","AX","AZ","BB","BD","BE","BF","BG","BH","BI","BJ","BL","BM","BN","BO","BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD","CF","CG","CH","CI","CK","CL","CM","CN","CO","CR","CU","CV","CX","CY","CZ","DE","DJ","DK","DM","DO","DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ","FK","FM","FO","FR","GA","GB","GD","GE","GF","GG","GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT","GU","GW","GY","HK","HM","HN","HR","HT","HU","ID","IE","IL","IM","IN","IO","IQ","IR","IS","IT","JE","JM","JO","JP","KE","KG","KH","KI","KM","KN","KP","KR","KW","KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT","LU","LV","LY","MA","MC","MD","ME","MG","MH","MK","ML","MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV","MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI","NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF","PG","PH","PK","PL","PM","PN","PR","PS","PT","PW","PY","QA","RE","RO","RS","RU","RW","SA","SB","SC","SD","SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO","SR","ST","SV","SY","SZ","TC","TD","TF","TG","TH","TJ","TK","TL","TM","TN","TO","TR","TT","TV","TW","TZ","UA","UG","UM","US","UY","UZ","VA","VC","VE","VG","VI","VN","VU","WF","WS","YE","YT","ZA","ZM","ZW"];//forgive me father, for I have sinned.
var blck = [];
for(var i = 0; i < regionlist.length; i++){
if(!reg.includes(regionlist[i])){
blck.push(regionlist[i]);
}
}
return blck;
}
var Getters = {
/* youtube.com */
yt: function (id, callback) {
if (!Config.get("youtube-v3-key")) {
return callback("The YouTube API now requires an API key. Please see the " +
"documentation for youtube-v3-key in config.template.yaml");
}
if(!Config.get("invidious-backend")){//legacy youtube backend (fucking yicky)
if (!Config.get("youtube-v3-key")) {
return callback("The YouTube API now requires an API key. You could sign up for an API key, but you're a lot better off using the invidious backend!" +
"See your config.yaml for the deets.");
}
YouTube.lookup(id).then(function (video) {
var meta = {};
if (video.meta.blocked) {
meta.restricted = video.meta.blocked;
}
if (video.meta.ytRating) {
meta.ytRating = video.meta.ytRating;
}
YouTube.lookup(id).then(function (video) {
var meta = {};
if (video.meta.blocked) {
meta.restricted = video.meta.blocked;
}
if (video.meta.ytRating) {
meta.ytRating = video.meta.ytRating;
}
var media = new Media(video.id, video.title, video.duration, "yt", meta);
callback(false, media);
}).catch(function (err) {
callback(err.message || err, null);
});
var media = new Media(video.id, video.title, video.duration, "yt", meta);
callback(false, media);
}).catch(function (err) {
callback(err.message || err, null);
});
}else{//invidious api calls (google bad)
var options = {
host: Config.get("invidious-source"),
port: 443,
path: "/api/v1/videos/" + id,
method: "GET",
timeout: 1000
};
urlRetrieve(https, options, function (status, data) {
if(status !== 200) {
return callback("Invidious HTTPS error code: " + status, null);
}
var vid = JSON.parse(data);
var meta = {}
if(getBlocked(vid.allowedRegions).length > 0){
meta.restricted = getBlocked(vid.allowedRegions);
}
if(vid.likeCount){
meta.ytRating = vid.likeCount;
}
var media = new Media(vid.videoId, vid.title, vid.lengthSeconds, "yt", meta);
return callback(false, media);
});
}
},
/* youtube.com playlists */
yp: function (id, callback) {
if (!Config.get("youtube-v3-key")) {
return callback("The YouTube API now requires an API key. Please see the " +
"documentation for youtube-v3-key in config.template.yaml");
}
if(!Config.get("invidious-backend")){//legacy youtube backend (fucking yicky)
if (!Config.get("youtube-v3-key")) {
return callback("The YouTube API now requires an API key. You could sign up for an API key, but you're a lot better off using the invidious backend!" +
"See your config.yaml for the deets.");
}
YouTube.lookupPlaylist(id).then(function (videos) {
videos = videos.map(function (video) {
var meta = {};
if (video.meta.blocked) {
meta.restricted = video.meta.blocked;
}
YouTube.lookupPlaylist(id).then(function (videos) {
videos = videos.map(function (video) {
var meta = {};
if (video.meta.blocked) {
meta.restricted = video.meta.blocked;
}
return new Media(video.id, video.title, video.duration, "yt", meta);
});
return new Media(video.id, video.title, video.duration, "yt", meta);
});
callback(null, videos);
}).catch(function (err) {
callback(err.message || err, null);
});
callback(null, videos);
}).catch(function (err) {
callback(err.message || err, null);
});
}else{//invidious api calls (google bad)
var options = {
host: Config.get("invidious-source"),
port: 443,
path: "/api/v1/playlists/" + id,
method: "GET",
timeout: 1000
};
urlRetrieve(https, options, function (status, data) {
if(status !== 200) {
return callback("Invidious HTTPS error code: " + status, null);
}
var pl = JSON.parse(data).videos;
var vids = pl.map(function(vid){
return new Media(vid.videoId, vid.title, vid.lengthSeconds, "yt", []);//return the vid as media obj, (skip out on meta to avoid extra api calls)
});
return callback(null, vids);
});
}
},
/* youtube.com search */
ytSearch: function (query, callback) {
if (!Config.get("youtube-v3-key")) {
return callback("The YouTube API now requires an API key. Please see the " +
"documentation for youtube-v3-key in config.template.yaml");
}
YouTube.search(query).then(function (res) {
var videos = res.results;
videos = videos.map(function (video) {
var meta = {};
if (video.meta.blocked) {
if(!Config.get("invidious-backend")){//legacy youtube backend (fucking yicky)
if (!Config.get("youtube-v3-key")) {
return callback("The YouTube API now requires an API key. Please see the " +
"documentation for youtube-v3-key in config.template.yaml");
}
YouTube.search(query).then(function (res) {
var videos = res.results;
videos = videos.map(function (video) {
var meta = {};
if (video.meta.blocked) {
meta.restricted = video.meta.blocked;
}
}
var media = new Media(video.id, video.title, video.duration, "yt", meta);
media.thumb = { url: video.meta.thumbnail };
return media;
});
var media = new Media(video.id, video.title, video.duration, "yt", meta);
media.thumb = { url: video.meta.thumbnail };
return media;
});
callback(null, videos);
}).catch(function (err) {
callback(err.message || err, null);
});
callback(null, videos);
}).catch(function (err) {
callback(err.message || err, null);
});
}else{//invidious api calls (google bad)
var options = {
host: Config.get("invidious-source"),
port: 443,
path: "/api/v1/search?q='" + query + "'",
method: "GET",
timeout: 1000
};
urlRetrieve(https, options, function (status, data) {
if(status !== 200) {
return callback("Invidious HTTPS error code: " + status, null);
}
var srch = JSON.parse(data);
var vids = srch.map(function(rslt, i){
var media;
if(rslt.type === "video"){
media = new Media(rslt.videoId, rslt.title, rslt.lengthSeconds, "yt", [])//create new media object from curent rslt
media.thumb = {url: rslt.videoThumbnails[5].url};
return media
}
});
return callback(null, vids.filter(rs => rs != null));
});
}
},
/* vimeo.com */

View file

@ -269,6 +269,7 @@
}
YouTubePlayer.prototype.load = function(data) {
this.setMediaProperties(data);
if (this.yt && this.yt.ready) {
return this.yt.loadVideoById(data.id, data.currentTime);
@ -279,20 +280,26 @@
YouTubePlayer.prototype.onReady = function() {
this.yt.ready = true;
this.latched = true;
handleVideoResize();
return this.setVolume(VOLUME);
};
YouTubePlayer.prototype.onStateChange = function(ev) {
setMini();
if (!CLIENT.leader && ev.data >= 2) {
this.unlatch();
}
if (ev.data === YT.PlayerState.PLAYING && this.pauseSeekRaceCondition) {
this.pause();
this.pauseSeekRaceCondition = false;
}
if ((ev.data === YT.PlayerState.PAUSED && !this.paused) || (ev.data === YT.PlayerState.PLAYING && this.paused)) {
this.paused = ev.data === YT.PlayerState.PAUSED;
if (CLIENT.leader) {
sendVideoUpdate();
}
}
this.paused = ev.data === YT.PlayerState.PAUSED;
}
if (ev.data === YT.PlayerState.ENDED && CLIENT.leader) {
return socket.emit('playNext');
}
@ -319,6 +326,7 @@
};
YouTubePlayer.prototype.setVolume = function(volume) {
setMini();
if (this.yt && this.yt.ready) {
if (volume > 0) {
this.yt.unMute();
@ -329,6 +337,10 @@
YouTubePlayer.prototype.setQuality = function(quality) {};
YouTubePlayer.prototype.getRes = function(cb) {
return cb([1920,1080]);
};
YouTubePlayer.prototype.getTime = function(cb) {
if (this.yt && this.yt.ready) {
return cb(this.yt.getCurrentTime());
@ -618,6 +630,7 @@
}
VideoJSPlayer.prototype.loadPlayer = function(data) {
this.latched = true;
return waitUntilDefined(window, 'videojs', (function(_this) {
return function() {
var attrs, video;
@ -1751,6 +1764,7 @@
PLAYER = window.PLAYER;
dispSTimes();
PLAYER.lastSTime = data.currentTime;
setDur();
if (!PLAYER.latched) {
return;
}