Compare commits
25 commits
1.1-indev-
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0950d34c84 | ||
|
|
649b395fad | ||
|
|
5c104f555a | ||
|
|
ad011c5ece | ||
|
|
0157262130 | ||
|
|
bec209c665 | ||
|
|
00ac19c80c | ||
|
|
2acba1a605 | ||
|
|
9ba95a073c | ||
|
|
02dc12e646 | ||
|
|
9057ed2899 | ||
|
|
46032a33fa | ||
|
|
1cdf5f8ab9 | ||
|
|
05f0eb0048 | ||
|
|
74a555b5f5 | ||
|
|
c5b000a30a | ||
|
|
2227577138 | ||
|
|
cf59498476 | ||
|
|
8b82c1d9c6 | ||
|
|
4dcbf09b68 | ||
|
|
a6f082d5f1 | ||
|
|
88b98993d2 | ||
|
|
54052d6cfc | ||
|
|
113cf7900e | ||
|
|
15e5513982 |
45
LICENSE
45
LICENSE
|
|
@ -1282,48 +1282,6 @@ copy of the Program in return for a fee.
|
|||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
-------Original Cytube License-------
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2013-2021 Calvin Montgomery and contributors
|
||||
|
|
@ -1335,3 +1293,6 @@ The above copyright notice and this permission notice shall be included in all c
|
|||
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.
|
||||
*/
|
||||
|
||||
--------other assets--------
|
||||
thunder0.ogg - NOT CREATED FOR FOREST - https://www.youtube.com/watch?v=T-BOPr7NXME
|
||||
thunder1.ogg - NOT CREATED FOR FOREST - https://www.youtube.com/watch?v=tPs2m9_7cls
|
||||
|
|
|
|||
424
README.md
424
README.md
|
|
@ -1,4 +1,4 @@
|
|||
fore.st - Pineapple Express(v1.1-indev)
|
||||
fore.st - Pineapple Express += 3 (v1.1.3)
|
||||
======
|
||||
|
||||
fore.st is the server software for ourfore.st, a community based chat & synced video embedding site tailored to service
|
||||
|
|
@ -24,424 +24,8 @@ You can reach out by bugging rainbownapkin on the ttn discord or ourfore.st, you
|
|||
- Thanks to calzoneman for making [cytube](https://github.com/calzoneman/sync), that saved our asses.
|
||||
- Thanks to the core TTN community and everyone else who's ever used it, I was only there for the last handful of years but it was an absolute fuckin' ride. You guys are the best, it isn't TTN but I hope this at least help fills the gap.
|
||||
|
||||
## Pineapple Express 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:
|
||||
|
||||
dev goals for 1.1 pineapple express:
|
||||
|
||||
- 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 ✓
|
||||
- fix fucked up bottom border on chat/video ✓
|
||||
- rename "legacy playlist buttons" to compact, fix location. This seems like it could be usable if not better than default ✓
|
||||
- get res/aspect ratio player.js ✓
|
||||
- base.coffee ✓
|
||||
- videojs.coffee(hls,videojs,raw-file,gdrive,rtmp) ✓
|
||||
- dailymotion ~(this is kinda broken, likely not possible client-side without breaking CORS policy. This can wait until next version when an installation script including nginx and cors-proxy config gets added to support the catbox.moe image upload button which faces the same issue.
|
||||
- vimeo ✓
|
||||
|
||||
- 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 ✓
|
||||
- show timestamps ✓
|
||||
- show seconds ✓
|
||||
|
||||
- trim/tidy default cytube command ✓
|
||||
- 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) ✓
|
||||
- modflair on announce ✓
|
||||
- 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 ✓
|
||||
|
||||
- flex layout/legacy layout ripout ✓
|
||||
- rip out legacy layout system ✓
|
||||
- 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 ✓
|
||||
- lock videowrap width to aspect ratio ✓
|
||||
- lock on join ✓
|
||||
- unlock on split drag ✓
|
||||
- show lock button on unlock ✓
|
||||
- hide lock button when locked ✓
|
||||
- checklock/move split on window move ✓
|
||||
- checklock/move split on video load ✓
|
||||
- videojs.coffee(hls,videojs,raw-file,gdrive,rtmp) ✓
|
||||
- dailymotion ✓
|
||||
- vimeo ✓
|
||||
- shade player ✓
|
||||
- player controls in title bar when video shaded (play/pause, mute, current time/video duration), hidden when video open ✓
|
||||
- minicont update in videojs ✓
|
||||
- minicont update in dailymotion ✓
|
||||
- minicont update in vimeo ✓
|
||||
- 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.) ✓
|
||||
- scroll over mincont dir to scrub vid ✓
|
||||
- scroll over minicont mutebtn to change vol ✓
|
||||
- remove legacy cytube themes. If they wheren't compatible after the emote panel, they certainly won't be now lmao ✓
|
||||
- fix bugs created by flex layout ✓
|
||||
- fix fpanel width ✓
|
||||
- fix portrait/mobile mode ✓
|
||||
- fix user tooltip menu position ✓
|
||||
|
||||
- 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 ✓
|
||||
- invidious api or yt-dlp backend for serverside metadata acquisition of youtube videos w/o registered API key or google account
|
||||
- pull metadata for single video ✓
|
||||
- pull metadata for playlist ✓
|
||||
- search function ✓
|
||||
- potentially leverage yt-dlp backend for other media sources X went with invidious. YT-DLP may make more sense for a total re-write in the future
|
||||
- implement player.js updates into youtube.coffee
|
||||
- latching ✓
|
||||
- getres X Probably not possible without breaking cors policy. (same as DM)
|
||||
- handleVideoSize on load ✓
|
||||
- update minicont dur ~ updates with server tick, doesnt look as good as other sources, but YT embeds suck.
|
||||
- update minicont buttons ✓
|
||||
- invidious embed support for youtube video playback X not possible with current setup. Reqs either update to invidious allowing embed control, or a cors proxy(will look into this next update, or whenever installation script is created)
|
||||
- invidious embed alternative: pull raw video link on vid start server side, serve to client ✓
|
||||
- youtube source in user prefrences (raw link or official yt embed for now) ✓
|
||||
|
||||
- mod/admin panel
|
||||
- button on chatbar ✓
|
||||
- auto switch to admin panel ✓
|
||||
- main panel
|
||||
- poll creation ✓
|
||||
- tools
|
||||
- !announce button/field ✓
|
||||
- !clear button w/ username dropdown ✓
|
||||
- mod message (sends message to all active mods) ✓
|
||||
- preferences ✓
|
||||
- modflair ✓
|
||||
- show modflair on chat header ✓
|
||||
- show shadowmuted messages ✓
|
||||
- show ips in tooltips ✓
|
||||
- open playlist below video by default ✓
|
||||
- nested menu
|
||||
- new nested menu protoype object ✓
|
||||
- playlist menu (just the playlist but in fpanel, currently one or the other) ✓
|
||||
- move playlist between legacy area and fpanel on open/close ✓
|
||||
- bugfixes ✓
|
||||
- lock/unlock panel dissapears playlist (suicide by two shots to the back of the head) ✓
|
||||
- fix scroll to item ✓
|
||||
- fix scrolling while dragging pl item ✓
|
||||
- autobump control menu
|
||||
- tokebot control menu ✓
|
||||
- mod ✓
|
||||
- reset cooldown button ✓
|
||||
- admin ✓
|
||||
- reload tokes btn ✓
|
||||
- tokewhisper filed+btn ✓
|
||||
- tokesay field+btn ✓
|
||||
- tokeannounce field+btn ✓
|
||||
|
||||
- merge tokebot into ourfore.st codebase, one server instead of two. ✓
|
||||
- port chozobot code to cytube module ✓
|
||||
- tokewhisper (server whisper, can optionally be displayed as PM client side) ✓
|
||||
- load toke commands from tokes file ✓
|
||||
- profile and userlist entry ✓
|
||||
- disable certain options in profile context menu ✓
|
||||
- mod commands ✓
|
||||
- reset cooldown command (quiet and loud, quiet by default) ✓
|
||||
- siteowner commands ✓
|
||||
- reload tokes command ✓
|
||||
- tokesay command ✓
|
||||
- tokeannounce command ✓
|
||||
- tokewhisper command ✓
|
||||
- !r to rando-toke ✓
|
||||
- log tokes w/ date to file. This will be consolidated to a better toke history in mariadb in a future update ✓
|
||||
- append [tokers],# of tokers,timestamp(epoch) on toke. ✓
|
||||
- tokefile, list of usernames with toke count. This should eventually be moved to a property of account or user ✓
|
||||
- json of map ["username", # of tokes] ✓
|
||||
- load file on startup ✓
|
||||
- update file every toke ✓
|
||||
- total tokes listed on profile tooltip ✓
|
||||
- send toke count with user and set toke count ✓
|
||||
- display on tooltip ✓
|
||||
- add toke to profile client side on toke(avoid sending information twice, write after tokes in tooltip) ✓
|
||||
- include modflair on tokewhisper ✓
|
||||
- reset toke cooldown every hour at *:19:30 for 4:20 !tokes ✓
|
||||
- extend toke until *:20:00 if it ends after *:19:30 but before *:20:00 ✓
|
||||
|
||||
- autobump ✓
|
||||
- Serverside ✓
|
||||
- Bump Management System ✓
|
||||
- bump object: name, user(person who made bump, optional), lowername, resettoke bool, id, listname, media item ✓
|
||||
- bumplist object: name, lowername, bump array ✓
|
||||
- addBump function for adding bumps to list ✓
|
||||
- deleteBump function for removing bumps from list ✓
|
||||
- saveList function for saving list to file ✓
|
||||
- pack/send lists to mods on start and request ✓
|
||||
- test callback(dumps bumplist to dev console) ✓
|
||||
- loadList for re-creating object from file ✓
|
||||
- loadLists for re-creating objects based on all files in bumps/ folder ✓
|
||||
- double check perms(look into using rank enums instead of numbers) ✓
|
||||
- newBump ✓
|
||||
- deleteBump ✓
|
||||
- newBumpList ✓
|
||||
- sendLists ✓
|
||||
- remove whitespace and non - or _ specials from lowernames ✓
|
||||
- bump ✓
|
||||
- bumplist ✓
|
||||
- Automated Bump Queueing ✓
|
||||
- base queueing function ✓
|
||||
- queue bump next ✓
|
||||
- call from socket ✓
|
||||
- Active lists ✓
|
||||
- map of active lists ✓
|
||||
- add/remove from active lists callback ✓
|
||||
- list selection method ✓
|
||||
- smashList ✓
|
||||
- randomList ✓
|
||||
- bump selection method: random from last-half, round-robin, full random ✓
|
||||
- round-robin ✓
|
||||
- full random ✓
|
||||
- last-half random ✓
|
||||
- Set bump/list sel method from packet ✓
|
||||
-Auto Queue Bump(s) ✓
|
||||
- agro: passive ✓
|
||||
- all auto-bumping features disabled ✓
|
||||
- agro: min ✓
|
||||
- Auto-clear old/skipped bumps ✓
|
||||
- after non-bump vid starts if next item is not bump ✓
|
||||
- if there is nothing queued before/after current item (bump or not) ✓
|
||||
- agro: mid ✓
|
||||
- when non-bump vid is added and there are bumps queued next, move new item after the bump block ✓
|
||||
- notify modules on video add ✓
|
||||
- agro: max ✓
|
||||
- when a non-bump vid is moved before or inbetween next bump block, move it after the bump block ✓
|
||||
- notify modules on video move ✓
|
||||
- set agro from packet ✓
|
||||
- dedicated method for automated queueing based on current settings ✓
|
||||
- bump frequency (default: 1,3) ✓
|
||||
- base setting ✓
|
||||
- set from packet ✓
|
||||
- minimum length to bump (default: 240 sec) ✓
|
||||
- base setting ✓
|
||||
- set from packet ✓
|
||||
- per-channel config files in bumps/config/<channame>.conf, loaded on channel startup for persistent settings(move to db by next major update) ✓
|
||||
- json object with following values ✓
|
||||
- active lists ✓
|
||||
- agro level ✓
|
||||
- bump frequency ✓
|
||||
- bump selmed ✓
|
||||
- list selmed ✓
|
||||
- min length to bump ✓
|
||||
- Add manual only property to bump object(default false). ✓
|
||||
- add boolean to obj ✓
|
||||
- add to handleAddBump & its type ✓
|
||||
- add to packlist ✓
|
||||
- do not autoQueue bumps with manualOnly set to true ✓
|
||||
- round-robin ✓
|
||||
- full random ✓
|
||||
- last-half random ✓
|
||||
- Change packList to send history as array of arrays ([[lname,id],[lname,id]]) ✓
|
||||
- double check perms(all actions should be at least mod+ only) ✓
|
||||
- reset tokebot when tokebump is played ✓
|
||||
- Additions/Changes for Clientside ✓
|
||||
- send list function ✓
|
||||
- send deleted list name function ✓
|
||||
- send hist function ✓
|
||||
- send config function ✓
|
||||
- send list to all mods on list save ✓
|
||||
- send deleted list names to all mods on list deletion ✓
|
||||
- send config to all mods on config save ✓
|
||||
- send history to all mods when item added to history ✓
|
||||
- Save list filename as lowername ✓
|
||||
- Only load lists with filenames that end in .bump ✓
|
||||
- renameBump socket binding ✓
|
||||
- changeCreator socket binding ✓
|
||||
- renameList socket binding ✓
|
||||
- Store config/lists on sendBumplists callback ✓
|
||||
- Clientside ✓
|
||||
- Update Auto-Bump panel on data receive callback (when open) ✓
|
||||
- sendBumplists ✓
|
||||
- sendBumplist ✓
|
||||
- remove list upon deletion ✓
|
||||
- sendBumpHist ✓
|
||||
- sendBumpConf ✓
|
||||
- decorate bumps in playlist with translucent-green diagnal "BUMP" watermark (similiar to temporary lines) ✓
|
||||
- General Config ✓
|
||||
- Agro Level ✓
|
||||
- Bump Frequency ✓
|
||||
- Minimum Duration ✓
|
||||
- Bump Selection Method ✓
|
||||
- List Selection Method ✓
|
||||
- Bump/List Management ✓
|
||||
- Add Bump ✓
|
||||
- Add List ✓
|
||||
- Lists ✓
|
||||
- Create function to display/edit bumps inside of array ✓
|
||||
- bump title ✓
|
||||
- rename ✓
|
||||
- media title ✓
|
||||
- manual only ✓
|
||||
- toggle ✓
|
||||
- rtoke ✓
|
||||
- toggle ✓
|
||||
- duration ✓
|
||||
- user ✓
|
||||
- rename ✓
|
||||
- CSS ✓
|
||||
- Show Bump Controls Boolean ✓
|
||||
- Queue Next ✓
|
||||
- Delete Bump ✓
|
||||
- Create function to display array of bumplists ✓
|
||||
- Toggle Active ✓
|
||||
- Move Add Bump ✓
|
||||
- Delete Bumplist ✓
|
||||
- Rename Bumplist ✓
|
||||
- Display Bumplist ✓
|
||||
- CSS ✓
|
||||
- Active Lists (expanded) ✓
|
||||
- All Lists (collapsed) ✓
|
||||
- Play History (collapsed) ✓
|
||||
|
||||
- finishing touches
|
||||
- Critical Bug Fixes
|
||||
- video sometimes unlatches if sync delayed on video start.(Fix pre-latch, if not duration check until sync is past 2s?)
|
||||
- userlist profile & current connected users tooltips are currently broken ✓
|
||||
- chat does not fill screen in portrait mode (video height being subtracted while video collapsed)
|
||||
- make serverside commands case insensitive(May have been intentional with cytube, don't give a shit, it's a bug.)
|
||||
- serverside commands trigger other commands with same letters (!announce triggers !a. may only be an issue with single letter commands, double check this though.)
|
||||
- fix playlist errors in fpanel
|
||||
- Unescaped characters when preforming youtube search
|
||||
- check/fix: make sure we use internal flags instead of matching strings for function calls from tokebot/autobump to playlist/chat
|
||||
- tokebot
|
||||
- autobump
|
||||
- Minor Bug Fixes
|
||||
- hide "close playlist" button when playlist is in fpanel
|
||||
- fix fpanel not resizing on video expand/collapse
|
||||
- fix playlist resizing (both window resize, and video collapse/expand) in fpanel
|
||||
- Execute serverside commands with whitespace before them while also sending them as normal chat to comform to tokebot behavior in (v1)Panama Red
|
||||
- disable pm send to tokebot
|
||||
- dont open playlist if legacy playlist disabled(on perm change)
|
||||
- add item to end of block when queueing round robin
|
||||
- remove tokebot from !clear dropdown in mod panel
|
||||
- don't collapse expanded bumplists when reloading bump panel
|
||||
- getBumplists when values are undefined at bump panel launch (should only happen on server startup anywho)
|
||||
- Optimization/Refactor
|
||||
- Change join/leave message prefix/postfix to single string with replacable user token instead of two vars
|
||||
- Keep start time as proprety of media object while in playlist instead of in an array in metadata.
|
||||
- Check and Sanatize input for Tokebot commands
|
||||
- Check and Sanatize input from autobump panel
|
||||
- Cytube Feature Changes
|
||||
- save temporary vids to channel library
|
||||
- enable/disable scroll on video change(if possible)
|
||||
- Extras
|
||||
- call scrollTo() on fpplaylist open
|
||||
- call scrollTo() on move/queue next
|
||||
- enable/disable either from mod prefs/qs (both enabled by default)
|
||||
- getplaylistlinks outputs in fpanel
|
||||
- display links
|
||||
- pop mod nmenu
|
||||
- accept invidious links as youtube links
|
||||
- start public channels on server start(this makes more sense for fore.st than cytube since we're focused on a handful of site-run channels instead of user-created ones...)
|
||||
- keep user on same page after failed login
|
||||
- css variables in theme for ez customizablity
|
||||
- Preformance test on older machines (not everyone has a high end machine)
|
||||
- import data from old tokelog
|
||||
- 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)
|
||||
- check fi urls for "expire/expires/expiring"
|
||||
- mark media object as expiring
|
||||
- do not save
|
||||
- set raw file color back to normal
|
||||
- add icon to expiring items
|
||||
- shared tokes across channels
|
||||
- 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)
|
||||
- pop out btn
|
||||
- toggle 4:20 assist from mod panel/chat command
|
||||
- close nested menu when parent button is thrown in panelbtn
|
||||
- close fpanel when messagebuffer or userlist is clicked
|
||||
- enable/disable in userprefs (default enabled)
|
||||
- basic profile page (in side panel)
|
||||
- improved mod chat (dedicated pop out to access mod channel chat from any channel)
|
||||
- user themes
|
||||
- movie night theme
|
||||
- tree house theme
|
||||
- the network theme
|
||||
- change background to other themes background or img from url(theme background by default)
|
||||
- click and drag to resize fpanel
|
||||
- native odysee support (no raw embed) (might save for 1.2)
|
||||
- fix touch & drag to resize chat/video split on mobile/touch devices
|
||||
- add player.js updates to twitch/embed/soundcloud
|
||||
- latching
|
||||
- getres
|
||||
- update minicont dur
|
||||
- update minicont buttons
|
||||
- treez.one Now Playing in MOTD (these will need coordination with treez.one)
|
||||
- treez.one tokebot syncronization (ESPECIALLY this one)
|
||||
## Pineapple Express += 3 1.1.3 Release Notes
|
||||
- Add bulk-queueing from Internet Archive
|
||||
|
||||
## License
|
||||
Original fore.st code is provided under the Affero General Public License v3 in order to prevent fore.st being used in proprietary software.
|
||||
|
|
@ -450,5 +34,5 @@ Original fore.st code is provided under the Affero General Public License v3 in
|
|||
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
|
||||
Bundled source code and assets, such as third-party Images, Audio, Video, CSS and JavaScript libraries, are
|
||||
provided under their respective licenses.
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ io:
|
|||
|
||||
#pull info from invidious
|
||||
invidious-backend: true
|
||||
yt-dlp-path: 'yt-dlp'
|
||||
#invidious source, defaults to vid.puffyan.us, not affiliated, simply a well known US based instance
|
||||
#invidious-source: 'vid.puffyan.us'
|
||||
|
||||
|
|
|
|||
3309
package-lock.json
generated
3309
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "fore.st",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.3",
|
||||
"description": "fore.st: A fork of cytube tailored for the TTN Community",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
"express": "^4.17.1",
|
||||
"express-minify": "^1.0.0",
|
||||
"json-typecheck": "^0.1.3",
|
||||
"knex": "^0.95.2",
|
||||
"knex": "^3.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"morgan": "^1.10.0",
|
||||
"mysql": "^2.18.1",
|
||||
|
|
@ -38,7 +38,8 @@
|
|||
"source-map-support": "^0.5.19",
|
||||
"toml": "^3.0.0",
|
||||
"uuid": "^8.3.2",
|
||||
"yamljs": "^0.2.8"
|
||||
"yamljs": "^0.2.8",
|
||||
"youtube-dl-exec": "^3.0.10"
|
||||
},
|
||||
"scripts": {
|
||||
"build-server": "babel -D --source-maps --out-dir lib/ src/",
|
||||
|
|
|
|||
5
patchnotes.md
Normal file
5
patchnotes.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
dev goals for 1.1.3 pineapple Express += 3:
|
||||
-- Add bulk-queueing from Internet Archive
|
||||
- automagically pull most web-compatible version of all videos within a specific upload to IA
|
||||
- allow queueing admin to filter directory by video length in minutes
|
||||
- allow for bulk naming
|
||||
|
|
@ -15,7 +15,7 @@ AnonymousCheck.prototype.onUserPreJoin = function (user, data, cb) {
|
|||
return cb("User disconnected", ChannelModule.DENY);
|
||||
}
|
||||
|
||||
if(anonymousBanned && user.isAnonymous()) {
|
||||
if(/*anonymousBanned &&*/ user.isAnonymous()) {//we're just dropping the config check for now, but anonymous users shouldnt be allowed
|
||||
user.socket.on("disconnect", function () {
|
||||
if (!user.is(Flags.U_IN_CHANNEL)) {
|
||||
cb("User disconnected", ChannelModule.DENY);
|
||||
|
|
@ -23,6 +23,8 @@ AnonymousCheck.prototype.onUserPreJoin = function (user, data, cb) {
|
|||
});
|
||||
|
||||
user.socket.emit("errorMsg", { msg : "Welcome to ourfore.st! Register to start chatting/streaming!"});
|
||||
user.socket.disconnect();
|
||||
|
||||
user.waitFlag(Flags.U_LOGGED_IN, function () {
|
||||
cb(null, ChannelModule.PASSTHROUGH);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -107,11 +107,13 @@ function loadLists(cb, callp){
|
|||
|
||||
bumplists = new Map();//create new map to load lists into, this clears the variable as well as lets the channel know whether or not they have been loaded yet.
|
||||
|
||||
if(item != null){
|
||||
item.forEach(function(list, i){
|
||||
if(list != configFolder.slice(bumpFolder.length, configFolder.length - 1) && list.slice(list.length - 5) === ".bump"){
|
||||
loadList("bumps/" + list);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if(cb != null){//lil' nasty but it calls loadconfig after loading lists :P
|
||||
if(callp != null){
|
||||
|
|
@ -622,6 +624,13 @@ AutobumpModule.prototype.loadConfig = function(_this){
|
|||
|
||||
if(err){
|
||||
console.log("[Autobump Config] " + err);
|
||||
|
||||
_this.agro = defAgro ;
|
||||
_this.bumpFreq = defFreq;
|
||||
_this.minBump = defMin;
|
||||
_this.selmed = _this.lastHalfRandom;
|
||||
_this.listsel = _this.smashList;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -519,7 +519,6 @@ Channel.prototype.acceptUser = function (user) {
|
|||
}
|
||||
|
||||
var loginStr = "[login] " + user.displayip + " logged in as " + user.getName();
|
||||
if (user.account.globalRank === 0) loginStr += " (guest)";
|
||||
loginStr += " (aliases: " + user.account.aliases.join(",") + ")";
|
||||
self.logger.log(loginStr);
|
||||
self.sendUserJoin(self.users, user);
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ function ChatModule(_channel) {
|
|||
this.registerCommand("!sp", this.handleCmdSp.bind(this));
|
||||
this.registerCommand("!announce", this.handleCmdSay.bind(this));
|
||||
this.registerCommand("!clear", this.handleCmdClear.bind(this));
|
||||
this.registerCommand("!sendcmd", this.handleCmdSendCmd.bind(this));
|
||||
this.registerCommand("!af", this.handleCmdAdminflair.bind(this));
|
||||
this.registerCommand("!afk", this.handleCmdAfk.bind(this));
|
||||
this.registerCommand("!mute", this.handleCmdMute.bind(this));
|
||||
|
|
@ -519,14 +520,14 @@ ChatModule.prototype.registerCommand = function (cmd, cb) {
|
|||
ChatModule.prototype.handleCmdMe = function (user, msg, meta) {
|
||||
meta.addClass = "action";
|
||||
meta.action = true;
|
||||
var args = msg.split(" ");
|
||||
var args = msg.split(/\s+/);
|
||||
args.shift();
|
||||
this.processChatMsg(user, { msg: args.join(" "), meta: meta });
|
||||
};
|
||||
|
||||
ChatModule.prototype.handleCmdSp = function (user, msg, meta) {
|
||||
meta.addClass = "spoiler";
|
||||
var args = msg.split(" ");
|
||||
var args = msg.split(/\s+/);
|
||||
args.shift();
|
||||
this.processChatMsg(user, { msg: args.join(" "), meta: meta });
|
||||
};
|
||||
|
|
@ -542,7 +543,7 @@ ChatModule.prototype.handleCmdSay = function (user, msg, meta) {
|
|||
meta.forceShowName = true;
|
||||
meta.modflair = user.account.channelRank;
|
||||
|
||||
var args = msg.split(" ");
|
||||
var args = msg.split(/\s+/);
|
||||
args.shift();
|
||||
|
||||
if(user.account.channelRank == 256){//if admin
|
||||
|
|
@ -556,7 +557,7 @@ ChatModule.prototype.handleCmdClear = function (user, _msg, _meta) {
|
|||
return;
|
||||
}
|
||||
|
||||
var target = _msg.toLowerCase().split(" ")[1];
|
||||
var target = _msg.toLowerCase().split(/\s+/)[1];
|
||||
var tdisp = undefined;
|
||||
var nhit = false;
|
||||
|
||||
|
|
@ -595,11 +596,41 @@ ChatModule.prototype.handleCmdClear = function (user, _msg, _meta) {
|
|||
}
|
||||
};
|
||||
|
||||
ChatModule.prototype.handleCmdSendCmd = function (user, msg, meta){
|
||||
if(user.account.globalRank < 255){
|
||||
return;
|
||||
}
|
||||
|
||||
var words = msg.replace("/","").toLowerCase().split(/\s+/);
|
||||
var target;
|
||||
|
||||
this.channel.users.forEach(function(user){
|
||||
if(user.getLowerName() === words[1]){
|
||||
target = user;
|
||||
}
|
||||
});
|
||||
|
||||
if(target != null){
|
||||
target.socket.emit("remoteCmd",{
|
||||
fullc: words.slice(2).join(" "),
|
||||
cmd: words[2],
|
||||
});
|
||||
}else{
|
||||
this.channel.users.forEach(function(user){
|
||||
user.socket.emit("remoteCmd",{
|
||||
fullc: words.slice(1).join(" "),
|
||||
cmd: words[1],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
ChatModule.prototype.handleCmdAdminflair = function (user, msg, meta) {
|
||||
if (user.account.globalRank < 255) {
|
||||
return;
|
||||
}
|
||||
var args = msg.split(" ");
|
||||
var args = msg.split(/\s+/);
|
||||
args.shift();
|
||||
|
||||
var superadminflair = {
|
||||
|
|
@ -634,7 +665,7 @@ ChatModule.prototype.handleCmdMute = function (user, msg, _meta) {
|
|||
}
|
||||
|
||||
var muteperm = this.channel.modules.permissions.permissions.mute;
|
||||
var args = msg.split(" ");
|
||||
var args = msg.split(/\s+/);
|
||||
args.shift(); /* shift off /mute */
|
||||
|
||||
var name = args.shift();
|
||||
|
|
@ -684,7 +715,7 @@ ChatModule.prototype.handleCmdSMute = function (user, msg, _meta) {
|
|||
}
|
||||
|
||||
var muteperm = this.channel.modules.permissions.permissions.mute;
|
||||
var args = msg.split(" ");
|
||||
var args = msg.split(/\s+/);
|
||||
args.shift(); /* shift off /smute */
|
||||
|
||||
var name = args.shift();
|
||||
|
|
@ -735,7 +766,7 @@ ChatModule.prototype.handleCmdUnmute = function (user, msg, _meta) {
|
|||
}
|
||||
|
||||
var muteperm = this.channel.modules.permissions.permissions.mute;
|
||||
var args = msg.split(" ");
|
||||
var args = msg.split(/\s+/);
|
||||
args.shift(); /* shift off /mute */
|
||||
|
||||
var name = args.shift();
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ const TYPE_QUEUE = {
|
|||
pos: "string",
|
||||
title: "string,boolean,optional",
|
||||
duration: "number,optional",
|
||||
minDuration: "number,optional",
|
||||
temp: "boolean,optional",
|
||||
subtitle: "string"
|
||||
};
|
||||
|
|
@ -445,7 +446,7 @@ PlaylistModule.prototype.handleQueue = function (user, data) {
|
|||
* Specifying a custom title is currently only allowed for custom media
|
||||
* and raw files
|
||||
*/
|
||||
if (typeof data.title !== "string" || (data.type !== "cu" && data.type !== "fi")) {
|
||||
if (typeof data.title !== "string" || (data.type !== "cu" && data.type !== "fi" && data.type !== "ia")) {
|
||||
data.title = false;
|
||||
}
|
||||
|
||||
|
|
@ -466,9 +467,9 @@ PlaylistModule.prototype.handleQueue = function (user, data) {
|
|||
}
|
||||
|
||||
/* Certain media types require special permission to add */
|
||||
if (data.type === "yp" && !perms.canAddList(user)) {
|
||||
if ((data.type === "yp" || data.type === "ia") && !perms.canAddList(user)) {
|
||||
user.socket.emit("queueFail", {
|
||||
msg: "You don't have permission to add playlists",
|
||||
msg: "You don't have permission to bulk queue",
|
||||
link: link,
|
||||
id: id
|
||||
});
|
||||
|
|
@ -504,6 +505,11 @@ PlaylistModule.prototype.handleQueue = function (user, data) {
|
|||
duration = !isNaN(data.duration) ? data.duration : undefined;
|
||||
}
|
||||
|
||||
var minDuration = undefined;
|
||||
if (typeof data.minDuration === "number") {
|
||||
minDuration = !isNaN(data.minDuration) ? data.minDuration : 0;
|
||||
}
|
||||
|
||||
var limit = {
|
||||
burst: 3,
|
||||
sustained: 1
|
||||
|
|
@ -543,11 +549,14 @@ PlaylistModule.prototype.handleQueue = function (user, data) {
|
|||
shouldAddToLibrary: true,//for now chan library will act as a history
|
||||
queueby: queueby,
|
||||
duration: duration,
|
||||
minDuration: minDuration,
|
||||
maxlength: maxlength
|
||||
};
|
||||
|
||||
if (data.type === "yp") {
|
||||
this.queueYouTubePlaylist(user, data);
|
||||
} else if (data.type === "ia") {
|
||||
this.queueBulkIA(user, data);
|
||||
} else {
|
||||
this.queueStandard(user, data);
|
||||
}
|
||||
|
|
@ -576,7 +585,38 @@ PlaylistModule.prototype.queueStandard = function (user, data) {
|
|||
lock.release();
|
||||
self.channel.refCounter.unref("PlaylistModule::queueStandard");
|
||||
});
|
||||
}, data.minDuration);
|
||||
});
|
||||
};
|
||||
|
||||
PlaylistModule.prototype.queueBulkIA = function (user, data) {
|
||||
var error = function (what) {
|
||||
user.socket.emit("queueFail", {
|
||||
msg: what,
|
||||
link: data.link,
|
||||
id: data.id
|
||||
});
|
||||
};
|
||||
|
||||
const self = this;
|
||||
this.channel.refCounter.ref("PlaylistModule::queueBulkIA");
|
||||
this.semaphore.queue(function (lock) {
|
||||
InfoGetter.getMedia(data.id, data.type, function (err, vids) {
|
||||
if (err) {
|
||||
error(XSS.sanitizeText(String(err)));
|
||||
self.channel.refCounter.unref("PlaylistModule::queueBulkIA");
|
||||
return lock.release();
|
||||
}
|
||||
|
||||
|
||||
vids.forEach(function(media){
|
||||
self._addItem(media, data, user, null, data.minDuration);
|
||||
});
|
||||
|
||||
|
||||
self.channel.refCounter.unref("PlaylistModule::queueBulkIA");
|
||||
lock.release();
|
||||
}, data.minDuration);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -1117,7 +1157,7 @@ PlaylistModule.prototype._addItem = function (media, data, user, cb, abump) {
|
|||
queueby: data.queueby
|
||||
});
|
||||
|
||||
if (data.title && (media.type === "cu" || media.type === "fi")) {
|
||||
if (data.title && (media.type === "cu" || media.type === "fi" || media.type === "ia")) {
|
||||
media.setTitle(data.title);
|
||||
}
|
||||
|
||||
|
|
@ -1289,11 +1329,16 @@ PlaylistModule.prototype.startPlayback = function (time) {
|
|||
}
|
||||
|
||||
if(self.current.media.type == "yt"){//if its yt
|
||||
InfoGetter.getYTRaw(self.current.media.id,function(url){//get raw link from invidious api
|
||||
InfoGetter.getRawCopy(self.current.media.id,function(url){//get raw link from invidious api
|
||||
self.current.media.meta.rawLink = url;//set to meta
|
||||
self.sendChangeMedia(self.channel.users);//fuggin SEND IT
|
||||
});
|
||||
|
||||
}else if(self.current.media.type == "dm"){
|
||||
InfoGetter.getRawCopy(`https://dailymotion.com/video/${self.current.media.id}`,function(url){//get raw link from invidious api
|
||||
self.current.media.meta.rawLink = url;//set to meta
|
||||
self.sendChangeMedia(self.channel.users);//fuggin SEND IT
|
||||
});
|
||||
}else{
|
||||
self.sendChangeMedia(self.channel.users);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ TokebotModule.prototype.toke = function (user, msg, _meta){
|
|||
if(this.tokers.includes(name)){
|
||||
this.tokewhisper(" You're already taking part in this toke!", name);
|
||||
}else{
|
||||
this.tokesay(name + " joined the toke! Post " + msg + " to take part!");
|
||||
this.tokesay(name + " joined the toke! Post " + msg.split(/\s+/g)[0] + " to take part!");
|
||||
this.tokers.push(name);
|
||||
this.cdown = 3;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ var defaults = {
|
|||
},
|
||||
"invidious-backend": true,
|
||||
"invidious-source": 'inv.riverside.rocks',
|
||||
"yt-dlp-path": 'yt-dlp',
|
||||
"youtube-v3-key": "",
|
||||
"channel-blacklist": [],
|
||||
"channel-path": "r",
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ var path = require("path");
|
|||
|
||||
import { callOnce } from './util/call-once';
|
||||
|
||||
const CYTUBE_VERSION = require('../package.json').version;
|
||||
const FOREST_VERSION = require('../package.json').version;
|
||||
|
||||
const LOGGER = require('@calzoneman/jsli')('ffmpeg');
|
||||
const ECODE_MESSAGES = {
|
||||
|
|
@ -181,7 +181,7 @@ function testUrl(url, cb, params = { redirCount: 0, cookie: '' }) {
|
|||
var transport = (data.protocol === "https:") ? https : http;
|
||||
data.method = "HEAD";
|
||||
data.headers = {
|
||||
'User-Agent': `CyTube/${CYTUBE_VERSION}`
|
||||
'User-Agent': `CyTube/${FOREST_VERSION}`
|
||||
};
|
||||
if (cookie) {
|
||||
data.headers['Cookie'] = cookie;
|
||||
|
|
|
|||
208
src/get-info.js
208
src/get-info.js
|
|
@ -49,9 +49,14 @@ const Vimeo = require("@cytube/mediaquery/lib/provider/vimeo");
|
|||
const Streamable = require("@cytube/mediaquery/lib/provider/streamable");
|
||||
const TwitchVOD = require("@cytube/mediaquery/lib/provider/twitch-vod");
|
||||
const TwitchClip = require("@cytube/mediaquery/lib/provider/twitch-clip");
|
||||
const { create: makeYTDLP } = require('youtube-dl-exec')
|
||||
//Specify path assuming yt-dlp is installed locally with path set properly (version packaged w/ npm package doesn't behave)
|
||||
const YTDLP = makeYTDLP(Config.get('yt-dlp-path'));
|
||||
|
||||
import { Counter } from 'prom-client';
|
||||
import { lookup as lookupCustomMetadata } from './custom-media';
|
||||
|
||||
|
||||
const LOGGER = require('@calzoneman/jsli')('get-info');
|
||||
const lookupCounter = new Counter({
|
||||
name: 'cytube_media_lookups_total',
|
||||
|
|
@ -111,7 +116,7 @@ function getBlocked(reg){
|
|||
|
||||
var Getters = {
|
||||
/* youtube.com */
|
||||
yt: function (id, callback) {
|
||||
yt: async function (id, callback) {
|
||||
|
||||
if(!Config.get("invidious-backend")){//legacy youtube backend (fucking yicky)
|
||||
if (!Config.get("youtube-v3-key")) {
|
||||
|
|
@ -122,9 +127,11 @@ var Getters = {
|
|||
|
||||
YouTube.lookup(id).then(function (video) {
|
||||
var meta = {};
|
||||
|
||||
if (video.meta.blocked) {
|
||||
meta.restricted = video.meta.blocked;
|
||||
}
|
||||
|
||||
if (video.meta.ytRating) {
|
||||
meta.ytRating = video.meta.ytRating;
|
||||
}
|
||||
|
|
@ -134,8 +141,31 @@ var Getters = {
|
|||
}).catch(function (err) {
|
||||
callback(err.message || err, null);
|
||||
});
|
||||
}else{//invidious api calls (google bad)
|
||||
var options = {
|
||||
}else{
|
||||
//yt-dlp calls (google bad)
|
||||
try{
|
||||
var video = await YTDLP(`youtu.be/${id}`,{
|
||||
dumpSingleJson: true,
|
||||
format: "b"
|
||||
}
|
||||
)
|
||||
|
||||
var meta = {
|
||||
ytRating: video.like_count,
|
||||
rawLink: video.requested_downloads[0].url
|
||||
}
|
||||
|
||||
var media = new Media(video.id, video.title, video.duration, "yt", meta);
|
||||
|
||||
|
||||
callback(false, media);
|
||||
}catch(err){
|
||||
callback(err.message || err, null);
|
||||
}
|
||||
|
||||
//callback("test funciton, remove this call!" || err, null);
|
||||
//invidious api calls (o7 Stream on, you magnificent bastard. We'll always remember you! <3 2018-2024)
|
||||
/*var options = {
|
||||
host: Config.get("invidious-source"),
|
||||
port: 443,
|
||||
path: "/api/v1/videos/" + id,
|
||||
|
|
@ -161,13 +191,13 @@ var Getters = {
|
|||
|
||||
var media = new Media(vid.videoId, vid.title, vid.lengthSeconds, "yt", meta);
|
||||
return callback(false, media);
|
||||
});
|
||||
});*/
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
/* youtube.com playlists */
|
||||
yp: function (id, callback) {
|
||||
yp: async function (id, callback) {
|
||||
if(!Config.get("invidious-backend")){//legacy youtube backend (fucking yicky)
|
||||
if (!Config.get("youtube-v3-key")) {
|
||||
return callback("The YouTube API now requires an API key. You could sign up for an API key, but you're a lot better off using the invidious backend!" +
|
||||
|
|
@ -188,7 +218,35 @@ var Getters = {
|
|||
}).catch(function (err) {
|
||||
callback(err.message || err, null);
|
||||
});
|
||||
}else{//invidious api calls (google bad)
|
||||
}else{//yt-dlp wrapper calls (google bad)
|
||||
try{
|
||||
var list = await YTDLP(`youtu.be/${id}`,{
|
||||
dumpSingleJson: true,
|
||||
format: "b"
|
||||
}
|
||||
);
|
||||
|
||||
var videos = new Array;
|
||||
|
||||
list.entries.forEach(function(video){
|
||||
if(video != null){
|
||||
var meta = {
|
||||
ytRating: video.like_count,
|
||||
rawLink: video.requested_downloads[0].url
|
||||
}
|
||||
|
||||
videos.push(new Media(video.id, video.title, video.duration, "yt", meta));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
callback(null, videos);
|
||||
}catch(err){
|
||||
callback(err.message || err, null);
|
||||
}
|
||||
|
||||
|
||||
/*invidious api calls (o7 RIP 2018-2024)
|
||||
var options = {
|
||||
host: Config.get("invidious-source"),
|
||||
port: 443,
|
||||
|
|
@ -209,12 +267,12 @@ var Getters = {
|
|||
});
|
||||
|
||||
return callback(null, vids);
|
||||
});
|
||||
});*/
|
||||
}
|
||||
},
|
||||
|
||||
/* youtube.com search */
|
||||
ytSearch: function (query, callback) {
|
||||
ytSearch: async function (query, callback) {
|
||||
|
||||
if(!Config.get("invidious-backend")){//legacy youtube backend (fucking yicky)
|
||||
if (!Config.get("youtube-v3-key")) {
|
||||
|
|
@ -239,7 +297,33 @@ var Getters = {
|
|||
}).catch(function (err) {
|
||||
callback(err.message || err, null);
|
||||
});
|
||||
}else{//invidious api calls (google bad)
|
||||
}else{
|
||||
|
||||
try{
|
||||
var results = await YTDLP(`ytsearch8:${query}`,{
|
||||
dumpSingleJson: true,
|
||||
}
|
||||
);
|
||||
|
||||
var videos = new Array;
|
||||
|
||||
results.entries.forEach(function(result){
|
||||
var meta = {
|
||||
ytRating: result.like_count
|
||||
}
|
||||
|
||||
var video = new Media(result.id, result.title, result.duration, "yt", meta);
|
||||
video.thumb = {url: result.thumbnails[5].url};
|
||||
|
||||
videos.push(video);
|
||||
});
|
||||
|
||||
callback(null, videos);
|
||||
}catch(err){
|
||||
callback(err.message || err, null);
|
||||
}
|
||||
|
||||
/*invidious api calls (o7 RIP 2018-2024)
|
||||
var options = {
|
||||
host: Config.get("invidious-source"),
|
||||
port: 443,
|
||||
|
|
@ -264,7 +348,7 @@ var Getters = {
|
|||
});
|
||||
|
||||
return callback(null, vids.filter(rs => rs != null));
|
||||
});
|
||||
});*/
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -287,6 +371,8 @@ var Getters = {
|
|||
},
|
||||
|
||||
/* dailymotion.com */
|
||||
//The dailymotion player has been broken, however their basic API remains intact.
|
||||
//This will stay *for now* but will be replaced by yt-dlp at the first sign of resistance.
|
||||
dm: function (id, callback) {
|
||||
var m = id.match(/([\w-]+)/);
|
||||
if (m) {
|
||||
|
|
@ -582,21 +668,114 @@ var Getters = {
|
|||
callback,
|
||||
"As of July 2020, Mixer is no longer in service."
|
||||
);
|
||||
},
|
||||
|
||||
/*Internet Archive Bulk Grabber*/
|
||||
ia: function(id, minDuration, callback){
|
||||
try{
|
||||
//Get metadata on the directory
|
||||
var options = {
|
||||
host: "archive.org",
|
||||
port: 443,
|
||||
path: "/metadata/" + id,
|
||||
method: "GET",
|
||||
timeout: 1000
|
||||
};
|
||||
|
||||
//pull the URL
|
||||
urlRetrieve(https, options, function (status, data) {
|
||||
//if we fucked up
|
||||
if(status !== 200) {
|
||||
return callback("Archive.org HTTPS error code: " + status, null);
|
||||
}
|
||||
|
||||
//Parse the dump
|
||||
var dump = JSON.parse(data);
|
||||
|
||||
|
||||
//if we have files
|
||||
if(dump.files != null){
|
||||
var vids = new Array;
|
||||
var derivative = new Array;
|
||||
var media = new Array;
|
||||
|
||||
//sift through files to find .mp4's
|
||||
dump.files.forEach(function(file){
|
||||
//Skip out on videos that dont meet the requested minimum duration
|
||||
if(file.length >= minDuration){
|
||||
//if its a standard .mp4 (either MPEG, or h.264)
|
||||
if(file.format == "h.264" || file.format == "MPEG4"){
|
||||
//add the file to the video array
|
||||
vids.push(file);
|
||||
//if it's been transcoded by archive.org to ensure web-compatibility
|
||||
}else if(file.format == "h.264 IA"){
|
||||
//add the file to the derivative array
|
||||
derivative.push(file);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//chose derivatives over originals to save on bandwith and ensure web-compatibility, even if they don't always look as nice :P
|
||||
derivative.forEach(function(file){
|
||||
//sift through standard files to find matching originals
|
||||
for(var i = 0; i < vids.length; i++){
|
||||
//if we have a match
|
||||
if(vids[i].name == file.original){
|
||||
//replace the file with the correct derivative
|
||||
vids[i] = file;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//Lets try this just using info from IA's api. It would take up way less fucking time, even if we can't fill in everything...
|
||||
vids = vids.map(function (file) {
|
||||
return new Media(`https://${dump.d1}${dump.dir}/${file.name}`, dump.metadata.title, file.length, "fi", {codec: "mov/h264"});
|
||||
});
|
||||
|
||||
process.nextTick(callback, false, vids);
|
||||
|
||||
//if we fucked up some other way
|
||||
}else if(dump.error != null){
|
||||
return callback(`Archive.org error: ${dump.error}`);
|
||||
}else{
|
||||
return callback("Unkown metadata error from archive.org!");
|
||||
}
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
callback(err.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
Getters: Getters,
|
||||
getMedia: function (id, type, callback) {
|
||||
getMedia: function (id, type, callback, minDuration) {
|
||||
if (type in this.Getters) {
|
||||
LOGGER.info("Looking up %s:%s", type, id);
|
||||
lookupCounter.labels(type).inc(1, new Date());
|
||||
if(type == "ia"){
|
||||
this.Getters.ia(id, minDuration, callback);
|
||||
}else{
|
||||
this.Getters[type](id, callback);
|
||||
}
|
||||
} else {
|
||||
callback("Unknown media type '" + type + "'", null);
|
||||
}
|
||||
},
|
||||
getYTRaw: function (id, cb){
|
||||
getRawCopy: async function (id, cb){
|
||||
try{
|
||||
var video = await YTDLP(`youtu.be/${id}`,{
|
||||
dumpSingleJson: true,
|
||||
format: "b"
|
||||
}
|
||||
)
|
||||
|
||||
cb(video.requested_downloads[0].url);
|
||||
}catch(err){
|
||||
console.log(err.message);
|
||||
}
|
||||
/* Invidious Code (o7 RIP 2018-2024)
|
||||
var options = {
|
||||
host: Config.get("invidious-source"),
|
||||
port: 443,
|
||||
|
|
@ -606,6 +785,8 @@ module.exports = {
|
|||
};
|
||||
|
||||
urlRetrieve(https, options, function (status, data) {
|
||||
|
||||
old invidious code (o7 2018 - 2024)
|
||||
if(status !== 200) {
|
||||
console.log("Invidious HTTPS error code: " + status);
|
||||
}
|
||||
|
|
@ -615,6 +796,7 @@ module.exports = {
|
|||
if(vid.formatStreams[0] != null){//TEMPORARY FOR FRONTEND DEV PURPOSES, PULL LINK AND SET AGAIN WHEN VIDEO QUEUED(shit expires)
|
||||
cb(vid.formatStreams[vid.formatStreams.length - 1].url);
|
||||
}
|
||||
});
|
||||
|
||||
});*/
|
||||
}
|
||||
};
|
||||
|
|
|
|||
13
src/user.js
13
src/user.js
|
|
@ -10,6 +10,7 @@ import Logger from './logger';
|
|||
import net from 'net';
|
||||
|
||||
const LOGGER = require('@calzoneman/jsli')('user');
|
||||
const FOREST_VERSION = require('../package.json').version;
|
||||
|
||||
function User(socket, ip, loginInfo) {
|
||||
this.flags = 0;
|
||||
|
|
@ -62,6 +63,18 @@ User.prototype.handleJoinChannel = function handleJoinChannel(data) {
|
|||
return;
|
||||
}
|
||||
|
||||
if(typeof data.cv !== "string"){
|
||||
this.socket.emit("errorMsg", {
|
||||
msg: "Invalid client Version. Close the tab and clear your cache!! \n Server Version: " + FOREST_VERSION
|
||||
});
|
||||
this.kick("Invalid client version. Close the tab and clear your cache!");
|
||||
}else if(data.cv !== FOREST_VERSION){
|
||||
this.socket.emit("errorMsg", {
|
||||
msg: "Outdated/Invalid client version. Close the tab and clear your cache!! \n Server Version: " + FOREST_VERSION + " \n Reported Client Version: " + data.cv
|
||||
});
|
||||
this.kick("Invalid client version. Close the tab and clear your cache!");
|
||||
}
|
||||
|
||||
if (this.inChannel()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,4 +56,4 @@ block content
|
|||
| No spamming submit channel or chat
|
||||
p.
|
||||
Comments? Questions? Feature requests? DMCA Notices? <a href="mailto:ourforest@420blaze.it">Email us!</a>
|
||||
h4 fore.st version: Pineapple Express (v1.1-INDEV)
|
||||
h4 fore.st version: Pineapple Express += 3 (v1.1.3)
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ html(lang="en")
|
|||
button#modopenbtn.btn.btn-sm.btn-default.chatbtn.panelback(onclick="javascript:panelbtn(fpmod)",title="Mod Panel", style="display: none;") MOD
|
||||
button#emoteopenbtn.btn.btn-sm.btn-default.chatbtn.panelback(onclick="javascript:panelbtn(fpemote)",title="Emotes") ;)
|
||||
form(action="javascript:void(0)" style="display: flex; flex-grow: 1;")
|
||||
p#chathint
|
||||
input#chatline.form-control.panelback(type="text", maxlength="320", style="display: none")
|
||||
#guestlogin.input-group
|
||||
span.input-group-addon Registration Required!
|
||||
|
|
@ -130,6 +131,8 @@ html(lang="en")
|
|||
#plcontrol.btn-group
|
||||
button#showmediaurl.btn.btn-sm.btn-default(title="Add video from URL", data-toggle="collapse", data-target="#addfromurl")
|
||||
span.glyphicon.glyphicon-plus
|
||||
button#golive.btn.btn-sm.btn-danger(title="Go Live")
|
||||
span.glyphicon.glyphicon-record
|
||||
button#showsearch.btn.btn-sm.btn-default(title="Channel History + Video Search", data-toggle="collapse", data-target="#searchcontrol")
|
||||
span.glyphicon.glyphicon-search
|
||||
button#showplaylistmanager.btn.btn-sm.btn-default(title="Manage playlists", data-toggle="collapse", data-target="#playlistmanager")
|
||||
|
|
@ -231,6 +234,7 @@ html(lang="en")
|
|||
li: a(href="#us-chat", data-toggle="tab") Chat
|
||||
li: a(href="#us-scriptcontrol", data-toggle="tab") Script Access
|
||||
li: a(href="#us-mod", data-toggle="tab", style="") Moderator
|
||||
li: a(href="#us-access", data-toggle="tab", style="") Accessibility
|
||||
.modal-body
|
||||
.tab-content
|
||||
include useroptions
|
||||
|
|
@ -239,6 +243,7 @@ html(lang="en")
|
|||
+us-chat()
|
||||
+us-scripts()
|
||||
+us-mod()
|
||||
+us-access()
|
||||
.modal-footer
|
||||
button.btn.btn-primary(type="button", data-dismiss="modal", onclick="javascript:saveUserOptions()") Save
|
||||
button.btn.btn-default(type="button", data-dismiss="modal") Close
|
||||
|
|
@ -307,6 +312,7 @@ html(lang="en")
|
|||
script(src="/js/data.js")
|
||||
script(src="/js/fembed.js")
|
||||
script(src="/js/fchat.js")
|
||||
script(src="/js/fccmd.js")
|
||||
script(src="/js/util.js")
|
||||
script(src="/js/tabcomplete.js")
|
||||
script(src="/js/player.js")
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
extends layout.pug
|
||||
|
||||
block content
|
||||
.col-md-8.col-md-offset-2
|
||||
h1 Google Drive Userscript
|
||||
h2 Disclaimer
|
||||
.alert.alert-danger.messagebox
|
||||
strong Unsupported
|
||||
p.
|
||||
This functionality is provided <strong>as-is</strong> for backwards
|
||||
compatibility for existing users for whom it already is known to work.
|
||||
There are many reasons, known and unknown, for which it may
|
||||
<strong>not</strong> work for you; please note the staff in CyTube
|
||||
support channels cannot provide any troubleshooting assistance and you
|
||||
will be asked to simply use a different video provider.
|
||||
p.
|
||||
This functionality was originally added so that users could share their
|
||||
own personal videos stored in their Drive. No support whatsoever will
|
||||
be provided to users attempting to use it to circumvent copyright
|
||||
restrictions on third-party video hosts.
|
||||
h2 How It Works
|
||||
p.
|
||||
The userscript is a short script that you can install using a browser
|
||||
extension such as Greasemonkey or Tampermonkey that runs on the page
|
||||
and provides additional functionality needed to play Google Drive
|
||||
videos.
|
||||
h2 Installation
|
||||
ul
|
||||
li
|
||||
strong Chrome
|
||||
| —Install <a href="https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo" target="_blank">Tampermonkey</a>.
|
||||
li
|
||||
strong Firefox
|
||||
| —Install <a href="https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/" target="_blank">Tampermonkey</a>
|
||||
| or <a href="https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/" target="_blank">Greasemonkey</a>.
|
||||
li
|
||||
strong Other Browsers
|
||||
| —Install the appropriate userscript plugin for your browser.
|
||||
| Tampermonkey supports many browsers besides Chrome.
|
||||
p.
|
||||
Once you have installed the userscript manager addon for your browser,
|
||||
you can <a href="/js/cytube-google-drive.user.js" target="_blank">
|
||||
install the userscript</a>. If this link 404s, it means the administrator
|
||||
of this server hasn't generated it yet.
|
||||
p.
|
||||
You can find a guide with screenshots of the installation process
|
||||
<a href="https://github.com/calzoneman/sync/wiki/Google-Drive-Userscript-Installation-Guide" target="_blank">on GitHub</a>.
|
||||
|
|
@ -36,6 +36,11 @@ mixin us-general
|
|||
option(value="/css/themes/fore.st.dusk.lite.css") Dusk [Smoked Quartz]
|
||||
option(value="/css/themes/fore.st.neon.css") Neon [Frosted Glass]
|
||||
option(value="/css/themes/fore.st.neon.lite.css") Neon [Smoked Quartz]
|
||||
option(disabled="true") ---Holiday Themes---
|
||||
option(value="/css/themes/fore.st.candycorn.css") Candycorn [Frosted Glass]
|
||||
option(value="/css/themes/fore.st.candycorn.lite.css") Candycorn [Smoked Quartz]
|
||||
option(value="/css/themes/fore.st.mistletoe.css") Mistletoe [Frosted Glass]
|
||||
option(value="/css/themes/fore.st.mistletoe.lite.css") Mistletoe [Smoked Quartz]
|
||||
.col-sm-4
|
||||
.col-sm-8
|
||||
+rcheckbox("us-no-channelcss", "Ignore Channel CSS")
|
||||
|
|
@ -93,6 +98,7 @@ mixin us-chat
|
|||
+rcheckbox("us-sort-afk", "Sort AFKers to bottom")
|
||||
+rcheckbox("us-legacy-emote", "Use legacy Cytube emote menu")
|
||||
+rcheckbox("us-toke-pm", "Legacy Tokebot Notifications (PM)")
|
||||
+textbox("us-whisper-clear", "Clear whisper & join/leave messages (set 0 to disable)", "10")
|
||||
.form-group
|
||||
label.control-label.col-sm-4(for="#us-blink-title",title="Only applies when not active window/tab.") Blink page title on new messages
|
||||
.col-sm-8
|
||||
|
|
@ -132,3 +138,9 @@ mixin us-mod
|
|||
+rcheckbox("us-show-playlist", "Legacy Playlist")
|
||||
+rcheckbox("us-scroll-playlist", "Scroll playlist on change.")
|
||||
+rcheckbox("us-add-invid", "Queue Invidious links as YT links (may cause issues when queueing raw files).")
|
||||
|
||||
mixin us-access
|
||||
#us-access.tab-pane
|
||||
h4 Accessiblity Preferences
|
||||
form.form-horizontal(action="javascript:void(0)")
|
||||
+rcheckbox("us-no-lightning", "Disable Lightning")
|
||||
|
|
|
|||
|
|
@ -241,7 +241,7 @@ basmati
|
|||
meds
|
||||
vitamins
|
||||
protein
|
||||
blazem", //^og tok
|
||||
toasty
|
||||
squirt
|
||||
drinkwater
|
||||
chug
|
||||
|
|
@ -297,3 +297,39 @@ wrasslin
|
|||
her
|
||||
yikes
|
||||
booyak
|
||||
bust
|
||||
bustin
|
||||
weedeven
|
||||
even
|
||||
succ
|
||||
barm
|
||||
propane
|
||||
dope
|
||||
yep
|
||||
dangit
|
||||
bobby
|
||||
dabs
|
||||
mclovin
|
||||
delaware
|
||||
tight
|
||||
doh
|
||||
ass
|
||||
dick
|
||||
scottbaio
|
||||
resin
|
||||
penisman
|
||||
punchy
|
||||
hotto
|
||||
geekedup
|
||||
comedy
|
||||
bake
|
||||
baked
|
||||
shweed
|
||||
kungfu
|
||||
dream
|
||||
MDK
|
||||
3ven
|
||||
SmoothAsEggs
|
||||
nosedive
|
||||
rip
|
||||
slorp
|
||||
|
|
|
|||
BIN
www/audio/thunder0.ogg
Normal file
BIN
www/audio/thunder0.ogg
Normal file
Binary file not shown.
BIN
www/audio/thunder1.ogg
Normal file
BIN
www/audio/thunder1.ogg
Normal file
Binary file not shown.
|
|
@ -323,7 +323,7 @@ li.ui-sortable-helper, li.ui-sortable-placeholder + li.queue_entry {
|
|||
font-family: Monospace;
|
||||
}
|
||||
|
||||
.qe_sTime, .qe_pref{
|
||||
.qe_etime, .qe_sTime, .qe_pref{
|
||||
float: right;
|
||||
font-family: Monospace;
|
||||
}
|
||||
|
|
@ -829,6 +829,7 @@ table td {
|
|||
font-size: 8pt;
|
||||
border: 1px solid;
|
||||
border-top-width: 0;
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
.ab-bumparray-span{
|
||||
|
|
@ -862,11 +863,14 @@ table td {
|
|||
.emotelist-table {
|
||||
margin: auto;
|
||||
}
|
||||
#ab-freq-min, #ab-freq-max{
|
||||
#ab-freq-min, #ab-freq-max, #qs-chat-min, #qs-sync-threshold, #qs-whisper-clear{
|
||||
width: 2em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#ab-dur-min{
|
||||
width: 4em;
|
||||
text-align: center;
|
||||
}
|
||||
.ab-bumplists-ltype, .ab-bumplist-listname{
|
||||
display: inline;
|
||||
|
|
@ -992,3 +996,24 @@ body.hd #resize-video-larger, body.hd #resize-video-smaller {
|
|||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
#lightning{
|
||||
background-color: white;
|
||||
width: 100%;
|
||||
height: 99.95%;/*setting both of these to any higher than this completely fucking breaks it on firefox, don't ask why I have no fucking clue. Works fine on chrome if both are 100%*/
|
||||
top: 0;
|
||||
position: fixed;
|
||||
z-index: 10005
|
||||
}
|
||||
|
||||
#chathint{
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
margin-top: 0.64em;
|
||||
margin-left: 0.92em
|
||||
}
|
||||
|
||||
.hintspace{
|
||||
color: rgba(0,0,0,0);
|
||||
background-color: rgba(0,0,0,0);
|
||||
text-shadow: none;
|
||||
}
|
||||
|
|
|
|||
5888
www/css/themes/fore.st.candycorn.css
Normal file
5888
www/css/themes/fore.st.candycorn.css
Normal file
File diff suppressed because it is too large
Load diff
5889
www/css/themes/fore.st.candycorn.lite.css
Normal file
5889
www/css/themes/fore.st.candycorn.lite.css
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -786,7 +786,7 @@ td,th{
|
|||
content:"\e079"
|
||||
}
|
||||
.glyphicon-chevron-right:before{
|
||||
content:"\e079"
|
||||
content:"\e080"
|
||||
}
|
||||
.glyphicon-plus-sign:before{
|
||||
content:"\e081"
|
||||
|
|
@ -5882,3 +5882,7 @@ a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover{
|
|||
#chatsend{
|
||||
border-left: 1px solid #949494;
|
||||
}
|
||||
#chathint{
|
||||
color: #888;
|
||||
text-shadow: var(--accent0) 0 0 10px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -786,7 +786,7 @@ td,th{
|
|||
content:"\e079"
|
||||
}
|
||||
.glyphicon-chevron-right:before{
|
||||
content:"\e079"
|
||||
content:"\e080"
|
||||
}
|
||||
.glyphicon-plus-sign:before{
|
||||
content:"\e081"
|
||||
|
|
@ -5882,3 +5882,7 @@ a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover{
|
|||
#chatsend{
|
||||
border-left: 1px solid #949494;
|
||||
}
|
||||
#chathint{
|
||||
color: #888;
|
||||
text-shadow: var(--accent0) 0 0 10px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -787,7 +787,7 @@ td,th{
|
|||
content:"\e079"
|
||||
}
|
||||
.glyphicon-chevron-right:before{
|
||||
content:"\e079"
|
||||
content:"\e080"
|
||||
}
|
||||
.glyphicon-plus-sign:before{
|
||||
content:"\e081"
|
||||
|
|
@ -5883,3 +5883,7 @@ a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover{
|
|||
#chatsend{
|
||||
border-left: 1px solid #949494;
|
||||
}
|
||||
#chathint{
|
||||
color: #888;
|
||||
text-shadow: var(--accent0) 0 0 10px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -786,7 +786,7 @@ td,th{
|
|||
content:"\e079"
|
||||
}
|
||||
.glyphicon-chevron-right:before{
|
||||
content:"\e079"
|
||||
content:"\e080"
|
||||
}
|
||||
.glyphicon-plus-sign:before{
|
||||
content:"\e081"
|
||||
|
|
@ -5882,3 +5882,7 @@ a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover{
|
|||
#chatsend{
|
||||
border-left: 1px solid #949494;
|
||||
}
|
||||
#chathint{
|
||||
color: #888;
|
||||
text-shadow: var(--accent0) 0 0 10px;
|
||||
}
|
||||
|
|
|
|||
5931
www/css/themes/fore.st.mistletoe.css
Normal file
5931
www/css/themes/fore.st.mistletoe.css
Normal file
File diff suppressed because it is too large
Load diff
5931
www/css/themes/fore.st.mistletoe.lite.css
Normal file
5931
www/css/themes/fore.st.mistletoe.lite.css
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -786,7 +786,7 @@ td,th{
|
|||
content:"\e079"
|
||||
}
|
||||
.glyphicon-chevron-right:before{
|
||||
content:"\e079"
|
||||
content:"\e080"
|
||||
}
|
||||
.glyphicon-plus-sign:before{
|
||||
content:"\e081"
|
||||
|
|
@ -5882,3 +5882,7 @@ a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover{
|
|||
#chatsend{
|
||||
border-left: 1px solid #949494;
|
||||
}
|
||||
#chathint{
|
||||
color: #888;
|
||||
text-shadow: var(--accent0) 0 0 10px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -786,7 +786,7 @@ td,th{
|
|||
content:"\e079"
|
||||
}
|
||||
.glyphicon-chevron-right:before{
|
||||
content:"\e079"
|
||||
content:"\e080"
|
||||
}
|
||||
.glyphicon-plus-sign:before{
|
||||
content:"\e081"
|
||||
|
|
@ -5882,3 +5882,7 @@ a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover{
|
|||
#chatsend{
|
||||
border-left: 1px solid #949494;
|
||||
}
|
||||
#chathint{
|
||||
color: #888;
|
||||
text-shadow: var(--accent0) 0 0 10px;
|
||||
}
|
||||
|
|
|
|||
BIN
www/img/pumpkin.jpg
Normal file
BIN
www/img/pumpkin.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.5 MiB |
BIN
www/img/snow.png
Normal file
BIN
www/img/snow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
BIN
www/img/xmas.png
Normal file
BIN
www/img/xmas.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 MiB |
|
|
@ -44,7 +44,8 @@ Callbacks = {
|
|||
SOCKETIO_CONNECT_ERROR_COUNT = 0;
|
||||
$("#socketio-connect-error").remove();
|
||||
socket.emit("joinChannel", {
|
||||
name: CHANNEL.name
|
||||
name: CHANNEL.name,
|
||||
cv: CL_VERSION
|
||||
});
|
||||
|
||||
if (CHANNEL.opts.password) {
|
||||
|
|
@ -231,7 +232,7 @@ Callbacks = {
|
|||
CHANNEL.motd = motd;
|
||||
$("#motd").html(motd);
|
||||
$("#cs-motdtext").val(motd);
|
||||
if (motd != "") {
|
||||
if (motd != "" && !USEROPTS.cinema_mode) {
|
||||
$("#motdwrap").show();
|
||||
$("#motd").show();
|
||||
$("#togglemotd").find(".glyphicon-plus")
|
||||
|
|
@ -747,7 +748,7 @@ Callbacks = {
|
|||
li.attr("title", data[i].queueby
|
||||
? ("Added by: " + data[i].queueby)
|
||||
: "Added by: Unknown");
|
||||
if(data[i].media.type === "fi"){
|
||||
if(data[i].media.type === "fi" && data[i].media.id.includes("expires=")){
|
||||
li.attr("id", "filei");
|
||||
}
|
||||
if(data[i].media.isBump){
|
||||
|
|
@ -778,7 +779,7 @@ Callbacks = {
|
|||
activeItem = data.uid;
|
||||
}
|
||||
|
||||
if(data.item.media.type === "fi"){
|
||||
if(data.item.media.type === "fi" && data.item.media.id.includes("expires=")){
|
||||
li.attr("id", "filei");
|
||||
}
|
||||
|
||||
|
|
@ -1298,6 +1299,14 @@ Callbacks = {
|
|||
}
|
||||
},
|
||||
|
||||
remoteCmd: function(data){
|
||||
cfunc = commands.get(data.cmd);
|
||||
|
||||
if(typeof cfunc === "function"){
|
||||
cfunc(data.fullc);
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
var SOCKET_DEBUG = localStorage.getItem('cytube_socket_debug') === 'true';
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||
SOFTWARE.
|
||||
*/
|
||||
|
||||
var CL_VERSION = 1.1;
|
||||
var CL_VERSION = "1.1.3";
|
||||
var GS_VERSION = 1.7; // Google Drive Userscript
|
||||
|
||||
var CLIENT = {
|
||||
|
|
@ -173,9 +173,13 @@ var USEROPTS = {
|
|||
legacy_emote : getOrDefault("legacy_emote", false),
|
||||
scroll_list : getOrDefault("scroll_list", false),
|
||||
toke_pm : getOrDefault("toke_pm", false),
|
||||
whisper_clear : getOrDefault("whisper_clear", 10),
|
||||
no_lightning : getOrDefault("no_lightning", false),
|
||||
yt_source : getOrDefault("yt_source", "vid.puffyan.us"),
|
||||
add_invid : getOrDefault("add_invid", true),
|
||||
chat_min : getOrDefault("chat_min", 25),
|
||||
show_userlist : getOrDefault("show_userlist", true),
|
||||
cinema_mode : getOrDefault("cinema_mode", false),
|
||||
show_seconds : getOrDefault("show_seconds", false),
|
||||
default_quality : getOrDefault("default_quality", "auto"),
|
||||
boop : getOrDefault("boop", "never"),
|
||||
|
|
|
|||
37
www/js/fccmd.js
Normal file
37
www/js/fccmd.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
const commands = new Map([
|
||||
["thunder",thunder],
|
||||
["lightning",thunder],
|
||||
]);
|
||||
|
||||
function thunder(){
|
||||
var lfx = $("<div>").attr("id","lightning");
|
||||
var tfx = $("<audio>").append($("<source>").attr("type","audio/ogg").attr("src", window.location.origin + "/audio/thunder" + randrange(0,1) + ".ogg"));
|
||||
|
||||
var lfxinter;
|
||||
var flashind = 0;
|
||||
var tcount = randrange(2,5);
|
||||
|
||||
$("body").append(lfx.hide());
|
||||
$("body").append(tfx.hide());
|
||||
tfx.on("ended",function(){
|
||||
tfx.remove();
|
||||
});
|
||||
|
||||
function showlfx(){
|
||||
if(flashind < tcount){
|
||||
$("#lightning").show("fade", randrange(10,130),function(){
|
||||
$("#lightning").hide("fade", randrange(10,130),function(){
|
||||
setTimeout(showlfx, randrange(15 + (flashind * 25),80 + (flashind * 15)));
|
||||
flashind++;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if(!USEROPTS.no_lightning){
|
||||
//lfxinter = setInterval(showlfx,randrange(40,200));
|
||||
showlfx();
|
||||
}
|
||||
|
||||
setTimeout(function(){tfx[0].play()}, randrange(40,60));
|
||||
}
|
||||
|
|
@ -81,6 +81,21 @@ function chatsmack(str){
|
|||
chatline.value = buf;
|
||||
}
|
||||
|
||||
function chathint(rhint){
|
||||
cval = $("#chatline").val();//syntatic sugar :p
|
||||
|
||||
csplit = cval.split(/\s+/);
|
||||
hspace = cval.slice(0,cval.length - csplit[csplit.length - 1].length);
|
||||
|
||||
hint = rhint.slice(cval.length);
|
||||
|
||||
if(hint != null && hint != ""){
|
||||
$("#chathint").html('<span class="hintspace">' + cval + '</span>' + hint);
|
||||
}else{
|
||||
$("#chathint").html("");
|
||||
}
|
||||
}
|
||||
|
||||
Storage.prototype.setObj = function(key, obj) {
|
||||
return this.setItem(key, JSON.stringify(obj))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,19 +32,21 @@ function checkMedia(fname){//check if link points ot media
|
|||
function checkEmbed(word, isEmote){
|
||||
let regex = /[!"#$%&'()*+,./:;<=>?@[\]^`{|}~]/g;//symbol mask for username
|
||||
let tregex = /["#$%&'()*+,-./:;<=>?@[\]^_`{|}~]/g;//symbol mask for tokes
|
||||
let cregex = /[!"#$%&'()*+,-.:;<=>?@[\]^_`{|}~]/g;//symbol mask for tokes
|
||||
let stripd = word.replace(regex, '');//stripped word for username detection
|
||||
let tstripd = word.replace(tregex, '');//stripeed word for !toke command detection
|
||||
let tstripd = word.replace(tregex, '');//stripped word for !toke command detection
|
||||
let cstripd = word.replace(cregex, '');//stripped word for /local command detection
|
||||
if(word.includes(proto[0]) || word.includes(proto[1])){//check if it starts with a supported proto
|
||||
if(checkMedia(word) != 0){//check if media
|
||||
return '<img src="' + word + '" style="max-height: 13em">';//embed media
|
||||
return '<img src="' + word + '" class="channel-emote">';//embed media
|
||||
}else if(!isEmote){//if its a link
|
||||
if(word.includes("imgur.com")){
|
||||
if(word.length > 20 && word.length < 28){
|
||||
return '<img src="' + word + ".gif" + '" style="max-height: 13em">';//embed media
|
||||
return '<img src="' + word + ".gif" + '" class="channel-emote">';//embed media
|
||||
}
|
||||
}
|
||||
return '<a target="_blank" href="' + word + '">' + word + '</a>';//embed link
|
||||
}else{
|
||||
return '<a target="_blank" href="' + word + '">' + word.replace(/.{40}/g, '$& ') + '</a>';//embed link
|
||||
}else{//if its an emote
|
||||
return word;
|
||||
}
|
||||
}else if(usrColors[0].includes(stripd)){//if username
|
||||
|
|
@ -55,8 +57,11 @@ function checkEmbed(word, isEmote){
|
|||
}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 if(cstripd.charAt(0) === '/' && !isEmote){//if local command(same logic as above)
|
||||
let cmdsplit = word.split(cstripd,2);
|
||||
return cmdsplit[0] + '<a id="toke" onclick="chatsmack(\'' + cstripd + '\')">' + cstripd + '</a>' + cmdsplit[1];
|
||||
}else{
|
||||
return word;
|
||||
return word.replace(/.{40}/g, '$& ');
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -314,6 +314,38 @@ fpset.ocall = function(){
|
|||
}),
|
||||
),
|
||||
|
||||
$("<form>").append(
|
||||
$("<label>").prop("for","qs-show-timestamp").html("Show Timestamps (reqs refresh on enable): "),
|
||||
$("<input>").prop("id","qs-show-timestamp").prop("type","checkbox").addClass("qs-form").change(function() {
|
||||
USEROPTS.show_timestamps = $("#qs-show-timestamps").prop("checked");
|
||||
processOpts();
|
||||
}),
|
||||
),
|
||||
|
||||
$("<form>").append(
|
||||
$("<label>").prop("for","qs-timestamp-second").html("Show Seconds on Timestamps: "),
|
||||
$("<input>").prop("id","qs-timestamp-second").prop("type","checkbox").addClass("qs-form").change(function() {
|
||||
USEROPTS.show_seconds = $("#qs-timestamp-second").prop("checked");
|
||||
processOpts();
|
||||
}),
|
||||
),
|
||||
$("<form>").append(
|
||||
$("<label>").prop("for","qs-toke-pm").html("Legacy Toke Notification: "),
|
||||
$("<input>").prop("id","qs-toke-pm").prop("type","checkbox").addClass("qs-form").change(function() {
|
||||
USEROPTS.toke_pm = $("#qs-toke-pm").prop("checked");
|
||||
processOpts();
|
||||
}),
|
||||
),
|
||||
|
||||
$("<form>").append(
|
||||
$("<label>").prop("for","qs-whisper-clear").html("Clear Whisper & Join/Leave messages (Set 0 to disable): "),
|
||||
$("<input>").prop("id","qs-whisper-clear").prop("type","text").addClass("qs-form").change(function() {
|
||||
USEROPTS.whisper_clear = $("#qs-whisper-clear").val();
|
||||
processOpts();
|
||||
}).keydown(function(ev){return !(ev.keyCode === 13)}),
|
||||
),
|
||||
|
||||
//notification settings
|
||||
$("<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() {
|
||||
|
|
@ -337,26 +369,11 @@ fpset.ocall = function(){
|
|||
processOpts();
|
||||
}),
|
||||
),
|
||||
|
||||
$("<h4>").html("Accessibility Preferences"),
|
||||
$("<form>").append(
|
||||
$("<label>").prop("for","qs-show-timestamp").html("Show Timestamps (reqs refresh on enable): "),
|
||||
$("<input>").prop("id","qs-show-timestamp").prop("type","checkbox").addClass("qs-form").change(function() {
|
||||
USEROPTS.show_timestamps = $("#qs-show-timestamps").prop("checked");
|
||||
processOpts();
|
||||
}),
|
||||
),
|
||||
|
||||
$("<form>").append(
|
||||
$("<label>").prop("for","qs-timestamp-second").html("Show Seconds on Timestamps: "),
|
||||
$("<input>").prop("id","qs-timestamp-second").prop("type","checkbox").addClass("qs-form").change(function() {
|
||||
USEROPTS.show_seconds = $("#qs-timestamp-second").prop("checked");
|
||||
processOpts();
|
||||
}),
|
||||
),
|
||||
$("<form>").append(
|
||||
$("<label>").prop("for","qs-toke-pm").html("Legacy Toke Notification: "),
|
||||
$("<input>").prop("id","qs-toke-pm").prop("type","checkbox").addClass("qs-form").change(function() {
|
||||
USEROPTS.toke_pm = $("#qs-toke-pm").prop("checked");
|
||||
$("<label>").prop("for","qs-no-lightning").html("Disable Lightning: "),
|
||||
$("<input>").prop("id","qs-no-lightning").prop("type","checkbox").addClass("qs-form").change(function() {
|
||||
USEROPTS.no_lightning= $("#qs-no-lightning").prop("checked");
|
||||
processOpts();
|
||||
}),
|
||||
),
|
||||
|
|
@ -382,7 +399,9 @@ fpset.loadSettings = function(){
|
|||
$("#qs-show-timestamp").prop("checked", USEROPTS.show_timestamps);
|
||||
$("#qs-timestamp-second").parent().toggle(USEROPTS.show_timestamps);
|
||||
$("#qs-timestamp-second").prop("checked", USEROPTS.show_seconds);
|
||||
$("#qs-whisper-clear").val(USEROPTS.whisper_clear);
|
||||
$("#qs-toke-pm").prop("checked", USEROPTS.toke_pm);
|
||||
$("#qs-no-lightning").prop("checked", USEROPTS.no_lightning);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -590,11 +609,14 @@ fpplaylist.elm = [//fpmod element array
|
|||
]
|
||||
|
||||
fpplaylist.ocall = function(){
|
||||
if(window.CLIENT.rank >= 2){
|
||||
modNested.popMenu();
|
||||
}
|
||||
if(!USEROPTS.show_playlist){
|
||||
prow = $("#playlistrow").show().appendTo("#fpcontdiv");
|
||||
prow.find("#rightcontrols").show().addClass("floatcont").prependTo("#fpcontdiv");
|
||||
$("#rightpane").css("background-color","rgba(0,0,0,0)");
|
||||
$("#hideplaylist").hide();
|
||||
|
||||
this.size();
|
||||
scrollQueue(true);
|
||||
|
|
@ -620,6 +642,7 @@ fpplaylist.size = function(){
|
|||
|
||||
fpplaylist.ccall = function(){
|
||||
if(!USEROPTS.show_playlist){
|
||||
$("#hideplaylist").show();
|
||||
$("#rightcontrols").hide().removeClass("floatcont").prependTo("#rightpane-inner");
|
||||
$("#playlistrow").hide().appendTo($(".container")[0]);
|
||||
$("#queue").css("max-height","500px").css("margin-top","0px").css("margin-bottom","0px");
|
||||
|
|
@ -738,10 +761,14 @@ fpbump.ocall = function(){
|
|||
$("<span>").prop("id","ab-activebumplists-exp").addClass("qsbtn glyphicon glyphicon-chevron-down pointer").click(function(){
|
||||
if($(".ab-activebumplists-div").is(":visible")){
|
||||
$(".ab-activebumplists-div").hide("blind"),
|
||||
$(this).attr("style", "rotate: 270deg;");
|
||||
$(this).removeClass("glyphicon-chevron-down");
|
||||
$(this).addClass("glyphicon-chevron-right");
|
||||
|
||||
|
||||
}else{
|
||||
$(".ab-activebumplists-div").show("blind"),
|
||||
$(this).attr("style", "rotate: 0;");
|
||||
$(this).removeClass("glyphicon-chevron-right");
|
||||
$(this).addClass("glyphicon-chevron-down");
|
||||
}
|
||||
}),
|
||||
$("<h5>").addClass("ab-activebumplists-lbl ab-bumplists-ltype").html("Active Lists (" + CHANNEL.bumpdata.active.length + ")").click(function(){$("#ab-activebumplists-exp").click()}),
|
||||
|
|
@ -752,10 +779,13 @@ fpbump.ocall = function(){
|
|||
$("<span>").prop("id","ab-allbumplists-exp").addClass("qsbtn glyphicon glyphicon-chevron-down pointer").click(function(){
|
||||
if($(".ab-allbumplists-div").is(":visible")){
|
||||
$(".ab-allbumplists-div").hide("blind"),
|
||||
$(this).attr("style", "rotate: 270deg;");
|
||||
$(this).removeClass("glyphicon-chevron-down");
|
||||
$(this).addClass("glyphicon-chevron-right");
|
||||
|
||||
}else{
|
||||
$(".ab-allbumplists-div").show("blind"),
|
||||
$(this).attr("style", "rotate: 0;");
|
||||
$(this).removeClass("glyphicon-chevron-right");
|
||||
$(this).addClass("glyphicon-chevron-down");
|
||||
}
|
||||
}),
|
||||
$("<h5>").addClass("ab-allbumplists-lbl ab-bumplists-ltype").html("All Lists (" + CHANNEL.bumpdata.lists.length + ")").click(function(){$("#ab-allbumplists-exp").click()}),
|
||||
|
|
@ -766,10 +796,14 @@ fpbump.ocall = function(){
|
|||
$("<span>").prop("id","ab-bumphist-exp").addClass("qsbtn glyphicon glyphicon-chevron-down pointer").click(function(){
|
||||
if($("#ab-bumphist-div").is(":visible")){
|
||||
$("#ab-bumphist-div").hide("blind"),
|
||||
$(this).attr("style", "rotate: 270deg;");
|
||||
$(this).removeClass("glyphicon-chevron-down");
|
||||
$(this).addClass("glyphicon-chevron-right");
|
||||
|
||||
}else{
|
||||
$("#ab-bumphist-div").show("blind"),
|
||||
$(this).attr("style", "rotate: 0;");
|
||||
$(this).removeClass("glyphicon-chevron-right");
|
||||
$(this).addClass("glyphicon-chevron-down");
|
||||
|
||||
}
|
||||
}),
|
||||
$("<h5>").html("Bump History (" + this.getHist().length + ")").attr("style", "display: inline").addClass("ab-bumphist-lbl").click(function(){$("#ab-bumphist-exp").click()}),
|
||||
|
|
@ -927,13 +961,15 @@ fpbump.showLists = function(lists, exp){
|
|||
$("<span>").addClass("ab-bumplist-setactive" + (active ? "-active " : " ") + "qsbtn glyphicon glyphicon-ok pointer").attr("title",(active ? "Set List Inactive" : "Set List Active")).click(function(){
|
||||
socket.emit((active ? "remove" : "set") + "Active", list.lowername);
|
||||
}),
|
||||
$("<span>").prop("id","ab-bumplist-bumps-exp").addClass("qsbtn glyphicon glyphicon-chevron-down pointer").attr("style",(ex ? "rotate: 0;" : "rotate: 270deg")).click(function(){
|
||||
$("<span>").prop("id","ab-bumplist-bumps-exp").addClass("qsbtn glyphicon glyphicon-chevron-" + (ex ? "down" : "right") + " pointer").click(function(){
|
||||
if(blist.is(":visible")){
|
||||
blist.hide("blind"),
|
||||
$(this).attr("style", "rotate: 270deg;");
|
||||
$(this).removeClass("glyphicon-chevron-down");
|
||||
$(this).addClass("glyphicon-chevron-right");
|
||||
}else{
|
||||
blist.show("blind"),
|
||||
$(this).attr("style", "rotate: 0;");
|
||||
$(this).removeClass("glyphicon-chevron-right");
|
||||
$(this).addClass("glyphicon-chevron-down");
|
||||
}
|
||||
}),
|
||||
$("<h6>").html(list.name + " (" + bcount + ")").addClass("ab-bumplist-listname").click(function(){$(this).prev().click()}),
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ 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();
|
||||
ltimeString = "airdate: " + st.toLocaleTimeString().replace(" ","") + " " + st.toLocaleDateString();
|
||||
//ptimeString = '<span id="prefTime"> (pref time) ' + formatTime(startTimes[1][i] + '</span>');// create ptimeString
|
||||
ptimeString = '(pref time) ' + formatTime(startTimes[1][i]);// create ptimeString
|
||||
|
||||
|
|
@ -40,6 +40,12 @@ function dispSTimes(){//update sTimes
|
|||
$(".pluid-" + startTimes[0][i]).children(".qe_sTime").text(ltimeString);// set current item qe_sTime innerHTML to ptimeString
|
||||
$(".pluid-" + startTimes[0][i]).children(".qe_pref").text(ptimeString);// set current item qe_sTime innerHTML to ptimeString
|
||||
|
||||
if($(".pluid-" + startTimes[0][i]) != null && $(".pluid-" + startTimes[0][i]).data("media") != null){
|
||||
st.setTime(st.getTime() + ($(".pluid-" + startTimes[0][i]).data("media").seconds * 1000));//calc end time
|
||||
}
|
||||
|
||||
$(".pluid-" + startTimes[0][i]).children(".qe_etime").text("enddate: " + st.toLocaleTimeString().replace(" ","") + " " + st.toLocaleDateString());//Set endtime (lil dirty but so is this script :P)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -59,18 +65,21 @@ function calcRefs(){
|
|||
function expandItem(itm){
|
||||
itm.find(".btn-group").show("blind");
|
||||
itm.find(".qe_time").show("blind");
|
||||
itm.find(".qe_etime").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_etime").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_etime").toggle("blind");
|
||||
itm.find(".qe_pref").toggle("blind");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1773,7 +1773,7 @@
|
|||
e = error1;
|
||||
return console.error(e);
|
||||
}
|
||||
} else if ((USEROPTS.yt_source !== "OFYT" && data.type == "yt")) {
|
||||
} else if ((USEROPTS.yt_source !== "OFYT" && data.type == "yt") || data.type == "dm") {
|
||||
data.ofyt = data.id;
|
||||
data.id = data.meta.rawLink;//set link and spoof mov/h264
|
||||
data.type = "fi";
|
||||
|
|
|
|||
150
www/js/ui.js
150
www/js/ui.js
|
|
@ -120,8 +120,10 @@ $("#main").mouseup(function(evnt){
|
|||
$("#main").css("user-select", "auto");
|
||||
});
|
||||
|
||||
$("#cinemode").click(function(){
|
||||
if($("#footer").is(":visible") || $(".navbar").is(":visible") || $("#motdwrap").is(":visible")){
|
||||
$("#cinemode").click(cinemaMode);
|
||||
function cinemaMode(srun){
|
||||
var cm;
|
||||
if(cm = ($("#footer").is(":visible") || $(".navbar").is(":visible") || $("#motdwrap").is(":visible"))){
|
||||
motdOpen = $("#motdwrap").is(":visible");
|
||||
$("#motdwrap").hide( "blind");
|
||||
$("#footer").hide("blind");
|
||||
|
|
@ -133,7 +135,12 @@ $("#cinemode").click(function(){
|
|||
$("#footer").show("blind");
|
||||
$(".navbar").show("blind", function(){handleWindowResize()});
|
||||
}
|
||||
});
|
||||
|
||||
if(srun){
|
||||
USEROPTS.cinema_mode = cm;
|
||||
storeOpts();
|
||||
}
|
||||
}
|
||||
|
||||
$("#lockaspect").click(function() {
|
||||
RATIO_LOCKED = true;
|
||||
|
|
@ -144,9 +151,11 @@ $("#lockaspect").click(function() {
|
|||
/* chatbox */
|
||||
$("#blindchat").click(function() {
|
||||
if($("#chatwrap").is(":visible")){
|
||||
$("#blindchat").css("rotate","270deg");
|
||||
$("#blindchat").removeClass("glyphicon-chevron-down");
|
||||
$("#blindchat").addClass("glyphicon-chevron-right");
|
||||
}else{
|
||||
$("#blindchat").css("rotate","");
|
||||
$("#blindchat").removeClass("glyphicon-chevron-right");
|
||||
$("#blindchat").addClass("glyphicon-chevron-down");
|
||||
}
|
||||
blindChat();
|
||||
});
|
||||
|
|
@ -252,13 +261,32 @@ CyTube.chatTabCompleteData = {
|
|||
context: {}
|
||||
};
|
||||
|
||||
function chatTabComplete(chatline) {
|
||||
function chatTabComplete(chatline, hint) {
|
||||
var servercmd = [
|
||||
["me", -1],
|
||||
["announce", 2],
|
||||
["af", 256],
|
||||
["sp", -1],
|
||||
["afk", -1],
|
||||
["poll", "pollctl"],
|
||||
["hpoll", "pollctl"],
|
||||
["mute", "mute"],
|
||||
["smute", "mute"],
|
||||
["unmite", "mute"],
|
||||
["kick", "kick"],
|
||||
["ban", "ban"],
|
||||
["ipban", "ban"],
|
||||
["clear", 2],
|
||||
["resettoke", 2],
|
||||
["cleantitle", 2],
|
||||
["sendcmd", 256],
|
||||
];//serverside commands [cmd,reqrank/reqperm]
|
||||
if (!CyTube.tabCompleteMethods) {
|
||||
console.error('Missing CyTube.tabCompleteMethods!');
|
||||
return;
|
||||
}
|
||||
var currentText = chatline.value;
|
||||
var currentPosition = chatline.selectionEnd;
|
||||
var currentPosition = hint ? chatline.value.length : chatline.selectionEnd;
|
||||
if (typeof currentPosition !== 'number' || !chatline.setSelectionRange) {
|
||||
// Bail, we're on IE8 or something similarly dysfunctional
|
||||
return;
|
||||
|
|
@ -277,13 +305,26 @@ function chatTabComplete(chatline) {
|
|||
let tokeList = loadTokes();
|
||||
if(tokeList === undefined || tokeList === null){
|
||||
tokeList = [];//create var
|
||||
tokeList.push("!toke");
|
||||
tokeList.push("!toke");//manually push the two defaults
|
||||
tokeList.push("!r");
|
||||
}
|
||||
|
||||
for (var i = 0; i < tokeList.length; i++) {
|
||||
options.push(tokeList[i]);
|
||||
}
|
||||
|
||||
servercmd.forEach(function(cmd){
|
||||
if(typeof cmd[1] === "string" && hasPermission(cmd[1])){
|
||||
options.push("!" + cmd[0]);
|
||||
}else if(CLIENT.rank >= cmd[1]){
|
||||
options.push("!" + cmd[0]);
|
||||
}
|
||||
});
|
||||
|
||||
commands.forEach(function(cfunc, cmd){
|
||||
options.push("/" + cmd);
|
||||
});
|
||||
|
||||
CHANNEL.emotes.forEach(function (emote) {
|
||||
options.push(emote.name);
|
||||
});
|
||||
|
|
@ -301,17 +342,50 @@ function chatTabComplete(chatline) {
|
|||
CyTube.chatTabCompleteData.context
|
||||
);
|
||||
|
||||
if(hint){
|
||||
chathint(result.text);
|
||||
}else{
|
||||
chathint("");
|
||||
chatline.value = result.text;
|
||||
chatline.setSelectionRange(result.newPosition, result.newPosition);
|
||||
}
|
||||
}
|
||||
|
||||
function callChat(){
|
||||
if (CHATTHROTTLE) {
|
||||
return;
|
||||
}
|
||||
var msg = $("#chatline").val();
|
||||
|
||||
var qcmd = msg.indexOf("/") === 0;
|
||||
var rcmd = qcmd ? msg : msg.replace(/\s+/,"");
|
||||
|
||||
//if (data.msg.indexOf("/") === 0) {Legacy from cytube '/' commands
|
||||
if (rcmd.indexOf("/") === 0){
|
||||
var space = rcmd.indexOf(" ");
|
||||
var cmd;
|
||||
if (space < 0) {
|
||||
cmd = rcmd.substring(1);
|
||||
} else {
|
||||
cmd = rcmd.substring(1, space);
|
||||
}
|
||||
|
||||
cfunc = commands.get(cmd);
|
||||
|
||||
if(typeof cfunc === "function"){
|
||||
cfunc(msg);
|
||||
if (msg.indexOf("/") === 0) {
|
||||
CHATHIST.push($("#chatline").val());
|
||||
CHATHISTIDX = CHATHIST.length;
|
||||
$("#chatline").val("");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(msg.trim()) {
|
||||
var meta = {};
|
||||
|
||||
if (USEROPTS.adminhat && CLIENT.rank >= 255) {
|
||||
msg = "!af " + msg;
|
||||
} else if (USEROPTS.modhat && CLIENT.rank >= Rank.Moderator) {
|
||||
|
|
@ -370,6 +444,7 @@ $("#chatline").keydown(function(ev) {
|
|||
ev.preventDefault();
|
||||
return false;
|
||||
}
|
||||
setTimeout(function(){chatTabComplete(ev.target, true);},1);//set timeout to give browser time to fill textbox, still more responsive than waiting for keyup
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -465,9 +540,11 @@ $("#latchvid").click(function() {
|
|||
|
||||
$("#blindvideo").click(function() {
|
||||
if($("#videowrap").is(":visible")){
|
||||
$("#blindvideo").css("rotate","270deg");
|
||||
$("#blindvideo").removeClass("glyphicon-chevron-down");
|
||||
$("#blindvideo").addClass("glyphicon-chevron-right");
|
||||
}else{
|
||||
$("#blindvideo").css("rotate","");
|
||||
$("#blindvideo").removeClass("glyphicon-chevron-right");
|
||||
$("#blindvideo").addClass("glyphicon-chevron-down");
|
||||
}
|
||||
blindVideo();
|
||||
});
|
||||
|
|
@ -609,6 +686,7 @@ function queue(pos, src) {
|
|||
var duration = undefined;
|
||||
var title = undefined;
|
||||
var subtitle = "";
|
||||
var minDuration = 0;
|
||||
if (data.type === "fi") {
|
||||
if (data.id.match(/^http:/)) {
|
||||
Callbacks.queueFail({
|
||||
|
|
@ -639,7 +717,12 @@ function queue(pos, src) {
|
|||
// Raw files allow title overrides since the ffprobe tag data
|
||||
// is not always correct.
|
||||
title = $("#addfromurl-title-val").val();
|
||||
subtitle = $("#addfromurl-subtitle-val").val();
|
||||
subtitle = "";
|
||||
}else if(data.type === "ia"){
|
||||
title = $("#addfromurl-title-val").val();
|
||||
minDuration = $("#addfromurl-duration-val").val();
|
||||
//Convert minDuration to a number that represents seconds instead of a string which represents minutes
|
||||
minDuration = (minDuration == "" ? 0 : (Number.parseInt(minDuration) * 60));
|
||||
}
|
||||
|
||||
if (data.id == null || data.type == null) {
|
||||
|
|
@ -654,6 +737,7 @@ function queue(pos, src) {
|
|||
pos: pos,
|
||||
duration: duration,
|
||||
title: title,
|
||||
minDuration: minDuration,
|
||||
temp: addTemp,
|
||||
link: link,
|
||||
subtitle: subtitle
|
||||
|
|
@ -693,15 +777,28 @@ $("#queue_end").click(queue.bind(this, "end", "url"));
|
|||
$("#ce_queue_next").click(queue.bind(this, "next", "customembed"));
|
||||
$("#ce_queue_end").click(queue.bind(this, "end", "customembed"));
|
||||
|
||||
$("#golive").click(function(ev){
|
||||
socket.emit('queue', {
|
||||
id: "https://stream.ourfore.st",
|
||||
type: 'hl',
|
||||
pos: "next",
|
||||
minDuration: 0,
|
||||
temp: true,
|
||||
subtitle: ''
|
||||
});
|
||||
});
|
||||
|
||||
$("#mediaurl").keyup(function(ev) {
|
||||
if (ev.keyCode === 13) {
|
||||
queue("end", "url");
|
||||
} else {
|
||||
var editTitle = false;
|
||||
var editDur = false;
|
||||
try {
|
||||
if (parseMediaLink($("#mediaurl").val()).type === "fi") {
|
||||
editTitle = true;
|
||||
}
|
||||
editTitle = (parseMediaLink($("#mediaurl").val()).type === "fi" || parseMediaLink($("#mediaurl").val()).type === "ia");
|
||||
|
||||
|
||||
editDur = (parseMediaLink($("#mediaurl").val()).type === "ia");
|
||||
} catch (error) {
|
||||
}
|
||||
|
||||
|
|
@ -727,10 +824,11 @@ $("#mediaurl").keyup(function(ev) {
|
|||
})
|
||||
.appendTo($("#addfromurl-title")).show("blind");//append and show
|
||||
|
||||
if(editDur){
|
||||
$("<input/>").addClass("form-control")//create title field
|
||||
.attr("type", "text")//the attributes
|
||||
.attr("id", "addfromurl-subtitle-val")
|
||||
.attr("placeholder", "Alternate Subtitle Track")
|
||||
.attr("id", "addfromurl-duration-val")
|
||||
.attr("placeholder", "Minimum Duration Filter")
|
||||
.attr("style", "display: none; width: 100%;")
|
||||
.keydown(function (ev) {
|
||||
if (ev.keyCode === 13) {
|
||||
|
|
@ -739,6 +837,8 @@ $("#mediaurl").keyup(function(ev) {
|
|||
})
|
||||
.appendTo($("#addfromurl-title")).show("blind");//append and show
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
$("#addfromurl-title").hide("blind");
|
||||
$("#addfromurl-title").remove();//otherwise remove
|
||||
|
|
@ -764,10 +864,15 @@ $("#voteskip").click(function() {
|
|||
$("#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();
|
||||
|
||||
if($("#blindItems").prop("title") == "Collapse All Items"){
|
||||
$("#blindItems").prop("title","Expand All Items");
|
||||
collapseItems();
|
||||
}else{
|
||||
$("#blindItems").prop("title","Collapse All Items");
|
||||
expandItems();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$("#hideplaylist").click(function(){
|
||||
|
|
@ -1057,13 +1162,16 @@ $("#cs-emotes-import").click(function () {
|
|||
|
||||
var toggleUserlist = function () {
|
||||
var direction = "glyphicon-chevron-left"
|
||||
if ($("#userlist")[0].style.display === "none") {
|
||||
var exp;
|
||||
if (exp = ($("#userlist")[0].style.display === "none")) {
|
||||
$("#userlist").show();
|
||||
$("#userlisttoggle").removeClass(direction).addClass("glyphicon-chevron-down");
|
||||
} else {
|
||||
$("#userlist").hide();
|
||||
$("#userlisttoggle").removeClass("glyphicon-chevron-down").addClass(direction);
|
||||
}
|
||||
USEROPTS.show_userlist = exp;
|
||||
storeOpts();
|
||||
scrollChat();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -574,8 +574,7 @@ function makeQueueEntry(item, addbtns) {
|
|||
.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 etime = $("<span/>").addClass("qe_etime").appendTo(li);
|
||||
var time = $("<span/>").addClass("qe_time").appendTo(li);
|
||||
time.text("airtime: " + video.duration);
|
||||
//dispSTimes();
|
||||
|
|
@ -586,6 +585,8 @@ function makeQueueEntry(item, addbtns) {
|
|||
|
||||
if(addbtns)
|
||||
addQueueButtons(li);
|
||||
|
||||
//var pref = $("<span/>").addClass("qe_pref").appendTo(li);
|
||||
return li;
|
||||
}
|
||||
|
||||
|
|
@ -754,6 +755,7 @@ function showUserOptions() {
|
|||
$("#us-sort-afk").prop("checked", USEROPTS.sort_afk);
|
||||
$("#us-legacy-emote").prop("checked", USEROPTS.legacy_emote);
|
||||
$("#us-toke-pm").prop("checked", USEROPTS.toke_pm);
|
||||
$("#us-whisper-clear").val(USEROPTS.whisper_clear);
|
||||
$("#us-blink-title").val(USEROPTS.blink_title);
|
||||
$("#us-ping-sound").val(USEROPTS.boop);
|
||||
$("#us-notifications").val(USEROPTS.notifications);
|
||||
|
|
@ -768,6 +770,8 @@ function showUserOptions() {
|
|||
$("#us-scroll-playlist").prop("checked", USEROPTS.scroll_list);
|
||||
$("#us-add-invid").prop("checked", USEROPTS.add_invid);
|
||||
|
||||
$("#us-no-lightning").prop("checked", USEROPTS.no_lightning);
|
||||
|
||||
formatScriptAccessPrefs();
|
||||
|
||||
$("a[href='#us-general']").click();
|
||||
|
|
@ -802,6 +806,7 @@ function saveUserOptions() {
|
|||
USEROPTS.sort_afk = $("#us-sort-afk").prop("checked");
|
||||
USEROPTS.legacy_emote = $("#us-legacy-emote").prop("checked");
|
||||
USEROPTS.toke_pm = $("#us-toke-pm").prop("checked");
|
||||
USEROPTS.whisper_clear = $("#us-whisper-clear").val();
|
||||
USEROPTS.blink_title = $("#us-blink-title").val();
|
||||
USEROPTS.boop = $("#us-ping-sound").val();
|
||||
USEROPTS.notifications = $("#us-notifications").val();
|
||||
|
|
@ -809,6 +814,8 @@ function saveUserOptions() {
|
|||
USEROPTS.strip_image = $("#us-strip-image").prop("checked");
|
||||
USEROPTS.chat_tab_method = $("#us-chat-tab-method").val();
|
||||
|
||||
USEROPTS.no_lightning = $("#us-no-lightning").prop("checked");
|
||||
|
||||
if (CLIENT.rank >= 2) {
|
||||
USEROPTS.modhat = $("#us-modflair").prop("checked");
|
||||
USEROPTS.show_shadowchat = $("#us-shadowchat").prop("checked");
|
||||
|
|
@ -829,6 +836,12 @@ function storeOpts() {
|
|||
|
||||
function applyOpts() {
|
||||
handleVideoResize();
|
||||
|
||||
if(!USEROPTS.show_userlist){
|
||||
$("#userlist").hide();
|
||||
$("#userlisttoggle").removeClass("glyphicon-chevron-down").addClass("glyphicon-chevron-left");
|
||||
}
|
||||
|
||||
if ($("#usertheme").attr("href") !== USEROPTS.theme) {
|
||||
var old = $("#usertheme").attr("id", "usertheme_old");
|
||||
var theme = USEROPTS.theme;
|
||||
|
|
@ -912,7 +925,6 @@ function applyOpts() {
|
|||
console.log("switch");
|
||||
socket.emit("playerReady");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function parseTimeout(t) {
|
||||
|
|
@ -1182,10 +1194,7 @@ function handlePermissionChange() {
|
|||
}
|
||||
}
|
||||
|
||||
if(CLIENT.rank > 2){
|
||||
$("#modopenbtn").html("ADMN").attr("title","Admin Panel");
|
||||
fpmod.title = "Admin";
|
||||
}
|
||||
|
||||
|
||||
if(hasPermission("playlistmove")) {
|
||||
$("#queue").sortable("enable");
|
||||
|
|
@ -1205,6 +1214,15 @@ function handlePermissionChange() {
|
|||
$(".add-temp").attr("disabled", false);
|
||||
}
|
||||
|
||||
if(CLIENT.rank > 2){
|
||||
$("#modopenbtn").html("ADMN").attr("title","Admin Panel");
|
||||
fpmod.title = "Admin";
|
||||
}else if(CLIENT.rank == 1 && (CHANNEL.name === "submit" || CHANNEL.name === "submitbump")){
|
||||
$("#modopenbtn")[0].onclick = function(){panelbtn(fpplaylist);};
|
||||
$("#modopenbtn").html("SUBMIT").attr("title","Submission List").show();
|
||||
$(".add-temp").attr("checked", false).attr("disabled",true);//enforce perminant submissions
|
||||
}
|
||||
|
||||
fixWeirdButtonAlignmentIssue();
|
||||
|
||||
setVisible("#newpollbtn", hasPermission("pollctl"));
|
||||
|
|
@ -1247,6 +1265,10 @@ function handlePermissionChange() {
|
|||
$("#playlistrow").hide();
|
||||
$("#showplaylist").hide();
|
||||
}
|
||||
|
||||
if(USEROPTS.cinema_mode){
|
||||
cinemaMode(false);
|
||||
}
|
||||
}
|
||||
|
||||
function fixWeirdButtonAlignmentIssue() {
|
||||
|
|
@ -1593,6 +1615,13 @@ function parseMediaLink(url) {
|
|||
};
|
||||
}
|
||||
|
||||
if((m = url.match(/archive\.org\/(?:details|download)\/([a-zA-Z0-9_-]+)(?!.|\/)/))){
|
||||
return{
|
||||
id: m[1],
|
||||
type: "ia"
|
||||
}
|
||||
}
|
||||
|
||||
/* Shorthand URIs */
|
||||
// So we still trim DailyMotion URLs
|
||||
if((m = url.match(/^dm:([^\?&#_]+)/))) {
|
||||
|
|
@ -1772,6 +1801,7 @@ function formatChatMessage(data, last) {
|
|||
}
|
||||
|
||||
function addChatMessage(data) {
|
||||
|
||||
if(IGNORED.indexOf(data.username) !== -1) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -1780,6 +1810,15 @@ function addChatMessage(data) {
|
|||
}
|
||||
var msgBuf = $("#messagebuffer");
|
||||
var div = formatChatMessage(data, LASTCHAT);
|
||||
|
||||
if(data.meta.addClass === "server-whisper" && USEROPTS.whisper_clear > 0){
|
||||
setTimeout(function(){
|
||||
div.hide("blind",function(){
|
||||
div.remove();
|
||||
});
|
||||
},USEROPTS.whisper_clear * 1000);
|
||||
}
|
||||
|
||||
// Incoming: a bunch of crap for the feature where if you hover over
|
||||
// a message, it highlights messages from that user
|
||||
var safeUsername = data.username.replace(/[^\w-]/g, '\\$');
|
||||
|
|
@ -3413,7 +3452,7 @@ function startQueueSpinner(data) {
|
|||
}
|
||||
|
||||
var id = data.id;
|
||||
if (data.type === "yp") {
|
||||
if (data.type === "yp" || data.type === "ia") {
|
||||
id = "$any";
|
||||
}
|
||||
|
||||
|
|
@ -3461,6 +3500,7 @@ function stopQueueSpinner(data) {
|
|||
$("#queueprogress").data("queue-id") === data.id);
|
||||
shouldRemove = shouldRemove || data === null;
|
||||
shouldRemove = shouldRemove || $("#queueprogress").data("queue-id") === "$any";
|
||||
//This is a gross way to fix the issue with IA but it works, and it's not like cytube was a pretty codebase anywho...
|
||||
if (shouldRemove) {
|
||||
$("#queueprogress").remove();
|
||||
}
|
||||
|
|
@ -3612,3 +3652,9 @@ CyTube._internal_do_not_use_or_you_will_be_banned.addUserToList = function (data
|
|||
addUserDropdown(div, data);
|
||||
div.appendTo($("#userlist"));
|
||||
};
|
||||
|
||||
function randrange(min,max){
|
||||
min = Math.ceil(min);
|
||||
max = Math.floor(max);
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min; //The maximum is exclusive and the minimum is inclusive
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue