Create 1.1-indev branch, list of updates in README

This commit is contained in:
rainbownapkin 2022-06-26 00:53:30 +00:00
parent 8ca4aa4327
commit 3f27bfdbdf
45 changed files with 16057 additions and 11847 deletions

1326
LICENSE

File diff suppressed because it is too large Load diff

185
README.md
View file

@ -1,7 +1,7 @@
fore.st - Panama Red(v1)
fore.st - Pineapple Express(v1.1-indev)
======
fore.st is the server software for ourfore.st, a community based chat & video embedding site tailored to service
fore.st is the server software for ourfore.st, a community based chat & synced video embedding site tailored to service
the TTN community post-shutdown. The softwre is made freely available both for legal reasons
but also as it seems as that is what is best for the community in the advant of another shutdown.
@ -10,20 +10,6 @@ and carries many of the same features. Modifcations to the software have been/ar
being made to make the platform a more familiar place for TTN users. Contributions
are welcome.
Current dev goals:
- image upload button/ audio(maybe? Autoplay disabled.) & mp4/webm embed support
- basic library features(add to lib without queueing, show videos from other channels)
- add 3 columns to lib table: type(show,movie,video), genre(comedy,food,horror,etc...), tags(stupidbullshit, etc...), sort or search by type/genre/tags
- improved profiles (profile pages, badges, stats)
- forum (a la ttn discussions)
- other custom chat commands (thunder, birdup, etc..)
- gold(toke-ns?)
- games?(in-browser? game servers that w/ steamid tie-in to toke-ns?)
Current goals for ourfore.st instance:
- fix gdrive support(we'll probably be moving hosts at some point...)
- continue w/ development goals and exist with the d00dz
## Installation
There is currently no installation guide for the software, however since not much has
changed in the backend, you should be fine with official [cytube docs](https://github.com/calzoneman/sync/wiki/CyTube-3.0-Installation-Guide).
@ -32,16 +18,173 @@ changed in the backend, you should be fine with official [cytube docs](https://g
You can reach out by bugging rainbownapkin on the ttn discord or ourfore.st, you can also send an email to ourforest(at)420blaze.it
## Shoutouts
- Thanks to Simon for making TTN, we wouldn't be here if it wheren't that shit.
- Thanks to jaredlego-aka-goops, Rongtern, n' Sassy for helping with moderation and content aggregation for ourfore.st, we'll be set fer fackin dayz lol.
- Thanks to Simon for making TTN,
- Thanks to our excellent mod team for chat moderation and content aggregation
- Thanks to etchingham for being a community contact while TTN's been winding down, I think all of us see you as a pillar of the community so your support means a lot.
- 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.
## License
## Pineapple Express Indev Release Notes
This is the first indev release for fore.st 1.1 Pineapple Express. This is the last push before the codebase is merged upstream with the newest version of cytube. Should probably get that done sooner than later. Heres a check list of the planned/completed features in this revision:
Original source code in this repository is provided under the MIT license
(see the LICENSE file for the full text).
-quick shit & bugfixes ✓
--change markdown filters to require three symbols on each side, quickest fix for filters ✓
--move refresh button to title bar ✓
--delete custom embed button, replace with button under generel "add video" button ✓
--move playlist item count and length next to buttons & compact ui ✓
--hide playlist frame and control from users who don't have permission to view playlist (no one can see the afterparty playlist except mods, why should they have the controls cluttering things up?) ✓
--add "mention" to userlist dropdown menu ✓
--add "toke with" to userlist dropdown menu for shits n gigs ✓
--unlatch sync on pause and scrub, show sync button on titlebar when sync is unlatched, this wont support classic yt at the moment. Invidious support planned ✓
--caption support for raw video (no saving captions in channel history just yet, this will be in the next update which will involve a rework/addition to the database) ✓
--fix bugged airdate after "queue next" ✓
--collapsing playlist items hides pref and airtime by default. Airdate on same line as title, airtime and pref below ✓
--collapse/expand all playlist item button ✓
--close playlist button ✓
--basic ui fixes/tweaks ✓
--disabled !toke link embedding on emote alt text ✓
--fix chatbar resizing on new message notification (might just by cytube+) ✓
--relicense to agpl ✓
--disable/remove unregistered channels ✓
--rename "legacy playlist buttons" to compact, fix location. This seems like it could be usable if not better than default ✓
--save temporary vids to channel library
-slide out panel (not an end user feature in and of itself, however a common UI element used for most menus, made to be quick and ezpz ✓
--function for opening, closing ✓
--allow switching menus on panel without having to open/close it ✓
--pretty slide out animation ✓
-improved poll UI ✓
--poll panel auto-opens when poll starts ✓
--button slides in chatbar from left, slides back after poll done ✓
--on click toggles poll ui on left of chat menu ✓
--improved poll chat announcements ~Original announcement removed. May implement once server whisper implemented.
-improved emote ui ✓
--slide out emote ui ✓
--search bar ✓
--optional alphabet sort(default) ✓
--optional legacy emote menu available ✓
-cytube+ ripout ✓
--scroll to current item button ✓
--orientation buttons in title bar ✓
--quick settings(icon next to emote button) ✓
---general pref
----theme ✓
---playback pref ✓
----video orientation ✓
----toggle orientation buttons ✓
----sync threshold (in seconds) ✓
----youtube source (add after degoogling) ~
---chat pref ✓
----use legacy cytube emote menu ✓
----blink title on chat ✓
----chat notification sound ✓
----chat desktop notification ✓
-trim/tidy default cytube commands
--replace / with ! as defualt server-side command indicator to match tokebot and TTN commands. / will be used for future client-side commands ✓
---me ✓
---sp ✓
---afk ✓
---poll ✓
---hpoll ✓
---mute ✓
---smute ✓
---unmute ✓
---kick ✓
---ban ✓
---ipban ✓
---clear ✓
---clean ✓
---cleantitle ✓
--remove/consolidate/add to commands ✓
---say -> announce(change tokebot modflair CSS to announce CSS, make normal modflair for bot/admin rank) ✓
---remove kickanons ✓
---remove d (drink) ✓
---add user specific function to clear ('!clear <username>' to clear chats by said user) ✓
--Merge Upstream to newest cytube commit
-improved server-whisper system
--public leave/join messages
--server-whisper target parameter for user specific whispers
--server-whisper name
-getplaylistlinks outputs in fpanel
--I mean its pretty fucking simple I dont know how you can screw this one up bud.
--ezpzlmnsqze
-ui sizing overhaul
--rip out all but the ONE TRUE layout. It's not 2014 anymore, one layout with dynamic elements will get you further than multiple static ones.
--chat/player sized to fit canvas with navbar (remove -+ buttons on player)
--theatre mode toggle icon on title bar (hides navbar, player/chat takes up entire screen)
--click to drag chat/player split
--shade player (hide and unload video, titel bar shows title and unshade icon only)
--shade chat (hide chat box, message box, send button. chat header bar collapses, retains user count and expansion arrows. Userlist/poll open/close is independent.)
--popout video button
--popout chat button
--remove legacy cytube themes. If they wheren't compatible after the emote panel, they certainly won't be now lmao
-decaffeinate player.js
--Coffee script was a bad idea then, and it makes even less sense now.
--fuck me its just javascript with worse syntax, no real debugging tools, and build times
--literally fucking why though?(I guess it made sense before ES6 but still fuck me)
-degoogling
--yt-dlp backend for serverside metadata acquisition of youtube videos w/o registered API key or google account
--potentially leverage yt-dlp backend for other media sources
--invidious embed support for youtube video playback
--youtube source in user prefrences (three or four invidious instances from different countries/continents, official youtube embed, or custom invidious instance)
-autobump
--sepearate bump lists, based on js/txt files at first, will be stored in db next update (may use multiple at once)
--skip next bump/disable bumping
--bump frequency (default: 1)
--queue method: random from last-half, round-robin, full random
--override next bump
--require video be at least 4 minutes to add bump (mods can override from bump menu)
-merge tokebot into ourfore.st codebase, one server instead of two.
--profile and userlist entry
---bot specific rank
--reset cooldown accessible from modmenu (quiet and loud, quiet by default)
--log tokes w/ date to file. This will be switched to mariadb in the next update
--tokefile, list of usernames with toke count. This will be switched to mariadb in the next update
--total tokes listed on profile tooltip
-mod panel
--button on chatbar
--mod message (sends message to all active mods)
--new poll button
--autobump control tab
--tokebot control tab
--playlist tab
--modflair
--open playlist below video (if closed, otherwise this does not appear)
--open playlist below video by default
-merge fore.st theme changes to fore.st dusk, consider moving some of them over to cytube.css for easier management
-extra shit(probs wait til next update, or hotfix)
--short chats (acronyms, emoji, single letters/numbers/symbols) pop in over video from left starting at top left, overflow pops in below, instead of in chat box. Chats slide back up into top of vid after 2s. (optional, default on)
--basic mod chat (save to mod channel + pm all online mods)
--basic profile page (in side panel)
--css variables in theme for ez customizablity
--user themes
--change background to other themes background or img from url(theme background by default)
--native odysee support (no raw embed)
## 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.
(see the LICENSE file for the full text.)
Cytube source code originally licensed under MIT license
(see the LICENSE file for the full text.)
Bundled source code, such as third-party CSS and JavaScript libraries, are
provided under their respective licenses.

7303
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -60,7 +60,7 @@
"babel-plugin-add-module-exports": "^1.0.4",
"coffeescript": "^1.9.2",
"eslint": "^7.32.0",
"mocha": "^8.4.0",
"mocha": "^10.0.0",
"sinon": "^10.0.0"
},
"babel": {

View file

@ -5,6 +5,9 @@ window.Player = class Player
@setMediaProperties(data)
@paused = false
@latched = true
@seeklatch = false #used to lock sync latch when seeking for sync
@lastSTime = 0
load: (data) ->
@setMediaProperties(data)
@ -19,7 +22,20 @@ window.Player = class Player
pause: ->
@paused = true
latch: ->
if not @latched
@latched = true
unlatch: ->
if not @seeklatch
if @latched
$("#latchvid").show()
@latched = false
else
@seeklatch = false
latchseek: ->
@seeklatch = true
getLatch: (cb) ->
cb(@latched)
seekTo: (time) ->
setVolume: (volume) ->

View file

@ -6,6 +6,7 @@ window.DailymotionPlayer = class DailymotionPlayer extends Player
@setMediaProperties(data)
@initialVolumeSet = false
@playbackReadyCb = null
@latched = true
waitUntilDefined(window, 'DM', =>
removeOld()
@ -37,6 +38,8 @@ window.DailymotionPlayer = class DailymotionPlayer extends Player
@paused = true
if CLIENT.leader
sendVideoUpdate()
else
@unlatch()
)
@dm.addEventListener('playing', =>
@ -48,6 +51,11 @@ window.DailymotionPlayer = class DailymotionPlayer extends Player
@setVolume(VOLUME)
@initialVolumeSet = true
)
@dm.addEventListener('seeked', =>
if not CLIENT.leader
@unlatch()
)
# Once the video stops, the internal state of the player
# becomes unusable and attempting to load() will corrupt it and

View file

@ -19,6 +19,7 @@ TYPE_MAP =
tc: TwitchClipPlayer
cm: VideoJSPlayer
window.loadMediaPlayer = (data) ->
try
if window.PLAYER
@ -41,16 +42,20 @@ window.handleMediaUpdate = (data) ->
PLAYER = window.PLAYER
#bodge for fcyp.js layout
handleWindowResize()
#handleWindowResize()
#update airdate
dispSTimes();
PLAYER.lastSTime = data.currentTime;
if not PLAYER.latched #check if the shits latched, if not stop while we're ahead.
return
# Do not update if the current time is past the end of the video, unless
# the video has length 0 (which is a special case for livestreams)
if typeof PLAYER.mediaLength is 'number' and
if (typeof PLAYER.mediaLength is 'number' and
PLAYER.mediaLength > 0 and
data.currentTime > PLAYER.mediaLength
data.currentTime > PLAYER.mediaLength)
return
# Negative currentTime indicates a lead-in for clients to load the video,
@ -66,6 +71,7 @@ window.handleMediaUpdate = (data) ->
if waiting
PLAYER.seekTo(0)
PLAYER.latchseek()
# YouTube player has a race condition that crashes the player if
# play(), seek(0), and pause() are called quickly without waiting
# for events to fire. Setting a flag variable that is checked in the
@ -83,6 +89,7 @@ window.handleMediaUpdate = (data) ->
if data.paused and not PLAYER.paused
PLAYER.seekTo(data.currentTime)
PLAYER.latchseek()
PLAYER.pause()
else if PLAYER.paused and not data.paused
PLAYER.play()
@ -101,6 +108,7 @@ window.handleMediaUpdate = (data) ->
if diff > accuracy
# The player is behind the correct time
PLAYER.seekTo(time)
PLAYER.latchseek()
else if diff < -accuracy
# The player is ahead of the correct time
# Don't seek all the way back, to account for possible buffering.
@ -109,7 +117,9 @@ window.handleMediaUpdate = (data) ->
if not (PLAYER instanceof DailymotionPlayer)
time += 1
PLAYER.seekTo(time)
PLAYER.latchseek()
)
window.removeOld = (replace) ->
$('#soundcloud-volume-holder').remove()

View file

@ -54,7 +54,6 @@ window.VideoJSPlayer = class VideoJSPlayer extends Player
constructor: (data) ->
if not (this instanceof VideoJSPlayer)
return new VideoJSPlayer(data)
@load(data)
loadPlayer: (data) ->
@ -97,20 +96,14 @@ window.VideoJSPlayer = class VideoJSPlayer extends Player
).appendTo(video)
)
if data.meta.textTracks
data.meta.textTracks.forEach((track) ->
label = track.name
attrs =
src: track.url
kind: 'subtitles'
type: track.type
label: label
if track.default? and track.default
attrs.default = ''
$('<track/>').attr(attrs).appendTo(video)
)
@player = videojs(video[0],
# https://github.com/Dash-Industry-Forum/dash.js/issues/2184
@ -150,6 +143,9 @@ window.VideoJSPlayer = class VideoJSPlayer extends Player
@paused = true
if CLIENT.leader
sendVideoUpdate()
else
@unlatch()
)
@player.on('play', =>
@ -157,11 +153,14 @@ window.VideoJSPlayer = class VideoJSPlayer extends Player
if CLIENT.leader
sendVideoUpdate()
)
# Workaround for IE-- even after seeking completes, the loading
# spinner remains.
@player.on('seeked', =>
$('.vjs-waiting').removeClass('vjs-waiting')
if not CLIENT.leader #this part has nothing to do with IE and all to do with sync latching :P
@unlatch()
)
# Workaround for Chrome-- it seems that the click bindings for
@ -184,12 +183,27 @@ window.VideoJSPlayer = class VideoJSPlayer extends Player
load: (data) ->
@setMediaProperties(data)
@latched = true
# Note: VideoJS does have facilities for loading new videos into the
# existing player object, however it appears to be pretty glitchy when
# a video can't be played (either previous or next video). It's safer
# to just reset the entire thing.
@destroy()
@loadPlayer(data)
@setTracks(data)
setTracks: (data) ->
if data.meta.textTracks
data.meta.textTracks.forEach((track) ->
label = track.name
$('<track>').attr(
src: track.url
kind: 'subtitles'
type: track.type
label: label
default: true
).prependTo("video")
)
play: ->
@paused = false
@ -200,6 +214,9 @@ window.VideoJSPlayer = class VideoJSPlayer extends Player
@paused = true
if @player and @player.readyState() > 0
@player.pause()
if not CLIENT.leader
@unlatch()
seekTo: (time) ->
if @player and @player.readyState() > 0

View file

@ -7,6 +7,7 @@ window.VimeoPlayer = class VimeoPlayer extends Player
load: (data) ->
@setMediaProperties(data)
@latched = true
waitUntilDefined(window, 'Vimeo', =>
video = $('<iframe/>')
@ -32,6 +33,9 @@ window.VimeoPlayer = class VimeoPlayer extends Player
@paused = true
if CLIENT.leader
sendVideoUpdate()
else
@unlatch()
)
@vimeo.on('play', =>
@ -39,6 +43,11 @@ window.VimeoPlayer = class VimeoPlayer extends Player
if CLIENT.leader
sendVideoUpdate()
)
@vimeo.on('seeked', =>
if not CLIENT.leader
@unlatch()
)
@play()
@setVolume(VOLUME)

View file

@ -9,6 +9,6 @@ fi
echo "Building from src/ to lib/"
npm run build-server
echo "Building from player/ to www/js/player.js"
echo "Building from player/ to www/js/player.js"
npm run build-player
echo "Done"

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.
*/
var ChannelModule = require("./module");
var Flags = require("../flags");
var fs = require("fs");
@ -94,8 +133,9 @@ function Channel(name) {
}, USERCOUNT_THROTTLE);
const self = this;
db.channels.load(this, function (err) {
if (err && err !== "Channel is not registered") {
self.emit("loadFail", "Failed to load channel data from the database. Please try again later.");
if (err /*&& err !== "Channel is not registered"*/) {
//self.emit("channelNotRegistered");
self.emit("loadFail", "Channel not found.");
self.setFlag(Flags.C_ERROR);
} else {
self.initModules();
@ -147,7 +187,6 @@ Channel.prototype.initModules = function () {
"./permissions" : "permissions",
"./emotes" : "emotes",
"./chat" : "chat",
"./drink" : "drink",
"./filters" : "filters",
"./customization" : "customization",
"./opts" : "options",

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.
*/
var Config = require("../config");
var XSS = require("../xss");
var ChannelModule = require("./module");
@ -36,18 +75,16 @@ function ChatModule(_channel) {
this.supportsDirtyCheck = true;
/* Default commands */
this.registerCommand("/me", this.handleCmdMe.bind(this));
this.registerCommand("/sp", this.handleCmdSp.bind(this));
this.registerCommand("/say", this.handleCmdSay.bind(this));
this.registerCommand("/rcv", this.handleCmdSay.bind(this));
this.registerCommand("/shout", this.handleCmdSay.bind(this));
this.registerCommand("/clear", this.handleCmdClear.bind(this));
this.registerCommand("/a", this.handleCmdAdminflair.bind(this));
this.registerCommand("/afk", this.handleCmdAfk.bind(this));
this.registerCommand("/mute", this.handleCmdMute.bind(this));
this.registerCommand("/smute", this.handleCmdSMute.bind(this));
this.registerCommand("/unmute", this.handleCmdUnmute.bind(this));
this.registerCommand("/unsmute", this.handleCmdUnmute.bind(this));
this.registerCommand("!me", this.handleCmdMe.bind(this));
this.registerCommand("!sp", this.handleCmdSp.bind(this));
this.registerCommand("!announce", this.handleCmdSay.bind(this));
this.registerCommand("!clear", this.handleCmdClear.bind(this));
this.registerCommand("!a", this.handleCmdAdminflair.bind(this));
this.registerCommand("!afk", this.handleCmdAfk.bind(this));
this.registerCommand("!mute", this.handleCmdMute.bind(this));
this.registerCommand("!smute", this.handleCmdSMute.bind(this));
this.registerCommand("!unmute", this.handleCmdUnmute.bind(this));
this.registerCommand("!unsmute", this.handleCmdUnmute.bind(this));
}
ChatModule.prototype = Object.create(ChannelModule.prototype);
@ -320,7 +357,8 @@ ChatModule.prototype.processChatMsg = function (user, data) {
msgobj.meta.addClass = "greentext";
}
if (data.msg.indexOf("/") === 0) {
//if (data.msg.indexOf("/") === 0) {Legacy from cytube '/' commands
if (data.msg.indexOf("!") === 0) {
var space = data.msg.indexOf(" ");
var cmd;
if (space < 0) {
@ -455,7 +493,8 @@ ChatModule.prototype.sendMessage = function (msgobj) {
};
ChatModule.prototype.registerCommand = function (cmd, cb) {
cmd = cmd.replace(/^\//, "");
//cmd = cmd.replace(/^\//, ""); Legacy from cytube '/' commands
cmd = cmd.replace(/^!/, "");
this.commandHandlers[cmd] = cb;
};
@ -494,12 +533,44 @@ ChatModule.prototype.handleCmdClear = function (user, _msg, _meta) {
if (!this.channel.modules.permissions.canClearChat(user)) {
return;
}
var target = _msg.toLowerCase().split(" ")[1];
var tdisp = undefined;
var nhit = false;
if(target != null){
for(var i = 0; i < this.channel.users.length; i++){
if(nhit = (target === this.channel.users[i].getLowerName())){
tdisp = this.channel.users[i].getName();
break;
}
}
target = nhit ? target : undefined;
}
this.dirty = true;
this.buffer = [];
this.channel.broadcastAll("clearchat", { clearedBy: user.getName() });
this.sendModMessage(user.getName() + " cleared chat.", -1);
this.channel.logger.log("[mod] " + user.getName() + " used /clear");
this.channel.broadcastAll("clearchat", { clearedBy: user.getName(), target: tdisp });
if(target == null){
this.buffer = [];
this.sendModMessage(user.getName() + " cleared chat.", -1);
this.channel.logger.log("[mod] " + user.getName() + " used !clear");
}else{
for(var x = 0; x < this.buffer.length; x++){
if(this.buffer[x].username.toLowerCase() == target){
//this.buffer.msg = '';
this.buffer.splice(x,1);
x--;
}
}
this.sendModMessage(user.getName() + " cleared chats from " + tdisp + ".", -1);
this.channel.logger.log("[mod] " + user.getName() + " cleared chats from " + tdisp + ".");
}
};
ChatModule.prototype.handleCmdAdminflair = function (user, msg, meta) {
@ -510,7 +581,7 @@ ChatModule.prototype.handleCmdAdminflair = function (user, msg, meta) {
args.shift();
var superadminflair = {
labelclass: "label-danger",
labelclass: "label-admin",
icon: "glyphicon-globe"
};
@ -547,7 +618,7 @@ ChatModule.prototype.handleCmdMute = function (user, msg, _meta) {
var name = args.shift();
if (typeof name !== "string") {
user.socket.emit("errorMsg", {
msg: "/mute requires a target name"
msg: "!mute requires a target name"
});
return;
}
@ -564,7 +635,7 @@ ChatModule.prototype.handleCmdMute = function (user, msg, _meta) {
if (!target) {
user.socket.emit("errorMsg", {
msg: "/mute target " + name + " not present in channel."
msg: "!mute target " + name + " not present in channel."
});
return;
}
@ -572,7 +643,7 @@ ChatModule.prototype.handleCmdMute = function (user, msg, _meta) {
if (target.account.effectiveRank >= user.account.effectiveRank
|| target.account.globalRank > user.account.globalRank) {
user.socket.emit("errorMsg", {
msg: "/mute failed - " + target.getName() + " has equal or higher rank " +
msg: "!mute failed - " + target.getName() + " has equal or higher rank " +
"than you."
});
return;
@ -597,7 +668,7 @@ ChatModule.prototype.handleCmdSMute = function (user, msg, _meta) {
var name = args.shift();
if (typeof name !== "string") {
user.socket.emit("errorMsg", {
msg: "/smute requires a target name"
msg: "!smute requires a target name"
});
return;
}
@ -614,7 +685,7 @@ ChatModule.prototype.handleCmdSMute = function (user, msg, _meta) {
if (!target) {
user.socket.emit("errorMsg", {
msg: "/smute target " + name + " not present in channel."
msg: "!smute target " + name + " not present in channel."
});
return;
}
@ -622,7 +693,7 @@ ChatModule.prototype.handleCmdSMute = function (user, msg, _meta) {
if (target.account.effectiveRank >= user.account.effectiveRank
|| target.account.globalRank > user.account.globalRank) {
user.socket.emit("errorMsg", {
msg: "/smute failed - " + target.getName() + " has equal or higher rank " +
msg: "!smute failed - " + target.getName() + " has equal or higher rank " +
"than you."
});
return;
@ -648,7 +719,7 @@ ChatModule.prototype.handleCmdUnmute = function (user, msg, _meta) {
var name = args.shift();
if (typeof name !== "string") {
user.socket.emit("errorMsg", {
msg: "/unmute requires a target name"
msg: "!unmute requires a target name"
});
return;
}

View file

@ -1,60 +0,0 @@
// TODO: figure out what to do with this module
// it serves a very niche use case and is only a core module because of
// legacy reasons (early channels requested it before I had criteria
// around what to include in core)
var ChannelModule = require("./module");
function DrinkModule(_channel) {
ChannelModule.apply(this, arguments);
this.drinks = 0;
}
DrinkModule.prototype = Object.create(ChannelModule.prototype);
DrinkModule.prototype.onUserPostJoin = function (user) {
user.socket.emit("drinkCount", this.drinks);
};
DrinkModule.prototype.onUserPreChat = function (user, data, cb) {
var msg = data.msg;
var perms = this.channel.modules.permissions;
if (msg.match(/^\/d-?[0-9]*/) && perms.canCallDrink(user)) {
msg = msg.substring(2);
var m = msg.match(/^(-?[0-9]+)/);
var count;
if (m) {
count = parseInt(m[1]);
if (isNaN(count) || count < -10000 || count > 10000) {
return;
}
msg = msg.replace(m[1], "").trim();
if (msg || count > 0) {
msg += " drink! (x" + count + ")";
} else {
this.drinks += count;
this.channel.broadcastAll("drinkCount", this.drinks);
return cb(null, ChannelModule.DENY);
}
} else {
msg = msg.trim() + " drink!";
count = 1;
}
this.drinks += count;
this.channel.broadcastAll("drinkCount", this.drinks);
data.msg = msg;
data.meta.addClass = "drink";
data.meta.forceShowName = true;
cb(null, ChannelModule.PASSTHROUGH);
} else {
cb(null, ChannelModule.PASSTHROUGH);
}
};
DrinkModule.prototype.onMediaChange = function () {
this.drinks = 0;
this.channel.broadcastAll("drinkCount", 0);
};
module.exports = DrinkModule;

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.
*/
var ChannelModule = require("./module");
var db = require("../database");
var Flags = require("../flags");
@ -19,11 +58,10 @@ function KickBanModule(_channel) {
ChannelModule.apply(this, arguments);
if (this.channel.modules.chat) {
this.channel.modules.chat.registerCommand("/kick", this.handleCmdKick.bind(this));
this.channel.modules.chat.registerCommand("/kickanons", this.handleCmdKickAnons.bind(this));
this.channel.modules.chat.registerCommand("/ban", this.handleCmdBan.bind(this));
this.channel.modules.chat.registerCommand("/ipban", this.handleCmdIPBan.bind(this));
this.channel.modules.chat.registerCommand("/banip", this.handleCmdIPBan.bind(this));
this.channel.modules.chat.registerCommand("!kick", this.handleCmdKick.bind(this));
this.channel.modules.chat.registerCommand("!ban", this.handleCmdBan.bind(this));
this.channel.modules.chat.registerCommand("!ipban", this.handleCmdIPBan.bind(this));
this.channel.modules.chat.registerCommand("!banip", this.handleCmdIPBan.bind(this));
}
}
@ -195,25 +233,6 @@ KickBanModule.prototype.handleCmdKick = function (user, msg, _meta) {
}
};
KickBanModule.prototype.handleCmdKickAnons = function (user, _msg, _meta) {
if (!this.channel.modules.permissions.canKick(user)) {
return;
}
var users = Array.prototype.slice.call(this.channel.users);
users.forEach(function (u) {
if (!u.is(Flags.U_LOGGED_IN)) {
u.kick("anonymous user");
}
});
this.channel.logger.log("[mod] " + user.getName() + " kicked anonymous users.");
if (this.channel.modules.chat) {
this.channel.modules.chat.sendModMessage(user.getName() + " kicked anonymous " +
"users");
}
};
/* /ban - name bans */
KickBanModule.prototype.handleCmdBan = function (user, msg, _meta) {
var args = msg.split(" ");

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.
*/
var ChannelModule = require("./module"); var ULList = require("../ullist");
var AsyncQueue = require("../asyncqueue");
var Media = require("../media");
@ -9,6 +48,7 @@ var db = require("../database");
var CustomEmbedFilter = require("../customembed").filter;
var XSS = require("../xss");
import { Counter } from 'prom-client';
import { validateTextTracks as tvalidate } from '../custom-media';
const LOGGER = require('@calzoneman/jsli')('playlist');
@ -27,7 +67,8 @@ const TYPE_QUEUE = {
pos: "string",
title: "string,boolean,optional",
duration: "number,optional",
temp: "boolean,optional"
temp: "boolean,optional",
subtitle: "string"
};
const TYPE_SET_TEMP = {
@ -105,8 +146,8 @@ function PlaylistModule(_channel) {
this._refreshing = false;
if (this.channel.modules.chat) {
this.channel.modules.chat.registerCommand("/clean", this.handleClean.bind(this));
this.channel.modules.chat.registerCommand("/cleantitle", this.handleClean.bind(this));
this.channel.modules.chat.registerCommand("!clean", this.handleClean.bind(this));
this.channel.modules.chat.registerCommand("!cleantitle", this.handleClean.bind(this));
}
this.supportsDirtyCheck = true;
@ -407,7 +448,12 @@ PlaylistModule.prototype.handleQueue = function (user, data) {
if (typeof data.title !== "string" || (data.type !== "cu" && data.type !== "fi")) {
data.title = false;
}
if (!data.subtitle){
data.subtitle = false;
}
var link = util.formatLink(id, type, null);
var perms = this.channel.modules.permissions;
@ -491,6 +537,7 @@ PlaylistModule.prototype.handleQueue = function (user, data) {
type: data.type,
pos: data.pos,
title: data.title,
subtitle: data.subtitle,
link: link,
temp: temp,
shouldAddToLibrary: !temp,
@ -1051,16 +1098,52 @@ PlaylistModule.prototype._addItem = function (media, data, user, cb) {
media.setTitle(data.title);
}
console.log("pre media subload");
console.log(data);
if (data.subtitle && (media.type === "cu" || media.type === "fi")) {
var ttracks = [{
"url": data.subtitle,
"contentType": "text/vtt",
"name": "English Subs",
"default": true
}];
console.log(Config.get('http.root-domain'));
try {
tvalidate(ttracks);
} catch (error) {
user.socket.emit("errorMsg", {
msg: `Invalid text track error:` + error
});
return;
}
media.meta.textTracks = ttracks;
}
var success = function () {
var tempST = 0;
//var packet = {
// item: item.pack(),
// after: item.prev ? item.prev.uid : "prepend"
//};
self.meta.count++;
self.items.forEach(function (item){//iterate items
self.items.find(item.uid).media.startTime = tempST;//current item start time = tempST
tempST += item.media.seconds;
});
/*self.meta.count++;
media.startTime = self.meta.rawTime;
self.meta.rawTime += media.seconds;
self.meta.time = util.formatTime(self.meta.rawTime);
self.meta.time = util.formatTime(self.meta.rawTime);*/ //old buggy shit
var sTemp = [[],[]];
@ -1297,8 +1380,8 @@ PlaylistModule.prototype.handleClean = function (user, msg, _meta) {
var cmd = args.shift();
if (args.length === 0) {
return user.socket.emit("errorMsg", {
msg: "No target given for " + cmd + ". Usage: /clean <username> or " +
"/cleantitle <title>"
msg: "No target given for " + cmd + ". Usage: !clean <username> or " +
"!cleantitle <title>"
});
}
var target;
@ -1315,9 +1398,9 @@ PlaylistModule.prototype.handleClean = function (user, msg, _meta) {
" with target regex: " + target);
var cleanfn;
if (cmd === "/clean") {
if (cmd === "!clean") {
cleanfn = function (item) { return target.test(item.queueby); };
} else if (cmd === "/cleantitle") {
} else if (cmd === "!cleantitle") {
cleanfn = function (item) { return target.exec(item.media.title) !== null; };
}

View file

@ -26,8 +26,8 @@ function PollModule(_channel) {
this.roomViewHidden = this.channel.uniqueName + ROOM_VIEW_HIDDEN;
this.roomNoViewHidden = this.channel.uniqueName + ROOM_NO_VIEW_HIDDEN;
if (this.channel.modules.chat) {
this.channel.modules.chat.registerCommand("poll", this.handlePollCmd.bind(this, false));
this.channel.modules.chat.registerCommand("hpoll", this.handlePollCmd.bind(this, true));
this.channel.modules.chat.registerCommand("!poll", this.handlePollCmd.bind(this, false));
this.channel.modules.chat.registerCommand("!hpoll", this.handlePollCmd.bind(this, true));
}
this.supportsDirtyCheck = true;
}
@ -254,7 +254,8 @@ PollModule.prototype.handlePollCmd = function (obscured, user, msg, _meta) {
// Ensure any existing poll is closed
this.handleClosePoll(user);
msg = msg.replace(/^\/h?poll/, "");
msg = msg.replace(/^!h?poll/, "");
//msg = msg.replace(/^\/h?poll/, "");
var args = msg.split(",");
var title = args.shift();

View file

@ -202,7 +202,7 @@ function validateSources(sources, data) {
}
}
function validateTextTracks(textTracks) {
export function validateTextTracks(textTracks) {
if (typeof textTracks === 'undefined') {
return;
}

View file

@ -102,10 +102,15 @@ User.prototype.handleJoinChannel = function handleJoinChannel(data) {
if (!chan.is(Flags.C_READY)) {
chan.once("loadFail", reason => {
this.socket.emit("errorMsg", {
msg: reason,
alert: true
});
if(reason == "Channel not found."){
this.socket.emit("channelNotFound");
}else{
this.socket.emit("errorMsg", {
msg: reason,
alert: true
});
}
this.kick(`Channel could not be loaded: ${reason}`);
});
}

View file

@ -1,6 +1,44 @@
extends layout.pug
block content
//
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.
.col-md-8.col-md-offset-2
.aboutText
h1 Welcome to ourfore.st!
@ -19,4 +57,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: Panama Red (v1)
h4 fore.st version: Pineapple Express (v1.1-INDEV)

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.
doctype html
html(lang="en")
head
@ -27,17 +66,22 @@ html(lang="en")
#motd
.clear
#announcements.row
#drinkbarwrap.row
#drinkbar.col-lg-12.col-md-12
h1#drinkcount
#main.row
#videowrap.col-lg-7.col-md-7
p#videowrap-header
span#resize-video-larger.glyphicon.glyphicon-plus.pointer(title="Make the video larger")
span#resize-video-smaller.glyphicon.glyphicon-minus.pointer(title="Make the video smaller")
span#mediarefresh.playercont.glyphicon.glyphicon-retweet.pointer(title="Reload the video player")
span#flipx-video.playercont.glyphicon.glyphicon-resize-horizontal.pointer(title="Flip Player Horizontally",onclick='javascript:$("#ytapiplayer").toggleClass("mirx")')
span#flipy-video.playercont.glyphicon.glyphicon-resize-vertical.pointer(title="Flip Player Vertically",onclick='javascript:$("#ytapiplayer").toggleClass("miry")')
span#showplaylist.playercont.glyphicon.glyphicon-list.pointer(style="display: none;", title="Show playlist")
span#resize-video-larger.playercont.glyphicon.glyphicon-plus.pointer(title="Make the video larger")
span#resize-video-smaller.playercont.glyphicon.glyphicon-minus.pointer(title="Make the video smaller")
span#latchvid.label.label-default.pull-right.pointer(style="display: none;") Sync
span#currenttitle Nothing Playing
.embed-responsive.embed-responsive-16by9
#ytapiplayer.embed-responsive-item
div#subliminaltoke
img(src="/img/tokeleaf.png")
h3 Take a <a onclick="chatsmack('!toke')">Toke!</a>
#chatwrap.col-lg-5.col-md-5
#chatheader
span#modflair.label.label-default.pull-right.pointer Name Color
@ -46,9 +90,16 @@ html(lang="en")
i#userlisttoggle.glyphicon.glyphicon-chevron-down.pull-left.pointer(title="Show/Hide Userlist")
#chatmain
#userlist
#fpaneldiv.fpanel(style="display: none;")
#fptitlediv.fptitlebar.fpanel
p#fptitle.fptitlebar.fpanel null
p#closefpanel.fptitlebar.fpanel.glyphicon.glyphicon-remove.pointer(onclick="javascript:closeFPanel()", title="Close null panel.")
#fpcontdiv.fpcont.fpanel
#messagebuffer.linewrap
#chatbar(style="display: flex;")
button#emotelistbtn.btn.btn-sm.btn-default(title="emotes") ;)
button#pollopenbtn.btn.btn-sm.btn-default.glyphicon.glyphicon-ok.chatbtn(onclick="javascript:panelbtn(fpoll)",style="display: none;", title="Poll")
button#prefopenbtn.btn.btn-sm.btn-default.glyphicon.glyphicon-cog.chatbtn(onclick="javascript:panelbtn(fpset)",title="Quick Settings")
button#emoteopenbtn.btn.btn-sm.btn-default.chatbtn(onclick="javascript:panelbtn(fpemote)",title="Emotes") ;)
form(action="javascript:void(0)" style="display: flex; flex-grow: 1;")
input#chatline.form-control(type="text", maxlength="320", style="display: none")
#guestlogin.input-group
@ -57,32 +108,37 @@ html(lang="en")
button#chatsend.btn.btn-sm.btn-default Send
#rightcontrols.col-lg-7.col-md-7
#plcontrol.btn-group
button#showsearch.btn.btn-sm.btn-default(title="Search for a video", data-toggle="collapse", data-target="#searchcontrol")
span.glyphicon.glyphicon-search
button#showmediaurl.btn.btn-sm.btn-default(title="Add video from URL", data-toggle="collapse", data-target="#addfromurl")
span.glyphicon.glyphicon-plus
button#showcustomembed.btn.btn-sm.btn-default(title="Embed a custom frame", data-toggle="collapse", data-target="#customembed")
span.glyphicon.glyphicon-th-large
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")
span.glyphicon.glyphicon-list
button#clearplaylist.btn.btn-sm.btn-default(title="Clear the playlist")
span.glyphicon.glyphicon-trash
button#shuffleplaylist.btn.btn-sm.btn-default(title="Shuffle the playlist")
span.glyphicon.glyphicon-sort
button#qlockbtn.btn.btn-sm.btn-danger(title="Playlist locked")
span.glyphicon.glyphicon-lock
#videocontrols.btn-group.pull-right
button#mediarefresh.btn.btn-sm.btn-default(title="Reload the video player")
span.glyphicon.glyphicon-retweet
button#fullscreenbtn.btn.btn-sm.btn-default(title="Make the video player fullscreen")
span.glyphicon.glyphicon-fullscreen
button#scrollitm.btn.btn-sm.btn-default(title="Scroll to Current Item",onclick="javascript:scrollQueue()")
span.glyphicon.glyphicon-hand-right
button#getplaylist.btn.btn-sm.btn-default(title="Retrieve playlist links")
span.glyphicon.glyphicon-link
button#voteskip.btn.btn-sm.btn-default(title="Voteskip")
span.glyphicon.glyphicon-step-forward
#leftcontrols.col-lg-5.col-md-5
button#shuffleplaylist.btn.btn-sm.btn-default(title="Shuffle the playlist")
span.glyphicon.glyphicon-sort
button#clearplaylist.btn.btn-sm.btn-default(title="Clear the playlist")
span.glyphicon.glyphicon-trash
button#qlockbtn.btn.btn-sm.btn-danger(title="Playlist locked")
span.glyphicon.glyphicon-lock
#playlistauxcont
span#hideplaylist.glyphicon.glyphicon-remove.pointer(title="Close Playlist")
span#blindItems.glyphicon.glyphicon-resize-small.pointer(title="Collapse All Items")
#plmeta
span#plcount 0 items
br
span#pllength 00:00:00
//#videocontrols.btn-group.pull-right
//button#fullscreenbtn.btn.btn-sm.btn-default(title="Make the video player fullscreen")This makes no sense, all supported players already have a full screen button. Not a fan of the placement of this anywho
//span.glyphicon.glyphicon-fullscreen
//button#voteskip.btn.btn-sm.btn-default(title="Voteskip") I don't like the way this is implemented, I think a poll based voteskip feature would be better, though I think it'd be better to not do a full rip-out
//span.glyphicon.glyphicon-step-forward
//this is being removed in favor of better UI
#leftcontrols.col-lg-5.col-md-5
button#newpollbtn.btn.btn-sm.btn-default New Poll
#playlistrow.row
#rightpane.col-lg-7.col-md-7
#rightpane-inner.row
@ -107,6 +163,9 @@ html(lang="en")
button#queue_next.btn.btn-default Queue next
span.input-group-btn
button#queue_end.btn.btn-default Queue last
span.input-group-btn#showcustomembed
button#showcustomembed.btn.btn-default(title="Embed a custom frame", data-toggle="collapse", data-target="#customembed")
span.glyphicon.glyphicon-th-large
.checkbox
label
input.add-temp(type="checkbox")
@ -139,12 +198,8 @@ html(lang="en")
| Add as temporary
ul#userpl_list.videolist
#queuefail.col-lg-12.col-md-12
.vertical-spacer
.col-lg-12.col-md-12
ul#queue.videolist
#plmeta
span#plcount 0 items
span#pllength 00:00:00
#leftpane.col-lg-5.col-md-5
#leftpane-inner.row
#pollwrap.col-lg-12.col-md-12
@ -238,6 +293,7 @@ html(lang="en")
include footer
+footer()
script(id="socketio-js", src=sioSource)
script(src="/js/fpanel.js")
script(src="/js/data.js")
script(src="/js/fembed.js")
script(src="/js/fchat.js")

View file

@ -1,3 +1,43 @@
//
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.
mixin lcheckbox(id, label)
.form-group
label.control-label.col-sm-4(for=id)= label

View file

@ -1,6 +1,44 @@
extends layout.pug
block content
//
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.
.col-lg-9.col-md-9
h3 Public Channels
table.table.table-bordered.table-striped

View file

@ -31,11 +31,12 @@ mixin us-general
.col-sm-8
select#us-theme.form-control
option(value="/css/themes/fore.st.css") fore.st
option(value="/css/themes/light.css") Light
option(value="/css/themes/bootstrap-theme.min.css") Bootstrap
option(value="/css/themes/slate.css") Slate
option(value="/css/themes/cyborg.css") Cyborg
option(value="/css/themes/modern.css") Modern
//
option(value="/css/themes/light.css") Light
option(value="/css/themes/slate.css") Slate
option(value="/css/themes/bootstrap-theme.min.css") Bootstrap
option(value="/css/themes/cyborg.css") Cyborg
option(value="/css/themes/modern.css") Modern
.col-sm-4
.col-sm-8
p.text-danger Changing layouts may require refreshing to take effect.
@ -67,7 +68,8 @@ mixin us-playback
p.text-info Setting <code>wmode=transparent</code> allows objects to be displayed above the video player, but may cause performance issues on some systems.
+rcheckbox("us-hidevideo", "Remove the video player")
+rcheckbox("us-playlistbuttons", "Hide playlist buttons by default")
+rcheckbox("us-oldbtns", "Old style playlist buttons")
+rcheckbox("us-oldbtns", "Compact playlist buttons")
+rcheckbox("us-video-orientation", "Show video orientation buttons above player")
.form-group
label.control-label.col-sm-4(for="#us-default-quality") Quality Preference
.col-sm-8
@ -91,6 +93,7 @@ mixin us-chat
+rcheckbox("us-chat-timestamp", "Show timestamps in chat")
+rcheckbox("us-sort-rank", "Sort userlist by rank")
+rcheckbox("us-sort-afk", "Sort AFKers to bottom")
+rcheckbox("us-legacy-emote", "Use legacy Cytube emote menu")
.col-sm-4
.col-sm-8
p.text-info The following 3 options apply to how and when you will be notified if a new chat message is received while CyTube is not the active window.

View file

@ -1,3 +1,41 @@
/*
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.
*/
.container-fluid {
padding-left: 15px;
@ -153,11 +191,15 @@
border-radius: 0;
}
#rightcontrols{
background-color: #111111C0;
}
#plmeta {
border-radius: 4px;
border-top: 0;
border-top-left-radius: 0;
border-top-right-radius: 0;
border: none;
float: right;
padding-right: 1em;
line-height: 1;
}
.videolist {
@ -212,10 +254,128 @@ li.ui-sortable-helper, li.ui-sortable-placeholder + li.queue_entry {
}
.qe_time {
float: right;
float: left;
font-family: Monospace;
}
.qe_sTime, .qe_pref{
float: right;
font-family: Monospace;
}
#playlistauxcont {
display: inline-block;
float: right;
line-height: 1;
}
#addformurl-title-val{
width: auto;
}
.fptitlebar{
display: inline-block;
font-size: 14px;
line-height: inherit !important;
color: #c8c8c8;
vertical-align: top;
margin: 0px;
}
#fpcontdiv{
}
#subliminaltoke{
background-color: #111111C0;
position: absolute;
left: calc(50% - 12em);
top: calc(50% - 13em);
text-align: center;
padding: 2em 2em 1em 2em;
border: 1px solid #949494;
display: none;
}
#subliminaltoke img{
max-height: 20em;
}
.emotecontdiv{
display: grid;
grid-template-columns: repeat(auto-fill, minmax(7em,auto));
overflow-x: hidden;
column-gap: 0.5em;
row-gap: 0.5em;
margin: 3.1em 0.5em 0 0.5em;
}
.emotediv{
width: 100%;
border: 1px solid #949494;
display: flex;
flex-direction: column;
justify-content: space-between;
text-align: center;
cursor: pointer;
}
#emspan{
display: flex;
height: 100%;
flex-direction: column;
justify-content: center;
}
.emotediv img{
width: 100%;
}
.emotediv p{
margin: 0;
padding: 0;
}
.chatbtn{
border-right: 1px solid #949494 !important;
position: inherit !important;
}
#polltitle, #endpollbtn{
margin-top: 5px;
margin-bottom: 20px;
display: inline-block;
}
#emotecont{
display: flex;
position: fixed;
width: 100%;
background-color: #1119;
backdrop-filter: blur(12px);
}
#esearchbar{
border-right: 1px solid #949494;
background-color: #0000 !important;
}
#anumspan{
display: flex;
flex-direction: column;
padding: 0em 0.5em 0 0.5em;
height: 100%;
}
#emotealphabox{
}
label[for="emotealphabox"]{
}
#fptitlediv{
width: 100%;
}
#closefpanel{
float: right;
top: auto;
}
#fpaneldiv{
float: left;
height: 1em;
width: 70%;
position: absolute;
z-index: 1;
}
#guestlogin{
flex-grow: 1;
}
#hideplaylist {
display: block;
}
.qe_clear {
clear: both;
}
@ -236,7 +396,9 @@ li.ui-sortable-helper, li.ui-sortable-placeholder + li.queue_entry {
#csstext, #jstext {
font-family: Monospace;
}
.fpcont{
overflow: scroll;
}
#optedit, #permedit, #filteredit, #motdedit, #cssedit, #jsedit,
#banlist, #loginhistory, #channelranks, #chanlog {
display: none;
@ -258,6 +420,8 @@ li.ui-sortable-helper, li.ui-sortable-placeholder + li.queue_entry {
background-color: rgba(129, 20, 21, 0.1);
}
.server-msg-reconnect {
border: 1px solid #009900;
line-height: 2;
@ -436,7 +600,7 @@ li.ui-sortable-helper, li.ui-sortable-placeholder + li.queue_entry {
}
.qfalert {
margin-bottom: 10px;
margin-bottom: 0px;
padding-left: 0!important;
padding-right: 0!important;
}
@ -692,10 +856,24 @@ input#logout[type="submit"]:hover {
margin-right: 10px;
}
#resize-video-larger, #resize-video-smaller {
.playercont {
float: right;
padding-right: 3px;
}
.mirx{
transform: scaleX(-1);
}
.miry{
transform: scaleY(-1);
}
.mirx.miry{
transform: rotate(180deg);
}
#fpsetdiv p{
font-weight: 700;
display: inline;
}
body.hd #resize-video-larger, body.hd #resize-video-smaller {
display: none;
}

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.
*//
@import url("//fonts.googleapis.com/css?family=Droid+Sans:400,700");
/*!
/*! * bootswatch v3.3.1+1 * Homepage: http://bootswatch.com * Copyright 2012-2014 Thomas Park * Licensed under MIT * Based on Bootstrap */
@ -49,6 +88,14 @@ sub,sup{
position:relative;
vertical-align:baseline
}
.label-admin, .label.label-admin{
background-color: #cc0000;
color: #000;
text-shadow: #000 0 0 10px;
box-shadow: #cc0000 0 0 10px;
margin-right: 0.3em;
}
sup{
top:-.5em
}
@ -1201,7 +1248,7 @@ pre code{
}
.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{
position:relative;
min-height:1px;
min-height:0px;
padding-left:0px;
padding-right:0px
}
@ -2027,7 +2074,7 @@ output{
-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;
transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s
}
.form-control:focus{
.form-control:focus, #esearchbar:focus{
border-color:#339933;
outline:0;
-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(51,153,51,.6);
@ -2505,6 +2552,32 @@ select[multiple].form-group-lg .form-control,select[multiple].input-lg,textarea.
line-height:1.5;
border-radius:0px
}
.btn-ln{
padding: 5px 10px;
font-size: 12px;
line-height: 1;
border-radius: 0px
}
.qs-form{
flex-grow: 1;
color:#888;
background-color:#fff;
background-image:none;
border:1px solid #282828;
border-radius:0px;
box-shadow:inset 0 1px 1px rgba(0,0,0,.075);
transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s
}
.qs-form:focus{
border-color:#339933;
outline:0;
-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(51,153,51,.6);
box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(51,153,51,.6)
}
input.qs-form[type="text"], input.qs-form[type="password"], input.qs-form[type="email"], textarea.qs-form {
background-color: #111111C0 !important;
color: #c8c8c8;
}
.btn-group-xs>.btn,.btn-xs{
padding:1px 5px;
font-size:12px;
@ -3839,7 +3912,7 @@ a.badge:focus,a.badge:hover{
}
.alert{
padding:15px;
margin-bottom:20px;
margin-bottom:0px;
border:1px solid transparent;
border-radius:0px
}
@ -5399,10 +5472,6 @@ a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover{
#drinkcount {
background-color: #000000;
color: #ffffff;
}
#plmeta {
border: 1px solid #949494;
border-radius: 0px;
}
#userlist, #messagebuffer {
border: 1px solid #949494;
@ -5423,19 +5492,41 @@ a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover{
.queue_entry.queue_active {
background-color: #333333;
}
#newmessages-indicator-bghack {
background: rgb(32, 32, 32);
background: rgba(32, 32, 32, 0.9);
#newmessages-indicator {
color:#339933;
text-shadow: #ded 0 0 1px;
text-decoration:none;
}
#chatheader, #userlist, #messagebuffer, #videowrap-header, .embed-responsive, #rightpane, #mainrow{
background-color: #111111C0;
.emotediv:hover{
color: #ded;
text-shadow: #339933 0 0 10px;
box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(51,153,51,.6);
border-color:#339933;
}
#newmessages-indicator:focus, #newmessages-indicator:hover {
color:#ded;
text-shadow: #339933 0 0 10px;
text-decoration: none;
}
.qe_sTime{
float: right;
font-family: Monospace;
#emotelistbtm, #chatsend, #motdwrap, #chatline, #rightcontrols, #chatheader, #userlist, #messagebuffer, #videowrap-header, .embed-responsive, #rightpane, #mainrow{
background-color: #111111C0;
backdrop-filter: blur(12px);
}
.aboutText{
#fpaneldiv{
border: 1px #949494 solid;
background-color: #1119;
backdrop-filter: blur(12px);
border-left: none;
border-bottom: none;
}
#fptitlediv{
border-bottom: 1px #949494 solid;
}
.aboutText{
background-color: #111111C0;
padding: 10px;
border: 1px solid #949494;
@ -5487,7 +5578,7 @@ a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover{
text-shadow: none;
}
#botmsg{
.shout{
text-align: center;
color: #FFFFFF;
text-shadow:
@ -5507,10 +5598,6 @@ a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover{
border-right: 1px solid #949494;
}
#emotelistbtn{
border-right: 1px solid #949494;
}
#chatsend{
border-left: 1px solid #949494;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,248 +0,0 @@
/* ----- Smoothing default CyTube CSS (works without JS) ----- */
/* -- body -- */
body {overflow-x:hidden}
/* -- buttons -- */
.btn {text-shadow:none}
/* -- modal windows -- */
@media screen and (min-width: 768px) {
.modal-dialog {max-width:950px !important; margin-top:10px}
}
/* -- brand -- */
.navbar-brand {cursor:default}
/* -- MOTD -- */
#motdwrap {padding:10px; border-color:white; box-shadow:none}
#togglemotd {margin-top:-5px}
/* -- superadmin icon -- */
.glyphicon-globe {margin-right:3px}
/* -- playlist row panes -- */
#rightpane, #leftpane {margin-top:5px !important; margin-bottom:5px !important}
/* -- left pane wells -- */
#leftpane .well {margin-bottom:5px}
/* -- queue titles -- */
.qe_title {margin-left:4px}
/* -- footer -- */
#footer {padding-bottom:5px; height:auto}
/* ----- Patching CSS after loading JS (comments only here, actual patches in the JS) ----- */
/*
#mainpage {padding-top:52px}
#motdrow, #announcements, #main, #playlistrow {border:solid 2px transparent; margin-bottom:5px}
#main > div, #playlistrow > div {
padding-left:5px; padding-right:5px; margin-top:5px; margin-bottom:5px;
}
#motdwrap {margin:5px -10px}
#announcements .alert {margin:0px -10px 5px}
#drinkcount {margin:0px}
*/
/* ----- CyTube Plus created elements CSS ----- */
/* -- channel avatar -- */
#chanavatar {margin-right:10px}
/* -- azuki row (user top logo) -- */
#azukirow {
padding-left:5px; padding-right:5px; border-left:solid 2px transparent; border-right:solid 2px transparent;
background:transparent; background-repeat:no-repeat !important; background-position:center center !important;
margin:0px -8px; min-height:5px;
}
/* -- MOTD elements -- */
#motdlogo {margin-bottom:8px}
#motdtabswrap {margin-top:8px}
.motdtabs-btn {margin-right:5px}
#motdtabscontent {padding:10px 5px}
/* -- rules button and panel -- */
#rulesbtnwrap {text-align:center}
#rules-btn {margin-top:10px; margin-bottom:5px}
#rulespanel-outer {width:100%; padding:0px}
#rulespanel {
max-width:700px; margin:0 auto; margin-top:0px; padding:4px; border:solid 2px white; text-align:left;
border-radius:8px; -moz-border-radius:8px; -webkit-border-radius:8px;
}
/* -- attention bar -- */
#attbarrow-outer {padding:0px 5px}
#attbar {height:22px; background-color:white}
/* -- full-sized title row -- */
#titlerow {
background-color:white;
background-image:linear-gradient(to right, #cccccc, #cccccc);
background-position:0px center;
background-size:0% 100%; background-repeat:no-repeat;
margin:-5px -8px 5px;
border-radius:4px;
}
#titlerow-outer {
padding:3px 5px; text-align:center; font-size:16pt; color:black;
text-shadow: 0px 0px 10px #666666, 0px 0px 10px #666666;
}
#titlerow #currenttitle {border:none; background:transparent}
/* -- media info bar -- */
#mediainfo {
background:transparent; margin-bottom:0px; border-width: 1px 1px 0px; border-style:solid solid none;
border-color:#CCC #CCC -moz-use-text-color; border-radius:5px 5px 0px 0px;
}
/* -- player alert (if hidden video after loading) -- */
#ytapiplayer .alert {text-align:left; margin:0px -15px}
/* -- player covering layer -- */
#coverpl {
position:absolute; left:5px; top:0px; background-color:white;
background-repeat:no-repeat; background-position:center center;
}
/* -- sounds and admin panels GUI layers -- */
#sounds-dropdown, #chatfunc-dropdown {
position:absolute; top:22px; display:block; z-index:10000; padding:5px;
overflow:auto; margin-right:5px;
}
#muteall-btn, #spamclear-btn, #antiafk-btn {width:100%}
/* -- chat controls buttons group -- */
#chatcontrols {margin-top:4px}
/* -- chat elements -- */
.squavatar {width:24px; height:24px; margin-right:3px; border:solid 6px; vertical-align:middle; display:inline-block}
.avatar {margin-right:3px}
.server-whisper + .squavatar {display:none}
.globalmod {margin-right:2px}
.embedimg {max-width:200px; max-height:300px}
.embedvid {max-width:200px; max-height:300px; border:0px; vertical-align:middle}
/* -- main fonts and emotes GUI layer -- */
#chatpanel {margin-top:0px; margin-bottom:0px}
#fontspanel, #emotespanel {
text-align:center; max-width:700px; margin:0px auto 5px auto; border:solid 2px white; border-radius:6px
}
.fluidpanel {max-width:1200px !important}
/* -- fonts panel -- */
#fontsbtnwrap {margin-top:5px; margin-bottom:5px}
#unibtnwrap {margin:5px 45px 2px}
@media (max-width:767px) {
#unibtnwrap {margin:5px 25px 2px}
}
#fontsbtnwrap .btn, #unibtnwrap .btn {margin:0px 3px 3px 3px}
/* -- emotes panel -- */
#emotespanel img {margin:5px; max-height:35px; cursor:pointer}
#emotesbtnwrap {text-align:center; margin:5px}
#emotespanel .alert {text-align:left; margin:5px -10px}
/* -- advanced playlist controls -- */
#advplcontrols {width:100%; padding-top:10px}
#advplcontrols button {width:25%}
/* -- database and galleries buttons gruoup -- */
#leftpanecontrols {margin-right:5px}
/* -- layout configuration box toggling button -- */
#layout-btn {margin-left:5px}
/* -- various configuration box elements -- */
#configbtnwrap, #modewrap, #themewrap, #funcbtnwrap, #cleardbwrap, #gallery-well, #hidewrap, #embedwrap {
text-align:center;
}
.conf-cap {padding-top:9px}
#mode-sel, #theme-sel, #gal-sel {width:80%; margin:0px auto}
.theme-header {text-align:center; font-size:9pt; font-style:italic}
#embed-help {cursor:pointer; margin-left:7px}
/* -- channel database -- */
.db-cat {overflow:auto; max-height:400px}
.db-break {width:100%}
.db-title {margin-left:4px}
.db-link {margin-left:5px}
#previewFrame {margin:0 auto; display:block}
/* -- channel galleries -- */
#galleryFrame {margin-bottom:10px}
#gal-sel {margin-bottom:20px}
/* -- custom footer -- */
#leftfooter {font-size:10pt}
div[id="leftfooter"] {text-align:center; margin-bottom:5px}
#rightfooter {float:right; margin:0px 0px 15px 15px}
/* ----- Additional JS classes ----- */
.relative {position:relative}
.covered {visibility:hidden; opacity:0}
.dist {background-color:gold; color:red; font-size:12pt; font-family:times new roman; padding:3px}
.mX {
-webkit-transform:scaleX(-1); -moz-transform:scaleX(-1); transform:scaleX(-1);
-ms-transform:scaleX(-1); -o-transform:scaleX(-1);
}
.mY {
-webkit-transform:scaleY(-1); -moz-transform:scaleY(-1); transform:scaleY(-1);
-ms-transform:scaleY(-1); -o-transform:scaleY(-1);
}
.rotate {
-webkit-transform:rotate(180deg); -moz-transform:rotate(180deg); transform:rotate(180deg);
-ms-transform:rotate(180deg); -o-transform:rotate(180deg);
}
.vertical {
-webkit-transform:rotate(270deg); -moz-transform:rotate(270deg); transform:rotate(270deg);
-ms-transform:rotate(270deg); -o-transform:rotate(270deg);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 MiB

After

Width:  |  Height:  |  Size: 5.2 MiB

BIN
www/img/tokeleaf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

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.
*/
(function () {
var chosenServer = IO_SERVERS[0]; // Is the array even necessary for the ACP?

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.
*/
Callbacks = {
/* fired when socket connection completes */
connect: function() {
@ -173,22 +212,17 @@ Callbacks = {
}, time));
},
channelNotRegistered: function() {
var div = $("<div/>").addClass("alert alert-info")
channelNotFound: function() {
$("#motdwrap").hide();
$("#chatline").prop("placeholder","Channel not found!");
var div = $("<div/>").addClass("alert alert-danger")
.appendTo($("<div/>").addClass("col-md-12").appendTo($("#announcements")));
$("<button/>").addClass("close pull-right")
.appendTo(div)
.click(function () {
div.parent().remove();
})
.html("&times;");
$("<h4/>").appendTo(div).text("Unregistered channel");
$("<h4/>").appendTo(div).text("Channel not found!");
$("<p/>").appendTo(div)
.html("This channel is not registered to a CyTube account. You can still " +
"use it, but some features will not be available. To register a " +
"channel to your account, visit your <a href='/account/channels'>" +
"channels</a> page.");
.html("Go watch one of the channels that actually exists <a href='/'>here</a>.");
$("#ytapiplayer").append('<embed type="text/html" src="https://vid.puffyan.us/embed/HS-xJLNROqE?Autoplay=1&loop=1">')//KSSSSSSSSSSSSSSSSSSSSSSSH
},
setMotd: function(motd) {
@ -515,9 +549,16 @@ Callbacks = {
}
},
clearchat: function() {
$("#messagebuffer").html("");
LASTCHAT.name = "";
clearchat: function(data) {
console.log(data);
if(data.target == null){
$("#messagebuffer").html("");
}else{
$(".chat-msg-" + data.target).html("");
}
LASTCHAT.name = (data.target == null || data.target == LASTCHAT.name.toLowerCase()) ? "" : LASTCHAT.name;
},
userlist: function(data) {
@ -820,6 +861,10 @@ Callbacks = {
}
function loadNext() {
if(PLAYER){
PLAYER.latch();
}
$("#latchvid").hide();
if (!PLAYER || data.type !== PLAYER.mediaType) {
loadMediaPlayer(data);
} else {
@ -932,72 +977,21 @@ Callbacks = {
/* REGION Polls */
newPoll: function(data) {
Callbacks.closePoll();
var pollMsg = $("<div/>").addClass("poll-notify")
.html(data.initiator + " opened a poll: \"" + data.title + "\"")
.appendTo($("#messagebuffer"));
scrollChat();
//lnewPoll(data);
fpoll.data = data;
panelbtn(fpoll)
$("#pollopenbtn").show("blinds");
var poll = $("<div/>").addClass("well active").prependTo($("#pollwrap"));
$("<button/>").addClass("close pull-right").html("&times;")
.appendTo(poll)
.click(function() { poll.remove(); });
if(hasPermission("pollctl")) {
$("<button/>").addClass("btn btn-danger btn-sm pull-right").text("End Poll")
.appendTo(poll)
.click(function() {
socket.emit("closePoll")
});
}
$("<h3/>").html(data.title).appendTo(poll);
for(var i = 0; i < data.options.length; i++) {
(function(i) {
var callback = function () {
socket.emit("vote", {
option: i
});
poll.find(".option button").each(function() {
$(this).removeClass("active");
$(this).parent().removeClass("option-selected");
});
$(this).addClass("active");
$(this).parent().addClass("option-selected");
}
$("<button/>").addClass("btn btn-default btn-sm").text(data.counts[i])
.prependTo($("<div/>").addClass("option").html(data.options[i])
.appendTo(poll))
.click(callback);
})(i);
}
$("<span/>").addClass("label label-default pull-right").data('timestamp',data.timestamp).appendTo(poll)
.attr('title', 'Poll opened by ' + data.initiator).data('initiator',data.initiator)
.text(new Date(data.timestamp).toTimeString().split(" ")[0]);
poll.find(".btn").attr("disabled", !hasPermission("pollvote"));
},
updatePoll: function(data) {
var poll = $("#pollwrap .active");
var i = 0;
poll.find(".option button").each(function() {
$(this).html(data.counts[i]);
i++;
});
fpoll.updatePoll(data);
},
closePoll: function() {
if($("#pollwrap .active").length != 0) {
var poll = $("#pollwrap .active");
poll.removeClass("active").addClass("muted");
poll.find(".option button").each(function() {
$(this).attr("disabled", true);
});
poll.find(".btn-danger").each(function() {
$(this).remove()
});
}
//lclosePoll();
fpoll.closePoll();
},
listPlaylists: function(data) {

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.
*/
var CL_VERSION = 3.0;
var GS_VERSION = 1.7; // Google Drive Userscript
@ -121,7 +160,6 @@ var USEROPTS = {
blink_title : getOrDefault("blink_title", "onlyping"),
sync_accuracy : getOrDefault("sync_accuracy", 2),
wmode_transparent : getOrDefault("wmode_transparent", true),
chatbtn : getOrDefault("chatbtn", false),
altsocket : getOrDefault("altsocket", false),
qbtn_hide : getOrDefault("qbtn_hide", false),
qbtn_idontlikechange : getOrDefault("qbtn_idontlikechange", false),
@ -130,6 +168,7 @@ var USEROPTS = {
ignore_channeljs : getOrDefault("ignore_channeljs", false),
sort_rank : getOrDefault("sort_rank", true),
sort_afk : getOrDefault("sort_afk", false),
legacy_emote : getOrDefault("legacy_emote", false),
default_quality : getOrDefault("default_quality", "auto"),
boop : getOrDefault("boop", "never"),
show_shadowchat : getOrDefault("show_shadowchat", false),
@ -138,7 +177,8 @@ var USEROPTS = {
strip_image : getOrDefault("strip_image", false),
chat_tab_method : getOrDefault("chat_tab_method", "Cycle options"),
notifications : getOrDefault("notifications", "never"),
show_ip_in_tooltip : getOrDefault("show_ip_in_tooltip", true)
show_ip_in_tooltip : getOrDefault("show_ip_in_tooltip", true),
show_orientation : getOrDefault("show_orientation", "true")
};
/* Backwards compatibility check */

View file

@ -1,4 +1,19 @@
//var colorList = ["#ff2e2e", "#5f5cff", "#4de024", "#f4861f", "##b88e8e", "#8f8eb8", "#8eb1b8", "#8eb897", "#abb88e"];
/*
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>
*/
var colorList = ["userlist_color0", "userlist_color1", "userlist_color2", "userlist_color3", "userlist_color4", "userlist_color5", "userlist_color6"];
var usrColors = ([[],[]]);

View file

@ -1,3 +1,19 @@
/*
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>
*/
img_ext = ['jpg', 'jpg:large', 'jpeg', 'png', 'tiff', 'gif'];
vid_ext = ['webm', 'gifv', 'mp4'];
proto = ['http', 'https'];
@ -36,7 +52,7 @@ function checkEmbed(word, isEmote){
let usersplit = word.split(stripd,2);//split word by stripd, array of before and after name
return usersplit[0] + '<span id="' + getColor(stripd) + '" onclick="chatpaste(\'' + stripd + '\')">' + stripd + '</span>' + usersplit[1];//decorate name
}else if(tstripd.charAt(0) === '!'){//if !toke command(same logic as above)
}else if(tstripd.charAt(0) === '!' && !isEmote){//if !toke command(same logic as above)
let tokesplit = word.split(tstripd,2);
return tokesplit[0] + '<a id="toke" onclick="chatsmack(\'' + tstripd + '\')">' + tstripd + '</a>' + tokesplit[1];
}else{

307
www/js/fpanel.js Normal file
View file

@ -0,0 +1,307 @@
/*
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>
*/
//---Global Variables---
CURRENTFPANEL = null;
//---Global Functions---
function closeFPanel(cb){//close and null out fpanel, cb function to call when panel is closed
$("#fpaneldiv").hide("slide", 250,function(){
$("#fpcontdiv").empty();
$("#fptitle").html("null Panel");
$("#closefpanel").prop("title", "Close null panel.");
CURRENTFPANEL = null;
if(typeof cb === 'function'){
cb();
}
});
}
function sizeFPDiv(){//set inner div height to fix overflow
$("#fpcontdiv").outerHeight($("#fpaneldiv").height() - $("#fptitlediv").outerHeight());
}
function panelbtn(panel){
if($("#fpaneldiv").is(":visible")){//if panel is visible
difClose = CURRENTFPANEL != panel;//set difclose to if CURRENTPANEL is the same one attatched to the button we're clicking
closeFPanel(function(){//closem panel
if(difClose){//if
panel.popMenu(panel.data);//pop da panel
}
});
}else{//else
panel.popMenu(panel.data);//pop that bitch son
}
}
//---base panel---
function fpmenu(title, elm, data, ocall){//fpmenu constructor
this.title = title;//title of menu
this.elm = elm;//elements to insert (good for simple menus)
this.data = data;//menu data (not used for all menus)
this.ocall = function ocall(){};//function to call upon menu opening (used for more advanced menus)
}
fpmenu.prototype.popMenu = function(idata){//POP goes the weasal!
this.data = idata//set data
$("#fpcontdiv").empty();//empty content div
$("#fptitle").html(this.title + " Panel");//set panel tittle
$("#closefpanel").prop("title", "Close " + this.title + " panel.");//set close button hover text
$("#fpcontdiv").append(this.elm);//append element array
this.ocall(this.data);//run open function
CURRENTFPANEL = this;
$("#fpaneldiv").show("slide", 250, function() {sizeFPDiv()});//show panel and correct size once open
}
//---fpanel poll---
var fpoll = new fpmenu("Poll");//create new panel fpoll
fpoll.elm = [//fpoll element array
$("<h3>").prop("id","polltitle").html("Null Poll Title")//poll title
]
fpoll.ocall = function(data){//fpoll open call function
$("#polltitle").html("Poll: " + data.title);//set poll title
if(hasPermission("pollctl")) {//if ur allowed to fuck wit da poll
$("<button/>").addClass("btn btn-danger btn-sm pull-right").text("End Poll")//add end poll button
.prop("id","endpollbtn")//set id
.appendTo("#fpcontdiv")//append
.click(function() {//click event
socket.emit("closePoll")//endem poll
});
}
for(var i = 0; i < data.options.length; i++) {//fer all the options in the poll
(function(i) {
var callback = function () {//create callback function
socket.emit("vote", {//send vote
option: i//use current option
});
$("#fpcontdiv").find(".option button").each(function() {//find currently selected button
$(this).removeClass("active");//unselect it
$(this).parent().removeClass("option-selected");
});
$(this).addClass("active");//set current option as actively selected
$(this).parent().addClass("option-selected");
}
$("<button/>").addClass("btn btn-default btn-sm").text(data.options[i])//add button
.prependTo($("<div/>").addClass("option").html('<p style="display: inline;">' + data.counts[i] + "</p>")//set cont labels
.appendTo("#fpcontdiv"))//append them to div
.click(callback);//add click event to callback function
})(i);
}
}
fpoll.updatePoll = function(data){//updatem poll
var poll = $("#pollwrap .active");
var i = 0;
$("#fpcontdiv").find(".option p").each(function() {//for every option
$(this).html(data.counts[i]);//correct count labels
i++;//increment counter
});
}
fpoll.closePoll = function(data){//close poll
tmpttl = $("#polltitle").html();//grab poll title
$("#polltitle").html(tmpttl + " (Poll Closed)");//add closed label
$("#pollopenbtn").hide("blinds");//hide poll button
$("#fpcontdiv").find(".option button").each(function() {//disable poll buttons
$(this).prop("disabled", true);
});
$("#fpcontdiv").find(".btn-danger").each(function() {//remove end poll button
$(this).remove()
});
}
//---emote panel---
var fpemote = new fpmenu("Emote");//create fpemote new fpanel obj
fpemote.isAlpha = true;//isAlpha to true because this emote panel is a fucking chad
fpemote.ocall = function(data){//fpemote open function
let symask = /["!#$%&'()*+,-./:;<=>?@[\]^_`{|}~]/g;//symbol mask
$("<form>").prop("action","javascript:void(0)").prop("id","emotecont").append([//top inputs
$("<input>").prop("placeholder","Search Emotes").prop("type","text").prop("id", "esearchbar").addClass("form-control").keyup(function(ev){//emote search bar
fpemote.searchEmote($("#esearchbar").val());//search for emotes when key as users finger lifts up
}),
$("<span>").prop("id","anumspan").append([//alphanumspan
$("<input>").prop("id","emotealphabox").prop("type", "checkbox").prop("checked", fpemote.isAlpha).click(function(){//alphanum sort checkbox
fpemote.alphaPop($("#emotealphabox").prop("checked"));//pop alphanum sort
}),
$("<label>").prop("for", "emotealphabox").addClass("glyphicon glyphicon-sort-by-alphabet")//alphanum sort label
])
]).appendTo("#fpcontdiv");//add search bar, adding this in elm causes strange behavior
$("<div>").addClass("emotecontdiv").appendTo("#fpcontdiv");//add emote container div, creating this in elm causes obnoxious behaviour
fpemote.alpha = window.CHANNEL.emotes.slice(0);//create second emote array
fpemote.alpha.sort((a, b) => a.name.replace(symask, '').toLowerCase().localeCompare(b.name.replace(symask, '').toLowerCase()));//sort it by alphabetical
earray = fpemote.isAlpha ? fpemote.alpha : window.CHANNEL.emotes//use the alphanum sorted array if alphanum sort is enabled
earray.forEach(function(value){//for every emote
fpemote.popEmote(value);//poulate emotes
});
}
fpemote.popEmote = function(emote){
$("<div>").addClass("emotediv").append([//add new div for every emote
$("<span>").prop("id","emspan").append($("<img>").prop("src", emote.image)),//insert emote thumbnails
$("<p>").html(emote.name),//insert emote name labels
]).click(function(){//find current emote and add onclick function
chatpaste(emote.name);//paste emote name
closeFPanel();//close fpanel
}).appendTo(".emotecontdiv");//append to emote container div
}
fpemote.searchEmote = function(sstring){
i = 0;
$(".emotecontdiv").empty();
earray = fpemote.isAlpha ? fpemote.alpha : window.CHANNEL.emotes
earray.forEach(function(value){
if(value.name.toLowerCase().includes(sstring.toLowerCase()) || (sstring == null || sstring == "")){
i++;
fpemote.popEmote(value);
}
});
if(i <= 2){
$(".emotecontdiv").prop("style", "grid-template-columns: auto");
}else{
$(".emotecontdiv").prop("style", "");
}
}
fpemote.alphaPop = function(setalpha){
$(".emotecontdiv").empty();
if(setalpha == null){
fpemote.isAlpha = !fpemote.isAlpha;
}else{
fpemote.isAlpha = setalpha
}
earray = fpemote.isAlpha ? fpemote.alpha : window.CHANNEL.emotes
if($("#esearchbar").val() != ""){
fpemote.searchEmote($("#esearchbar").val());
}else{
earray.forEach(function(value){//for every emote
fpemote.popEmote(value);//poulate emotes
});
}
}
//---Quick Settings Panel---
var fpset = new fpmenu("Quick Settings");
fpset.ocall = function(){
$("#fpcontdiv").append(spanel = $("<div>").prop("id","fpsetdiv"));
spanel.append([
$("<h4>").html("General Preferences"),
$("<form>").append(
$("<label>").prop("for","qs-theme").html("Theme: "),
$("<select>").prop("id","qs-theme").addClass("qs-form").change(function() {
USEROPTS.theme = $("#qs-theme").val();
processOpts();
}),
),
$("<h4>").html("Playback Preferences"),
$("<p>").html("Video Orientation: "),
$("<span>").prop("id","flipx-video").addClass("qsbtn glyphicon glyphicon-resize-horizontal pointer").prop("title", "Flip Player Horizontally").attr("onclick",'javascript:$("#ytapiplayer").toggleClass("mirx")'),
$("<span>").prop("id","flipy-video").addClass("qsbtn glyphicon glyphicon-resize-vertical pointer").prop("title", "Flip Player Vertically").attr("onclick",'javascript:$("#ytapiplayer").toggleClass("miry")'),
$("<form>").append(
$("<label>").prop("for","qs-orient-buttons").html("Video Orientation Buttons on Titlebar: "),
$("<input>").prop("id","qs-orient-buttons").prop("type","checkbox").addClass("qs-form").change(function() {
USEROPTS.show_orientation = $("#qs-orient-buttons").prop("checked");
processOpts();
}),
),
$("<form>").append(
$("<label>").prop("for","qs-sync-threshold").html("Sync Threshold(seconds): "),
$("<input>").prop("id","qs-sync-threshold").prop("type","text").addClass("qs-form"),
$("<button/>").addClass("btn btn-primary btn-ln").text("Save").prop("type","button").prop("id","qs-sync-threshold-save").click(function() {
USEROPTS.sync_accuracy = parseFloat($("#qs-sync-threshold").val()) || 2;
processOpts();
}),
),
$("<form>").append(
$("<label>").prop("for","qs-yt-source").html("Youtube Source: "),
$("<select>").prop("id","qs-yt-source").addClass("qs-form"),
),
$("<h4>").html("Chat Preferences"),
$("<form>").append(
$("<label>").prop("for","qs-legacy-emote").html("Use legacy cytube emote menu: "),
$("<input>").prop("id","qs-legacy-emote").prop("type","checkbox").addClass("qs-form").change(function() {
USEROPTS.legacy_emote = $("#qs-legacy-emote").prop("checked");
processOpts();
}),
),
$("<form>").append(
$("<label>").prop("for","qs-blink-title").html("Blink page title on new messages: "),
$("<select>").prop("id","qs-blink-title").addClass("qs-form").change(function() {
USEROPTS.blink_title = $("#qs-blink-title").val();
processOpts();
}),
),
$("<form>").append(
$("<label>").prop("for","qs-boop").html("Beep on new message: "),
$("<select>").prop("id","qs-boop").addClass("qs-form").change(function() {
USEROPTS.boop = $("#qs-boop").val();
processOpts();
}),
),
$("<form>").append(
$("<label>").prop("for","qs-notifications").html("Browser Notification on new message: "),
$("<select>").prop("id","qs-notifications").addClass("qs-form").change(function() {
USEROPTS.notifications = $("#qs-notifications").val();
processOpts();
}),
),
])
fpset.loadSettings();
}
fpset.loadSettings = function(){
$("#us-theme").children().clone().appendTo($("#qs-theme"));
$("#qs-theme").val(USEROPTS.theme);
$("#qs-orient-buttons").prop("checked", USEROPTS.show_orientation);
$("#qs-sync-threshold").val(USEROPTS.sync_accuracy);
$("#qs-legacy-emote").prop("checked", USEROPTS.legacy_emote);
$("#us-blink-title").children().clone().appendTo($("#qs-blink-title"));
$("#qs-blink-title").val(USEROPTS.blink_title);
$("#us-ping-sound").children().clone().appendTo($("#qs-boop"));
$("#qs-boop").val(USEROPTS.boop);
$("#us-notifications").children().clone().appendTo($("#qs-notifications"));
$("#qs-notifications").val(USEROPTS.notifications);
}

View file

@ -1,3 +1,19 @@
/*
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>
*/
var startTimes = [[],[]]//UID's, StartTimes
var activeItem = 0;//active UID;
var rptime = 0;//reference playlist time
@ -14,12 +30,14 @@ function dispSTimes(){//update sTimes
for(var i = 0; i < startTimes[0].length; i++){//for every item startTime
var rdif = startTimes[1][i] - rptime;
st.setTime(rltime + (rdif * 1000));
ltimeString = "airdate: " + st.toLocaleTimeString() + " " + st.toLocaleDateString() + "</br>";
ptimeString = "(pref time) " + formatTime(startTimes[1][i]);// create ptimeString
ltimeString = "airdate: " + st.toLocaleTimeString() + " " + st.toLocaleDateString();
//ptimeString = '<span id="prefTime"> (pref time) ' + formatTime(startTimes[1][i] + '</span>');// create ptimeString
ptimeString = '(pref time) ' + formatTime(startTimes[1][i]);// create ptimeString
if(document.getElementsByClassName("pluid-" + startTimes[0][i])[0] != null || document.getElementsByClassName("pluid-" + startTimes[0][i])[0] != undefined){//if current item isnt null or undefined
if($(".pluid-" + startTimes[0][i]) != null || $(".pluid-" + startTimes[0][i]) != undefined){//if current item isnt null or undefined
document.getElementsByClassName("pluid-" + startTimes[0][i])[0].getElementsByClassName("qe_sTime")[0].innerHTML = ltimeString + ptimeString;// set current item qe_sTime innerHTML to ptimeString
$(".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
}
}
@ -36,3 +54,39 @@ function calcRefs(){
PLAYER.getTime(function(seek){ctime = seek});
rltime = ld.getTime() - (ctime * 1000);
}
function expandItem(itm){
itm.find(".btn-group").show("blind");
itm.find(".qe_time").show("blind");
itm.find(".qe_pref").show("blind");
}
function collapseItem(itm){
itm.find(".btn-group").hide("blind");
itm.find(".qe_time").hide("blind");
itm.find(".qe_pref").hide("blind");
}
function toggleItem(itm){
itm.find(".btn-group").toggle("blind");
itm.find(".qe_time").toggle("blind");
itm.find(".qe_pref").toggle("blind");
}
function collapseItems(){
$.each($("#queue").find("li"), function(i,el){
collapseItem($(el));
})
}
function expandItems(){
$.each($("#queue").find("li"), function(i,el){
expandItem($(el));
})
}
function toggleItems(){
$.each($("#queue").find("li"), function(i,el){
toggleItem($(el));
})
}

View file

@ -10,6 +10,9 @@
}
this.setMediaProperties(data);
this.paused = false;
this.latched = true;
this.seeklatch = false;
this.lastSTime = 0;
}
Player.prototype.load = function(data) {
@ -30,6 +33,31 @@
return this.paused = true;
};
Player.prototype.latch = function() {
if (!this.latched) {
return this.latched = true;
}
};
Player.prototype.unlatch = function() {
if (!this.seeklatch) {
if (this.latched) {
$("#latchvid").show();
}
return this.latched = false;
} else {
return this.seeklatch = false;
}
};
Player.prototype.latchseek = function() {
return this.seeklatch = true;
};
Player.prototype.getLatch = function(cb) {
return cb(this.latched);
};
Player.prototype.seekTo = function(time) {};
Player.prototype.setVolume = function(volume) {};
@ -64,6 +92,7 @@
VimeoPlayer.prototype.load = function(data) {
this.setMediaProperties(data);
this.latched = true;
return waitUntilDefined(window, 'Vimeo', (function(_this) {
return function() {
var video;
@ -88,6 +117,8 @@
_this.paused = true;
if (CLIENT.leader) {
return sendVideoUpdate();
} else {
return _this.unlatch();
}
});
_this.vimeo.on('play', function() {
@ -96,6 +127,11 @@
return sendVideoUpdate();
}
});
_this.vimeo.on('seeked', function() {
if (!CLIENT.leader) {
return _this.unlatch();
}
});
_this.play();
return _this.setVolume(VOLUME);
};
@ -294,6 +330,7 @@
this.setMediaProperties(data);
this.initialVolumeSet = false;
this.playbackReadyCb = null;
this.latched = true;
waitUntilDefined(window, 'DM', (function(_this) {
return function() {
var params, quality;
@ -324,6 +361,8 @@
_this.paused = true;
if (CLIENT.leader) {
return sendVideoUpdate();
} else {
return _this.unlatch();
}
});
_this.dm.addEventListener('playing', function() {
@ -336,6 +375,11 @@
return _this.initialVolumeSet = true;
}
});
_this.dm.addEventListener('seeked', function() {
if (!CLIENT.leader) {
return _this.unlatch();
}
});
_this.dm.addEventListener('video_end', function() {
return _this.dmReady = false;
});
@ -557,22 +601,6 @@
}).appendTo(video);
});
}
if (data.meta.textTracks) {
data.meta.textTracks.forEach(function(track) {
var label;
label = track.name;
attrs = {
src: track.url,
kind: 'subtitles',
type: track.type,
label: label
};
if ((track["default"] != null) && track["default"]) {
attrs["default"] = '';
}
return $('<track/>').attr(attrs).appendTo(video);
});
}
_this.player = videojs(video[0], {
autoplay: _this.sources[0].type !== 'application/dash+xml',
controls: true,
@ -614,6 +642,8 @@
_this.paused = true;
if (CLIENT.leader) {
return sendVideoUpdate();
} else {
return _this.unlatch();
}
});
_this.player.on('play', function() {
@ -623,7 +653,10 @@
}
});
_this.player.on('seeked', function() {
return $('.vjs-waiting').removeClass('vjs-waiting');
$('.vjs-waiting').removeClass('vjs-waiting');
if (!CLIENT.leader) {
return _this.unlatch();
}
});
return setTimeout(function() {
return $('#ytapiplayer .vjs-subtitles-button .vjs-menu-item').each(function(i, elem) {
@ -646,8 +679,26 @@
VideoJSPlayer.prototype.load = function(data) {
this.setMediaProperties(data);
this.latched = true;
this.destroy();
return this.loadPlayer(data);
this.loadPlayer(data);
return this.setTracks(data);
};
VideoJSPlayer.prototype.setTracks = function(data) {
if (data.meta.textTracks) {
return data.meta.textTracks.forEach(function(track) {
var label;
label = track.name;
return $('<track>').attr({
src: track.url,
kind: 'subtitles',
type: track.type,
label: label,
"default": true
}).prependTo("video");
});
}
};
VideoJSPlayer.prototype.play = function() {
@ -660,7 +711,10 @@
VideoJSPlayer.prototype.pause = function() {
this.paused = true;
if (this.player && this.player.readyState() > 0) {
return this.player.pause();
this.player.pause();
if (!CLIENT.leader) {
return this.unlatch();
}
}
};
@ -1624,8 +1678,11 @@
window.handleMediaUpdate = function(data) {
var PLAYER, waiting;
PLAYER = window.PLAYER;
handleWindowResize();
dispSTimes();
PLAYER.lastSTime = data.currentTime;
if (!PLAYER.latched) {
return;
}
if (typeof PLAYER.mediaLength === 'number' && PLAYER.mediaLength > 0 && data.currentTime > PLAYER.mediaLength) {
return;
}
@ -1639,6 +1696,7 @@
}
if (waiting) {
PLAYER.seekTo(0);
PLAYER.latchseek();
if (PLAYER instanceof YouTubePlayer) {
PLAYER.pauseSeekRaceCondition = true;
} else {
@ -1653,6 +1711,7 @@
}
if (data.paused && !PLAYER.paused) {
PLAYER.seekTo(data.currentTime);
PLAYER.latchseek();
PLAYER.pause();
} else if (PLAYER.paused && !data.paused) {
PLAYER.play();
@ -1666,12 +1725,14 @@
accuracy = Math.max(accuracy, 5);
}
if (diff > accuracy) {
return PLAYER.seekTo(time);
PLAYER.seekTo(time);
return PLAYER.latchseek();
} else if (diff < -accuracy) {
if (!(PLAYER instanceof DailymotionPlayer)) {
time += 1;
}
return PLAYER.seekTo(time);
PLAYER.seekTo(time);
return PLAYER.latchseek();
}
});
};

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.
*/
CyTube.tabCompleteMethods = {};
// Bash-style completion

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.
*/
(function () {
var c = document.cookie.split(";").map(function (s) {
return s.trim();

View file

@ -1,3 +1,41 @@
/*
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.
*/
/* window focus/blur */
CyTube.ui.onPageFocus = function () {
FOCUSED = true;
@ -16,7 +54,7 @@ $(".modal").focus(CyTube.ui.onPageFocus);
$("#togglemotd").click(function () {
var hidden = $("#motdwrap")[0].style.display === "none";
$("#motdwrap").toggle();
$("#motdwrap").toggle("blind");
if (hidden) {
$("#togglemotd").find(".glyphicon-plus")
.removeClass("glyphicon-plus")
@ -37,13 +75,13 @@ $("#modflair").click(function () {
m.removeClass("label-success");
if (SUPERADMIN) {
USEROPTS.adminhat = true;
m.addClass("label-danger");
m.addClass("label-admin");
} else {
m.addClass("label-default");
}
} else if (m.hasClass("label-danger")) {
} else if (m.hasClass("label-admin")) {
USEROPTS.adminhat = false;
m.removeClass("label-danger")
m.removeClass("label-admin")
.addClass("label-default");
} else {
USEROPTS.modhat = true;
@ -183,7 +221,7 @@ function callChat(){
if(msg.trim()) {
var meta = {};
if (USEROPTS.adminhat && CLIENT.rank >= 255) {
msg = "/a " + msg;
msg = "!a " + msg;
} else if (USEROPTS.modhat && CLIENT.rank >= Rank.Moderator) {
meta.modflair = CLIENT.rank;
}
@ -329,6 +367,14 @@ $("#mediarefresh").click(function() {
socket.emit("playerReady");
});
$("#latchvid").click(function() {
PLAYER.latch();
PLAYER.play();
PLAYER.latchseek()
PLAYER.seekTo(PLAYER.lastSTime);
$("#latchvid").hide();
});
/* playlist controls */
$("#queue").sortable({
@ -406,6 +452,7 @@ function queue(pos, src) {
var duration = undefined;
var title = undefined;
var subtitle = "";
if (data.type === "fi") {
if (data.id.match(/^http:/)) {
Callbacks.queueFail({
@ -436,6 +483,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 = $("#addfromurl-subtitle-val").val();
}
if (data.id == null || data.type == null) {
@ -451,7 +499,8 @@ function queue(pos, src) {
duration: duration,
title: title,
temp: addTemp,
link: link
link: link,
subtitle: subtitle
});
}
});
@ -500,26 +549,45 @@ $("#mediaurl").keyup(function(ev) {
} catch (error) {
}
if (editTitle) {
if (editTitle) {//Add title and subtitles fieldS
var title = $("#addfromurl-title");
if (title.length === 0) {
title = $("<div/>")
.attr("id", "addfromurl-title")
.appendTo($("#addfromurl"));
$("<span/>").text("Title (optional; for raw files only)")
.appendTo(title);
$("<input/>").addClass("form-control")
.attr("type", "text")
var subtitle = $("#addfromurl-subtitle");
if (title.length === 0) {//if there is no title set
title = $("<div/>")//create title div
.attr("id", "addfromurl-title")//title div attr
.insertAfter($("#addfromurl").find(".input-group"));//insert after URL bar
$("<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%;")
.keydown(function (ev) {
if (ev.keyCode === 13) {
queue("end", "url");
}
})
.appendTo($("#addfromurl-title"));
.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").remove();
$("#addfromurl-title").hide("blind");
$("#addfromurl-title").remove();//otherwise remove
}
}
});
@ -539,6 +607,27 @@ $("#voteskip").click(function() {
$("#voteskip").attr("disabled", true);
});
$("#blindItems").click(function(){
$("#blindItems").toggleClass("glyphicon-resize-small");
$("#blindItems").toggleClass("glyphicon-resize-full");
$("#blindItems").prop("title",
($("#blindItems").prop("title") == "Collapse All Items") ? "Expand All Items" : "Collapse All Items"
)
toggleItems();
});
$("#hideplaylist").click(function(){
$("#rightcontrols").hide("blind");
$("#playlistrow").hide("blind");
$("#showplaylist").show("");
});
$("#showplaylist").click(function(){
$("#rightcontrols").show("blind");
$("#playlistrow").show("blind");
$("#showplaylist").hide("");
});
$("#getplaylist").click(function() {
var callback = function(data) {
var idx = socket.listeners("errorMsg").indexOf(errCallback);
@ -912,6 +1001,7 @@ EMOTELISTMODAL.find(".emotelist-alphabetical").change(function () {
});
EMOTELISTMODAL.find(".emotelist-alphabetical").prop("checked", USEROPTS.emotelist_sort);
$("#fullscreenbtn").click(function () {
var elem = document.querySelector("#videowrap .embed-responsive");
// this shit is why frontend web development sucks
@ -962,4 +1052,3 @@ $("#resize-video-smaller").click(function () {
console.error(error);
}
});

View file

@ -1,4 +1,42 @@
function makeAlert(title, text, klass, textOnly) {
/*
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.
*/
function makeAlert(title, text, klass, textOnly) {
if(!klass) {
klass = "alert-info";
}
@ -211,6 +249,60 @@ function addUserDropdown(entry) {
var btngroup = $("<div/>").addClass("btn-group-vertical").appendTo(menu);
/* give/remove leader (moderator+ only) */
if (hasPermission("leaderctl")) {
var ldr = $("<button/>").addClass("btn btn-xs btn-default")
.appendTo(btngroup);
if(leader) {
ldr.text("Remove Leader");
ldr.click(function () {
socket.emit("assignLeader", {
name: ""
});
});
} else {
ldr.text("Give Leader");
ldr.click(function () {
socket.emit("assignLeader", {
name: name
});
});
}
}
/* pm button */
if (name !== CLIENT.name) {
var pm = $("<button/>").addClass("btn btn-xs btn-default")
.text("Private Message")
.appendTo(btngroup)
.click(function () {
initPm(name).find(".panel-heading").click();
menu.hide();
});
}
/* mention button */
var pm = $("<button/>").addClass("btn btn-xs btn-default")
.text("Mention")
.appendTo(btngroup)
.click(function () {
chatpaste(name);
menu.hide();
});
/* toke invide button */
var pm = $("<button/>").addClass("btn btn-xs btn-default")
.text("Toke" + (name !== CLIENT.name ? " With" : ""))
.appendTo(btngroup)
.click(function () {
if (name !== CLIENT.name) {
chatsmack(" !toke up " + name);
}else{
chatsmack(" !toke up fuckers");
}
menu.hide();
});
/* ignore button */
if (name !== CLIENT.name) {
var ignore = $("<button/>").addClass("btn btn-xs btn-default")
@ -236,55 +328,6 @@ function addUserDropdown(entry) {
}
}
/* pm button */
if (name !== CLIENT.name) {
var pm = $("<button/>").addClass("btn btn-xs btn-default")
.text("Private Message")
.appendTo(btngroup)
.click(function () {
initPm(name).find(".panel-heading").click();
menu.hide();
});
}
/* give/remove leader (moderator+ only) */
if (hasPermission("leaderctl")) {
var ldr = $("<button/>").addClass("btn btn-xs btn-default")
.appendTo(btngroup);
if(leader) {
ldr.text("Remove Leader");
ldr.click(function () {
socket.emit("assignLeader", {
name: ""
});
});
} else {
ldr.text("Give Leader");
ldr.click(function () {
socket.emit("assignLeader", {
name: name
});
});
}
}
/* kick button */
if(hasPermission("kick")) {
$("<button/>").addClass("btn btn-xs btn-default")
.text("Kick")
.click(function () {
var reason = prompt("Enter kick reason (optional)");
if (reason === null) {
return;
}
socket.emit("chatMsg", {
msg: "/kick " + name + " " + reason,
meta: {}
});
})
.appendTo(btngroup);
}
/* mute buttons */
if (hasPermission("mute")) {
var mute = $("<button/>").addClass("btn btn-xs btn-default")
@ -322,6 +365,23 @@ function addUserDropdown(entry) {
}
}
/* kick button */
if(hasPermission("kick")) {
$("<button/>").addClass("btn btn-xs btn-default")
.text("Kick")
.click(function () {
var reason = prompt("Enter kick reason (optional)");
if (reason === null) {
return;
}
socket.emit("chatMsg", {
msg: "/kick " + name + " " + reason,
meta: {}
});
})
.appendTo(btngroup);
}
/* ban buttons */
if(hasPermission("ban")) {
$("<button/>").addClass("btn btn-xs btn-default")
@ -466,7 +526,6 @@ function scrollQueue() {
function makeQueueEntry(item, addbtns) {
var video = item.media;
var li = $("<li/>");
console.log(item);
li.addClass("queue_entry");
li.addClass("pluid-" + item.uid);
li.data("uid", item.uid);
@ -482,11 +541,12 @@ function makeQueueEntry(item, addbtns) {
.text(video.title)
.attr("href", formatURL(video))
.attr("target", "_blank");
var sTime = $("<span/>").addClass("qe_sTime").appendTo(li);
$("<br/>").appendTo(li);
var pref = $("<span/>").addClass("qe_pref").appendTo(li);
pref.text(" \n");
var time = $("<span/>").addClass("qe_time").appendTo(li);
time.text("airtime: " + video.duration);
$("<br/>").appendTo(li);
var sTime = $("<span/>").addClass("qe_sTime").appendTo(li);
sTime.text(" \n");
//dispSTimes();
var clear = $("<div/>").addClass("qe_clear").appendTo(li);
if(item.temp) {
@ -522,6 +582,8 @@ function makeSearchEntry(video) {
function addQueueButtons(li) {
li.find(".btn-group").remove();
var menu = $("<div/>").addClass("btn-group").appendTo(li);
var atime = li.find(".qe_time");
var ptime = li.find(".qe_pref");
// Play
if(hasPermission("playlistjump")) {
$("<button/>").addClass("btn btn-xs btn-default qbtn-play")
@ -573,8 +635,6 @@ function addQueueButtons(li) {
// I DON'T LIKE CHANGE
if(USEROPTS.qbtn_idontlikechange) {
menu.addClass("pull-left");
menu.detach().prependTo(li);
menu.find(".btn").each(function() {
// Clear icon
var icon = $(this).find(".glyphicon");
@ -584,17 +644,24 @@ function addQueueButtons(li) {
menu.find(".qbtn-play").addClass("btn-success");
menu.find(".qbtn-delete").addClass("btn-danger");
}
else if(menu.find(".btn").length != 0) {
if(menu.find(".btn").length != 0) {
li.unbind("contextmenu");
li.contextmenu(function(ev) {
// Allow shift+click to open context menu
// (Chrome workaround, works by default on Firefox)
if (ev.shiftKey) return true;
ev.preventDefault();
if(menu.css("display") == "none")
menu.show("blind");
else
menu.hide("blind");
if(menu.css("display") == "none"){
//menu.show("blind");
//atime.show("blind");
//ptime.show("blind");
expandItem(li);
}else{
//menu.hide("blind");
//atime.hide("blind");
//ptime.hide("blind");
collapseItem(li);
}
return false;
});
}
@ -646,15 +713,16 @@ function showUserOptions() {
$("#us-hidevideo").prop("checked", USEROPTS.hidevid);
$("#us-playlistbuttons").prop("checked", USEROPTS.qbtn_hide);
$("#us-oldbtns").prop("checked", USEROPTS.qbtn_idontlikechange);
$("#us-video-orientation").prop("checked", USEROPTS.show_orientation);
$("#us-default-quality").val(USEROPTS.default_quality || "auto");
$("#us-chat-timestamp").prop("checked", USEROPTS.show_timestamps);
$("#us-sort-rank").prop("checked", USEROPTS.sort_rank);
$("#us-sort-afk").prop("checked", USEROPTS.sort_afk);
$("#us-legacy-emote").prop("checked", USEROPTS.legacy_emote);
$("#us-blink-title").val(USEROPTS.blink_title);
$("#us-ping-sound").val(USEROPTS.boop);
$("#us-notifications").val(USEROPTS.notifications);
$("#us-sendbtn").prop("checked", USEROPTS.chatbtn);
$("#us-no-emotes").prop("checked", USEROPTS.no_emotes);
$("#us-strip-image").prop("checked", USEROPTS.strip_image);
$("#us-chat-tab-method").val(USEROPTS.chat_tab_method);
@ -683,15 +751,16 @@ function saveUserOptions() {
USEROPTS.hidevid = $("#us-hidevideo").prop("checked");
USEROPTS.qbtn_hide = $("#us-playlistbuttons").prop("checked");
USEROPTS.qbtn_idontlikechange = $("#us-oldbtns").prop("checked");
USEROPTS.show_orientation = $("#us-video-orientation").prop("checked");
USEROPTS.default_quality = $("#us-default-quality").val();
USEROPTS.show_timestamps = $("#us-chat-timestamp").prop("checked");
USEROPTS.sort_rank = $("#us-sort-rank").prop("checked");
USEROPTS.sort_afk = $("#us-sort-afk").prop("checked");
USEROPTS.legacy_emote = $("#us-legacy-emote").prop("checked");
USEROPTS.blink_title = $("#us-blink-title").val();
USEROPTS.boop = $("#us-ping-sound").val();
USEROPTS.notifications = $("#us-notifications").val();
USEROPTS.chatbtn = $("#us-sendbtn").prop("checked");
USEROPTS.no_emotes = $("#us-no-emotes").prop("checked");
USEROPTS.strip_image = $("#us-strip-image").prop("checked");
USEROPTS.chat_tab_method = $("#us-chat-tab-method").val();
@ -700,7 +769,10 @@ function saveUserOptions() {
USEROPTS.modhat = $("#us-modflair").prop("checked");
USEROPTS.show_shadowchat = $("#us-shadowchat").prop("checked");
}
processOpts();
}
function processOpts(){
storeOpts();
applyOpts();
}
@ -727,7 +799,7 @@ function applyOpts() {
fixWeirdButtonAlignmentIssue();
}
switch (USEROPTS.layout) {
/*switch (USEROPTS.layout) {
case "synchtube-fluid":
fluidLayout();
case "synchtube":
@ -740,8 +812,20 @@ function applyOpts() {
hdLayout();
break;
default:
compactLayout();
fluidLayout();
//compactLayout();Actual fucking cancer layout. Even for 2014. What the actual shit, this was a barely acceptable layout in 05'
break;
}*/
fluidLayout();//Temporary measure until the other layouts are removed for good.
$("#emoteopenbtn").attr("onclick", (USEROPTS.legacy_emote ? "javascript:EMOTELISTMODAL.modal()" : "javascript:panelbtn(fpemote)"));
if(USEROPTS.show_orientation){
$("#flipx-video").show();
$("#flipy-video").show();
}else{
$("#flipx-video").hide();
$("#flipy-video").hide();
}
if(USEROPTS.hidevid) {
@ -1014,6 +1098,8 @@ function handlePermissionChange() {
setVisible("#chatline", CLIENT.rank >= 0);
setVisible("#queue", hasPermission("seeplaylist"));
setVisible("#plmeta", hasPermission("seeplaylist"));
setVisible("#rightcontrols", hasPermission("seeplaylist"));
setVisible("#playlistrow", hasPermission("seeplaylist"));
$("#getplaylist").attr("disabled", !hasPermission("seeplaylist"));
setVisible("#showplaylistmanager", hasPermission("seeplaylist"));
@ -1133,6 +1219,11 @@ function addLibraryButtons(li, item, source) {
var id = item.id;
var type = item.type;
//item.subtitle = (!subtitle) ? item.subtitle : "";
if(item.subtitle == null || item.subtitle != undefined){
item.subtitle = "";
}
if(hasPermission("playlistadd")) {
if(hasPermission("playlistnext")) {
@ -1143,6 +1234,7 @@ function addLibraryButtons(li, item, source) {
id: id,
pos: "next",
type: type,
subtitle: item.subtitle,
temp: $(".add-temp").prop("checked")
});
})
@ -1155,6 +1247,7 @@ function addLibraryButtons(li, item, source) {
id: id,
pos: "end",
type: type,
subtitle: item.subtitle,
temp: $(".add-temp").prop("checked")
});
})
@ -1530,10 +1623,10 @@ function formatChatMessage(data, last) {
addClassToNameAndTimestamp: data.msgclass
};
}
//break if toke command
if(data.msg.charAt() === '!'){
//break if toke command [DEPRICATED] No longer in use as chozobot is no longer used
/*if(data.msg.charAt() === '!'){
return;
}
}*/
// Phase 1: Determine whether to show the username or not
var skip = data.username === last.name;
if(data.meta.addClass === "server-whisper")
@ -1549,15 +1642,9 @@ function formatChatMessage(data, last) {
last.name = data.username;
var div = $("<div/>");
/* drink is a special case because the entire container gets the class, not
just the message */
if (data.meta.addClass === "drink") {
div.addClass("drink");
data.meta.addClass = "";
}
// Add timestamps (unless disabled)
if (USEROPTS.show_timestamps && data.meta.modflair != 4) {
if (USEROPTS.show_timestamps && data.meta.addClass != "shout") {
var time = $("<span/>").addClass("timestamp").appendTo(div);
var timestamp = new Date(data.time).toTimeString().split(" ")[0];
time.text("["+timestamp+"] ");
@ -1571,9 +1658,18 @@ function formatChatMessage(data, last) {
if (!skip) {
name.appendTo(div);
}
assignColors(data.username);
if(!data.meta.addClass === "shout"){
assignColors(data.username);
}
console.log(data.meta.addClass);
if (data.meta.superadminflair) {
$("<strong/>").addClass("username").attr('onclick',"chatpaste('" + data.username + "')").text(data.username + "> ").appendTo(name);
$("<strong/>").addClass("username").attr('onclick',"chatpaste('" + data.username + "')").text(data.username +
(data.meta.addClass === "shout" ? '' : ">")
).appendTo(name);
if(data.meta.addClass === "shout"){
$("<br>").appendTo(name);
}
name.addClass("label")
.addClass(data.meta.superadminflair.labelclass);
$("<span/>").addClass(data.meta.superadminflair.icon)
@ -1581,12 +1677,16 @@ function formatChatMessage(data, last) {
.css("margin-right", "3px")
.prependTo(name);
}else if (data.meta.modflair) {
if(data.meta.modflair === 4){
if(data.meta.addClass === "shout"){
console.log('asdasd');
$("<strong/>").addClass("username").attr('onclick',"chatpaste('" + data.username + "')").text(data.username + "\n").appendTo(name);
}else{
$("<strong/>").addClass("username").attr('onclick',"chatpaste('" + data.username + "')").text(data.username + "> ").appendTo(name);
}
name.addClass(getNameColor(data.meta.modflair));
}else if(data.meta.addClass === "shout"){
console.log('asdasd');
$("<strong/>").addClass("username").attr('onclick',"chatpaste('" + data.username + "')").text(data.username + "\n").appendTo(name);
}else{
$("<strong/>").addClass("username").attr('onclick',"chatpaste('" + data.username + "')").attr('id',getColor(data.username)).text(data.username + "> ").appendTo(name);
}
@ -1627,8 +1727,10 @@ function addChatMessage(data) {
// a message, it highlights messages from that user
var safeUsername = data.username.replace(/[^\w-]/g, '\\$');
div.addClass("chat-msg-" + safeUsername);
if(data.meta.modflair === 4){
div.attr("id", "botmsg");
console.log(data.meta.modflair);
if(data.meta.addClass === "shout"){//legacy tokebot junk
div.addClass("shout");
}
div.appendTo(msgBuf);
div.mouseover(function() {
@ -1648,7 +1750,7 @@ function addChatMessage(data) {
var newMessageDiv = $("#newmessages-indicator");
if (!newMessageDiv.length) {
newMessageDiv = $("<div/>").attr("id", "newmessages-indicator")
.insertBefore($("#chatline"));
.insertBefore($("#chatbar"));
var bgHack = $("<span/>").attr("id", "newmessages-indicator-bghack")
.appendTo(newMessageDiv);
@ -1926,6 +2028,7 @@ function handleWindowResize() {
$("#chatheader").outerHeight();
$("#messagebuffer").outerHeight(h);
$("#userlist").outerHeight(h);
$("#sidepanel").outerHeight(h);
return;
} else {
handleVideoResize();
@ -1946,7 +2049,8 @@ function handleVideoResize() {
var height = responsiveFrame.outerHeight() - $("#chatline").outerHeight() - 2;
$("#messagebuffer").height(height);
$("#userlist").height(height);
$("#fpaneldiv").height(height);
sizeFPDiv();
$("#ytapiplayer").attr("height", VHEIGHT = responsiveFrame.outerHeight());
$("#ytapiplayer").attr("width", VWIDTH = responsiveFrame.outerWidth());
};

View file

@ -104,7 +104,7 @@ function setupAudioTracks(player, tech) {
'use strict';
exports.__esModule = true;
exports['default'] = setupTextTracks;
//exports['default'] = setupTextTracks;
var _dashjs = (typeof window !== "undefined" ? window['dashjs'] : typeof global !== "undefined" ? global['dashjs'] : null);
@ -210,7 +210,7 @@ function attachDashTextTracksToVideojs(player, tech, tracks) {
}
// Update dash when videojs's selected text track changes.
player.textTracks().on('change', updateActiveDashTextTrack);
//player.textTracks().on('change', updateActiveDashTextTrack);
// Cleanup event listeners whenever we start loading a new source
player.one('loadstart', function () {
@ -445,7 +445,7 @@ var Html5DashJS = function () {
_setupAudioTracks2['default'].call(null, this.player, tech);
// Setup text tracks
_setupTextTracks2['default'].call(null, this.player, tech, options);
//_setupTextTracks2['default'].call(null, this.player, tech, options);
// Attach the source with any protection data
this.mediaPlayer_.setProtectionData(this.keySystemOptions_);
@ -633,4 +633,4 @@ _video2['default'].Html5DashJS = Html5DashJS;
exports['default'] = Html5DashJS;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./setup-audio-tracks":1,"./setup-text-tracks":2,"global/window":3}]},{},[4])(4)
});
});