328 lines
17 KiB
Plaintext
328 lines
17 KiB
Plaintext
//
|
|
fore.st is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
fore.st is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with fore.st. If not, see < http://www.gnu.org/licenses/ >.
|
|
(C) 2022- by rainbownapkin, <ourforest@420blaze.it>
|
|
|
|
//
|
|
Original cytube license:
|
|
MIT License
|
|
|
|
Copyright (c) 2013-2022 Calvin Montgomery
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
|
|
doctype html
|
|
html(lang="en")
|
|
head
|
|
include head
|
|
+head()
|
|
link(href="//code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css", rel="stylesheet")
|
|
link(rel="stylesheet", href="/css/video-js.css")
|
|
link(rel="stylesheet", href="/css/videojs-resolution-switcher.css")
|
|
body
|
|
#wrap
|
|
nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation")
|
|
include nav
|
|
+navheader()
|
|
#nav-collapsible.collapse.navbar-collapse
|
|
ul.nav.navbar-nav
|
|
+navdefaultlinks()
|
|
li: a(href="javascript:void(0)", onclick="javascript:showUserOptions()") Options
|
|
li: a#showchansettings(href="javascript:void(0)", onclick="javascript:showChannelSettings()") Channel Settings
|
|
+navsuperadmin(true)
|
|
+navloginlogout()
|
|
section#mainpage
|
|
.container
|
|
#motdrow.row
|
|
#motdwrap.well
|
|
button#togglemotd.close.pull-right(type="button")
|
|
span.glyphicon.glyphicon-minus
|
|
#motd
|
|
.clear
|
|
#announcements.row
|
|
#titles.row
|
|
p#videowrap-header
|
|
i#blindvideo.glyphicon.glyphicon-chevron-down.pointer(title="Hide Player")
|
|
span#currenttitle Nothing Playing
|
|
span#minicontrol
|
|
span#vidmute.glyphicon.glyphicon-volume-up.pointer(title="Mute")
|
|
span#vidplay.glyphicon.glyphicon-play.pointer(title="Play")
|
|
span#viddur 0:00/0:00
|
|
span#mediarefresh.playercont.glyphicon.glyphicon-retweet.pointer(title="Reload the video player")
|
|
span#flipx-video.playercont.glyphicon.glyphicon-resize-horizontal.pointer(title="Flip Player Horizontally",onclick='javascript:$("#ytapiplayer").toggleClass("mirx")')
|
|
span#flipy-video.playercont.glyphicon.glyphicon-resize-vertical.pointer(title="Flip Player Vertically",onclick='javascript:$("#ytapiplayer").toggleClass("miry")')
|
|
span#showplaylist.playercont.glyphicon.glyphicon-list.pointer(style="display: none;", title="Show playlist")
|
|
span#cinemode.playercont.glyphicon.glyphicon-film.pointer(title="Toggle Cinema Mode")
|
|
span#lockaspect.playercont.glyphicon.glyphicon-picture.pointer(style="display: none;", title="Lock to Aspect Ratio")
|
|
span#latchvid.label.label-default.pull-right.pointer(style="display: none;") Sync
|
|
#chatheader
|
|
i#blindchat.glyphicon.glyphicon-chevron-down.pointer(title="Hide Chat")
|
|
span#modflair.label.label-default.pull-right.pointer Name Color
|
|
span(style="flex-grow: 2;")
|
|
span#usercount.pointer Not Connected
|
|
i#userlisttoggle.glyphicon.glyphicon-chevron-down.pull-left.pointer(title="Show/Hide Userlist")
|
|
#main.row
|
|
#videowrap
|
|
.embed-responsive.embed-responsive-16by9
|
|
#ytapiplayer.embed-responsive-item
|
|
div#subliminaltoke
|
|
img(src="/img/tokeleaf.png")
|
|
h3 Take a <a onclick="chatsmack('!toke')">Toke!</a>
|
|
#chatwrap
|
|
#chatmain
|
|
#userlist
|
|
#fpaneldiv.fpanel(style="display: none;")
|
|
#fptitlediv.fptitlebar.fpanel
|
|
p#fptitle.fptitlebar.fpanel null
|
|
p#closefpanel.fptitlebar.fpanel.glyphicon.glyphicon-remove.pointer(onclick="javascript:closeFPanel()", title="Close null panel.")
|
|
#fpcontdiv.fpcont.fpanel
|
|
#messagebuffer.linewrap
|
|
#chatbar(style="display: flex;")
|
|
button#pollopenbtn.btn.btn-sm.btn-default.glyphicon.glyphicon-ok.chatbtn(onclick="javascript:panelbtn(fpoll)",style="display: none;", title="Poll")
|
|
button#prefopenbtn.btn.btn-sm.btn-default.glyphicon.glyphicon-cog.chatbtn(onclick="javascript:panelbtn(fpset)",title="Quick Settings")
|
|
button#emoteopenbtn.btn.btn-sm.btn-default.chatbtn(onclick="javascript:panelbtn(fpemote)",title="Emotes") ;)
|
|
form(action="javascript:void(0)" style="display: flex; flex-grow: 1;")
|
|
input#chatline.form-control(type="text", maxlength="320", style="display: none")
|
|
#guestlogin.input-group
|
|
span.input-group-addon Registration Required!
|
|
//input#guestname.form-control(type="text", placeholder="Name")
|
|
button#chatsend.btn.btn-sm.btn-default Send
|
|
|
|
//#videocontrols.btn-group.pull-right
|
|
//button#fullscreenbtn.btn.btn-sm.btn-default(title="Make the video player fullscreen")This makes no sense, all supported players already have a full screen button. Not a fan of the placement of this anywho
|
|
//span.glyphicon.glyphicon-fullscreen
|
|
//button#voteskip.btn.btn-sm.btn-default(title="Voteskip") I don't like the way this is implemented, I think a poll based voteskip feature would be better, though I think it'd be better to not do a full rip-out
|
|
//span.glyphicon.glyphicon-step-forward
|
|
//this is being removed in favor of better UI
|
|
//#leftcontrols.col-lg-5.col-md-5
|
|
//button#newpollbtn.btn.btn-sm.btn-default New Poll
|
|
#playlistrow.row
|
|
#rightpane
|
|
#rightpane-inner.row
|
|
#rightcontrols
|
|
#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#showsearch.btn.btn-sm.btn-default(title="Channel History + Video Search", data-toggle="collapse", data-target="#searchcontrol")
|
|
span.glyphicon.glyphicon-search
|
|
button#showplaylistmanager.btn.btn-sm.btn-default(title="Manage playlists", data-toggle="collapse", data-target="#playlistmanager")
|
|
span.glyphicon.glyphicon-list
|
|
button#scrollitm.btn.btn-sm.btn-default(title="Scroll to Current Item",onclick="javascript:scrollQueue()")
|
|
span.glyphicon.glyphicon-hand-right
|
|
button#getplaylist.btn.btn-sm.btn-default(title="Retrieve playlist links")
|
|
span.glyphicon.glyphicon-link
|
|
button#shuffleplaylist.btn.btn-sm.btn-default(title="Shuffle the playlist")
|
|
span.glyphicon.glyphicon-sort
|
|
button#clearplaylist.btn.btn-sm.btn-default(title="Clear the playlist")
|
|
span.glyphicon.glyphicon-trash
|
|
button#qlockbtn.btn.btn-sm.btn-danger(title="Playlist locked")
|
|
span.glyphicon.glyphicon-lock
|
|
#playlistauxcont
|
|
span#hideplaylist.glyphicon.glyphicon-remove.pointer(title="Close Playlist")
|
|
span#blindItems.glyphicon.glyphicon-resize-small.pointer(title="Collapse All Items")
|
|
#plmeta
|
|
span#plcount 0 items
|
|
br
|
|
span#pllength 00:00:00
|
|
#searchcontrol.collapse.plcontrol-collapse.col-lg-12.col-md-12
|
|
.vertical-spacer
|
|
.input-group
|
|
input#library_query.form-control(type="text", placeholder="Search query")
|
|
span.input-group-btn
|
|
button#library_search.btn.btn-default Library
|
|
span.input-group-btn
|
|
button#youtube_search.btn.btn-default YouTube
|
|
.checkbox
|
|
label
|
|
input.add-temp(type="checkbox")
|
|
| Add as temporary
|
|
ul#library.videolist.col-lg-12.col-md-12
|
|
#addfromurl.collapse.plcontrol-collapse.col-lg-12.col-md-12
|
|
.vertical-spacer
|
|
.input-group
|
|
input#mediaurl.form-control(type="text", placeholder="Media URL")
|
|
span.input-group-btn
|
|
button#queue_next.btn.btn-default Queue next
|
|
span.input-group-btn
|
|
button#queue_end.btn.btn-default Queue last
|
|
span.input-group-btn#showcustomembed
|
|
button#showcustomembed.btn.btn-default(title="Embed a custom frame", data-toggle="collapse", data-target="#customembed")
|
|
span.glyphicon.glyphicon-th-large
|
|
.checkbox
|
|
label
|
|
input.add-temp(type="checkbox")
|
|
| Add as temporary
|
|
div#addfromurl-queue
|
|
#customembed.collapse.plcontrol-collapse.col-lg-12.col-md-12
|
|
.vertical-spacer
|
|
.input-group
|
|
input#customembed-title.form-control(type="text", placeholder="Title (optional)")
|
|
span.input-group-btn
|
|
button#ce_queue_next.btn.btn-default Queue next
|
|
span.input-group-btn
|
|
button#ce_queue_end.btn.btn-default Queue last
|
|
.checkbox
|
|
label
|
|
input.add-temp(type="checkbox")
|
|
| Add as temporary
|
|
| Paste the embed code below and click Next or At End.
|
|
| Acceptable embed codes are <code><iframe></code> and <code><object></code> tags. <strong>CUSTOM EMBEDS CANNOT BE SYNCHRONIZED.</strong>
|
|
textarea#customembed-content.input-block-level.form-control(rows="3")
|
|
#playlistmanager.collapse.plcontrol-collapse.col-lg-12.col-md-12
|
|
.vertical-spacer
|
|
.input-group
|
|
input#userpl_name.form-control(type="text", placeholder="Playlist Name")
|
|
span.input-group-btn
|
|
button#userpl_save.btn.btn-default Save
|
|
.checkbox
|
|
label
|
|
input.add-temp(type="checkbox")
|
|
| Add as temporary
|
|
ul#userpl_list.videolist
|
|
#queuefail.col-lg-12.col-md-12
|
|
.col-lg-12.col-md-12
|
|
ul#queue.videolist
|
|
//#leftpane.col-lg-5.col-md-5
|
|
//#leftpane-inner.row
|
|
//#pollwrap.col-lg-12.col-md-12
|
|
//#playlistmanagerwrap.col-lg-12.col-md-12
|
|
#resizewrap.row
|
|
.col-lg-5.col-md-5
|
|
#videowidth.col-lg-7.col-md-7
|
|
#sitefooter
|
|
include pagefooter
|
|
#useroptions.modal.fade(tabindex="-1", role="dialog", aria-hidden="true")
|
|
.modal-dialog
|
|
.modal-content
|
|
.modal-header
|
|
button.close(data-dismiss="modal", aria-hidden="true") ×
|
|
h4 User Preferences
|
|
ul.nav.nav-tabs
|
|
li: a(href="#us-general", data-toggle="tab") General
|
|
li: a(href="#us-playback", data-toggle="tab") Playback
|
|
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
|
|
.modal-body
|
|
.tab-content
|
|
include useroptions
|
|
+us-general()
|
|
+us-playback()
|
|
+us-chat()
|
|
+us-scripts()
|
|
+us-mod()
|
|
.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
|
|
#emotelist.modal.fade(tabindex="-1", role="dialog", aria-hidden="true")
|
|
.modal-dialog.modal-dialog-nonfluid
|
|
.modal-content
|
|
.modal-header
|
|
button.close(data-dismiss="modal", aria-hidden="true") ×
|
|
h4 Emote List
|
|
.modal-body
|
|
.pull-left
|
|
input.emotelist-search.form-control(type="text", placeholder="Search")
|
|
.pull-right
|
|
.checkbox
|
|
label
|
|
input.emotelist-alphabetical(type="checkbox")
|
|
| Sort alphabetically
|
|
.emotelist-paginator-container
|
|
table.emotelist-table
|
|
tbody
|
|
.modal-footer
|
|
#channeloptions.modal.fade(tabindex="-1", role="dialog", aria-hidden="true")
|
|
.modal-dialog
|
|
.modal-content
|
|
.modal-header
|
|
button.close(data-dismiss="modal", aria-hidden="true") ×
|
|
h4 Channel Settings
|
|
ul.nav.nav-tabs
|
|
li.active: a(href="#cs-miscoptions", data-toggle="tab") General Settings
|
|
li: a(href="#cs-adminoptions", data-toggle="tab") Admin Settings
|
|
li.dropdown
|
|
a#cs-edit-dd-toggle(href="#", data-toggle="dropdown") Edit
|
|
span.caret
|
|
ul.dropdown-menu
|
|
li: a(href="#cs-chatfilters", data-toggle="tab", onclick="javascript:socket.emit('requestChatFilters')") Chat Filters
|
|
li: a(href="#cs-emotes", data-toggle="tab") Emotes
|
|
li: a(href="#cs-motdeditor", data-toggle="tab", tabindex="-1") MOTD
|
|
li: a(href="#cs-csseditor", data-toggle="tab", tabindex="-1") CSS
|
|
li: a(href="#cs-jseditor", data-toggle="tab", tabindex="-1") Javascript
|
|
li: a(href="#cs-permedit", data-toggle="tab", tabindex="-1") Permissions
|
|
li: a(href="#cs-chanranks", data-toggle="tab", tabindex="-1", onclick="javascript:socket.emit('requestChannelRanks')") Moderators
|
|
li: a(href="#cs-banlist", data-toggle="tab", tabindex="-1", onclick="javascript:socket.emit('requestBanlist')") Ban list
|
|
li: a(href="#cs-chanlog", data-toggle="tab", onclick="javascript:socket.emit('readChanLog')") Log
|
|
.modal-body
|
|
.tab-content
|
|
include channeloptions
|
|
+miscoptions()
|
|
+adminoptions()
|
|
+motdeditor()
|
|
+csseditor()
|
|
+jseditor()
|
|
+banlist()
|
|
+recentjoins()
|
|
+chanranks()
|
|
+chatfilters()
|
|
+emotes()
|
|
+chanlog()
|
|
+permeditor()
|
|
.modal-footer
|
|
button.btn.btn-default(type="button", data-dismiss="modal") Close
|
|
#pmbar
|
|
include footer
|
|
+footer()
|
|
script(id="socketio-js", src=sioSource)
|
|
script(src="/js/fpanel.js")
|
|
script(src="/js/data.js")
|
|
script(src="/js/fembed.js")
|
|
script(src="/js/fchat.js")
|
|
script(src="/js/util.js")
|
|
script(src="/js/tabcomplete.js")
|
|
script(src="/js/player.js")
|
|
script(src="/js/paginator.js")
|
|
script(src="/js/ui.js")
|
|
script(src="/js/callbacks.js")
|
|
script(src="/js/fschd.js")
|
|
script(defer, src="https://www.youtube.com/iframe_api")
|
|
script(defer, src="https://api.dmcdn.net/all.js")
|
|
script(defer, src="https://player.vimeo.com/api/player.js")
|
|
script(defer, src="/js/sc.js")
|
|
script(defer, src="/js/video.js")
|
|
script(defer, src="/js/playerjs-0.0.12.js")
|
|
script(defer, src="/js/videojs-contrib-hls.min.js")
|
|
script(defer, src="/js/videojs-resolution-switcher.js")
|
|
script(defer, src="/js/dash.all.min.js")
|
|
script(defer, src="/js/videojs-dash.js")
|
|
script(defer, src="https://player.twitch.tv/js/embed/v1.js")
|
|
script(type='text/javascript').
|
|
handleWindowResize();
|