Compare commits
2 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
860de262ce | ||
|
|
751624f90b |
42
README.md
42
README.md
|
|
@ -1,4 +1,4 @@
|
|||
fore.st - Pineapple Express += 3 (v1.1.3)
|
||||
fore.st - Pineapple Express++ (v1.1.1)
|
||||
======
|
||||
|
||||
fore.st is the server software for ourfore.st, a community based chat & synced video embedding site tailored to service
|
||||
|
|
@ -24,8 +24,44 @@ You can reach out by bugging rainbownapkin on the ttn discord or ourfore.st, you
|
|||
- Thanks to calzoneman for making [cytube](https://github.com/calzoneman/sync), that saved our asses.
|
||||
- Thanks to the core TTN community and everyone else who's ever used it, I was only there for the last handful of years but it was an absolute fuckin' ride. You guys are the best, it isn't TTN but I hope this at least help fills the gap.
|
||||
|
||||
## Pineapple Express += 3 1.1.3 Release Notes
|
||||
- Add bulk-queueing from Internet Archive
|
||||
## Pineapple Express++ 1.1.1 Release Notes
|
||||
Abriged Patchnotes:
|
||||
- bugfixes/QoL tweaks
|
||||
- remove duplicated blazem from tokefile
|
||||
- fix playlist on submit channel for standard users
|
||||
- only show nested menu on playlist if user rank is <= 2
|
||||
- show "playlist" button for users rank 1 on r/submit channel
|
||||
- stop tokebot from including full toke messages
|
||||
- fully disconnect/kick unregistered users from channels connect
|
||||
- limit words/link display text(DO NOT MODIFY LINK HREF) to 40 chars to prevent breaking chat width
|
||||
- limit image embed width
|
||||
- fix video/chat/autobump UI components not expanding after collapsing in chromium based browsers
|
||||
- remove close playlist button when legacy playlist is disabled
|
||||
- fix expand/shade playlist items button
|
||||
- add end date to playlist items
|
||||
- userlist collapse/expand persistent accross browser sessions
|
||||
- cinema mode persistent accross browser sessions
|
||||
- dissapearing server whispers/join messages
|
||||
- fix input field lengths in quicksettings
|
||||
- prevent temporary items on submit channel
|
||||
- always block anonymous user (ignore channel setting)
|
||||
- use regex for whitespace detection in commands
|
||||
|
||||
- client side commands
|
||||
- process commands that start with "/" or "(whitespace)/" in the browser
|
||||
- allow admins to trigger client side commands remotely
|
||||
- show words that start with "/" as a link that chatsmacks command(similiar to behavior with words that start with !)
|
||||
- /thunder
|
||||
- Disable lightning (seizure prevention)
|
||||
|
||||
- update tab completion
|
||||
- add non-toke server-side commands to tab completion(check perms for respective commands)
|
||||
- add client-side commands to tab completion
|
||||
- show autocomplete in text box
|
||||
|
||||
- kick users w/ old client
|
||||
|
||||
- spooktober themes
|
||||
|
||||
## License
|
||||
Original fore.st code is provided under the Affero General Public License v3 in order to prevent fore.st being used in proprietary software.
|
||||
|
|
|
|||
|
|
@ -114,7 +114,6 @@ io:
|
|||
|
||||
#pull info from invidious
|
||||
invidious-backend: true
|
||||
yt-dlp-path: 'yt-dlp'
|
||||
#invidious source, defaults to vid.puffyan.us, not affiliated, simply a well known US based instance
|
||||
#invidious-source: 'vid.puffyan.us'
|
||||
|
||||
|
|
|
|||
3287
package-lock.json
generated
3287
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "fore.st",
|
||||
"version": "1.1.3",
|
||||
"version": "1.2-indev",
|
||||
"description": "fore.st: A fork of cytube tailored for the TTN Community",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
"express": "^4.17.1",
|
||||
"express-minify": "^1.0.0",
|
||||
"json-typecheck": "^0.1.3",
|
||||
"knex": "^3.1.0",
|
||||
"knex": "^0.95.2",
|
||||
"lodash": "^4.17.21",
|
||||
"morgan": "^1.10.0",
|
||||
"mysql": "^2.18.1",
|
||||
|
|
@ -38,8 +38,7 @@
|
|||
"source-map-support": "^0.5.19",
|
||||
"toml": "^3.0.0",
|
||||
"uuid": "^8.3.2",
|
||||
"yamljs": "^0.2.8",
|
||||
"youtube-dl-exec": "^3.0.10"
|
||||
"yamljs": "^0.2.8"
|
||||
},
|
||||
"scripts": {
|
||||
"build-server": "babel -D --source-maps --out-dir lib/ src/",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,60 @@
|
|||
dev goals for 1.1.3 pineapple Express += 3:
|
||||
-- Add bulk-queueing from Internet Archive
|
||||
- automagically pull most web-compatible version of all videos within a specific upload to IA
|
||||
- allow queueing admin to filter directory by video length in minutes
|
||||
- allow for bulk naming
|
||||
dev goals for 1.1.1 pineapple express++:
|
||||
|
||||
- bugfixes/QoL tweaks
|
||||
- remove duplicated blazem from tokefile
|
||||
- fix playlist on submit channel for standard users
|
||||
- only show nested menu on playlist if user rank is <= 2
|
||||
- show "playlist" button for users rank 1 on r/submit channel
|
||||
- stop tokebot from including full toke messages
|
||||
- fully disconnect/kick unregistered users from channels connect
|
||||
- limit words/link display text(DO NOT MODIFY LINK HREF) to 40 chars to prevent breaking chat width
|
||||
- limit image embed width
|
||||
- fix video/chat/autobump UI components not expanding after collapsing in chromium based browsers
|
||||
- remove close playlist button when legacy playlist is disabled
|
||||
- fix expand/shade playlist items button
|
||||
- add end date to playlist items
|
||||
- userlist collapse/expand persistent accross browser sessions
|
||||
- cinema mode persistent accross browser sessions
|
||||
- dissapearing server whispers/join messages
|
||||
- fix input field lengths in quicksettings
|
||||
- prevent temporary items on submit channel
|
||||
- always block anonymous user (ignore channel setting)
|
||||
- use regex for whitespace detection in commands
|
||||
|
||||
- client side commands
|
||||
- process commands that start with "/" or "(whitespace)/" in the browser
|
||||
- allow admins to trigger client side commands remotely
|
||||
- remove / from client command when calling sendcmd if present
|
||||
- do not send command as chat (unless it starts with whitespace, matching serverside command behavior)
|
||||
- show words that start with "/" as a link that chatsmacks command(similiar to behavior with words that start with !)
|
||||
- /thunder
|
||||
- Disable lightning (seizure prevention)
|
||||
|
||||
- update tab completion
|
||||
- add non-toke server-side commands to tab completion(check perms for respective commands)
|
||||
- me
|
||||
- announce
|
||||
- af
|
||||
- sp
|
||||
- afk
|
||||
- poll
|
||||
- hpoll
|
||||
- mute
|
||||
- smute
|
||||
- unmute
|
||||
- kick
|
||||
- ban
|
||||
- ipban
|
||||
- clear
|
||||
- clean
|
||||
- cleantitle
|
||||
- sendcmd
|
||||
- add client-side commands to tab completion
|
||||
- show autocomplete in text box
|
||||
|
||||
- send clientside version on connect
|
||||
- kick if version old/not received
|
||||
|
||||
- spooktober themes
|
||||
- Candycorn
|
||||
- Candycorn lite
|
||||
|
|
|
|||
|
|
@ -107,13 +107,11 @@ function loadLists(cb, callp){
|
|||
|
||||
bumplists = new Map();//create new map to load lists into, this clears the variable as well as lets the channel know whether or not they have been loaded yet.
|
||||
|
||||
if(item != null){
|
||||
item.forEach(function(list, i){
|
||||
if(list != configFolder.slice(bumpFolder.length, configFolder.length - 1) && list.slice(list.length - 5) === ".bump"){
|
||||
loadList("bumps/" + list);
|
||||
}
|
||||
});
|
||||
}
|
||||
item.forEach(function(list, i){
|
||||
if(list != configFolder.slice(bumpFolder.length, configFolder.length - 1) && list.slice(list.length - 5) === ".bump"){
|
||||
loadList("bumps/" + list);
|
||||
}
|
||||
});
|
||||
|
||||
if(cb != null){//lil' nasty but it calls loadconfig after loading lists :P
|
||||
if(callp != null){
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ const TYPE_QUEUE = {
|
|||
pos: "string",
|
||||
title: "string,boolean,optional",
|
||||
duration: "number,optional",
|
||||
minDuration: "number,optional",
|
||||
temp: "boolean,optional",
|
||||
subtitle: "string"
|
||||
};
|
||||
|
|
@ -446,7 +445,7 @@ PlaylistModule.prototype.handleQueue = function (user, data) {
|
|||
* Specifying a custom title is currently only allowed for custom media
|
||||
* and raw files
|
||||
*/
|
||||
if (typeof data.title !== "string" || (data.type !== "cu" && data.type !== "fi" && data.type !== "ia")) {
|
||||
if (typeof data.title !== "string" || (data.type !== "cu" && data.type !== "fi")) {
|
||||
data.title = false;
|
||||
}
|
||||
|
||||
|
|
@ -467,9 +466,9 @@ PlaylistModule.prototype.handleQueue = function (user, data) {
|
|||
}
|
||||
|
||||
/* Certain media types require special permission to add */
|
||||
if ((data.type === "yp" || data.type === "ia") && !perms.canAddList(user)) {
|
||||
if (data.type === "yp" && !perms.canAddList(user)) {
|
||||
user.socket.emit("queueFail", {
|
||||
msg: "You don't have permission to bulk queue",
|
||||
msg: "You don't have permission to add playlists",
|
||||
link: link,
|
||||
id: id
|
||||
});
|
||||
|
|
@ -505,11 +504,6 @@ PlaylistModule.prototype.handleQueue = function (user, data) {
|
|||
duration = !isNaN(data.duration) ? data.duration : undefined;
|
||||
}
|
||||
|
||||
var minDuration = undefined;
|
||||
if (typeof data.minDuration === "number") {
|
||||
minDuration = !isNaN(data.minDuration) ? data.minDuration : 0;
|
||||
}
|
||||
|
||||
var limit = {
|
||||
burst: 3,
|
||||
sustained: 1
|
||||
|
|
@ -543,20 +537,17 @@ PlaylistModule.prototype.handleQueue = function (user, data) {
|
|||
type: data.type,
|
||||
pos: data.pos,
|
||||
title: data.title,
|
||||
subtitle: data.subtitle,
|
||||
subtitle: data.subtitle,
|
||||
link: link,
|
||||
temp: temp,
|
||||
shouldAddToLibrary: true,//for now chan library will act as a history
|
||||
queueby: queueby,
|
||||
duration: duration,
|
||||
minDuration: minDuration,
|
||||
maxlength: maxlength
|
||||
};
|
||||
|
||||
if (data.type === "yp") {
|
||||
this.queueYouTubePlaylist(user, data);
|
||||
} else if (data.type === "ia") {
|
||||
this.queueBulkIA(user, data);
|
||||
} else {
|
||||
this.queueStandard(user, data);
|
||||
}
|
||||
|
|
@ -585,38 +576,7 @@ PlaylistModule.prototype.queueStandard = function (user, data) {
|
|||
lock.release();
|
||||
self.channel.refCounter.unref("PlaylistModule::queueStandard");
|
||||
});
|
||||
}, data.minDuration);
|
||||
});
|
||||
};
|
||||
|
||||
PlaylistModule.prototype.queueBulkIA = function (user, data) {
|
||||
var error = function (what) {
|
||||
user.socket.emit("queueFail", {
|
||||
msg: what,
|
||||
link: data.link,
|
||||
id: data.id
|
||||
});
|
||||
};
|
||||
|
||||
const self = this;
|
||||
this.channel.refCounter.ref("PlaylistModule::queueBulkIA");
|
||||
this.semaphore.queue(function (lock) {
|
||||
InfoGetter.getMedia(data.id, data.type, function (err, vids) {
|
||||
if (err) {
|
||||
error(XSS.sanitizeText(String(err)));
|
||||
self.channel.refCounter.unref("PlaylistModule::queueBulkIA");
|
||||
return lock.release();
|
||||
}
|
||||
|
||||
|
||||
vids.forEach(function(media){
|
||||
self._addItem(media, data, user, null, data.minDuration);
|
||||
});
|
||||
|
||||
|
||||
self.channel.refCounter.unref("PlaylistModule::queueBulkIA");
|
||||
lock.release();
|
||||
}, data.minDuration);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -1157,7 +1117,7 @@ PlaylistModule.prototype._addItem = function (media, data, user, cb, abump) {
|
|||
queueby: data.queueby
|
||||
});
|
||||
|
||||
if (data.title && (media.type === "cu" || media.type === "fi" || media.type === "ia")) {
|
||||
if (data.title && (media.type === "cu" || media.type === "fi")) {
|
||||
media.setTitle(data.title);
|
||||
}
|
||||
|
||||
|
|
@ -1329,17 +1289,12 @@ PlaylistModule.prototype.startPlayback = function (time) {
|
|||
}
|
||||
|
||||
if(self.current.media.type == "yt"){//if its yt
|
||||
InfoGetter.getRawCopy(self.current.media.id,function(url){//get raw link from invidious api
|
||||
InfoGetter.getYTRaw(self.current.media.id,function(url){//get raw link from invidious api
|
||||
self.current.media.meta.rawLink = url;//set to meta
|
||||
self.sendChangeMedia(self.channel.users);//fuggin SEND IT
|
||||
});
|
||||
|
||||
}else if(self.current.media.type == "dm"){
|
||||
InfoGetter.getRawCopy(`https://dailymotion.com/video/${self.current.media.id}`,function(url){//get raw link from invidious api
|
||||
self.current.media.meta.rawLink = url;//set to meta
|
||||
self.sendChangeMedia(self.channel.users);//fuggin SEND IT
|
||||
});
|
||||
}else{
|
||||
}else{
|
||||
self.sendChangeMedia(self.channel.users);
|
||||
}
|
||||
self.channel.notifyModules("onMediaChange", [self.current.media]);
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ var defaults = {
|
|||
},
|
||||
"invidious-backend": true,
|
||||
"invidious-source": 'inv.riverside.rocks',
|
||||
"yt-dlp-path": 'yt-dlp',
|
||||
"youtube-v3-key": "",
|
||||
"channel-blacklist": [],
|
||||
"channel-path": "r",
|
||||
|
|
|
|||
220
src/get-info.js
220
src/get-info.js
|
|
@ -49,14 +49,9 @@ const Vimeo = require("@cytube/mediaquery/lib/provider/vimeo");
|
|||
const Streamable = require("@cytube/mediaquery/lib/provider/streamable");
|
||||
const TwitchVOD = require("@cytube/mediaquery/lib/provider/twitch-vod");
|
||||
const TwitchClip = require("@cytube/mediaquery/lib/provider/twitch-clip");
|
||||
const { create: makeYTDLP } = require('youtube-dl-exec')
|
||||
//Specify path assuming yt-dlp is installed locally with path set properly (version packaged w/ npm package doesn't behave)
|
||||
const YTDLP = makeYTDLP(Config.get('yt-dlp-path'));
|
||||
|
||||
import { Counter } from 'prom-client';
|
||||
import { lookup as lookupCustomMetadata } from './custom-media';
|
||||
|
||||
|
||||
const LOGGER = require('@calzoneman/jsli')('get-info');
|
||||
const lookupCounter = new Counter({
|
||||
name: 'cytube_media_lookups_total',
|
||||
|
|
@ -116,7 +111,7 @@ function getBlocked(reg){
|
|||
|
||||
var Getters = {
|
||||
/* youtube.com */
|
||||
yt: async function (id, callback) {
|
||||
yt: function (id, callback) {
|
||||
|
||||
if(!Config.get("invidious-backend")){//legacy youtube backend (fucking yicky)
|
||||
if (!Config.get("youtube-v3-key")) {
|
||||
|
|
@ -127,13 +122,11 @@ var Getters = {
|
|||
|
||||
YouTube.lookup(id).then(function (video) {
|
||||
var meta = {};
|
||||
|
||||
if (video.meta.blocked) {
|
||||
meta.restricted = video.meta.blocked;
|
||||
meta.restricted = video.meta.blocked;
|
||||
}
|
||||
|
||||
if (video.meta.ytRating) {
|
||||
meta.ytRating = video.meta.ytRating;
|
||||
meta.ytRating = video.meta.ytRating;
|
||||
}
|
||||
|
||||
var media = new Media(video.id, video.title, video.duration, "yt", meta);
|
||||
|
|
@ -141,31 +134,8 @@ var Getters = {
|
|||
}).catch(function (err) {
|
||||
callback(err.message || err, null);
|
||||
});
|
||||
}else{
|
||||
//yt-dlp calls (google bad)
|
||||
try{
|
||||
var video = await YTDLP(`youtu.be/${id}`,{
|
||||
dumpSingleJson: true,
|
||||
format: "b"
|
||||
}
|
||||
)
|
||||
|
||||
var meta = {
|
||||
ytRating: video.like_count,
|
||||
rawLink: video.requested_downloads[0].url
|
||||
}
|
||||
|
||||
var media = new Media(video.id, video.title, video.duration, "yt", meta);
|
||||
|
||||
|
||||
callback(false, media);
|
||||
}catch(err){
|
||||
callback(err.message || err, null);
|
||||
}
|
||||
|
||||
//callback("test funciton, remove this call!" || err, null);
|
||||
//invidious api calls (o7 Stream on, you magnificent bastard. We'll always remember you! <3 2018-2024)
|
||||
/*var options = {
|
||||
}else{//invidious api calls (google bad)
|
||||
var options = {
|
||||
host: Config.get("invidious-source"),
|
||||
port: 443,
|
||||
path: "/api/v1/videos/" + id,
|
||||
|
|
@ -191,13 +161,13 @@ var Getters = {
|
|||
|
||||
var media = new Media(vid.videoId, vid.title, vid.lengthSeconds, "yt", meta);
|
||||
return callback(false, media);
|
||||
});*/
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
/* youtube.com playlists */
|
||||
yp: async function (id, callback) {
|
||||
yp: function (id, callback) {
|
||||
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!" +
|
||||
|
|
@ -218,35 +188,7 @@ var Getters = {
|
|||
}).catch(function (err) {
|
||||
callback(err.message || err, null);
|
||||
});
|
||||
}else{//yt-dlp wrapper calls (google bad)
|
||||
try{
|
||||
var list = await YTDLP(`youtu.be/${id}`,{
|
||||
dumpSingleJson: true,
|
||||
format: "b"
|
||||
}
|
||||
);
|
||||
|
||||
var videos = new Array;
|
||||
|
||||
list.entries.forEach(function(video){
|
||||
if(video != null){
|
||||
var meta = {
|
||||
ytRating: video.like_count,
|
||||
rawLink: video.requested_downloads[0].url
|
||||
}
|
||||
|
||||
videos.push(new Media(video.id, video.title, video.duration, "yt", meta));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
callback(null, videos);
|
||||
}catch(err){
|
||||
callback(err.message || err, null);
|
||||
}
|
||||
|
||||
|
||||
/*invidious api calls (o7 RIP 2018-2024)
|
||||
}else{//invidious api calls (google bad)
|
||||
var options = {
|
||||
host: Config.get("invidious-source"),
|
||||
port: 443,
|
||||
|
|
@ -267,12 +209,12 @@ var Getters = {
|
|||
});
|
||||
|
||||
return callback(null, vids);
|
||||
});*/
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/* youtube.com search */
|
||||
ytSearch: async function (query, callback) {
|
||||
ytSearch: function (query, callback) {
|
||||
|
||||
if(!Config.get("invidious-backend")){//legacy youtube backend (fucking yicky)
|
||||
if (!Config.get("youtube-v3-key")) {
|
||||
|
|
@ -297,33 +239,7 @@ var Getters = {
|
|||
}).catch(function (err) {
|
||||
callback(err.message || err, null);
|
||||
});
|
||||
}else{
|
||||
|
||||
try{
|
||||
var results = await YTDLP(`ytsearch8:${query}`,{
|
||||
dumpSingleJson: true,
|
||||
}
|
||||
);
|
||||
|
||||
var videos = new Array;
|
||||
|
||||
results.entries.forEach(function(result){
|
||||
var meta = {
|
||||
ytRating: result.like_count
|
||||
}
|
||||
|
||||
var video = new Media(result.id, result.title, result.duration, "yt", meta);
|
||||
video.thumb = {url: result.thumbnails[5].url};
|
||||
|
||||
videos.push(video);
|
||||
});
|
||||
|
||||
callback(null, videos);
|
||||
}catch(err){
|
||||
callback(err.message || err, null);
|
||||
}
|
||||
|
||||
/*invidious api calls (o7 RIP 2018-2024)
|
||||
}else{//invidious api calls (google bad)
|
||||
var options = {
|
||||
host: Config.get("invidious-source"),
|
||||
port: 443,
|
||||
|
|
@ -348,7 +264,7 @@ var Getters = {
|
|||
});
|
||||
|
||||
return callback(null, vids.filter(rs => rs != null));
|
||||
});*/
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -371,8 +287,6 @@ var Getters = {
|
|||
},
|
||||
|
||||
/* dailymotion.com */
|
||||
//The dailymotion player has been broken, however their basic API remains intact.
|
||||
//This will stay *for now* but will be replaced by yt-dlp at the first sign of resistance.
|
||||
dm: function (id, callback) {
|
||||
var m = id.match(/([\w-]+)/);
|
||||
if (m) {
|
||||
|
|
@ -668,114 +582,21 @@ var Getters = {
|
|||
callback,
|
||||
"As of July 2020, Mixer is no longer in service."
|
||||
);
|
||||
},
|
||||
|
||||
/*Internet Archive Bulk Grabber*/
|
||||
ia: function(id, minDuration, callback){
|
||||
try{
|
||||
//Get metadata on the directory
|
||||
var options = {
|
||||
host: "archive.org",
|
||||
port: 443,
|
||||
path: "/metadata/" + id,
|
||||
method: "GET",
|
||||
timeout: 1000
|
||||
};
|
||||
|
||||
//pull the URL
|
||||
urlRetrieve(https, options, function (status, data) {
|
||||
//if we fucked up
|
||||
if(status !== 200) {
|
||||
return callback("Archive.org HTTPS error code: " + status, null);
|
||||
}
|
||||
|
||||
//Parse the dump
|
||||
var dump = JSON.parse(data);
|
||||
|
||||
|
||||
//if we have files
|
||||
if(dump.files != null){
|
||||
var vids = new Array;
|
||||
var derivative = new Array;
|
||||
var media = new Array;
|
||||
|
||||
//sift through files to find .mp4's
|
||||
dump.files.forEach(function(file){
|
||||
//Skip out on videos that dont meet the requested minimum duration
|
||||
if(file.length >= minDuration){
|
||||
//if its a standard .mp4 (either MPEG, or h.264)
|
||||
if(file.format == "h.264" || file.format == "MPEG4"){
|
||||
//add the file to the video array
|
||||
vids.push(file);
|
||||
//if it's been transcoded by archive.org to ensure web-compatibility
|
||||
}else if(file.format == "h.264 IA"){
|
||||
//add the file to the derivative array
|
||||
derivative.push(file);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//chose derivatives over originals to save on bandwith and ensure web-compatibility, even if they don't always look as nice :P
|
||||
derivative.forEach(function(file){
|
||||
//sift through standard files to find matching originals
|
||||
for(var i = 0; i < vids.length; i++){
|
||||
//if we have a match
|
||||
if(vids[i].name == file.original){
|
||||
//replace the file with the correct derivative
|
||||
vids[i] = file;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//Lets try this just using info from IA's api. It would take up way less fucking time, even if we can't fill in everything...
|
||||
vids = vids.map(function (file) {
|
||||
return new Media(`https://${dump.d1}${dump.dir}/${file.name}`, dump.metadata.title, file.length, "fi", {codec: "mov/h264"});
|
||||
});
|
||||
|
||||
process.nextTick(callback, false, vids);
|
||||
|
||||
//if we fucked up some other way
|
||||
}else if(dump.error != null){
|
||||
return callback(`Archive.org error: ${dump.error}`);
|
||||
}else{
|
||||
return callback("Unkown metadata error from archive.org!");
|
||||
}
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
callback(err.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
Getters: Getters,
|
||||
getMedia: function (id, type, callback, minDuration) {
|
||||
if (type in this.Getters) {
|
||||
LOGGER.info("Looking up %s:%s", type, id);
|
||||
lookupCounter.labels(type).inc(1, new Date());
|
||||
if(type == "ia"){
|
||||
this.Getters.ia(id, minDuration, callback);
|
||||
}else{
|
||||
getMedia: function (id, type, callback) {
|
||||
if(type in this.Getters) {
|
||||
LOGGER.info("Looking up %s:%s", type, id);
|
||||
lookupCounter.labels(type).inc(1, new Date());
|
||||
this.Getters[type](id, callback);
|
||||
}
|
||||
} else {
|
||||
callback("Unknown media type '" + type + "'", null);
|
||||
callback("Unknown media type '" + type + "'", null);
|
||||
}
|
||||
},
|
||||
getRawCopy: async function (id, cb){
|
||||
try{
|
||||
var video = await YTDLP(`youtu.be/${id}`,{
|
||||
dumpSingleJson: true,
|
||||
format: "b"
|
||||
}
|
||||
)
|
||||
|
||||
cb(video.requested_downloads[0].url);
|
||||
}catch(err){
|
||||
console.log(err.message);
|
||||
}
|
||||
/* Invidious Code (o7 RIP 2018-2024)
|
||||
getYTRaw: function (id, cb){
|
||||
var options = {
|
||||
host: Config.get("invidious-source"),
|
||||
port: 443,
|
||||
|
|
@ -785,8 +606,6 @@ module.exports = {
|
|||
};
|
||||
|
||||
urlRetrieve(https, options, function (status, data) {
|
||||
|
||||
old invidious code (o7 2018 - 2024)
|
||||
if(status !== 200) {
|
||||
console.log("Invidious HTTPS error code: " + status);
|
||||
}
|
||||
|
|
@ -796,7 +615,6 @@ module.exports = {
|
|||
if(vid.formatStreams[0] != null){//TEMPORARY FOR FRONTEND DEV PURPOSES, PULL LINK AND SET AGAIN WHEN VIDEO QUEUED(shit expires)
|
||||
cb(vid.formatStreams[vid.formatStreams.length - 1].url);
|
||||
}
|
||||
|
||||
});*/
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -56,4 +56,5 @@ block content
|
|||
| No spamming submit channel or chat
|
||||
p.
|
||||
Comments? Questions? Feature requests? DMCA Notices? <a href="mailto:ourforest@420blaze.it">Email us!</a>
|
||||
h4 fore.st version: Pineapple Express += 3 (v1.1.3)
|
||||
h4 fore.st version: Gorilla Glue(v1.2-indev)
|
||||
|
||||
|
|
|
|||
|
|
@ -131,8 +131,6 @@ html(lang="en")
|
|||
#plcontrol.btn-group
|
||||
button#showmediaurl.btn.btn-sm.btn-default(title="Add video from URL", data-toggle="collapse", data-target="#addfromurl")
|
||||
span.glyphicon.glyphicon-plus
|
||||
button#golive.btn.btn-sm.btn-danger(title="Go Live")
|
||||
span.glyphicon.glyphicon-record
|
||||
button#showsearch.btn.btn-sm.btn-default(title="Channel History + Video Search", data-toggle="collapse", data-target="#searchcontrol")
|
||||
span.glyphicon.glyphicon-search
|
||||
button#showplaylistmanager.btn.btn-sm.btn-default(title="Manage playlists", data-toggle="collapse", data-target="#playlistmanager")
|
||||
|
|
|
|||
47
templates/google_drive_userscript.pug
Normal file
47
templates/google_drive_userscript.pug
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
extends layout.pug
|
||||
|
||||
block content
|
||||
.col-md-8.col-md-offset-2
|
||||
h1 Google Drive Userscript
|
||||
h2 Disclaimer
|
||||
.alert.alert-danger.messagebox
|
||||
strong Unsupported
|
||||
p.
|
||||
This functionality is provided <strong>as-is</strong> for backwards
|
||||
compatibility for existing users for whom it already is known to work.
|
||||
There are many reasons, known and unknown, for which it may
|
||||
<strong>not</strong> work for you; please note the staff in CyTube
|
||||
support channels cannot provide any troubleshooting assistance and you
|
||||
will be asked to simply use a different video provider.
|
||||
p.
|
||||
This functionality was originally added so that users could share their
|
||||
own personal videos stored in their Drive. No support whatsoever will
|
||||
be provided to users attempting to use it to circumvent copyright
|
||||
restrictions on third-party video hosts.
|
||||
h2 How It Works
|
||||
p.
|
||||
The userscript is a short script that you can install using a browser
|
||||
extension such as Greasemonkey or Tampermonkey that runs on the page
|
||||
and provides additional functionality needed to play Google Drive
|
||||
videos.
|
||||
h2 Installation
|
||||
ul
|
||||
li
|
||||
strong Chrome
|
||||
| —Install <a href="https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo" target="_blank">Tampermonkey</a>.
|
||||
li
|
||||
strong Firefox
|
||||
| —Install <a href="https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/" target="_blank">Tampermonkey</a>
|
||||
| or <a href="https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/" target="_blank">Greasemonkey</a>.
|
||||
li
|
||||
strong Other Browsers
|
||||
| —Install the appropriate userscript plugin for your browser.
|
||||
| Tampermonkey supports many browsers besides Chrome.
|
||||
p.
|
||||
Once you have installed the userscript manager addon for your browser,
|
||||
you can <a href="/js/cytube-google-drive.user.js" target="_blank">
|
||||
install the userscript</a>. If this link 404s, it means the administrator
|
||||
of this server hasn't generated it yet.
|
||||
p.
|
||||
You can find a guide with screenshots of the installation process
|
||||
<a href="https://github.com/calzoneman/sync/wiki/Google-Drive-Userscript-Installation-Guide" target="_blank">on GitHub</a>.
|
||||
|
|
@ -32,15 +32,14 @@ mixin us-general
|
|||
select#us-theme.form-control
|
||||
option(value="/css/themes/fore.st.css") Dawn [Frosted Glass]
|
||||
option(value="/css/themes/fore.st.lite.css") Dawn [Smoked Quartz]
|
||||
option(value="/css/themes/fore.st.mistletoe.css") Mistletoe [Frosted Glass]
|
||||
option(value="/css/themes/fore.st.mistletoe.lite.css") Mistletoe [Smoked Quartz]
|
||||
option(value="/css/themes/fore.st.dusk.css") Dusk [Frosted Glass]
|
||||
option(value="/css/themes/fore.st.dusk.lite.css") Dusk [Smoked Quartz]
|
||||
option(value="/css/themes/fore.st.neon.css") Neon [Frosted Glass]
|
||||
option(value="/css/themes/fore.st.neon.lite.css") Neon [Smoked Quartz]
|
||||
option(disabled="true") ---Holiday Themes---
|
||||
option(value="/css/themes/fore.st.candycorn.css") Candycorn [Frosted Glass]
|
||||
option(value="/css/themes/fore.st.candycorn.lite.css") Candycorn [Smoked Quartz]
|
||||
option(value="/css/themes/fore.st.mistletoe.css") Mistletoe [Frosted Glass]
|
||||
option(value="/css/themes/fore.st.mistletoe.lite.css") Mistletoe [Smoked Quartz]
|
||||
.col-sm-4
|
||||
.col-sm-8
|
||||
+rcheckbox("us-no-channelcss", "Ignore Channel CSS")
|
||||
|
|
|
|||
|
|
@ -299,37 +299,3 @@ yikes
|
|||
booyak
|
||||
bust
|
||||
bustin
|
||||
weedeven
|
||||
even
|
||||
succ
|
||||
barm
|
||||
propane
|
||||
dope
|
||||
yep
|
||||
dangit
|
||||
bobby
|
||||
dabs
|
||||
mclovin
|
||||
delaware
|
||||
tight
|
||||
doh
|
||||
ass
|
||||
dick
|
||||
scottbaio
|
||||
resin
|
||||
penisman
|
||||
punchy
|
||||
hotto
|
||||
geekedup
|
||||
comedy
|
||||
bake
|
||||
baked
|
||||
shweed
|
||||
kungfu
|
||||
dream
|
||||
MDK
|
||||
3ven
|
||||
SmoothAsEggs
|
||||
nosedive
|
||||
rip
|
||||
slorp
|
||||
|
|
|
|||
|
|
@ -748,7 +748,7 @@ Callbacks = {
|
|||
li.attr("title", data[i].queueby
|
||||
? ("Added by: " + data[i].queueby)
|
||||
: "Added by: Unknown");
|
||||
if(data[i].media.type === "fi" && data[i].media.id.includes("expires=")){
|
||||
if(data[i].media.type === "fi"){
|
||||
li.attr("id", "filei");
|
||||
}
|
||||
if(data[i].media.isBump){
|
||||
|
|
@ -779,7 +779,7 @@ Callbacks = {
|
|||
activeItem = data.uid;
|
||||
}
|
||||
|
||||
if(data.item.media.type === "fi" && data.item.media.id.includes("expires=")){
|
||||
if(data.item.media.type === "fi"){
|
||||
li.attr("id", "filei");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||
SOFTWARE.
|
||||
*/
|
||||
|
||||
var CL_VERSION = "1.1.3";
|
||||
var CL_VERSION = "1.2-indev";
|
||||
var GS_VERSION = 1.7; // Google Drive Userscript
|
||||
|
||||
var CLIENT = {
|
||||
|
|
|
|||
|
|
@ -1773,11 +1773,11 @@
|
|||
e = error1;
|
||||
return console.error(e);
|
||||
}
|
||||
} else if ((USEROPTS.yt_source !== "OFYT" && data.type == "yt") || data.type == "dm") {
|
||||
data.ofyt = data.id;
|
||||
data.id = data.meta.rawLink;//set link and spoof mov/h264
|
||||
data.type = "fi";
|
||||
data.meta.codec = "mov/h264";
|
||||
} else if ((USEROPTS.yt_source !== "OFYT" && data.type == "yt")) {
|
||||
data.ofyt = data.id;
|
||||
data.id = data.meta.rawLink;//set link and spoof mov/h264
|
||||
data.type = "fi";
|
||||
data.meta.codec = "mov/h264";
|
||||
try {
|
||||
return window.PLAYER = new FilePlayer(data);
|
||||
} catch (error1) {
|
||||
|
|
|
|||
57
www/js/ui.js
57
www/js/ui.js
|
|
@ -686,7 +686,6 @@ function queue(pos, src) {
|
|||
var duration = undefined;
|
||||
var title = undefined;
|
||||
var subtitle = "";
|
||||
var minDuration = 0;
|
||||
if (data.type === "fi") {
|
||||
if (data.id.match(/^http:/)) {
|
||||
Callbacks.queueFail({
|
||||
|
|
@ -717,12 +716,7 @@ function queue(pos, src) {
|
|||
// Raw files allow title overrides since the ffprobe tag data
|
||||
// is not always correct.
|
||||
title = $("#addfromurl-title-val").val();
|
||||
subtitle = "";
|
||||
}else if(data.type === "ia"){
|
||||
title = $("#addfromurl-title-val").val();
|
||||
minDuration = $("#addfromurl-duration-val").val();
|
||||
//Convert minDuration to a number that represents seconds instead of a string which represents minutes
|
||||
minDuration = (minDuration == "" ? 0 : (Number.parseInt(minDuration) * 60));
|
||||
subtitle = $("#addfromurl-subtitle-val").val();
|
||||
}
|
||||
|
||||
if (data.id == null || data.type == null) {
|
||||
|
|
@ -737,7 +731,6 @@ function queue(pos, src) {
|
|||
pos: pos,
|
||||
duration: duration,
|
||||
title: title,
|
||||
minDuration: minDuration,
|
||||
temp: addTemp,
|
||||
link: link,
|
||||
subtitle: subtitle
|
||||
|
|
@ -777,28 +770,15 @@ $("#queue_end").click(queue.bind(this, "end", "url"));
|
|||
$("#ce_queue_next").click(queue.bind(this, "next", "customembed"));
|
||||
$("#ce_queue_end").click(queue.bind(this, "end", "customembed"));
|
||||
|
||||
$("#golive").click(function(ev){
|
||||
socket.emit('queue', {
|
||||
id: "https://stream.ourfore.st",
|
||||
type: 'hl',
|
||||
pos: "next",
|
||||
minDuration: 0,
|
||||
temp: true,
|
||||
subtitle: ''
|
||||
});
|
||||
});
|
||||
|
||||
$("#mediaurl").keyup(function(ev) {
|
||||
if (ev.keyCode === 13) {
|
||||
queue("end", "url");
|
||||
} else {
|
||||
var editTitle = false;
|
||||
var editDur = false;
|
||||
try {
|
||||
editTitle = (parseMediaLink($("#mediaurl").val()).type === "fi" || parseMediaLink($("#mediaurl").val()).type === "ia");
|
||||
|
||||
|
||||
editDur = (parseMediaLink($("#mediaurl").val()).type === "ia");
|
||||
if (parseMediaLink($("#mediaurl").val()).type === "fi") {
|
||||
editTitle = true;
|
||||
}
|
||||
} catch (error) {
|
||||
}
|
||||
|
||||
|
|
@ -815,8 +795,8 @@ $("#mediaurl").keyup(function(ev) {
|
|||
$("<input/>").addClass("form-control")//create title field
|
||||
.attr("type", "text")//the attributes
|
||||
.attr("id", "addfromurl-title-val")
|
||||
.attr("placeholder", "Alternate Title")
|
||||
.attr("style", "display: none; width: 100%;")
|
||||
.attr("placeholder", "Alternate Title")
|
||||
.attr("style", "display: none; width: 100%;")
|
||||
.keydown(function (ev) {
|
||||
if (ev.keyCode === 13) {
|
||||
queue("end", "url");
|
||||
|
|
@ -824,20 +804,17 @@ $("#mediaurl").keyup(function(ev) {
|
|||
})
|
||||
.appendTo($("#addfromurl-title")).show("blind");//append and show
|
||||
|
||||
if(editDur){
|
||||
$("<input/>").addClass("form-control")//create title field
|
||||
.attr("type", "text")//the attributes
|
||||
.attr("id", "addfromurl-duration-val")
|
||||
.attr("placeholder", "Minimum Duration Filter")
|
||||
.attr("style", "display: none; width: 100%;")
|
||||
.keydown(function (ev) {
|
||||
if (ev.keyCode === 13) {
|
||||
queue("end", "url");
|
||||
}
|
||||
})
|
||||
.appendTo($("#addfromurl-title")).show("blind");//append and show
|
||||
}
|
||||
|
||||
$("<input/>").addClass("form-control")//create title field
|
||||
.attr("type", "text")//the attributes
|
||||
.attr("id", "addfromurl-subtitle-val")
|
||||
.attr("placeholder", "Alternate Subtitle Track")
|
||||
.attr("style", "display: none; width: 100%;")
|
||||
.keydown(function (ev) {
|
||||
if (ev.keyCode === 13) {
|
||||
queue("end", "url");
|
||||
}
|
||||
})
|
||||
.appendTo($("#addfromurl-title")).show("blind");//append and show
|
||||
}
|
||||
} else {
|
||||
$("#addfromurl-title").hide("blind");
|
||||
|
|
|
|||
|
|
@ -1615,13 +1615,6 @@ function parseMediaLink(url) {
|
|||
};
|
||||
}
|
||||
|
||||
if((m = url.match(/archive\.org\/(?:details|download)\/([a-zA-Z0-9_-]+)(?!.|\/)/))){
|
||||
return{
|
||||
id: m[1],
|
||||
type: "ia"
|
||||
}
|
||||
}
|
||||
|
||||
/* Shorthand URIs */
|
||||
// So we still trim DailyMotion URLs
|
||||
if((m = url.match(/^dm:([^\?&#_]+)/))) {
|
||||
|
|
@ -3452,7 +3445,7 @@ function startQueueSpinner(data) {
|
|||
}
|
||||
|
||||
var id = data.id;
|
||||
if (data.type === "yp" || data.type === "ia") {
|
||||
if (data.type === "yp") {
|
||||
id = "$any";
|
||||
}
|
||||
|
||||
|
|
@ -3500,8 +3493,7 @@ function stopQueueSpinner(data) {
|
|||
$("#queueprogress").data("queue-id") === data.id);
|
||||
shouldRemove = shouldRemove || data === null;
|
||||
shouldRemove = shouldRemove || $("#queueprogress").data("queue-id") === "$any";
|
||||
//This is a gross way to fix the issue with IA but it works, and it's not like cytube was a pretty codebase anywho...
|
||||
if (shouldRemove) {
|
||||
if (shouldRemove) {
|
||||
$("#queueprogress").remove();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue