Compare commits

...

21 commits

Author SHA1 Message Date
rainbow napkin 0950d34c84 Edit get-info.js 2025-05-05 21:50:37 +00:00
rainbow napkin 649b395fad Added yt-dlp path to config 2025-05-02 04:24:19 -04:00
rainbow napkin 5c104f555a Fixed bugged yt-dlp import. 2025-05-01 06:23:42 -04:00
rainbow napkin ad011c5ece More fixes for youtube... 2025-05-01 06:18:42 -04:00
rainbow napkin 0157262130 Continued youtube improvements. 2025-05-01 06:03:09 -04:00
rainbow napkin bec209c665 Fixed more issues with YTDLP 2025-05-01 05:54:20 -04:00
rainbow napkin 00ac19c80c Fixed broken raw media playback for youtube, added go live button. 2025-05-01 05:35:40 -04:00
rainbownapkin 2acba1a605 Fix broken link grabbing logic. 2024-10-29 22:41:28 -04:00
rainbownapkin 9ba95a073c Somehow this only breaks on certain archives... 2024-10-29 18:59:15 -04:00
rainbownapkin 02dc12e646 Added bulk-queueing from Internet Archive. 2024-10-29 18:44:01 -04:00
forest 9057ed2899 Merge remote-tracking branch 'refs/remotes/origin/main' 2024-10-12 12:13:33 +00:00
forest 46032a33fa Added more tokes 2024-10-12 12:10:20 +00:00
rainbownapkin 1cdf5f8ab9 Emergency Update: Pineapple Express += 2 (1.1.2) 2024-10-12 08:01:30 -04:00
rainbow napkin 05f0eb0048 Update tokes 2023-12-03 21:00:59 +00:00
rainbownapkin 74a555b5f5 re-arrange themes 2023-01-01 01:43:24 +00:00
rainbownapkin c5b000a30a channels will now genrate new bump configs instead of freaking out when there is non 2022-12-04 01:36:43 +00:00
rainbownapkin 2227577138 Fixed useroptions 2022-12-02 04:32:34 +00:00
rainbownapkin cf59498476 XMAS theme 2022-12-02 04:12:55 +00:00
rainbow napkin 8b82c1d9c6 Bust 2022-10-12 00:16:21 +00:00
rainbow napkin 4dcbf09b68 Trim junk off of AGPL 2022-10-10 17:21:00 +00:00
rainbownapkin a6f082d5f1 Hotfix 1:
Critical Bugfix: Users unable to sync due to null reference in fschd.js.
Null checks added, should work fine now.
2022-10-01 12:28:03 +00:00
25 changed files with 14525 additions and 1285 deletions

42
LICENSE
View file

@ -1282,48 +1282,6 @@ copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program 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.
This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.
-------Original Cytube License-------
The MIT License (MIT)
Copyright (c) 2013-2021 Calvin Montgomery and contributors

View file

@ -1,4 +1,4 @@
fore.st - Pineapple Express++ (v1.1.1)
fore.st - Pineapple Express += 3 (v1.1.3)
======
fore.st is the server software for ourfore.st, a community based chat & synced video embedding site tailored to service
@ -24,44 +24,8 @@ 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++ 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
## Pineapple Express += 3 1.1.3 Release Notes
- Add bulk-queueing from Internet Archive
## 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.

View file

@ -114,6 +114,7 @@ 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'

3311
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "fore.st",
"version": "1.1.1",
"version": "1.1.3",
"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": "^0.95.2",
"knex": "^3.1.0",
"lodash": "^4.17.21",
"morgan": "^1.10.0",
"mysql": "^2.18.1",
@ -38,7 +38,8 @@
"source-map-support": "^0.5.19",
"toml": "^3.0.0",
"uuid": "^8.3.2",
"yamljs": "^0.2.8"
"yamljs": "^0.2.8",
"youtube-dl-exec": "^3.0.10"
},
"scripts": {
"build-server": "babel -D --source-maps --out-dir lib/ src/",

View file

@ -1,60 +1,5 @@
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
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

View file

@ -107,11 +107,13 @@ 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);
}
});
}
if(cb != null){//lil' nasty but it calls loadconfig after loading lists :P
if(callp != null){
@ -622,6 +624,13 @@ AutobumpModule.prototype.loadConfig = function(_this){
if(err){
console.log("[Autobump Config] " + err);
_this.agro = defAgro ;
_this.bumpFreq = defFreq;
_this.minBump = defMin;
_this.selmed = _this.lastHalfRandom;
_this.listsel = _this.smashList;
return;
}

View file

@ -67,6 +67,7 @@ const TYPE_QUEUE = {
pos: "string",
title: "string,boolean,optional",
duration: "number,optional",
minDuration: "number,optional",
temp: "boolean,optional",
subtitle: "string"
};
@ -445,7 +446,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")) {
if (typeof data.title !== "string" || (data.type !== "cu" && data.type !== "fi" && data.type !== "ia")) {
data.title = false;
}
@ -466,9 +467,9 @@ PlaylistModule.prototype.handleQueue = function (user, data) {
}
/* Certain media types require special permission to add */
if (data.type === "yp" && !perms.canAddList(user)) {
if ((data.type === "yp" || data.type === "ia") && !perms.canAddList(user)) {
user.socket.emit("queueFail", {
msg: "You don't have permission to add playlists",
msg: "You don't have permission to bulk queue",
link: link,
id: id
});
@ -504,6 +505,11 @@ 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,11 +549,14 @@ PlaylistModule.prototype.handleQueue = function (user, data) {
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);
}
@ -576,7 +585,38 @@ 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);
});
};
@ -1117,7 +1157,7 @@ PlaylistModule.prototype._addItem = function (media, data, user, cb, abump) {
queueby: data.queueby
});
if (data.title && (media.type === "cu" || media.type === "fi")) {
if (data.title && (media.type === "cu" || media.type === "fi" || media.type === "ia")) {
media.setTitle(data.title);
}
@ -1289,11 +1329,16 @@ PlaylistModule.prototype.startPlayback = function (time) {
}
if(self.current.media.type == "yt"){//if its yt
InfoGetter.getYTRaw(self.current.media.id,function(url){//get raw link from invidious api
InfoGetter.getRawCopy(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{
self.sendChangeMedia(self.channel.users);
}

View file

@ -67,6 +67,7 @@ var defaults = {
},
"invidious-backend": true,
"invidious-source": 'inv.riverside.rocks',
"yt-dlp-path": 'yt-dlp',
"youtube-v3-key": "",
"channel-blacklist": [],
"channel-path": "r",

View file

@ -49,9 +49,14 @@ 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',
@ -111,7 +116,7 @@ function getBlocked(reg){
var Getters = {
/* youtube.com */
yt: function (id, callback) {
yt: async function (id, callback) {
if(!Config.get("invidious-backend")){//legacy youtube backend (fucking yicky)
if (!Config.get("youtube-v3-key")) {
@ -122,9 +127,11 @@ var Getters = {
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;
}
@ -134,8 +141,31 @@ var Getters = {
}).catch(function (err) {
callback(err.message || err, null);
});
}else{//invidious api calls (google bad)
var options = {
}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 = {
host: Config.get("invidious-source"),
port: 443,
path: "/api/v1/videos/" + id,
@ -161,13 +191,13 @@ var Getters = {
var media = new Media(vid.videoId, vid.title, vid.lengthSeconds, "yt", meta);
return callback(false, media);
});
});*/
}
},
/* youtube.com playlists */
yp: function (id, callback) {
yp: async 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!" +
@ -188,7 +218,35 @@ var Getters = {
}).catch(function (err) {
callback(err.message || err, null);
});
}else{//invidious api calls (google bad)
}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)
var options = {
host: Config.get("invidious-source"),
port: 443,
@ -209,12 +267,12 @@ var Getters = {
});
return callback(null, vids);
});
});*/
}
},
/* youtube.com search */
ytSearch: function (query, callback) {
ytSearch: async function (query, callback) {
if(!Config.get("invidious-backend")){//legacy youtube backend (fucking yicky)
if (!Config.get("youtube-v3-key")) {
@ -239,7 +297,33 @@ var Getters = {
}).catch(function (err) {
callback(err.message || err, null);
});
}else{//invidious api calls (google bad)
}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)
var options = {
host: Config.get("invidious-source"),
port: 443,
@ -264,7 +348,7 @@ var Getters = {
});
return callback(null, vids.filter(rs => rs != null));
});
});*/
}
},
@ -287,6 +371,8 @@ 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) {
@ -582,21 +668,114 @@ 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) {
if(type in this.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{
this.Getters[type](id, callback);
}
} else {
callback("Unknown media type '" + type + "'", null);
}
},
getYTRaw: function (id, cb){
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)
var options = {
host: Config.get("invidious-source"),
port: 443,
@ -606,6 +785,8 @@ module.exports = {
};
urlRetrieve(https, options, function (status, data) {
old invidious code (o7 2018 - 2024)
if(status !== 200) {
console.log("Invidious HTTPS error code: " + status);
}
@ -615,6 +796,7 @@ 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);
}
});
});*/
}
};

