init commit

This commit is contained in:
zino
2021-02-16 23:07:41 +01:00
parent ec3fc78e0f
commit 12b4ef5db4
5000 changed files with 2596132 additions and 0 deletions

View File

@@ -0,0 +1,464 @@
/*
* Copyright (C) 2016-2020 phantombot.github.io/PhantomBot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author ScaniaTV
*/
:root {
--body-color: #1b1b1b;
--tab-color: #212121;
--url-color: #6441a5;
--url-hover-color: #6b49ab;
--text-color: #cccccc;
--table-odd: #2f2e2e;
--table-even: #424242;
--btn-bgn-color: #6441a5;
--btn-bgn-hover: #6b49ab;
--btn-txt-color: #2c2e2f;
--btn-bor-color: #4b3671;
--devider: #424242;
--modal-body: #1b1b1b;
--input-box: #212121;
--drop-hover: #232323;
}
body {
background-color: var(--body-color);
max-width: 100%;
margin-bottom: 45px;
overflow-x: hidden;
}
* {
font-family: "Roboto", arial, sans-serif;
color: var(--text-color);
}
a {
text-decoration: none;
color: var(--url-color);
}
a:hover {
text-decoration: none;
color: var(--url-hover-color);
}
.header {
background-color: #6441a5;
margin-top: -20px;
margin-bottom: 10px;
max-width: 100%;
height: 85px;
-webkit-box-shadow: 0 3px 5px #000;
-moz-box-shadow: 0 3px 5px #000;
box-shadow: 0 3px 5px #000;
overflow: hidden;
}
.logo-header {
height: 65px;
margin-left: 15px;
float: left;
display: inline-block;
margin-top: 17px;
}
.copyright {
font-size: 14px;
float: left;
width: 100%;
padding-top: 5px;
text-align: center;
bottom: 0;
background-color: var(--tab-color);
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2);
position: fixed;
height: 30px;
}
.player-section {
display: block;
height: 715px;
width: 100%;
float: left;
margin-top: 15px;
background-color: var(--tab-color);
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2);
}
.queue-section {
display: block;
height: 350px;
width: 100%;
float: right;
margin-top: 15px;
background-color: var(--tab-color);
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2);
}
.playlist-section {
display: block;
height: 350px;
width: 100%;
float: right;
margin-top: 15px;
background-color: var(--tab-color);
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2);
}
.yt-frame {
margin-top: 15px;
margin-left: 15px;
margin-right: 15px;
height: 514px;
width: calc(100% - 30px);
}
.player-controls {
margin-top: 10px;
margin-left: 15px;
margin-right: 15px;
}
.left-controls {
display: inline-block;
width: calc(70% - 10px);
}
.play-pause-controls {
display: inline-block;
width: 135px;
}
.progress-slider {
display: inline-block;
width: calc(100% - 170px);
margin-right: 10px;
}
.right-controls {
display: inline-block;
width: 30%;
}
.mute-control {
display: inline-block;
width: 30px;
}
.volume-slider {
display: inline-block;
width: calc(100% - 40px);
}
#volume-slider-value {
display: inline-block;
float: right;
}
#progress-slider-value {
display: block;
float: right;
}
.video-information {
font-size: 14px;
margin-left: 15px;
margin-top: 5px;
display: block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
#queue-table {
margin-top: 15px;
margin-left: 15px;
}
.queue-table {
margin-top: 15px;
margin-left: 15px;
margin-right: 15px;
max-height: 265px;
overflow-y: scroll;
}
.playlist-table {
margin-top: 15px;
margin-left: 15px;
margin-right: 15px;
max-height: 265px;
overflow-y: scroll;
}
.queue-section-title {
margin: 15px;
}
.devider {
display: block;
border-top-width: 1px;
border-top-style: solid;
margin: 0;
border-top-color: var(--devider);
}
.header-button {
float: right;
}
.clickable {
cursor: pointer;
}
/* Loader */
.loader {
position: absolute;
left: 50%;
top: 50%;
z-index: 1;
margin: -75px 0 0 -75px;
border: 16px solid #f2f2f2;
border-radius: 50%;
border-top: 16px solid #6441A4;
width: 120px;
height: 120px;
-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* Bootstrap buttons */
.btn {
border-radius: 0;
cursor: pointer;
}
.btn:focus,
.btn:active:focus,
.btn.active:focus {
border-radius: 0;
outline: none;
box-shadow: none;
}
.btn-secondary,
.btn-primary {
color: var(--text-color);
background-color: var(--btn-bgn-color);
border-color: var(--btn-bor-color);
}
.btn-secondary:hover,
.btn-primary:hover {
color: var(--text-color);
background-color: var(--btn-bgn-hover);
border-color: var(--btn-bor-color);
}
.btn-secondary.active,
.btn-secondary:active,
.show>.btn-secondary.dropdown-toggle {
color: var(--text-color);
background-color: var(--drop-hover);
border-color: var(--input-box);
}
/* Dropdown */
.dropdown,
.dropdown-toggle {
text-align: left;
width: 100%;
color: var(--text-color);
background-color: var(--input-box);
border-color: var(--input-box);
}
.dropdown:hover,
.dropdown-toggle:hover {
color: var(--text-color);
background-color: var(--drop-hover);
border-color: var(--drop-hover);
}
.dropdown-menu {
border-radius: 0;
text-align: left;
width: 100%;
background-color: #171717;
}
.dropdown-item {
text-align: left;
width: 100%;
color: var(--text-color);
}
.dropdown-item:hover {
background-color: #232323;
color: var(--text-color);
}
.dropdown-toggle::after {
display: inline-block;
width: 0;
height: 0;
margin-left: .3em;
vertical-align: middle;
content: "";
border-top: .3em solid;
border-right: .3em solid transparent;
border-left: .3em solid transparent;
float: right;
margin-top: 8px;
}
/* Bootsrap sliders */
.slider.slider-horizontal {
margin-left: 10px;
margin-right: 10px;
width: 100%;
}
.slider-handle,
.min-slider-handle,
.round {
cursor: pointer;
}
.slider-selection {
background-image: linear-gradient(to bottom, #989797 0, #989797 100%);
}
.slider-track,
.slider-track-low,
.slider-track-high {
background-color: #989797;
background-image: linear-gradient(to bottom, #989797 0, #989797 100%);
}
.slider-handle {
background-image: linear-gradient(to bottom, #6441a5 0, #6441a5 100%);
}
/* Table code */
table {
border: none;
width: 100%;
}
table tr:nth-child(odd) {
background: var(--table-odd);
color: black;
font-size: 14px;
}
table tr:nth-child(even) {
background: var(--table-even);
color: black;
font-size: 14px;
}
table td {
padding: 5px;
text-align: left;
vertical-align: middle;
}
table th {
padding: 5px;
text-align: left;
vertical-align: middle;
}
/* Modals */
.modal-content {
border-radius: 0;
}
.modal-body {
background-color: var(--modal-body);
}
.modal-footer {
border-top: 1px solid var(--devider);
background-color: var(--modal-body);
}
.modal-header {
border-bottom: 1px solid var(--devider);
background-color: var(--modal-body);
}
.form-control {
color: var(--text-color);
background-color: var(--input-box);
border-radius: 0;
border: 1px solid rgba(70, 70, 70, 0.15);
}
.form-control:focus, .form-control:active {
color: var(--text-color);
background-color: var(--drop-hover);
border-color: var(--btn-bgn-color);
border-radius: 0;
}
.form-control:hover {
background-color: var(--drop-hover);
}
.close,
.close:focus,
.close:hover {
color: var(--text-color);
}
.off {
display: none;
}

View File

@@ -0,0 +1,229 @@
<!--
Copyright (C) 2016-2020 phantombot.github.io/PhantomBot
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
@author ScaniaTV
-->
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Meta data -->
<meta charset="utf-8">
<!-- Page title -->
<title>PhantomBot YouTube Player</title>
<!-- Load Bootstrap 4.0 -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css">
<!-- Load Fontawesome 5.0.4 -->
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.4/css/all.css">
<!-- Bootstrap sliders 10.0.0 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/10.0.0/css/bootstrap-slider.min.css">
<!-- Toast notifications v2.1.3 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css">
<!-- Load our styles -->
<link rel="stylesheet" href="/ytplayer/css/style.css">
<!-- Load Google fonts -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto">
<!-- Load our favicon -->
<link rel="icon" href="/favicon.ico">
</head>
<body>
<!-- Loader -->
<div class="loader"></div>
<div id="main" style="display: none;">
<!-- Header -->
<div class="header">
<img class="logo-header" alt="Logo" src="/common/images/logo.png">
</div>
<!-- Container div -->
<div class="container-fluid">
<!-- Row div -->
<div class="row">
<!-- Left side -->
<section class="col-md-7" id="left-section">
<!-- Player div with all controls -->
<div class="player-section">
<!-- Player frame -->
<div class="yt-frame" id="player-frame"></div>
<!-- Player controls -->
<div class="player-controls">
<!-- Left play, pause, skip, and save button and slider -->
<div class="left-controls">
<!-- Play pause controls -->
<div class="play-pause-controls">
<div class="btn-group btn-group-justified">
<!-- Play and pause button handled in js -->
<button type="button" class="btn btn-secondary btn-sm" id="pause-button" data-toggle="tooltip" title="Play song">
<i class="fas fa-play" id="play-pause-button"></i>
</button>
<!-- Next song button -->
<button type="button" class="btn btn-secondary btn-sm" id="skip-button" data-toggle="tooltip" title="Skip song">
<i class="fas fa-fast-forward"></i>
</button>
<!-- Add to playlist button -->
<button type="button" class="btn btn-secondary btn-sm" id="fav-button" data-toggle="tooltip" title="Add to playlist (steal song)">
<i class="fas fa-star"></i>
</button>
<!-- delete from playlist button -->
<button type="button" class="btn btn-secondary btn-sm" id="del-cur-playlist-button" data-toggle="tooltip" title="Delete from playlist">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<!-- Progress slider -->
<div class="progress-slider">
<input type="text" id="progress-slider" />
<!-- Progress slider value -->
<label for="progress-slider" id="progress-slider-value">0:00</label>
</div>
</div>
<!-- Right volume button and slier -->
<div class="right-controls">
<!-- Volume controls -->
<div class="mute-control">
<!-- Add to playlist button -->
<button type="button" class="btn btn-secondary btn-sm" id="mute-button" data-toggle="tooltip" title="Mute song">
<i class="fas fa-volume-down" id="mute-button-icon"></i>
</button>
</div>
<!-- Volume slider -->
<div class="volume-slider">
<input type="text" id="volume-slider" />
<!-- Volume slider value -->
<label for="volume-slider" id="volume-slider-value">0%</label>
</div>
</div>
</div>
<!-- Video information -->
<div class="video-information">
<p><b>Title:</b> <span id="video-title"></span></p>
<p><b>URL:</b> <span id="video-url"></span></p>
<p><b>Requested by:</b> <span id="user-requester"></span></p>
</div>
</div>
</section>
<!-- Right side -->
<section class="col-md-5" id="right-section">
<!-- Queue section top panel -->
<div class="queue-section">
<!-- Header with buttons -->
<div class="queue-section-title">Queue
<div class="btn-group btn-group-justified header-button">
<!-- Add song to queue button -->
<button type="button" class="btn btn-secondary btn-sm" id="queue-add-song-button" data-toggle="tooltip" title="Add song">
<i class="fas fa-plus"></i>
</button>
<!-- Settings button -->
<button type="button" class="btn btn-secondary btn-sm" id="settings-button" data-toggle="tooltip" title="Settings">
<i class="fas fa-cog"></i>
</button>
<!-- Clear queue button -->
<!-- <button type="button" class="btn btn-secondary btn-sm" data-toggle="tooltip" title="Clear queue">
<i class="fas fa-trash"></i>
</button> -->
</div>
</div>
<!-- Devider -->
<div class="devider"></div>
<!-- Queue table -->
<div class="queue-table">
<table id="queue-table-content" style="width: 100%;">
<tr>
<th style="width: 5%;">#</th>
<th style="width: 50%;">Song</th>
<th style="width: 15%;">Duration</th>
<th style="width: 15%;">Requester</th>
<th style="width: 10%; text-align: right; padding-right: 16px;">Actions</th>
</tr>
</table>
</div>
</div>
<!-- Playlist -->
<div class="playlist-section">
<!-- Header with buttons -->
<div class="queue-section-title">Playlist <span id="playlist-name"></span>
<div class="btn-group btn-group-justified header-button">
<!-- Add song to playlist button -->
<button type="button" class="btn btn-secondary btn-sm" id="playlist-add-song-button" data-toggle="tooltip" title="Add song">
<i class="fas fa-plus"></i>
</button>
<!-- Shuffle button -->
<button type="button" class="btn btn-secondary btn-sm" id="playlist-shuffle-button" data-toggle="tooltip" title="Shuffle songs">
<i class="fas fa-random"></i>
</button>
<!-- Load playlist button -->
<button type="button" class="btn btn-secondary btn-sm" id="load-playlist-button" data-toggle="tooltip" title="Load playlist">
<i class="fas fa-upload"></i>
</button>
</div>
</div>
<!-- Devider -->
<div class="devider"></div>
<!-- Playlist section bottom panel -->
<div class="playlist-table" id="playlist-table-id">
<table id="playlist-table-content" style="width: 100%; overflow: scroll;">
<tbody id="playlist-content">
<tr>
<th style="width: 5%;">#</th>
<th style="width: 70%;">Song</th>
<th style="width: 15%;">Duration</th>
<th style="width: 10%;">Actions</th>
</tr>
</tbody>
</table>
</div>
</div>
</section>
</div>
</div>
<!-- Copyright bar -->
<footer class="copyright">
<a href="https://phantombot.github.io/PhantomBot">PhantomBot</a> &copy; 2016 -
<script>
document.write(new Date().getFullYear());
</script>
</footer>
</div>
<!-- Load jQuery 3.3.0 -->
<script src="https://code.jquery.com/jquery-3.3.0.min.js"></script>
<!-- Load tether 1.4.0 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js"></script>
<!-- Load Bootstrap 4.0 -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"></script>
<!-- Bootstrap sliders 10.0.0 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/10.0.0/bootstrap-slider.min.js"></script>
<!-- Toast notifications v2.1.3 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js"></script>
<!-- Clusterize -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/clusterize.js/0.18.0/clusterize.min.js"></script>
<!-- Load the player config file -->
<script src="/ytplayer/js/playerConfig.js"></script>
<!-- Helper functions -->
<script src="/ytplayer/js/util/helpers.js"></script>
<!-- Load the main player script -->
<script src="/ytplayer/js/index.js"></script>
<!-- Load the websocket -->
<script src="/ytplayer/js/util/socket.js"></script>
</body>
</html>

View File

@@ -0,0 +1,621 @@
/*
* Copyright (C) 2016-2020 phantombot.github.io/PhantomBot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author ScaniaTV
*/
/*
* Main player functions and listeners.
*
* Every global functions should be added under the "player" object.
* The iframe API from YouTube can be accessed from "player.API".
* Basic information about the current song can be access with "player.temp.song", "player.temp.title", and "player.temp.requester".
* For more functions under the "player" object, take a look at the socket.js script.
* You can also generate modals with jQuery, see util/helpers.js for more information.
*/
$(function() {
var cluster = null,
timer = null;
/*
* @function Loads the player page.
*
* @param {boolean} hasPlaylistData - If a playlist is loaded.
*/
const openPlayer = hasPlaylistData => {
$('.loader').fadeOut(6e2, () => {
$(this).remove();
});
// Show the page.
$('#main').fadeIn(5e2);
if (!hasPlaylistData && player.hasAPIKey() && !player.secondConnection) {
toastr.error('Failed to load a playlist with songs.');
// Create a fake progress slider.
player.progressSlider = $('#progress-slider').slider({
'value': 0,
'min': 0,
'step': 0.1,
'tooltip': 'hide',
'selection': 'none'
});
// Error the to user.
helpers.getErrorModal('Playlist Error', 'Failed to load a playlist with songs, please load a playlist.', () => {
player.dbQuery('get_playlists', 'yt_playlists_registry', (results) => {
// Get the keys.
results = Object.keys(results);
const playlists = [];
for (let i = 0; i < results.length; i++) {
if (results[i].indexOf('ytPlaylist_') !== -1) {
playlists.push(results[i].substr(results[i].indexOf('_') + 1, results[i].length));
}
}
helpers.getPlaylistModal('Load Playlist', 'Playlist Name', 'Load', 'Playlist', playlists, () => {
let playlist = $('#playlist-load').find(':selected').text();
if (playlist === 'Select a playlist') {
toastr.error('Please select a valid playlist.');
} else {
if (playlist.length > 0) {
player.loadPlaylist(playlist);
toastr.success('Loading playlist: ' + playlist);
if (player.firstLoad === true) {
player.ready();
}
}
}
}).modal('toggle');
});
}).modal('toggle');
}
};
/*
* Global function that is called once the socket is connected and that the YouTube iframe is loaded.
*/
window.onYouTubeIframeAPIReady = () => {
// Set a timer in case no songs load to send a error.
timer = setTimeout(openPlayer, 5e3, false);
// Set a var for the slider.
player.canSlide = true;
// Set a var for the first load.
player.firstLoad = true;
// Check if the player is disabled right away.
player.dbQuery('get_module_status', 'modules', (data) => {
if (data['./systems/youtubePlayer.js'] == 'false') {
helpers.getErrorModal('Module Disabled', 'The YouTube player module is disabled, please go and enable it.', () => {
window.location.reload();
}).modal('toggle');
openPlayer(true);
clearTimeout(timer);
}
});
// Add a listener to load the main playlist.
player.addListener('playlist', (e) => {
let table = [],
playlist = e.playlist;
// Set the playlist name.
$('#playlist-name').html('(' + e.playlistname + ')');
// Table header.
table.push(($('<tr>').append($('<th/>', {
'style': 'width: 5%;',
'html': '#'
})).append($('<th/>', {
'style': 'width: 70%;',
'html': 'Song'
})).append($('<th/>', {
'style': 'width: 15%;',
'html': 'Duration'
})).append($('<th/>', {
'style': 'width: 10%; text-align: right; padding-right: 16px;',
'html': 'Actions'
}))).html());
for (let i = 0; i < playlist.length; i++) {
let row = $('<tr/>');
// Add position.
row.append($('<td/>', {
'text': i,
'style': 'width: 5%;'
}));
// Add song name.
row.append($('<td/>', {
'text': playlist[i].title,
'style': 'width: 70%;'
}));
// Add duration.
row.append($('<td/>', {
'text': playlist[i].duration,
'style': 'width: 15%;'
}));
// Add buttons.
row.append($('<td/>', {
'html': $('<div/>', {
'class': 'btn-group btn-group-justified header-button'
}).append($('<button/>', {
'type': 'button',
'class': 'btn btn-secondary btn-sm',
'data-toggle': 'tooltip',
'title': 'Play song',
'data-song': playlist[i].song,
'data-song-play': 'on',
'html': $('<i/>', {
'class': 'fas fa-play'
})
})).append($('<button/>', {
'type': 'button',
'class': 'btn btn-secondary btn-sm',
'data-toggle': 'tooltip',
'title': 'Delete song',
'data-song': playlist[i].song,
'data-song-remove': 'on',
'html': $('<i/>', {
'class': 'fas fa-trash'
})
})),
'style': 'width: 10%;'
}));
// Append the row.
table.push(row[0].outerHTML);
}
// Render the data.
if (cluster !== null) {
cluster.update(table);
} else {
cluster = new Clusterize({
rows: table,
scrollId: 'playlist-table-id',
contentId: 'playlist-content',
callbacks: {
clusterChanged: () => {
// Remove old events and register new ones.
$('[data-song-play="on"]').off().on('click', (e) => {
// Play the song.
player.updateSong($(e.currentTarget).data('song'));
// Hide the tooltip.
$(e.currentTarget).tooltip('hide');
});
// Remove old events and register new ones.
$('[data-song-remove="on"]').off().on('click', (e) => {
// Delete the song.
player.removeSongFromPlaylist($(e.currentTarget).data('song'));
// Hide the tooltip.
$(e.currentTarget).tooltip('hide');
// Remove the row.
$(e.currentTarget.closest('tr')).remove();
});
}
}
});
}
});
// Add a listener for the songrequest queue.
player.addListener('songlist', (e) => {
let table = $('#queue-table-content'),
songlist = e.songlist;
// Remove the current data from the table.
table.find('tr:gt(0)').remove();
for (let i = 0; i < songlist.length; i++) {
let row = $('<tr/>');
// Add position.
row.append($('<td/>', {
'text': i
}));
// Add song name.
row.append($('<td/>', {
'text': songlist[i].title
}));
// Add duration.
row.append($('<td/>', {
'text': songlist[i].duration
}));
// Add requester.
row.append($('<td/>', {
'text': songlist[i].requester
}));
// Add buttons.
row.append($('<td/>', {
'html': $('<div/>', {
'class': 'btn-group btn-group-justified header-button'
}).append($('<button/>', {
'type': 'button',
'class': 'btn btn-secondary btn-sm',
'data-toggle': 'tooltip',
'data-song': songlist[i].song,
'title': 'Play song',
'html': $('<i/>', {
'class': 'fas fa-play'
}),
'click': (e) => {
// Jump to the song.
player.updateSong($(e.currentTarget).data('song'));
// The song once jumped to it.
player.removeSongFromRequest($(e.currentTarget).data('song'));
// Hide the tooltip, or could stay opened.
$(e.currentTarget).tooltip('hide');
}
})).append($('<button/>', {
'type': 'button',
'class': 'btn btn-secondary btn-sm',
'data-toggle': 'tooltip',
'title': 'Delete song',
'data-song': songlist[i].song,
'html': $('<i/>', {
'class': 'fas fa-trash'
}),
'click': (e) => {
// Delete song.
player.removeSongFromRequest($(e.currentTarget).data('song'));
// Hide the tooltip, or could stay opened.
$(e.currentTarget).tooltip('hide');
}
}))
}));
// Append the row.
table.append(row);
}
});
// Add a listener for the volume.
player.addListener('setvolume', (e) => {
player.API.setVolume(e.setvolume);
// Update the value under the slider.
$('#volume-slider-value').html(e.setvolume + '%');
// Update the icon.
$('#mute-button-icon').attr('class', (e.setvolume === 0 ? 'fas fa-volume-off' : (e.setvolume > 50 ? 'fas fa-volume-up' : 'fas fa-volume-down')));
// Always destroy the old slider.
if (player.volumeSlider !== undefined) {
player.volumeSlider.slider('destroy');
}
// Add the volume slider event.
player.volumeSlider = $('#volume-slider').slider({
'value': e.setvolume,
'min': 0,
'max': 100,
'step': 1,
'tooltip': 'hide'
}).on('slide', (e) => {
// Make sure the player wasn't muted.
if (player.API.isMuted()) {
player.API.unMute();
}
// Update the player volume
player.API.setVolume(e.value);
// Update the value under the slider.
$('#volume-slider-value').html(e.value + '%');
// Update the icon.
$('#mute-button-icon').attr('class', (e.value === 0 ? 'fas fa-volume-off' : (e.value > 50 ? 'fas fa-volume-up' : 'fas fa-volume-down')));
}).on('slideStop', (e) => {
// Update the player volume
player.API.setVolume(e.value);
// Send the volume update event to the bot once done updating.
player.updateVolume(e.value);
// Update the value under the slider.
$('#volume-slider-value').html(e.value + '%');
// Update the icon.
$('#mute-button-icon').attr('class', (e.value === 0 ? 'fas fa-volume-off' : (e.value > 50 ? 'fas fa-volume-up' : 'fas fa-volume-down')));
});
});
// Add a listener for the play event.
player.addListener('play', (e) => {
// If this is the first load, start the player paused.
if (player.firstLoad === true) {
// Queue the first video
player.API.cueVideoById(e.play, 0, 'medium');
// Mark as not first load.
player.firstLoad = false;
// Clear timer
clearTimeout(timer);
// Remove loader.
openPlayer(true);
// Alert the user.
toastr.info('Song queued: ' + (e.title.length > 30 ? e.title.substring(0, 30) + '...' : e.title));
} else {
player.API.loadVideoById(e.play, 0, 'medium');
toastr.success('Now playing: ' + (e.title.length > 30 ? e.title.substring(0, 30) + '...' : e.title));
}
// Update the value under the slider.
$('#progress-slider-value').html(e.duration);
// Always destroy the old slider.
if (player.progressSlider !== undefined) {
player.progressSlider.slider('destroy');
}
// Add progress slider event.
player.progressSlider = $('#progress-slider').slider({
'value': 0,
'min': 0,
'step': 0.1,
'tooltip': 'hide',
'selection': 'none'
}).on('slide', (e) => {
player.API.seekTo(e.value, true);
}).on('slideStart', () => {
player.canSlide = false;
}).on('slideStop', () => {
player.canSlide = true;
});
// Update title information.
$('#video-title').html(e.title);
$('#video-url').html('<a href="https://youtu.be/' + e.play + '" target="_blank">https://youtu.be/' + e.play + '</a>');
$('#user-requester').html(e.requester);
// Request the songlist to remove played songs.
player.requestRequestList('songlist');
// Save this info for other use.
player.temp = {
song: e.play,
title: e.title,
user: e.requester
};
});
// Pause listener.
player.addListener('pause', () => {
if (player.API.getPlayerState() === 2) {
player.API.playVideo();
} else {
player.API.pauseVideo();
}
});
// Load the player.
player.API = new YT.Player('player-frame', {
events: {
'onReady': () => {
// Request the playlist.
player.requestPlaylist('playlist');
// Request the songlist.
player.requestRequestList('songlist');
// Send the ready event.
player.ready();
// Interval that updates the progess slider.
setInterval(() => {
if (player.progressSlider !== undefined && player.API.getPlayerState() === 1 && player.canSlide) {
player.progressSlider.slider('setAttribute', 'max', player.API.getDuration());
player.progressSlider.slider('setValue', player.API.getCurrentTime());
}
}, 5e2);
},
'onStateChange': (e) => {
// Make sure the button shows pause.
if (e.data === 1) {
$('#play-pause-button').attr('class', 'fas fa-pause');
} else if (e.data === 2) {
$('#play-pause-button').attr('class', 'fas fa-play');
}
player.updateState(e.data);
},
'onError': (e) => {
player.sendError(e.data);
}
},
playerVars: {
iv_load_policy: 3,
controls: 0,
showinfo: 0,
showsearch: 0,
autoplay: 1,
rel: 0
}
});
};
});
// Buttons and events.
$(function() {
// Delete current song from playlist button.
$('#del-cur-playlist-button').on('click', () => {
player.deleteFromPlaylist();
toastr.success('Deleted from playlist: ' + (player.temp.title.length > 30 ? player.temp.title.substring(0, 30) + '...' : player.temp.title));
});
// Fav button to steal a song.
$('#fav-button').on('click', () => {
player.addSongToPlaylist();
toastr.success('Added to playlist: ' + (player.temp.title.length > 30 ? player.temp.title.substring(0, 30) + '...' : player.temp.title));
});
// Skip song button.
$('#skip-button').on('click', () => {
// Mark the state as paused.
player.updateState(2);
// Skip the song.
player.skipSong();
});
// Mute button.
$('#mute-button').on('click', () => {
if (player.API.isMuted()) {
player.API.unMute();
$('#mute-button-icon').attr('class', (player.API.getVolume() === 0 ? 'fas fa-volume-off' : (player.API.getVolume() > 50 ? 'fas fa-volume-up' : 'fas fa-volume-down')));
} else {
player.API.mute();
$('#mute-button-icon').attr('class', 'fas fa-volume-off');
}
});
// Pause button.
$('#pause-button').on('click', () => {
if (player.API.getPlayerState() === 2 || player.API.getPlayerState() === 5) {
player.API.playVideo();
$('#play-pause-button').attr('class', 'fas fa-pause');
} else {
player.API.pauseVideo();
$('#play-pause-button').attr('class', 'fas fa-play');
}
});
// Add song to queue button.
$('#queue-add-song-button').on('click', () => {
helpers.getSongModal('Add Song to Queue', 'Song Name or YouTube Url', 'Add', 'https://youtu.be/dQw4w9WgXcQ', () => {
let song = $('#song-url').val();
if (song.length > 0) {
player.requestSong(song);
}
}).modal('toggle');
});
// Add song to playlist button.
$('#playlist-add-song-button').on('click', () => {
helpers.getSongModal('Add Song to Playlist', 'Song Name or YouTube Url', 'Add', 'https://youtu.be/dQw4w9WgXcQ', () => {
let song = $('#song-url').val();
if (song.length > 0) {
player.addSongToPlaylist(song);
}
}).modal('toggle');
});
// Load playlist button.
$('#load-playlist-button').on('click', () => {
player.dbQuery('get_playlists', 'yt_playlists_registry', (results) => {
// Get the keys.
results = Object.keys(results);
const playlists = [];
for (let i = 0; i < results.length; i++) {
if (results[i].indexOf('ytPlaylist_') !== -1) {
playlists.push(results[i].substr(results[i].indexOf('_') + 1, results[i].length));
}
}
helpers.getPlaylistModal('Load Playlist', 'Playlist Name', 'Load', 'Playlist', playlists, () => {
let playlist = $('#playlist-load').find(':selected').text();
if (playlist === 'Select a playlist') {
toastr.error('Please select a valid playlist.');
} else {
if (playlist.length > 0) {
player.loadPlaylist(playlist);
toastr.success('Loading playlist: ' + playlist);
}
}
}).modal('toggle');
});
});
// Settings button.
$('#settings-button').on('click', () => {
helpers.getSettingsModal(() => {
// Save the new size.
localStorage.setItem('phantombot_ytplayer_size', $('#player-size-btn').text().toLowerCase());
// Set the new size.
helpers.setPlayerSize();
// Update DJ name.
let djName = $('#dj-name').val();
if (djName.length > 0) {
player.dbUpdate('dj_name_up', 'ytSettings', 'playlistDJname', String(djName));
}
// Update user max songs.
let maxSongs = $('#max-song-user').val();
if (parseInt(maxSongs) > 0) {
player.dbUpdate('max_song_up', 'ytSettings', 'songRequestsMaxParallel', String(maxSongs));
}
// Update max song length.
let maxSongLen = $('#max-song-length').val();
if (parseInt(maxSongLen) > 0) {
player.dbUpdate('max_song_len_up', 'ytSettings', 'songRequestsMaxSecondsforVideo', String(maxSongLen));
}
//Update the votecount.
let voteCount = $('#vote-count').val();
if (parseInt(voteCount) > 0) {
player.dbUpdate('vote_count', 'ytSettings', 'voteCount', String(voteCount));
}
});
});
// Playlist shuffle button.
$('#playlist-shuffle-button').on('click', () => {
player.shufflePlaylist();
});
// Enable global tooltips.
$('body').tooltip({
selector: '[data-toggle="tooltip"]',
container: 'body',
trigger: 'hover',
delay: {
show: 350,
hide: 50
}
});
});
// Load other player settings.
$(function() {
// Toastr options.
toastr.options.progressBar = true;
toastr.options.preventDuplicates = true;
// Set the player size.
helpers.setPlayerSize();
if (helpers.urlIsIP()) {
toaster.warning('You may be accessing the YouTube player by using an IP Address in the URL.<br/><br/>'
+ 'YouTube\'s embed API really hates this and may refuse to work.<br/><br/>You should switch to using a '
+ 'Hostname, Domain, or Sub-Domain to avoid issues.', 'Potential Conflict Detected',
{
timeOut: 60000,
extendedTimeOut: 120000,
closeButton: true
});
}
});

View File

@@ -0,0 +1,11 @@
// Configuration for YTPlayer
// Automatically Generated by PhantomBot at Startup
// Do NOT Modify! Overwritten when PhantomBot is restarted!
var playerPort = 25000;
var channelName = "zino1337";
var auth="5TeJqWPU4j5avTqmdwgvsPPhqd8yY1";
var http="https://";
function getPlayerPort() { return playerPort; }
function getChannelName() { return channelName; }
function getAuth() { return auth; }
function getProtocol() { return http; }

View File

@@ -0,0 +1,282 @@
// Script that has helper functions.
$(function() {
var helpers = {};
/*
* @function Generates a basic modal, you have to append your own body with jQuery.
*
* @param {String} id
* @param {String} title
* @param {String} btn
* @param {Object} body
* @param {Function} onClose
* @return {Object}
*/
helpers.getModal = (id, title, btn, body, onClose) => {
return $('<div/>', {
'class': 'modal fade',
'id': id
}).append($('<div/>', {
'class': 'modal-dialog'
}).append($('<div/>', {
'class': 'modal-content'
}).append($('<div/>', {
'class': 'modal-header',
}).append($('<h5/>', {
'class': 'modal-title',
'text': title
})).append($('<button/>', {
'type': 'button',
'class': 'close',
'data-dismiss': 'modal',
'html': '&times;'
}))).append($('<div/>', {
'class': 'modal-body',
'html': body
})).append($('<div/>', {
'class': 'modal-footer',
}).append($('<button/>', {
'class': 'btn btn-primary',
'type': 'button',
'text': btn,
'data-dismiss': 'modal',
'click': onClose
}))))).on('hidden.bs.modal', () => {
$('#' + id).remove();
});
};
/*
* @function Generates a simple add song modal.
*
* @param {String} title
* @param {String} label
* @param {String} btn
* @param {String} placeholder
* @param {Function} onClose
* @return {Object}
*/
helpers.getSongModal = (title, label, btn, placeholder, onClose) => {
return helpers.getModal('song-modal', title, btn, $('<div/>', {
'class': 'form-group'
}).append($('<label/>', {
'text': label
})).append($('<input/>', {
'class': 'form-control',
'type': 'text',
'placeholder': placeholder,
'id': 'song-url',
'focus': () => {
$('#song-url').attr('placeholder', '');
},
'blur': () => {
$('#song-url').attr('placeholder', placeholder);
}
})), onClose);
};
/*
* @function Generates a load playlist modal
*
* @param {String} title
* @param {String} label
* @param {String} btn
* @param {String} placeholder
* @param {Array} playlists
* @param {Function} onClose
* @return {Object}
*/
helpers.getPlaylistModal = (title, label, btn, placeholder, playlists, onClose) => {
return helpers.getModal('playlist-load-modal', title, btn, $('<div/>', {
'class': 'form-group'
}).append($('<label/>', {
'text': label
})).append($('<select/>', {
'class': 'form-control',
'id': 'playlist-load',
'text': 'Select a playlist',
'style': 'width: 100%; cursor: pointer;',
'data-toggle': 'dropdown'
}).append($('<option/>', {
'html': 'Select a playlist',
'selected': 'true',
'disabled': 'true',
'hidden': 'true'
})).append(playlists.map(function(playlist) {
return $('<option/>', {
'html': playlist
});
})).append($('<option/>', {
'html': 'Select a playlist',
'disabled': 'true',
'hidden': 'true'
}))), onClose);
};
/*
* @function Generates a load playlist modal
*
* @param {String} title
* @param {String} body
* @param {Function} onClose
* @return {Object}
*/
helpers.getErrorModal = (title, body, onClose) => {
return helpers.getModal('err-modal', title, 'Ok', $('<div/>', {
'class': 'form-group'
}).append($('<p/>', {
'text': body
})), onClose);
};
/*
* @function Generates the settings modal
*
* @param {Function} onClose
*/
helpers.getSettingsModal = (onClose) => {
player.dbQuery('yt_settings', 'ytSettings', (e) => {
helpers.getModal('settings-modal', 'YouTube Player and Request Settings', 'Save', $('<form/>').append($('<div/>', {
'class': 'form-group'
}).append($('<label/>', {
'text': 'Player Size'
})).append($('<div/>', {
'class': 'dropdown'
}).append($('<button/>', {
'class': 'btn btn-secondary dropdown-toggle',
'type': 'button',
'data-toggle': 'dropdown',
'text': helpers.getPlayerSize(),
'id': 'player-size-btn'
})).append($('<div/>', {
'class': 'dropdown-menu',
'aria-labelledby': 'player-size-btn'
}).append($('<a/>', {
'class': 'dropdown-item',
'href': '#',
'text': 'Default',
'click': () => {
$('#player-size-btn').text('Default');
}
})).append($('<a/>', {
'class': 'dropdown-item',
'href': '#',
'text': 'Half',
'click': () => {
$('#player-size-btn').text('Half');
}
})).append($('<a/>', {
'class': 'dropdown-item',
'href': '#',
'text': 'Small',
'click': () => {
$('#player-size-btn').text('Small');
}
})).append($('<a/>', {
'class': 'dropdown-item',
'href': '#',
'text': 'Tiny',
'click': () => {
$('#player-size-btn').text('Tiny');
}
})).append($('<a/>', {
'class': 'dropdown-item',
'href': '#',
'text': 'Hidden',
'click': () => {
$('#player-size-btn').text('Hidden');
}
}))))).append($('<div/>', {
'class': 'form-group'
}).append($('<label/>', {
'text': 'Player DJ Name'
})).append($('<input/>', {
'type': 'text',
'data-toggle': 'tooltip',
'title': 'Name of the default playlist user.',
'class': 'form-control',
'id': 'dj-name',
'value': e.playlistDJname
}))).append($('<div/>', {
'class': 'form-group',
}).append($('<label/>', {
'text': 'Maximum Songs'
})).append($('<input/>', {
'type': 'number',
'data-toggle': 'tooltip',
'title': 'How many songs one user can have in the queue.',
'class': 'form-control',
'id': 'max-song-user',
'value': e.songRequestsMaxParallel
}))).append($('<div/>', {
'class': 'form-group'
}).append($('<label/>', {
'text': 'Maximum Song Duration'
})).append($('<input/>', {
'type': 'number',
'data-toggle': 'tooltip',
'id': 'max-song-length',
'title': 'How long in seconds a song can be.',
'class': 'form-control',
'value': e.songRequestsMaxSecondsforVideo
}))).append($('<div/>', {
'class': 'form-group'
}).append($('<label/>', {
'text': 'Vote Count'
})).append($('<input/>', {
'type': 'number',
'data-toggle': 'tooltip',
'id': 'vote-count',
'title': 'How many votes it takes to Skip.',
'class': 'form-control',
'value': e.voteCount
}))),onClose).modal('toggle');
});
};
/*
* @function Gets the player size.
*
* @return {String}
*/
helpers.getPlayerSize = () => {
let size = localStorage.getItem('phantombot_ytplayer_size');
return (size === null ? 'Default' : size[0].toUpperCase() + size.substr(1));
};
/*
* @function Sets the new player size.
*/
helpers.setPlayerSize = () => {
switch (localStorage.getItem('phantombot_ytplayer_size')) {
case 'half':
$('#left-section').attr('class', 'col-md-6').removeClass('off');
$('#right-section').attr('class', 'col-md-6');
break;
case 'small':
$('#left-section').attr('class', 'col-md-5').removeClass('off');
$('#right-section').attr('class', 'col-md-7');
break;
case 'tiny':
$('#left-section').attr('class', 'col-md-4').removeClass('off');
$('#right-section').attr('class', 'col-md-8');
break;
case 'hidden':
$('#left-section').addClass('off');
$('#right-section').attr('class', 'col-md-12');
break;
default:
$('#left-section').attr('class', 'col-md-7').removeClass('off');
$('#right-section').attr('class', 'col-md-5');
}
};
helpers.urlIsIP = () => {
var rx=/^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|$)){4}$/;
return rx.test(window.location.hostname);
}
// Export object.
window.helpers = helpers;
});

View File

@@ -0,0 +1,419 @@
/*
* Copyright (C) 2016-2020 phantombot.github.io/PhantomBot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author ScaniaTV
*/
$(function() {
var socket = new WebSocket((getProtocol() === 'https://' || window.location.protocol === 'https:' ? 'wss://' : 'ws://') + window.location.host + '/ws/ytplayer'),
listeners = [],
player = {},
hasAPIKey = true,
secondConnection = false;
/*
* @function sends data to the socket, this should only be used in this script.
*
* @param {Object} data
*/
var sendToSocket = (data) => {
// Do not send any requests or any other data to the Core for processing if there is no key.
if (!hasAPIKey) {
return;
}
try {
socket.send(JSON.stringify(data));
} catch (ex) {
console.error('Failed to send message to socket: ' + ex.message);
}
};
/*
* @function to determine is a second connection was detected.
*/
player.secondConnection = () => {
return secondConnection;
}
/*
* @function to determine if an API key exists.
*/
player.hasAPIKey = () => {
return hasAPIKey;
}
/*
* @function gets the playlist.
*
* @param {String} callback_id
*/
player.requestPlaylist = (callback_id) => {
// Request the data.
sendToSocket({
query: 'playlist'
});
};
/*
* @function gets the request list.
*
* @param {String} callback_id
*/
player.requestRequestList = (callback_id) => {
// Request the data.
sendToSocket({
query: 'songlist'
});
};
/*
* @function deletes a song from the current playlist.
*/
player.deleteFromPlaylist = () => {
sendToSocket({
command: 'deletecurrent'
});
};
/*
* @function "steals" a song and adds it to the default playlist.
*
* @param {String} song_id
* @param {String} requester
*/
player.addSongToPlaylist = (song_id, requester) => {
// Update the data.
sendToSocket({
command: 'stealsong',
youTubeID: (song_id === undefined ? '' : song_id),
requester: (requester === undefined ? '' : requester)
});
};
/*
* @function adds a song to the queue.
*
* @param {String} song_id
*/
player.requestSong = (song_id) => {
// Add song to queue.
sendToSocket({
command: 'songrequest',
search: song_id
});
};
/*
* @function removes a song from the default playlist.
*
* @param {String} song_id
* @param {String} requester
*/
player.removeSongFromPlaylist = (song_id) => {
// Update the data.
sendToSocket({
deletepl: song_id
});
};
/*
* @function removes a song from the queue.
*
* @param {String} song_id
* @param {String} requester
*/
player.removeSongFromRequest = (song_id) => {
// Update the data.
sendToSocket({
deletesr: song_id
});
};
/*
* @function shuffles the playlist.
*/
player.shufflePlaylist = () => {
// Update the data.
sendToSocket({
command: 'togglerandom'
});
};
/*
* @function loads a new playlist
*
* @param {String} playlist
*/
player.loadPlaylist = (playlist) => {
// Update the data.
sendToSocket({
command: 'loadpl',
playlist: playlist
});
};
/*
* @function skips the current song.
*/
player.skipSong = () => {
sendToSocket({
command: 'skipsong'
});
};
/*
* @function updates the player status.
*
* @param {Number} status
*/
player.updateState = (status) => {
sendToSocket({
status: {
state: status
}
});
};
/*
* @function updates the current song.
*
* @param {String} id
*/
player.updateSong = (id) => {
sendToSocket({
status: {
currentid: id
}
});
};
/*
* @function puts the player in ready mode.
*/
player.ready = () => {
sendToSocket({
status: {
ready: true
}
});
};
/*
* @function sends error code from YouTube Player
*
* @param {Number} status
*/
player.sendError = (status) => {
sendToSocket({
status: {
errorcode: status
}
});
};
/*
* @function updates the player volume.+
*
* @param {Number} volume
*/
player.updateVolume = (volume) => {
sendToSocket({
status: {
volume: volume
}
});
};
/*
* @function puts the player in ready-pause mode.
*/
player.readyPause = () => {
sendToSocket({
status: {
readypause: true
}
});
};
/*
* @function Gets all keys and values from a table
*
* @param {String} callback_id
* @param {String} table
* @param {Function} callback
*/
player.dbQuery = (callback_id, table, callback) => {
listeners[callback_id] = callback;
sendToSocket({
dbquery: true,
query_id: callback_id,
table: table
});
};
/*
* @function Updates a key and value in the database
*
* @param {String} callback_id
* @param {String} table
* @param {String} key
* @param {String} value
* @param {Function} callback
*/
player.dbUpdate = (callback_id, table, key, value, callback) => {
if (callback !== undefined) {
listeners[callback_id] = callback;
}
sendToSocket({
dbupdate: true,
query_id: callback_id,
update: {
table: table,
key: key,
value: value
}
});
};
/*
* @function adds a listener to the socket.
*
* @param {String} listener_id
* @param {Function} listener
*/
player.addListener = (listener_id, listener) => {
listeners[listener_id] = listener;
};
/* Socket functions */
/*
* @function is called when the socket opens.
*/
socket.onopen = (e) => {
console.info('Connection established with the websocket.');
// Send the auth to the bot.
sendToSocket({
authenticate: getAuth()
});
// Load the YouTube iframe.
$('body').append($('<script/>', {
src: 'https://www.youtube.com/iframe_api'
}));
};
/*
* @function the socket calls when it closes
*/
socket.onclose = (e) => {
console.error('Connection lost with the websocket.');
if (secondConnection) {
toastr.error('PhantomBot has closed the WebSocket.', '', {timeOut: 0});
} else {
toastr.error('Connection with WebSocket was lost. Refresh once reestablished.', '', {timeOut: 0});
}
};
/*
* @function the socket calls when it gets message.
*/
socket.onmessage = (e) => {
try {
let message = JSON.parse(e.data);
if (message.ping !== undefined) {
sendToSocket({
pong: "pong"
});
return;
}
// Check this message here before doing anything else.
if (message.secondconnection !== undefined) {
if (message.secondconnection === true) {
secondConnection = true;
toastr.error('PhantomBot rejected the connection due to a player window already being open.', '',
{timeOut: 0, extendedTimeOut: 0});
console.error('Only one instance allowed.');
}
return;
}
// Check this message here before doing anything else.
if (message.authresult !== undefined) {
if (message.authresult === false) {
console.error('Failed to auth with the socket.');
}
return;
}
// Check to ensure that there is an API key.
if (message.ytkeycheck !== undefined) {
if (message.ytkeycheck === false) {
hasAPIKey = false;
console.error("Missing YouTube API Key.");
toastr.error('A YouTube API key has not been configured. Please review the instructions ' +
'<a href="https://phantombot.github.io/PhantomBot/guides/#guide=content/integrations/youtubesetup">here' +
'</a> on the PhantomBot Community Forum.', 'Missing YouTube API Key',
{timeOut: 0, extendedTimeOut: 0});
}
return;
}
if (message.query_id !== undefined) {
if (listeners[message.query_id] !== undefined) {
listeners[message.query_id](message);
delete listeners[message.query_id];
}
} else if (message.command !== undefined) {
if (typeof message.command === 'object') {
let keys = Object.keys(message.command);
for (let i = 0; i < keys.length; i++) {
if (listeners[keys[i]] !== undefined) {
listeners[keys[i]](message.command);
return;
}
}
} else {
if (listeners[message.command] !== undefined) {
listeners[message.command](message);
}
}
} else {
let keys = Object.keys(message);
for (let i = 0; i < keys.length; i++) {
if (listeners[keys[i]] !== undefined) {
listeners[keys[i]](message);
return;
}
}
}
} catch (ex) {
console.error('Failed to parse message from socket: ' + ex.message);
}
}
// Make the player object global.
window.player = player;
});