View file

@ -56,4 +56,4 @@ 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++ (v1.1.1)
h4 fore.st version: Pineapple Express += 3 (v1.1.3)

View file

@ -131,6 +131,8 @@ 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")

View file

@ -1,47 +0,0 @@
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
| &mdash;Install <a href="https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo" target="_blank">Tampermonkey</a>.
li
strong Firefox
| &mdash;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
| &mdash;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>.

View file

@ -36,8 +36,11 @@ mixin us-general
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")

View file

@ -297,3 +297,39 @@ wrasslin
her
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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

BIN
www/img/snow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
www/img/xmas.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 MiB

View file

@ -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"){
if(data[i].media.type === "fi" && data[i].media.id.includes("expires=")){
li.attr("id", "filei");
}
if(data[i].media.isBump){
@ -779,7 +779,7 @@ Callbacks = {
activeItem = data.uid;
}
if(data.item.media.type === "fi"){
if(data.item.media.type === "fi" && data.item.media.id.includes("expires=")){
li.attr("id", "filei");
}

View file

@ -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.1";
var CL_VERSION = "1.1.3";
var GS_VERSION = 1.7; // Google Drive Userscript
var CLIENT = {

View file

@ -40,7 +40,9 @@ function dispSTimes(){//update sTimes
$(".pluid-" + startTimes[0][i]).children(".qe_sTime").text(ltimeString);// set current item qe_sTime innerHTML to ptimeString
$(".pluid-" + startTimes[0][i]).children(".qe_pref").text(ptimeString);// set current item qe_sTime innerHTML to ptimeString
if($(".pluid-" + startTimes[0][i]) != null && $(".pluid-" + startTimes[0][i]).data("media") != null){
st.setTime(st.getTime() + ($(".pluid-" + startTimes[0][i]).data("media").seconds * 1000));//calc end time
}
$(".pluid-" + startTimes[0][i]).children(".qe_etime").text("enddate: " + st.toLocaleTimeString().replace(" ","") + " " + st.toLocaleDateString());//Set endtime (lil dirty but so is this script :P)

View file

@ -1773,7 +1773,7 @@
e = error1;
return console.error(e);
}
} else if ((USEROPTS.yt_source !== "OFYT" && data.type == "yt")) {
} 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";

View file

@ -686,6 +686,7 @@ 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({
@ -716,7 +717,12 @@ function queue(pos, src) {
// Raw files allow title overrides since the ffprobe tag data
// is not always correct.
title = $("#addfromurl-title-val").val();
subtitle = $("#addfromurl-subtitle-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));
}
if (data.id == null || data.type == null) {
@ -731,6 +737,7 @@ function queue(pos, src) {
pos: pos,
duration: duration,
title: title,
minDuration: minDuration,
temp: addTemp,
link: link,
subtitle: subtitle
@ -770,15 +777,28 @@ $("#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 {
if (parseMediaLink($("#mediaurl").val()).type === "fi") {
editTitle = true;
}
editTitle = (parseMediaLink($("#mediaurl").val()).type === "fi" || parseMediaLink($("#mediaurl").val()).type === "ia");
editDur = (parseMediaLink($("#mediaurl").val()).type === "ia");
} catch (error) {
}
@ -804,10 +824,11 @@ $("#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-subtitle-val")
.attr("placeholder", "Alternate Subtitle Track")
.attr("id", "addfromurl-duration-val")
.attr("placeholder", "Minimum Duration Filter")
.attr("style", "display: none; width: 100%;")
.keydown(function (ev) {
if (ev.keyCode === 13) {
@ -816,6 +837,8 @@ $("#mediaurl").keyup(function(ev) {
})
.appendTo($("#addfromurl-title")).show("blind");//append and show
}
}
} else {
$("#addfromurl-title").hide("blind");
$("#addfromurl-title").remove();//otherwise remove

View file

@ -1615,6 +1615,13 @@ 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:([^\?&#_]+)/))) {
@ -3445,7 +3452,7 @@ function startQueueSpinner(data) {
}
var id = data.id;
if (data.type === "yp") {
if (data.type === "yp" || data.type === "ia") {
id = "$any";
}
@ -3493,6 +3500,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) {
$("#queueprogress").remove();
}