|
@@ -1,1172 +1,1762 @@
|
|
|
<template>
|
|
|
- <official-header v-if='type == "official"'></official-header>
|
|
|
- <community-header v-if='type == "community"'></community-header>
|
|
|
-
|
|
|
- <song-queue v-if='modals.addSongToQueue'></song-queue>
|
|
|
- <add-to-playlist v-if='modals.addSongToPlaylist'></add-to-playlist>
|
|
|
- <edit-playlist v-if='modals.editPlaylist'></edit-playlist>
|
|
|
- <create-playlist v-if='modals.createPlaylist'></create-playlist>
|
|
|
- <edit-station v-show='modals.editStation'></edit-station>
|
|
|
- <report v-if='modals.report'></report>
|
|
|
-
|
|
|
- <songs-list-sidebar v-if='sidebars.songslist'></songs-list-sidebar>
|
|
|
- <playlist-sidebar v-if='sidebars.playlist'></playlist-sidebar>
|
|
|
- <users-sidebar v-if='sidebars.users'></users-sidebar>
|
|
|
-
|
|
|
- <div class='progress' v-show='!ready'></div>
|
|
|
- <div class='station' v-show="ready">
|
|
|
- <div v-show="noSong" class="no-song">
|
|
|
- <h1>No song is currently playing</h1>
|
|
|
- <h4 v-if='type === "community" && station.partyMode && (!station.locked || (station.locked && $parent.loggedIn && $parent.userId === station.owner))'>
|
|
|
- <a href='#' class='no-song' @click='modals.addSongToQueue = true'>Add a song to the queue</a>
|
|
|
- </h4>
|
|
|
- <h4 v-if='type === "community" && !station.partyMode && $parent.userId === station.owner && !station.privatePlaylist'>
|
|
|
- <a href='#' class='no-song' @click='sidebars.playlist = true'>Play a private playlist</a>
|
|
|
- </h4>
|
|
|
- <h1 v-if='type === "community" && !station.partyMode && $parent.userId === station.owner && station.privatePlaylist'>Maybe you can add some songs to your selected private playlist and then press the skip button</h1>
|
|
|
- </div>
|
|
|
- <div class="columns" v-show="!noSong">
|
|
|
- <div class="column is-8-desktop is-offset-2-desktop is-12-mobile">
|
|
|
- <div class="video-container">
|
|
|
- <div id="player"></div>
|
|
|
- </div>
|
|
|
- <div class="seeker-bar-container white" id="preview-progress">
|
|
|
- <div class="seeker-bar light-blue" style="width: 0%;"></div>
|
|
|
- </div>
|
|
|
+ <div>
|
|
|
+ <official-header v-if="type == 'official'" />
|
|
|
+ <community-header v-if="type == 'community'" />
|
|
|
+
|
|
|
+ <song-queue v-if="modals.addSongToQueue" />
|
|
|
+ <add-to-playlist v-if="modals.addSongToPlaylist" />
|
|
|
+ <edit-playlist v-if="modals.editPlaylist" />
|
|
|
+ <create-playlist v-if="modals.createPlaylist" />
|
|
|
+ <edit-station v-show="modals.editStation" />
|
|
|
+ <report v-if="modals.report" />
|
|
|
+
|
|
|
+ <transition name="slide">
|
|
|
+ <songs-list-sidebar v-if="sidebars.songslist" />
|
|
|
+ </transition>
|
|
|
+ <transition name="slide">
|
|
|
+ <playlist-sidebar v-if="sidebars.playlist" />
|
|
|
+ </transition>
|
|
|
+ <transition name="slide">
|
|
|
+ <users-sidebar v-if="sidebars.users" />
|
|
|
+ </transition>
|
|
|
+
|
|
|
+ <div v-show="loading" class="progress" />
|
|
|
+ <div v-show="!loading && exists" class="station">
|
|
|
+ <div v-show="noSong" class="no-song">
|
|
|
+ <h1>No song is currently playing</h1>
|
|
|
+ <h4
|
|
|
+ v-if="
|
|
|
+ type === 'community' &&
|
|
|
+ station.partyMode &&
|
|
|
+ (!station.locked ||
|
|
|
+ (station.locked &&
|
|
|
+ $parent.loggedIn &&
|
|
|
+ $parent.userId === station.owner))
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <a
|
|
|
+ href="#"
|
|
|
+ class="no-song"
|
|
|
+ @click="
|
|
|
+ openModal({
|
|
|
+ sector: 'station',
|
|
|
+ modal: 'addSongToQueue'
|
|
|
+ })
|
|
|
+ "
|
|
|
+ >Add a song to the queue</a
|
|
|
+ >
|
|
|
+ </h4>
|
|
|
+ <h4
|
|
|
+ v-if="
|
|
|
+ type === 'community' &&
|
|
|
+ !station.partyMode &&
|
|
|
+ $parent.userId === station.owner &&
|
|
|
+ !station.privatePlaylist
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <a
|
|
|
+ href="#"
|
|
|
+ class="no-song"
|
|
|
+ @click="sidebars.playlist = true"
|
|
|
+ >Play a private playlist</a
|
|
|
+ >
|
|
|
+ </h4>
|
|
|
+ <h1
|
|
|
+ v-if="
|
|
|
+ type === 'community' &&
|
|
|
+ !station.partyMode &&
|
|
|
+ $parent.userId === station.owner &&
|
|
|
+ station.privatePlaylist
|
|
|
+ "
|
|
|
+ >
|
|
|
+ Maybe you can add some songs to your selected private
|
|
|
+ playlist and then press the skip button
|
|
|
+ </h1>
|
|
|
</div>
|
|
|
- <div class="desktop-only column is-3-desktop card playlistCard experimental">
|
|
|
- <div class='title' v-if='type === "community"'>Queue</div>
|
|
|
- <div class='title' v-else>Playlist</div>
|
|
|
- <article class="media" v-if="!noSong">
|
|
|
- <figure class="media-left">
|
|
|
- <p class="image is-64x64">
|
|
|
- <img :src="currentSong.thumbnail" onerror="this.src='/assets/notes-transparent.png'">
|
|
|
- </p>
|
|
|
- </figure>
|
|
|
- <div class="media-content">
|
|
|
- <div class="content">
|
|
|
+ <div v-show="!noSong" class="columns">
|
|
|
+ <div
|
|
|
+ class="column is-8-desktop is-offset-2-desktop is-12-mobile"
|
|
|
+ >
|
|
|
+ <div class="video-container">
|
|
|
+ <div id="player" />
|
|
|
+ <div
|
|
|
+ class="player-can-not-autoplay"
|
|
|
+ v-if="!canAutoplay"
|
|
|
+ >
|
|
|
<p>
|
|
|
- Current Song:
|
|
|
- <br>
|
|
|
- <strong>{{ currentSong.title }}</strong>
|
|
|
- <br>
|
|
|
- <small>{{ currentSong.artists }}</small>
|
|
|
+ Please click anywhere on the screen for the
|
|
|
+ video to start
|
|
|
</p>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div class="media-right">
|
|
|
- {{ formatTime(currentSong.duration) }}
|
|
|
+ <div
|
|
|
+ id="preview-progress"
|
|
|
+ class="seeker-bar-container white"
|
|
|
+ >
|
|
|
+ <div class="seeker-bar light-blue" style="width: 0%;" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ class="desktop-only column is-3-desktop card playlistCard experimental"
|
|
|
+ >
|
|
|
+ <div v-if="type === 'community'" class="title">
|
|
|
+ Queue
|
|
|
+ </div>
|
|
|
+ <div v-else class="title">
|
|
|
+ Playlist
|
|
|
</div>
|
|
|
- </article>
|
|
|
- <p v-if="noSong" class="center">There is currently no song playing.</p>
|
|
|
-
|
|
|
- <article class="media" v-for='song in songsList'>
|
|
|
- <div class="media-content">
|
|
|
- <div class="content">
|
|
|
- <strong class="songTitle">{{ song.title }}</strong>
|
|
|
- <br>
|
|
|
- <small>{{ song.artists.join(', ') }}</small>
|
|
|
- <br>
|
|
|
+ <article v-if="!noSong" class="media">
|
|
|
+ <figure class="media-left">
|
|
|
+ <p class="image is-64x64">
|
|
|
+ <img
|
|
|
+ :src="currentSong.thumbnail"
|
|
|
+ onerror="this.src='/assets/notes-transparent.png'"
|
|
|
+ />
|
|
|
+ </p>
|
|
|
+ </figure>
|
|
|
+ <div class="media-content">
|
|
|
+ <div class="content">
|
|
|
+ <p>
|
|
|
+ Current Song:
|
|
|
+ <br />
|
|
|
+ <strong>{{ currentSong.title }}</strong>
|
|
|
+ <br />
|
|
|
+ <small>{{ currentSong.artists }}</small>
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="media-right">
|
|
|
+ {{ formatTime(currentSong.duration) }}
|
|
|
+ </div>
|
|
|
+ </article>
|
|
|
+ <p v-if="noSong" class="center">
|
|
|
+ There is currently no song playing.
|
|
|
+ </p>
|
|
|
+
|
|
|
+ <article
|
|
|
+ v-for="(song, index) in songsList"
|
|
|
+ :key="index"
|
|
|
+ class="media"
|
|
|
+ >
|
|
|
+ <div class="media-content">
|
|
|
+ <div class="content">
|
|
|
+ <strong class="songTitle">{{
|
|
|
+ song.title
|
|
|
+ }}</strong>
|
|
|
+ <br />
|
|
|
+ <small>{{ song.artists.join(", ") }}</small>
|
|
|
+ <br />
|
|
|
<div v-if="station.partyMode">
|
|
|
- <br>
|
|
|
- <small>Requested by <b>{{this.$parent.$parent.getUsernameFromId(song.requestedBy)}} {{this.userIdMap['Z' + song.requestedBy]}}</b></small>
|
|
|
- <button class="button" @click="removeFromQueue(song.songId)" v-if="isOwnerOnly() || isAdminOnly()">REMOVE</button>
|
|
|
+ <br />
|
|
|
+ <small>
|
|
|
+ Requested by
|
|
|
+ <b>
|
|
|
+ <user-id-to-username
|
|
|
+ :userId="song.requestedBy"
|
|
|
+ :link="true"
|
|
|
+ />
|
|
|
+ </b>
|
|
|
+ </small>
|
|
|
+ <button
|
|
|
+ v-if="isOwnerOnly() || isAdminOnly()"
|
|
|
+ class="button"
|
|
|
+ @click="removeFromQueue(song.songId)"
|
|
|
+ >
|
|
|
+ REMOVE
|
|
|
+ </button>
|
|
|
</div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- <div class="media-right">
|
|
|
- {{ $parent.formatTime(song.duration) }}
|
|
|
- </div>
|
|
|
- </article>
|
|
|
- <a class='button add-to-queue' href='#' @click='modals.addSongToQueue = !modals.addSongToQueue' v-if="type === 'community' && $parent.loggedIn">Add Song to Queue</a>
|
|
|
+ <div class="media-right">
|
|
|
+ {{ formatTime(song.duration) }}
|
|
|
+ </div>
|
|
|
+ </article>
|
|
|
+ <a
|
|
|
+ v-if="type === 'community' && $parent.loggedIn"
|
|
|
+ class="button add-to-queue"
|
|
|
+ href="#"
|
|
|
+ @click="
|
|
|
+ openModal({
|
|
|
+ sector: 'station',
|
|
|
+ modal: 'addSongToQueue'
|
|
|
+ })
|
|
|
+ "
|
|
|
+ >Add Song to Queue</a
|
|
|
+ >
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- <div class="desktop-only columns is-mobile" v-show="!noSong">
|
|
|
- <div class="column is-8-desktop is-offset-2-desktop is-12-mobile">
|
|
|
- <div class="columns is-mobile">
|
|
|
- <div class="column is-12-desktop">
|
|
|
- <h4 id="time-display">{{timeElapsed}} / {{formatTime(currentSong.duration)}}</h4>
|
|
|
- <h3>{{currentSong.title}}</h3>
|
|
|
- <h4 class="thin" style="margin-left: 0">{{currentSong.artists}}</h4>
|
|
|
- <div class="columns is-mobile">
|
|
|
- <form style="margin-top: 12px; margin-bottom: 0;" action="#" class="column is-7-desktop is-4-mobile">
|
|
|
- <p class='volume-slider-wrapper'>
|
|
|
- <i class="material-icons" @click='toggleMute()' v-if='muted'>volume_mute</i>
|
|
|
- <i class="material-icons" @click='toggleMute()' v-else>volume_down</i>
|
|
|
- <input type="range" id="volumeSlider" min="0" max="10000" class="active" v-on:change="changeVolume()" v-on:input="changeVolume()">
|
|
|
- <i class="material-icons" @click='increaseVolume()'>volume_up</i>
|
|
|
- </p>
|
|
|
- </form>
|
|
|
- <div class="column is-8-mobile is-5-desktop" style="float: right;">
|
|
|
- <ul id="ratings" v-if="currentSong.likes !== -1 && currentSong.dislikes !== -1">
|
|
|
- <li id="like" class="right" @click="toggleLike()">
|
|
|
- <span class="flow-text">{{currentSong.likes}} </span>
|
|
|
- <i id="thumbs_up" class="material-icons grey-text" v-bind:class="{ liked: liked }">thumb_up</i>
|
|
|
- <a class='absolute-a behind' @click="toggleLike()" href='#'></a>
|
|
|
- </li>
|
|
|
- <li style="margin-right: 10px;" id="dislike" class="right" @click="toggleDislike()">
|
|
|
- <span class="flow-text">{{currentSong.dislikes}} </span>
|
|
|
- <i id="thumbs_down" class="material-icons grey-text" v-bind:class="{ disliked: disliked }">thumb_down</i>
|
|
|
- <a class='absolute-a behind' @click="toggleDislike()" href='#'></a>
|
|
|
- </li>
|
|
|
- </ul>
|
|
|
+ <div v-show="!noSong" class="desktop-only columns is-mobile">
|
|
|
+ <div
|
|
|
+ class="column is-8-desktop is-offset-2-desktop is-12-mobile"
|
|
|
+ >
|
|
|
+ <div class="columns is-mobile">
|
|
|
+ <div class="column is-12-desktop">
|
|
|
+ <h4 id="time-display">
|
|
|
+ {{ timeElapsed }} /
|
|
|
+ {{ formatTime(currentSong.duration) }}
|
|
|
+ </h4>
|
|
|
+ <h3>{{ currentSong.title }}</h3>
|
|
|
+ <h4 class="thin" style="margin-left: 0">
|
|
|
+ {{ currentSong.artists }}
|
|
|
+ </h4>
|
|
|
+ <div class="columns is-mobile">
|
|
|
+ <form
|
|
|
+ style="margin-top: 12px; margin-bottom: 0;"
|
|
|
+ action="#"
|
|
|
+ class="column is-7-desktop is-4-mobile"
|
|
|
+ >
|
|
|
+ <p class="volume-slider-wrapper">
|
|
|
+ <i
|
|
|
+ v-if="muted"
|
|
|
+ class="material-icons"
|
|
|
+ @click="toggleMute()"
|
|
|
+ >volume_mute</i
|
|
|
+ >
|
|
|
+ <i
|
|
|
+ v-else
|
|
|
+ class="material-icons"
|
|
|
+ @click="toggleMute()"
|
|
|
+ >volume_down</i
|
|
|
+ >
|
|
|
+ <input
|
|
|
+ id="volumeSlider"
|
|
|
+ type="range"
|
|
|
+ min="0"
|
|
|
+ max="10000"
|
|
|
+ class="active"
|
|
|
+ @change="changeVolume()"
|
|
|
+ @input="changeVolume()"
|
|
|
+ />
|
|
|
+ <i
|
|
|
+ class="material-icons"
|
|
|
+ @click="increaseVolume()"
|
|
|
+ >volume_up</i
|
|
|
+ >
|
|
|
+ </p>
|
|
|
+ </form>
|
|
|
+ <div
|
|
|
+ class="column is-8-mobile is-5-desktop"
|
|
|
+ style="float: right;"
|
|
|
+ >
|
|
|
+ <ul
|
|
|
+ v-if="
|
|
|
+ currentSong.likes !== -1 &&
|
|
|
+ currentSong.dislikes !== -1
|
|
|
+ "
|
|
|
+ id="ratings"
|
|
|
+ >
|
|
|
+ <li
|
|
|
+ id="like"
|
|
|
+ class="right"
|
|
|
+ @click="toggleLike()"
|
|
|
+ >
|
|
|
+ <span class="flow-text">{{
|
|
|
+ currentSong.likes
|
|
|
+ }}</span>
|
|
|
+ <i
|
|
|
+ id="thumbs_up"
|
|
|
+ class="material-icons grey-text"
|
|
|
+ :class="{ liked: liked }"
|
|
|
+ >thumb_up</i
|
|
|
+ >
|
|
|
+ <a
|
|
|
+ class="absolute-a behind"
|
|
|
+ href="#"
|
|
|
+ @click="toggleLike()"
|
|
|
+ />
|
|
|
+ </li>
|
|
|
+ <li
|
|
|
+ id="dislike"
|
|
|
+ style="margin-right: 10px;"
|
|
|
+ class="right"
|
|
|
+ @click="toggleDislike()"
|
|
|
+ >
|
|
|
+ <span class="flow-text">{{
|
|
|
+ currentSong.dislikes
|
|
|
+ }}</span>
|
|
|
+ <i
|
|
|
+ id="thumbs_down"
|
|
|
+ class="material-icons grey-text"
|
|
|
+ :class="{
|
|
|
+ disliked: disliked
|
|
|
+ }"
|
|
|
+ >thumb_down</i
|
|
|
+ >
|
|
|
+ <a
|
|
|
+ class="absolute-a behind"
|
|
|
+ href="#"
|
|
|
+ @click="toggleDislike()"
|
|
|
+ />
|
|
|
+ </li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- <div class="column is-3-desktop experimental" v-if="!simpleSong">
|
|
|
- <img class="image" :src="currentSong.thumbnail" alt="Song Thumbnail" onerror="this.src='/assets/notes-transparent.png'" />
|
|
|
+ <div
|
|
|
+ v-if="!simpleSong"
|
|
|
+ class="column is-3-desktop experimental"
|
|
|
+ >
|
|
|
+ <img
|
|
|
+ class="image"
|
|
|
+ :src="currentSong.thumbnail"
|
|
|
+ alt="Song Thumbnail"
|
|
|
+ onerror="this.src='/assets/notes-transparent.png'"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- <div class="mobile-only" v-show="!noSong">
|
|
|
- <div>
|
|
|
+ <div v-show="!noSong" class="mobile-only">
|
|
|
<div>
|
|
|
<div>
|
|
|
- <h3>{{currentSong.title}}</h3>
|
|
|
- <h4 class="thin">{{currentSong.artists}}</h4>
|
|
|
- <h5>{{timeElapsed}} / {{formatTime(currentSong.duration)}}</h5>
|
|
|
<div>
|
|
|
- <form class="columns" action="#">
|
|
|
- <p class='column is-11-mobile volume-slider-wrapper'>
|
|
|
- <i class="material-icons" @click='toggleMute()' v-if='muted'>volume_mute</i>
|
|
|
- <i class="material-icons" @click='toggleMute()' v-else>volume_down</i>
|
|
|
- <input type="range" id="volumeSlider" min="0" max="10000" class="active" v-on:change="changeVolume()" v-on:input="changeVolume()">
|
|
|
- <i class="material-icons" @click='increaseVolume()'>volume_up</i>
|
|
|
- </p>
|
|
|
- </form>
|
|
|
+ <h3>{{ currentSong.title }}</h3>
|
|
|
+ <h4 class="thin">
|
|
|
+ {{ currentSong.artists }}
|
|
|
+ </h4>
|
|
|
+ <h5>
|
|
|
+ {{ timeElapsed }} /
|
|
|
+ {{ formatTime(currentSong.duration) }}
|
|
|
+ </h5>
|
|
|
<div>
|
|
|
- <ul id="ratings" style="display: inline-block;" v-if="currentSong.likes !== -1 && currentSong.dislikes !== -1">
|
|
|
- <li id="dislike" style="display: inline-block;margin-right: 10px;" @click="toggleDislike()">
|
|
|
- <span class="flow-text">{{currentSong.dislikes}} </span>
|
|
|
- <i id="thumbs_down" class="material-icons grey-text" v-bind:class="{ disliked: disliked }">thumb_down</i>
|
|
|
- <a class='absolute-a behind' @click="toggleDislike()" href='#'></a>
|
|
|
- </li>
|
|
|
- <li id="like" style="display: inline-block;" @click="toggleLike()">
|
|
|
- <span class="flow-text">{{currentSong.likes}} </span>
|
|
|
- <i id="thumbs_up" class="material-icons grey-text" v-bind:class="{ liked: liked }">thumb_up</i>
|
|
|
- <a class='absolute-a behind' @click="toggleLike()" href='#'></a>
|
|
|
- </li>
|
|
|
- </ul>
|
|
|
+ <form class="columns" action="#">
|
|
|
+ <p
|
|
|
+ class="column is-11-mobile volume-slider-wrapper"
|
|
|
+ >
|
|
|
+ <i
|
|
|
+ v-if="muted"
|
|
|
+ class="material-icons"
|
|
|
+ @click="toggleMute()"
|
|
|
+ >volume_mute</i
|
|
|
+ >
|
|
|
+ <i
|
|
|
+ v-else
|
|
|
+ class="material-icons"
|
|
|
+ @click="toggleMute()"
|
|
|
+ >volume_down</i
|
|
|
+ >
|
|
|
+ <input
|
|
|
+ id="volumeSlider"
|
|
|
+ type="range"
|
|
|
+ min="0"
|
|
|
+ max="10000"
|
|
|
+ class="active"
|
|
|
+ @change="changeVolume()"
|
|
|
+ @input="changeVolume()"
|
|
|
+ />
|
|
|
+ <i
|
|
|
+ class="material-icons"
|
|
|
+ @click="increaseVolume()"
|
|
|
+ >volume_up</i
|
|
|
+ >
|
|
|
+ </p>
|
|
|
+ </form>
|
|
|
+ <div>
|
|
|
+ <ul
|
|
|
+ v-if="
|
|
|
+ currentSong.likes !== -1 &&
|
|
|
+ currentSong.dislikes !== -1
|
|
|
+ "
|
|
|
+ id="ratings"
|
|
|
+ style="display: inline-block;"
|
|
|
+ >
|
|
|
+ <li
|
|
|
+ id="dislike"
|
|
|
+ style="display: inline-block;margin-right: 10px;"
|
|
|
+ @click="toggleDislike()"
|
|
|
+ >
|
|
|
+ <span class="flow-text">{{
|
|
|
+ currentSong.dislikes
|
|
|
+ }}</span>
|
|
|
+ <i
|
|
|
+ id="thumbs_down"
|
|
|
+ class="material-icons grey-text"
|
|
|
+ :class="{
|
|
|
+ disliked: disliked
|
|
|
+ }"
|
|
|
+ >thumb_down</i
|
|
|
+ >
|
|
|
+ <a
|
|
|
+ class="absolute-a behind"
|
|
|
+ href="#"
|
|
|
+ @click="toggleDislike()"
|
|
|
+ />
|
|
|
+ </li>
|
|
|
+ <li
|
|
|
+ id="like"
|
|
|
+ style="display: inline-block;"
|
|
|
+ @click="toggleLike()"
|
|
|
+ >
|
|
|
+ <span class="flow-text">{{
|
|
|
+ currentSong.likes
|
|
|
+ }}</span>
|
|
|
+ <i
|
|
|
+ id="thumbs_up"
|
|
|
+ class="material-icons grey-text"
|
|
|
+ :class="{ liked: liked }"
|
|
|
+ >thumb_up</i
|
|
|
+ >
|
|
|
+ <a
|
|
|
+ class="absolute-a behind"
|
|
|
+ href="#"
|
|
|
+ @click="toggleLike()"
|
|
|
+ />
|
|
|
+ </li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+
|
|
|
+ <Z404 v-if="!exists"></Z404>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
- import { Toast } from 'vue-roaster';
|
|
|
-
|
|
|
- import SongQueue from '../Modals/AddSongToQueue.vue';
|
|
|
- import AddToPlaylist from '../Modals/AddSongToPlaylist.vue';
|
|
|
- import EditPlaylist from '../Modals/Playlists/Edit.vue';
|
|
|
- import CreatePlaylist from '../Modals/Playlists/Create.vue';
|
|
|
- import EditStation from '../Modals/EditStation.vue';
|
|
|
- import Report from '../Modals/Report.vue';
|
|
|
-
|
|
|
- import SongsListSidebar from '../Sidebars/SongsList.vue';
|
|
|
- import PlaylistSidebar from '../Sidebars/Playlist.vue';
|
|
|
- import UsersSidebar from '../Sidebars/UsersList.vue';
|
|
|
-
|
|
|
- import OfficialHeader from './OfficialHeader.vue';
|
|
|
- import CommunityHeader from './CommunityHeader.vue';
|
|
|
- import MainFooter from '../MainFooter.vue';
|
|
|
- import io from '../../io';
|
|
|
- import auth from '../../auth';
|
|
|
-
|
|
|
- export default {
|
|
|
- data() {
|
|
|
- return {
|
|
|
- ready: false,
|
|
|
- type: '',
|
|
|
- playerReady: false,
|
|
|
- previousSong: null,
|
|
|
- currentSong: {},
|
|
|
- player: undefined,
|
|
|
- timePaused: 0,
|
|
|
- paused: false,
|
|
|
- muted: false,
|
|
|
- timeElapsed: '0:00',
|
|
|
- liked: false,
|
|
|
- disliked: false,
|
|
|
- modals: {
|
|
|
- addSongToQueue: false,
|
|
|
- addSongToPlaylist: false,
|
|
|
- editPlaylist: false,
|
|
|
- createPlaylist: false,
|
|
|
- editStation: false,
|
|
|
- report: false
|
|
|
- },
|
|
|
- sidebars: {
|
|
|
- songslist: false,
|
|
|
- users: false,
|
|
|
- playlist: false
|
|
|
- },
|
|
|
- noSong: false,
|
|
|
- simpleSong: false,
|
|
|
- songsList: [],
|
|
|
- timeBeforePause: 0,
|
|
|
- station: {},
|
|
|
- skipVotes: 0,
|
|
|
- privatePlaylistQueueSelected: null,
|
|
|
- automaticallyRequestedSongId: null,
|
|
|
- systemDifference: 0,
|
|
|
- users: [],
|
|
|
- userCount: 0,
|
|
|
- userIdMap: this.$parent.userIdMap
|
|
|
- }
|
|
|
- },
|
|
|
- methods: {
|
|
|
- isOwnerOnly: function () {
|
|
|
- return this.$parent.loggedIn && this.$parent.userId === this.station.owner;
|
|
|
- },
|
|
|
- isAdminOnly: function() {
|
|
|
- return this.$parent.loggedIn && this.$parent.role === 'admin';
|
|
|
+import { mapState, mapActions } from "vuex";
|
|
|
+
|
|
|
+import { Toast } from "vue-roaster";
|
|
|
+
|
|
|
+import SongQueue from "../Modals/AddSongToQueue.vue";
|
|
|
+import AddToPlaylist from "../Modals/AddSongToPlaylist.vue";
|
|
|
+import EditPlaylist from "../Modals/Playlists/Edit.vue";
|
|
|
+import CreatePlaylist from "../Modals/Playlists/Create.vue";
|
|
|
+import EditStation from "../Modals/EditStation.vue";
|
|
|
+import Report from "../Modals/Report.vue";
|
|
|
+
|
|
|
+import SongsListSidebar from "../Sidebars/SongsList.vue";
|
|
|
+import PlaylistSidebar from "../Sidebars/Playlist.vue";
|
|
|
+import UsersSidebar from "../Sidebars/UsersList.vue";
|
|
|
+
|
|
|
+import OfficialHeader from "./OfficialHeader.vue";
|
|
|
+import CommunityHeader from "./CommunityHeader.vue";
|
|
|
+
|
|
|
+import UserIdToUsername from "../UserIdToUsername.vue";
|
|
|
+import Z404 from "../404.vue";
|
|
|
+
|
|
|
+import io from "../../io";
|
|
|
+
|
|
|
+export default {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ loading: true,
|
|
|
+ ready: false,
|
|
|
+ exists: true,
|
|
|
+ type: "",
|
|
|
+ playerReady: false,
|
|
|
+ previousSong: null,
|
|
|
+ currentSong: {},
|
|
|
+ player: undefined,
|
|
|
+ timePaused: 0,
|
|
|
+ paused: false,
|
|
|
+ muted: false,
|
|
|
+ timeElapsed: "0:00",
|
|
|
+ liked: false,
|
|
|
+ disliked: false,
|
|
|
+ sidebars: {
|
|
|
+ songslist: false,
|
|
|
+ users: false,
|
|
|
+ playlist: false
|
|
|
},
|
|
|
- removeFromQueue: function(songId) {
|
|
|
- socket.emit('stations.removeFromQueue', this.station._id, songId, res => {
|
|
|
- if (res.status === 'success') {
|
|
|
- Toast.methods.addToast('Successfully removed song from the queue.', 4000);
|
|
|
+ noSong: false,
|
|
|
+ simpleSong: false,
|
|
|
+ songsList: [],
|
|
|
+ timeBeforePause: 0,
|
|
|
+ skipVotes: 0,
|
|
|
+ privatePlaylistQueueSelected: null,
|
|
|
+ automaticallyRequestedSongId: null,
|
|
|
+ systemDifference: 0,
|
|
|
+ users: [],
|
|
|
+ userCount: 0,
|
|
|
+ attemptsToPlayVideo: 0,
|
|
|
+ canAutoplay: true,
|
|
|
+ lastTimeRequestedIfCanAutoplay: 0
|
|
|
+ };
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ ...mapState("modals", {
|
|
|
+ modals: state => state.modals.station
|
|
|
+ }),
|
|
|
+ ...mapState("station", {
|
|
|
+ station: state => state.station
|
|
|
+ })
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ isOwnerOnly() {
|
|
|
+ return (
|
|
|
+ this.$parent.loggedIn &&
|
|
|
+ this.$parent.userId === this.station.owner
|
|
|
+ );
|
|
|
+ },
|
|
|
+ isAdminOnly() {
|
|
|
+ return this.$parent.loggedIn && this.$parent.role === "admin";
|
|
|
+ },
|
|
|
+ removeFromQueue(songId) {
|
|
|
+ window.socket.emit(
|
|
|
+ "stations.removeFromQueue",
|
|
|
+ this.station._id,
|
|
|
+ songId,
|
|
|
+ res => {
|
|
|
+ if (res.status === "success") {
|
|
|
+ Toast.methods.addToast(
|
|
|
+ "Successfully removed song from the queue.",
|
|
|
+ 4000
|
|
|
+ );
|
|
|
} else Toast.methods.addToast(res.message, 8000);
|
|
|
- });
|
|
|
- },
|
|
|
- editPlaylist: function (id) {
|
|
|
- this.playlistBeingEdited = id;
|
|
|
- this.modals.editPlaylist = !this.modals.editPlaylist;
|
|
|
- },
|
|
|
- editStation: function () {
|
|
|
- let _this = this;
|
|
|
- this.$broadcast('editStation', {
|
|
|
- _id: _this.station._id,
|
|
|
- name: _this.station.name,
|
|
|
- type: _this.type,
|
|
|
- partyMode: _this.station.partyMode,
|
|
|
- description: _this.station.description,
|
|
|
- privacy: _this.station.privacy,
|
|
|
- displayName: _this.station.displayName
|
|
|
- });
|
|
|
- },
|
|
|
- toggleSidebar: function (type) {
|
|
|
- Object.keys(this.sidebars).forEach(sidebar => {
|
|
|
- if (sidebar !== type) this.sidebars[sidebar] = false;
|
|
|
- else this.sidebars[type] = !this.sidebars[type];
|
|
|
- });
|
|
|
- },
|
|
|
- youtubeReady: function() {
|
|
|
- let local = this;
|
|
|
- if (!local.player) {
|
|
|
- local.player = new YT.Player("player", {
|
|
|
- height: 270,
|
|
|
- width: 480,
|
|
|
- videoId: local.currentSong.songId,
|
|
|
- startSeconds: local.getTimeElapsed() / 1000 + local.currentSong.skipDuration,
|
|
|
- playerVars: {controls: 0, iv_load_policy: 3, rel: 0, showinfo: 0},
|
|
|
- events: {
|
|
|
- 'onReady': function (event) {
|
|
|
- local.playerReady = true;
|
|
|
- let volume = parseInt(localStorage.getItem("volume"));
|
|
|
- volume = (typeof volume === "number") ? volume : 20;
|
|
|
- local.player.setVolume(volume);
|
|
|
- if (volume > 0) local.player.unMute();
|
|
|
- local.playVideo();
|
|
|
- },
|
|
|
- 'onError': function(err) {
|
|
|
- console.log("iframe error", err);
|
|
|
- local.voteSkipStation();
|
|
|
- },
|
|
|
- 'onStateChange': function (event) {
|
|
|
- if (event.data === 1 && local.videoLoading === true) {
|
|
|
- local.videoLoading = false;
|
|
|
- local.player.seekTo(local.getTimeElapsed() / 1000 + local.currentSong.skipDuration, true);
|
|
|
- if (local.paused) local.player.pauseVideo();
|
|
|
- } else if (event.data === 1 && local.paused) {
|
|
|
- local.player.seekTo(local.timeBeforePause / 1000, true);
|
|
|
- local.player.pauseVideo();
|
|
|
- }
|
|
|
- if (event.data === 2 && !local.paused && !local.noSong && (local.player.getDuration() / 1000) < local.currentSong.duration) {
|
|
|
- local.player.seekTo(local.getTimeElapsed() / 1000 + local.currentSong.skipDuration, true);
|
|
|
- local.player.playVideo();
|
|
|
- }
|
|
|
+ }
|
|
|
+ );
|
|
|
+ },
|
|
|
+ toggleSidebar(type) {
|
|
|
+ Object.keys(this.sidebars).forEach(sidebar => {
|
|
|
+ if (sidebar !== type) this.sidebars[sidebar] = false;
|
|
|
+ else this.sidebars[type] = !this.sidebars[type];
|
|
|
+ });
|
|
|
+ },
|
|
|
+ youtubeReady() {
|
|
|
+ const local = this;
|
|
|
+ if (!local.player) {
|
|
|
+ local.player = new window.YT.Player("player", {
|
|
|
+ height: 270,
|
|
|
+ width: 480,
|
|
|
+ videoId: local.currentSong.songId,
|
|
|
+ startSeconds:
|
|
|
+ local.getTimeElapsed() / 1000 +
|
|
|
+ local.currentSong.skipDuration,
|
|
|
+ playerVars: {
|
|
|
+ controls: 0,
|
|
|
+ iv_load_policy: 3,
|
|
|
+ rel: 0,
|
|
|
+ showinfo: 0
|
|
|
+ },
|
|
|
+ events: {
|
|
|
+ onReady() {
|
|
|
+ local.playerReady = true;
|
|
|
+ let volume = parseInt(
|
|
|
+ localStorage.getItem("volume")
|
|
|
+ );
|
|
|
+
|
|
|
+ volume = typeof volume === "number" ? volume : 20;
|
|
|
+
|
|
|
+ local.player.setVolume(volume);
|
|
|
+
|
|
|
+ if (volume > 0) {
|
|
|
+ local.player.unMute();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (local.muted) local.player.mute();
|
|
|
+
|
|
|
+ local.playVideo();
|
|
|
+ },
|
|
|
+ onError(err) {
|
|
|
+ console.log("iframe error", err);
|
|
|
+ local.voteSkipStation();
|
|
|
+ },
|
|
|
+ onStateChange(event) {
|
|
|
+ if (
|
|
|
+ event.data === 1 &&
|
|
|
+ local.videoLoading === true
|
|
|
+ ) {
|
|
|
+ local.videoLoading = false;
|
|
|
+ local.player.seekTo(
|
|
|
+ local.getTimeElapsed() / 1000 +
|
|
|
+ local.currentSong.skipDuration,
|
|
|
+ true
|
|
|
+ );
|
|
|
+ if (local.paused) local.player.pauseVideo();
|
|
|
+ } else if (event.data === 1 && local.paused) {
|
|
|
+ local.player.seekTo(
|
|
|
+ local.timeBeforePause / 1000,
|
|
|
+ true
|
|
|
+ );
|
|
|
+ local.player.pauseVideo();
|
|
|
+ }
|
|
|
+ if (
|
|
|
+ event.data === 2 &&
|
|
|
+ !local.paused &&
|
|
|
+ !local.noSong &&
|
|
|
+ local.player.getDuration() / 1000 <
|
|
|
+ local.currentSong.duration
|
|
|
+ ) {
|
|
|
+ local.player.seekTo(
|
|
|
+ local.getTimeElapsed() / 1000 +
|
|
|
+ local.currentSong.skipDuration,
|
|
|
+ true
|
|
|
+ );
|
|
|
+ local.player.playVideo();
|
|
|
}
|
|
|
}
|
|
|
- });
|
|
|
- }
|
|
|
- },
|
|
|
- getTimeElapsed: function() {
|
|
|
- let local = this;
|
|
|
- if (local.currentSong) return Date.currently() - local.startedAt - local.timePaused;
|
|
|
- else return 0;
|
|
|
- },
|
|
|
- playVideo: function() {
|
|
|
- let local = this;
|
|
|
- if (local.playerReady) {
|
|
|
- local.videoLoading = true;
|
|
|
- local.player.loadVideoById(local.currentSong.songId, local.getTimeElapsed() / 1000 + local.currentSong.skipDuration);
|
|
|
-
|
|
|
- if (local.currentSong.artists) local.currentSong.artists = local.currentSong.artists.join(", ");
|
|
|
- if (window.stationInterval !== 0) clearInterval(window.stationInterval);
|
|
|
- window.stationInterval = setInterval(function () {
|
|
|
- local.resizeSeekerbar();
|
|
|
- local.calculateTimeElapsed();
|
|
|
- }, 150);
|
|
|
- }
|
|
|
- },
|
|
|
- resizeSeekerbar: function() {
|
|
|
- let local = this;
|
|
|
- if (!local.paused) {
|
|
|
- $(".seeker-bar").width(parseFloat(((local.getTimeElapsed() / 1000) / local.currentSong.duration * 100)) + "%");
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ getTimeElapsed() {
|
|
|
+ const local = this;
|
|
|
+ if (local.currentSong) {
|
|
|
+ let { timePaused } = local;
|
|
|
+ if (local.paused)
|
|
|
+ timePaused += Date.currently() - local.pausedAt;
|
|
|
+ return Date.currently() - local.startedAt - timePaused;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+ },
|
|
|
+ playVideo() {
|
|
|
+ const local = this;
|
|
|
+ if (local.playerReady) {
|
|
|
+ local.videoLoading = true;
|
|
|
+ local.player.loadVideoById(
|
|
|
+ local.currentSong.songId,
|
|
|
+ local.getTimeElapsed() / 1000 +
|
|
|
+ local.currentSong.skipDuration
|
|
|
+ );
|
|
|
+
|
|
|
+ if (window.stationInterval !== 0)
|
|
|
+ clearInterval(window.stationInterval);
|
|
|
+ window.stationInterval = setInterval(() => {
|
|
|
+ local.resizeSeekerbar();
|
|
|
+ local.calculateTimeElapsed();
|
|
|
+ }, 150);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ resizeSeekerbar() {
|
|
|
+ const local = this;
|
|
|
+ if (!local.paused) {
|
|
|
+ document.getElementsByClassName(
|
|
|
+ "seeker-bar"
|
|
|
+ )[0].style.width = `${parseFloat(
|
|
|
+ (local.getTimeElapsed() /
|
|
|
+ 1000 /
|
|
|
+ local.currentSong.duration) *
|
|
|
+ 100
|
|
|
+ )}%`;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ formatTime(duration) {
|
|
|
+ const dur = moment.duration(duration, "seconds");
|
|
|
+ if (duration < 0) return "0:00";
|
|
|
+
|
|
|
+ const getHours = () => {
|
|
|
+ if (dur.hours > 0) {
|
|
|
+ if (dur.hours() < 10) return `0${dur.hours()}:`;
|
|
|
+ return `${dur.hours()}:`;
|
|
|
}
|
|
|
- },
|
|
|
- formatTime: function(duration) {
|
|
|
- let d = moment.duration(duration, 'seconds');
|
|
|
- if (duration < 0) return "0:00";
|
|
|
- return ((d.hours() > 0) ? (d.hours() < 10 ? ("0" + d.hours() + ":") : (d.hours() + ":")) : "") + (d.minutes() + ":") + (d.seconds() < 10 ? ("0" + d.seconds()) : d.seconds());
|
|
|
- },
|
|
|
- calculateTimeElapsed: function() {
|
|
|
- let local = this;
|
|
|
- let currentTime = Date.currently();
|
|
|
+ return "";
|
|
|
+ };
|
|
|
|
|
|
- if (local.currentTime !== undefined && local.paused) {
|
|
|
- local.timePaused += (Date.currently() - local.currentTime);
|
|
|
- local.currentTime = undefined;
|
|
|
+ return `${getHours()}${dur.minutes()}:${
|
|
|
+ dur.seconds() < 10 ? `0${dur.seconds()}` : dur.seconds()
|
|
|
+ }`;
|
|
|
+ },
|
|
|
+ calculateTimeElapsed() {
|
|
|
+ const local = this;
|
|
|
+
|
|
|
+ if (
|
|
|
+ local.playerReady &&
|
|
|
+ local.currentSong &&
|
|
|
+ local.player.getPlayerState() === -1
|
|
|
+ ) {
|
|
|
+ if (local.attemptsToPlayVideo >= 5) {
|
|
|
+ if (
|
|
|
+ Date.now() - local.lastTimeRequestedIfCanAutoplay >
|
|
|
+ 2000
|
|
|
+ ) {
|
|
|
+ local.lastTimeRequestedIfCanAutoplay = Date.now();
|
|
|
+ window.canAutoplay.video().then(({ result }) => {
|
|
|
+ if (result) {
|
|
|
+ local.attemptsToPlayVideo = 0;
|
|
|
+ local.canAutoplay = true;
|
|
|
+ } else {
|
|
|
+ local.canAutoplay = false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ local.player.playVideo();
|
|
|
+ local.attemptsToPlayVideo += 1;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- let duration = (Date.currently() - local.startedAt - local.timePaused) / 1000;
|
|
|
- let songDuration = local.currentSong.duration;
|
|
|
- if (songDuration <= duration) local.player.pauseVideo();
|
|
|
- if ((!local.paused) && duration <= songDuration) local.timeElapsed = local.formatTime(duration);
|
|
|
- },
|
|
|
- toggleLock: function () {
|
|
|
- let _this = this;
|
|
|
- socket.emit('stations.toggleLock', this.station._id, res => {
|
|
|
- if (res.status === 'success') {
|
|
|
- Toast.methods.addToast('Successfully toggled the queue lock.', 4000);
|
|
|
- } else Toast.methods.addToast(res.message, 8000);
|
|
|
- });
|
|
|
- },
|
|
|
- changeVolume: function() {
|
|
|
- let local = this;
|
|
|
- let volume = $("#volumeSlider").val();
|
|
|
- localStorage.setItem("volume", volume / 100);
|
|
|
- if (local.playerReady) {
|
|
|
- local.player.setVolume(volume / 100);
|
|
|
- if (volume > 0) local.player.unMute();
|
|
|
- }
|
|
|
- },
|
|
|
- resumeLocalStation: function() {
|
|
|
- this.paused = false;
|
|
|
- if (!this.noSong) {
|
|
|
- if (this.playerReady) {
|
|
|
- this.player.seekTo(this.getTimeElapsed() / 1000 + this.currentSong.skipDuration);
|
|
|
- this.player.playVideo();
|
|
|
- }
|
|
|
+ if (!local.paused) {
|
|
|
+ const timeElapsed = local.getTimeElapsed();
|
|
|
+ const currentPlayerTime = local.player.getCurrentTime() * 1000;
|
|
|
+
|
|
|
+ const difference = timeElapsed - currentPlayerTime;
|
|
|
+ // console.log(difference123);
|
|
|
+ if (difference < -200) {
|
|
|
+ // console.log("Difference0.8");
|
|
|
+ local.player.setPlaybackRate(0.8);
|
|
|
+ } else if (difference < -50) {
|
|
|
+ // console.log("Difference0.9");
|
|
|
+ local.player.setPlaybackRate(0.9);
|
|
|
+ } else if (difference < -25) {
|
|
|
+ // console.log("Difference0.99");
|
|
|
+ local.player.setPlaybackRate(0.99);
|
|
|
+ } else if (difference > 200) {
|
|
|
+ // console.log("Difference1.2");
|
|
|
+ local.player.setPlaybackRate(1.2);
|
|
|
+ } else if (difference > 50) {
|
|
|
+ // console.log("Difference1.1");
|
|
|
+ local.player.setPlaybackRate(1.1);
|
|
|
+ } else if (difference > 25) {
|
|
|
+ // console.log("Difference1.01");
|
|
|
+ local.player.setPlaybackRate(1.01);
|
|
|
+ } else if (local.player.getPlaybackRate !== 1.0) {
|
|
|
+ // console.log("NDifference1.0");
|
|
|
+ local.player.setPlaybackRate(1.0);
|
|
|
}
|
|
|
- },
|
|
|
- pauseLocalStation: function() {
|
|
|
- this.paused = true;
|
|
|
- if (!this.noSong) {
|
|
|
- this.timeBeforePause = this.getTimeElapsed();
|
|
|
- if (this.playerReady) this.player.pauseVideo();
|
|
|
+ }
|
|
|
+
|
|
|
+ /* if (local.currentTime !== undefined && local.paused) {
|
|
|
+ local.timePaused += Date.currently() - local.currentTime;
|
|
|
+ local.currentTime = undefined;
|
|
|
+ } */
|
|
|
+
|
|
|
+ let { timePaused } = local;
|
|
|
+ if (local.paused) timePaused += Date.currently() - local.pausedAt;
|
|
|
+
|
|
|
+ const duration =
|
|
|
+ (Date.currently() - local.startedAt - timePaused) / 1000;
|
|
|
+
|
|
|
+ const songDuration = local.currentSong.duration;
|
|
|
+ if (songDuration <= duration) local.player.pauseVideo();
|
|
|
+ if (!local.paused && duration <= songDuration)
|
|
|
+ local.timeElapsed = local.formatTime(duration);
|
|
|
+ },
|
|
|
+ toggleLock() {
|
|
|
+ window.socket.emit("stations.toggleLock", this.station._id, res => {
|
|
|
+ if (res.status === "success") {
|
|
|
+ Toast.methods.addToast(
|
|
|
+ "Successfully toggled the queue lock.",
|
|
|
+ 4000
|
|
|
+ );
|
|
|
+ } else Toast.methods.addToast(res.message, 8000);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ changeVolume() {
|
|
|
+ const local = this;
|
|
|
+ const volume = document.getElementById("volumeSlider").value;
|
|
|
+ localStorage.setItem("volume", volume / 100);
|
|
|
+ if (local.playerReady) {
|
|
|
+ local.player.setVolume(volume / 100);
|
|
|
+ if (volume > 0) {
|
|
|
+ local.player.unMute();
|
|
|
+ localStorage.setItem("muted", false);
|
|
|
+ local.muted = false;
|
|
|
}
|
|
|
- },
|
|
|
- skipStation: function () {
|
|
|
- let _this = this;
|
|
|
- _this.socket.emit('stations.forceSkip', _this.station._id, data => {
|
|
|
- if (data.status !== 'success') Toast.methods.addToast(`Error: ${data.message}`, 8000);
|
|
|
- else Toast.methods.addToast('Successfully skipped the station\'s current song.', 4000);
|
|
|
- });
|
|
|
- },
|
|
|
- voteSkipStation: function () {
|
|
|
- let _this = this;
|
|
|
- _this.socket.emit('stations.voteSkip', _this.station._id, data => {
|
|
|
- if (data.status !== 'success') Toast.methods.addToast(`Error: ${data.message}`, 8000);
|
|
|
- else Toast.methods.addToast('Successfully voted to skip the current song.', 4000);
|
|
|
- });
|
|
|
- },
|
|
|
- resumeStation: function () {
|
|
|
- let _this = this;
|
|
|
- _this.socket.emit('stations.resume', _this.station._id, data => {
|
|
|
- if (data.status !== 'success') Toast.methods.addToast(`Error: ${data.message}`, 8000);
|
|
|
- else Toast.methods.addToast('Successfully resumed the station.', 4000);
|
|
|
- });
|
|
|
- },
|
|
|
- pauseStation: function () {
|
|
|
- let _this = this;
|
|
|
- _this.socket.emit('stations.pause', _this.station._id, data => {
|
|
|
- if (data.status !== 'success') Toast.methods.addToast(`Error: ${data.message}`, 8000);
|
|
|
- else Toast.methods.addToast('Successfully paused the station.', 4000);
|
|
|
- });
|
|
|
- },
|
|
|
- toggleMute: function () {
|
|
|
+ }
|
|
|
+ },
|
|
|
+ resumeLocalStation() {
|
|
|
+ this.paused = false;
|
|
|
+ if (!this.noSong) {
|
|
|
if (this.playerReady) {
|
|
|
- let previousVolume = parseFloat(localStorage.getItem("volume"));
|
|
|
- let volume = this.player.getVolume() * 100 <= 0 ? previousVolume : 0;
|
|
|
- this.muted = !this.muted;
|
|
|
- $("#volumeSlider").val(volume * 100);
|
|
|
- this.player.setVolume(volume);
|
|
|
- if (!this.muted) localStorage.setItem("volume", volume);
|
|
|
+ this.player.seekTo(
|
|
|
+ this.getTimeElapsed() / 1000 +
|
|
|
+ this.currentSong.skipDuration
|
|
|
+ );
|
|
|
+ this.player.playVideo();
|
|
|
}
|
|
|
- },
|
|
|
- increaseVolume: function () {
|
|
|
- if (this.playerReady) {
|
|
|
- let previousVolume = parseInt(localStorage.getItem("volume"));
|
|
|
- let volume = previousVolume + 5;
|
|
|
- if (previousVolume === 0) this.muted = false;
|
|
|
- if (volume > 100) volume = 100;
|
|
|
- $("#volumeSlider").val(volume * 100);
|
|
|
- this.player.setVolume(volume);
|
|
|
- localStorage.setItem("volume", volume);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ pauseLocalStation() {
|
|
|
+ this.paused = true;
|
|
|
+ if (!this.noSong) {
|
|
|
+ this.timeBeforePause = this.getTimeElapsed();
|
|
|
+ if (this.playerReady) this.player.pauseVideo();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ skipStation() {
|
|
|
+ const _this = this;
|
|
|
+ _this.socket.emit("stations.forceSkip", _this.station._id, data => {
|
|
|
+ if (data.status !== "success")
|
|
|
+ Toast.methods.addToast(`Error: ${data.message}`, 8000);
|
|
|
+ else
|
|
|
+ Toast.methods.addToast(
|
|
|
+ "Successfully skipped the station's current song.",
|
|
|
+ 4000
|
|
|
+ );
|
|
|
+ });
|
|
|
+ },
|
|
|
+ voteSkipStation() {
|
|
|
+ const _this = this;
|
|
|
+ _this.socket.emit("stations.voteSkip", _this.station._id, data => {
|
|
|
+ if (data.status !== "success")
|
|
|
+ Toast.methods.addToast(`Error: ${data.message}`, 8000);
|
|
|
+ else
|
|
|
+ Toast.methods.addToast(
|
|
|
+ "Successfully voted to skip the current song.",
|
|
|
+ 4000
|
|
|
+ );
|
|
|
+ });
|
|
|
+ },
|
|
|
+ resumeStation() {
|
|
|
+ const _this = this;
|
|
|
+ _this.socket.emit("stations.resume", _this.station._id, data => {
|
|
|
+ if (data.status !== "success")
|
|
|
+ Toast.methods.addToast(`Error: ${data.message}`, 8000);
|
|
|
+ else
|
|
|
+ Toast.methods.addToast(
|
|
|
+ "Successfully resumed the station.",
|
|
|
+ 4000
|
|
|
+ );
|
|
|
+ });
|
|
|
+ },
|
|
|
+ pauseStation() {
|
|
|
+ const _this = this;
|
|
|
+ _this.socket.emit("stations.pause", _this.station._id, data => {
|
|
|
+ if (data.status !== "success")
|
|
|
+ Toast.methods.addToast(`Error: ${data.message}`, 8000);
|
|
|
+ else
|
|
|
+ Toast.methods.addToast(
|
|
|
+ "Successfully paused the station.",
|
|
|
+ 4000
|
|
|
+ );
|
|
|
+ });
|
|
|
+ },
|
|
|
+ toggleMute() {
|
|
|
+ if (this.playerReady) {
|
|
|
+ const previousVolume = parseFloat(
|
|
|
+ localStorage.getItem("volume")
|
|
|
+ );
|
|
|
+ const volume =
|
|
|
+ this.player.getVolume() * 100 <= 0 ? previousVolume : 0;
|
|
|
+ this.muted = !this.muted;
|
|
|
+ localStorage.setItem("muted", this.muted);
|
|
|
+ document.getElementById("volumeSlider").value = volume * 100;
|
|
|
+ this.player.setVolume(volume);
|
|
|
+ if (!this.muted) localStorage.setItem("volume", volume);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ increaseVolume() {
|
|
|
+ if (this.playerReady) {
|
|
|
+ const previousVolume = parseInt(localStorage.getItem("volume"));
|
|
|
+ let volume = previousVolume + 5;
|
|
|
+ if (previousVolume === 0) {
|
|
|
+ this.muted = false;
|
|
|
+ localStorage.setItem("muted", false);
|
|
|
}
|
|
|
- },
|
|
|
- toggleLike: function() {
|
|
|
- let _this = this;
|
|
|
- if (_this.liked) _this.socket.emit('songs.unlike', _this.currentSong.songId, data => {
|
|
|
- if (data.status !== 'success') Toast.methods.addToast(`Error: ${data.message}`, 8000);
|
|
|
- }); else _this.socket.emit('songs.like', _this.currentSong.songId, data => {
|
|
|
- if (data.status !== 'success') Toast.methods.addToast(`Error: ${data.message}`, 8000);
|
|
|
- });
|
|
|
- },
|
|
|
- toggleDislike: function() {
|
|
|
- let _this = this;
|
|
|
- if (_this.disliked) return _this.socket.emit('songs.undislike', _this.currentSong.songId, data => {
|
|
|
- if (data.status !== 'success') Toast.methods.addToast(`Error: ${data.message}`, 8000);
|
|
|
- });
|
|
|
- _this.socket.emit('songs.dislike', _this.currentSong.songId, data => {
|
|
|
- if (data.status !== 'success') Toast.methods.addToast(`Error: ${data.message}`, 8000);
|
|
|
+ if (volume > 100) volume = 100;
|
|
|
+ document.getElementById("volumeSlider").value = volume * 100;
|
|
|
+ this.player.setVolume(volume);
|
|
|
+ localStorage.setItem("volume", volume);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ toggleLike() {
|
|
|
+ const _this = this;
|
|
|
+ if (_this.liked)
|
|
|
+ _this.socket.emit(
|
|
|
+ "songs.unlike",
|
|
|
+ _this.currentSong.songId,
|
|
|
+ data => {
|
|
|
+ if (data.status !== "success")
|
|
|
+ Toast.methods.addToast(
|
|
|
+ `Error: ${data.message}`,
|
|
|
+ 8000
|
|
|
+ );
|
|
|
+ }
|
|
|
+ );
|
|
|
+ else
|
|
|
+ _this.socket.emit(
|
|
|
+ "songs.like",
|
|
|
+ _this.currentSong.songId,
|
|
|
+ data => {
|
|
|
+ if (data.status !== "success")
|
|
|
+ Toast.methods.addToast(
|
|
|
+ `Error: ${data.message}`,
|
|
|
+ 8000
|
|
|
+ );
|
|
|
+ }
|
|
|
+ );
|
|
|
+ },
|
|
|
+ toggleDislike() {
|
|
|
+ const _this = this;
|
|
|
+ if (_this.disliked)
|
|
|
+ return _this.socket.emit(
|
|
|
+ "songs.undislike",
|
|
|
+ _this.currentSong.songId,
|
|
|
+ data => {
|
|
|
+ if (data.status !== "success")
|
|
|
+ Toast.methods.addToast(
|
|
|
+ `Error: ${data.message}`,
|
|
|
+ 8000
|
|
|
+ );
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ return _this.socket.emit(
|
|
|
+ "songs.dislike",
|
|
|
+ _this.currentSong.songId,
|
|
|
+ data => {
|
|
|
+ if (data.status !== "success")
|
|
|
+ Toast.methods.addToast(`Error: ${data.message}`, 8000);
|
|
|
+ }
|
|
|
+ );
|
|
|
+ },
|
|
|
+ addFirstPrivatePlaylistSongToQueue() {
|
|
|
+ const _this = this;
|
|
|
+ let isInQueue = false;
|
|
|
+ const { userId } = _this.$parent;
|
|
|
+ if (_this.type === "community") {
|
|
|
+ _this.songsList.forEach(queueSong => {
|
|
|
+ if (queueSong.requestedBy === userId) isInQueue = true;
|
|
|
});
|
|
|
- },
|
|
|
- addFirstPrivatePlaylistSongToQueue: function() {
|
|
|
- let _this = this;
|
|
|
- let isInQueue = false;
|
|
|
- let userId = _this.$parent.userId;
|
|
|
- if (_this.type === 'community') {
|
|
|
- _this.songsList.forEach((queueSong) => {
|
|
|
- if (queueSong.requestedBy === userId) isInQueue = true;
|
|
|
- });
|
|
|
- if (!isInQueue && _this.privatePlaylistQueueSelected) {
|
|
|
-
|
|
|
- _this.socket.emit('playlists.getFirstSong', _this.privatePlaylistQueueSelected, data => {
|
|
|
- if (data.status === 'success') {
|
|
|
- if (data.song.duration < 15 * 60) {
|
|
|
- let songId = data.song._id;
|
|
|
- _this.automaticallyRequestedSongId = data.song.songId;
|
|
|
- _this.socket.emit('stations.addToQueue', _this.station._id, data.song.songId, data2 => {
|
|
|
- if (data2.status === 'success') {
|
|
|
- _this.socket.emit('playlists.moveSongToBottom', _this.privatePlaylistQueueSelected, data.song.songId, data3 => {
|
|
|
- if (data3.status === 'success') {
|
|
|
- }
|
|
|
- });
|
|
|
+ if (!isInQueue && _this.privatePlaylistQueueSelected) {
|
|
|
+ _this.socket.emit(
|
|
|
+ "playlists.getFirstSong",
|
|
|
+ _this.privatePlaylistQueueSelected,
|
|
|
+ data => {
|
|
|
+ if (data.status === "success") {
|
|
|
+ if (data.song.duration < 15 * 60) {
|
|
|
+ _this.automaticallyRequestedSongId =
|
|
|
+ data.song.songId;
|
|
|
+ _this.socket.emit(
|
|
|
+ "stations.addToQueue",
|
|
|
+ _this.station._id,
|
|
|
+ data.song.songId,
|
|
|
+ data2 => {
|
|
|
+ if (data2.status === "success") {
|
|
|
+ _this.socket.emit(
|
|
|
+ "playlists.moveSongToBottom",
|
|
|
+ _this.privatePlaylistQueueSelected,
|
|
|
+ data.song.songId,
|
|
|
+ data3 => {
|
|
|
+ if (
|
|
|
+ data3.status ===
|
|
|
+ "success"
|
|
|
+ ) {} // eslint-disable-line
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }
|
|
|
}
|
|
|
- });
|
|
|
+ );
|
|
|
} else {
|
|
|
- Toast.methods.addToast(`Top song in playlist was too long to be added.`, 3000);
|
|
|
- _this.socket.emit('playlists.moveSongToBottom', _this.privatePlaylistQueueSelected, data.song.songId, data3 => {
|
|
|
- if (data3.status === 'success') {
|
|
|
- setTimeout(() => {
|
|
|
- this.addFirstPrivatePlaylistSongToQueue();
|
|
|
- }, 3000);
|
|
|
+ Toast.methods.addToast(
|
|
|
+ `Top song in playlist was too long to be added.`,
|
|
|
+ 3000
|
|
|
+ );
|
|
|
+ _this.socket.emit(
|
|
|
+ "playlists.moveSongToBottom",
|
|
|
+ _this.privatePlaylistQueueSelected,
|
|
|
+ data.song.songId,
|
|
|
+ data3 => {
|
|
|
+ if (data3.status === "success") {
|
|
|
+ setTimeout(() => {
|
|
|
+ this.addFirstPrivatePlaylistSongToQueue();
|
|
|
+ }, 3000);
|
|
|
+ }
|
|
|
}
|
|
|
- });
|
|
|
+ );
|
|
|
}
|
|
|
}
|
|
|
- });
|
|
|
- }
|
|
|
+ }
|
|
|
+ );
|
|
|
}
|
|
|
- },
|
|
|
- joinStation: function () {
|
|
|
- let _this = this;
|
|
|
- _this.socket.emit('stations.join', _this.stationName, res => {
|
|
|
- if (res.status === 'success') {
|
|
|
- _this.station = {
|
|
|
- _id: res.data._id,
|
|
|
- name: _this.stationName,
|
|
|
- displayName: res.data.displayName,
|
|
|
- description: res.data.description,
|
|
|
- privacy: res.data.privacy,
|
|
|
- locked: res.data.locked,
|
|
|
- partyMode: res.data.partyMode,
|
|
|
- owner: res.data.owner,
|
|
|
- privatePlaylist: res.data.privatePlaylist
|
|
|
- };
|
|
|
- _this.currentSong = (res.data.currentSong) ? res.data.currentSong : {};
|
|
|
- _this.type = res.data.type;
|
|
|
- _this.startedAt = res.data.startedAt;
|
|
|
- _this.paused = res.data.paused;
|
|
|
- _this.timePaused = res.data.timePaused;
|
|
|
- _this.userCount = res.data.userCount;
|
|
|
- _this.users = res.data.users;
|
|
|
- if (res.data.currentSong) {
|
|
|
- _this.noSong = false;
|
|
|
- _this.simpleSong = (res.data.currentSong.likes === -1 && res.data.currentSong.dislikes === -1);
|
|
|
- if (_this.simpleSong) {
|
|
|
- _this.currentSong.skipDuration = 0;
|
|
|
- }
|
|
|
- _this.youtubeReady();
|
|
|
- _this.playVideo();
|
|
|
- _this.socket.emit('songs.getOwnSongRatings', res.data.currentSong.songId, data => {
|
|
|
+ }
|
|
|
+ },
|
|
|
+ join() {
|
|
|
+ const _this = this;
|
|
|
+ _this.socket.emit("stations.join", _this.stationName, res => {
|
|
|
+ if (res.status === "success") {
|
|
|
+ _this.loading = false;
|
|
|
+
|
|
|
+ const {
|
|
|
+ _id,
|
|
|
+ displayName,
|
|
|
+ description,
|
|
|
+ privacy,
|
|
|
+ locked,
|
|
|
+ partyMode,
|
|
|
+ owner,
|
|
|
+ privatePlaylist
|
|
|
+ } = res.data;
|
|
|
+
|
|
|
+ document.title = `Musare - ${displayName}`;
|
|
|
+
|
|
|
+ _this.joinStation({
|
|
|
+ _id,
|
|
|
+ name: _this.stationName,
|
|
|
+ displayName,
|
|
|
+ description,
|
|
|
+ privacy,
|
|
|
+ locked,
|
|
|
+ partyMode,
|
|
|
+ owner,
|
|
|
+ privatePlaylist
|
|
|
+ });
|
|
|
+ _this.currentSong = res.data.currentSong
|
|
|
+ ? res.data.currentSong
|
|
|
+ : {};
|
|
|
+ if (_this.currentSong.artists)
|
|
|
+ _this.currentSong.artists = _this.currentSong.artists.join(
|
|
|
+ ", "
|
|
|
+ );
|
|
|
+ _this.type = res.data.type;
|
|
|
+ _this.startedAt = res.data.startedAt;
|
|
|
+ _this.paused = res.data.paused;
|
|
|
+ _this.timePaused = res.data.timePaused;
|
|
|
+ _this.userCount = res.data.userCount;
|
|
|
+ _this.users = res.data.users;
|
|
|
+ _this.pausedAt = res.data.pausedAt;
|
|
|
+ if (res.data.currentSong) {
|
|
|
+ _this.noSong = false;
|
|
|
+ _this.simpleSong =
|
|
|
+ res.data.currentSong.likes === -1 &&
|
|
|
+ res.data.currentSong.dislikes === -1;
|
|
|
+ if (_this.simpleSong) {
|
|
|
+ _this.currentSong.skipDuration = 0;
|
|
|
+ }
|
|
|
+ _this.youtubeReady();
|
|
|
+ _this.playVideo();
|
|
|
+ _this.socket.emit(
|
|
|
+ "songs.getOwnSongRatings",
|
|
|
+ res.data.currentSong.songId,
|
|
|
+ data => {
|
|
|
if (_this.currentSong.songId === data.songId) {
|
|
|
_this.liked = data.liked;
|
|
|
_this.disliked = data.disliked;
|
|
|
}
|
|
|
- });
|
|
|
- } else {
|
|
|
- if (_this.playerReady) _this.player.pauseVideo();
|
|
|
- _this.noSong = true;
|
|
|
- }
|
|
|
- if (_this.type === 'community') {
|
|
|
- _this.socket.emit('stations.getQueue', _this.station._id, data => {
|
|
|
- if (data.status === 'success') _this.songsList = data.queue;
|
|
|
- });
|
|
|
- }
|
|
|
- if (_this.type === 'official') {
|
|
|
- _this.socket.emit('stations.getPlaylist', _this.station._id, res => {
|
|
|
- if (res.status == 'success') _this.songsList = res.data;
|
|
|
- });
|
|
|
- }
|
|
|
+ }
|
|
|
+ );
|
|
|
} else {
|
|
|
- _this.$router.go('/404');
|
|
|
- Toast.methods.addToast(res.message, 3000);
|
|
|
+ if (_this.playerReady) _this.player.pauseVideo();
|
|
|
+ _this.noSong = true;
|
|
|
}
|
|
|
// UNIX client time before ping
|
|
|
- let beforePing = Date.now();
|
|
|
- _this.socket.emit('apis.ping', res => {
|
|
|
+ const beforePing = Date.now();
|
|
|
+ _this.socket.emit("apis.ping", pong => {
|
|
|
// UNIX client time after ping
|
|
|
- let afterPing = Date.now();
|
|
|
+ const afterPing = Date.now();
|
|
|
// Average time in MS it took between the server responding and the client receiving
|
|
|
- let connectionLatency = (afterPing - beforePing) / 2;
|
|
|
+ const connectionLatency = (afterPing - beforePing) / 2;
|
|
|
console.log(connectionLatency, beforePing - afterPing);
|
|
|
// UNIX server time
|
|
|
- let serverDate = res.date;
|
|
|
+ const serverDate = pong.date;
|
|
|
// Difference between the server UNIX time and the client UNIX time after ping, with the connectionLatency added to the server UNIX time
|
|
|
- let difference = (serverDate + connectionLatency) - afterPing;
|
|
|
+ const difference =
|
|
|
+ serverDate + connectionLatency - afterPing;
|
|
|
console.log("Difference: ", difference);
|
|
|
if (difference > 3000 || difference < -3000) {
|
|
|
- console.log("System time difference is bigger than 3 seconds.");
|
|
|
+ console.log(
|
|
|
+ "System time difference is bigger than 3 seconds."
|
|
|
+ );
|
|
|
}
|
|
|
_this.systemDifference = difference;
|
|
|
});
|
|
|
- });
|
|
|
- }
|
|
|
+ }
|
|
|
+ });
|
|
|
},
|
|
|
- ready: function() {
|
|
|
- let _this = this;
|
|
|
-
|
|
|
- Date.currently = () => {
|
|
|
- return new Date().getTime() + _this.systemDifference;
|
|
|
- };
|
|
|
-
|
|
|
- _this.stationName = _this.$route.params.id;
|
|
|
-
|
|
|
- window.stationInterval = 0;
|
|
|
-
|
|
|
- io.getSocket(socket => {
|
|
|
- _this.socket = socket;
|
|
|
-
|
|
|
- io.removeAllListeners();
|
|
|
- if (_this.socket.connected) _this.joinStation();
|
|
|
- io.onConnect(() => {
|
|
|
- _this.joinStation();
|
|
|
- });
|
|
|
- _this.socket.emit('stations.findByName', _this.stationName, res => {
|
|
|
- if (res.status === 'error') {
|
|
|
- _this.$router.go('/404');
|
|
|
- Toast.methods.addToast(res.message, 3000);
|
|
|
- } else {
|
|
|
- _this.ready = true;
|
|
|
- }
|
|
|
- });
|
|
|
- _this.socket.on('event:songs.next', data => {
|
|
|
- _this.previousSong = (_this.currentSong.songId) ? _this.currentSong : null;
|
|
|
- _this.currentSong = (data.currentSong) ? data.currentSong : {};
|
|
|
- _this.startedAt = data.startedAt;
|
|
|
- _this.paused = data.paused;
|
|
|
- _this.timePaused = data.timePaused;
|
|
|
- if (data.currentSong) {
|
|
|
- _this.noSong = false;
|
|
|
- _this.simpleSong = (data.currentSong.likes === -1 && data.currentSong.dislikes === -1);
|
|
|
- if (_this.simpleSong) _this.currentSong.skipDuration = 0;
|
|
|
- if (!_this.playerReady) _this.youtubeReady();
|
|
|
- else _this.playVideo();
|
|
|
- _this.socket.emit('songs.getOwnSongRatings', data.currentSong.songId, (data) => {
|
|
|
- if (_this.currentSong.songId === data.songId) {
|
|
|
- _this.liked = data.liked;
|
|
|
- _this.disliked = data.disliked;
|
|
|
+ ...mapActions("modals", ["openModal"]),
|
|
|
+ ...mapActions("station", ["joinStation"])
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ const _this = this;
|
|
|
+
|
|
|
+ Date.currently = () => {
|
|
|
+ return new Date().getTime() + _this.systemDifference;
|
|
|
+ };
|
|
|
+
|
|
|
+ _this.stationName = _this.$route.params.id;
|
|
|
+
|
|
|
+ window.stationInterval = 0;
|
|
|
+
|
|
|
+ io.getSocket(socket => {
|
|
|
+ _this.socket = socket;
|
|
|
+
|
|
|
+ io.removeAllListeners();
|
|
|
+ if (_this.socket.connected) _this.join();
|
|
|
+ io.onConnect(_this.join);
|
|
|
+ _this.socket.emit("stations.findByName", _this.stationName, res => {
|
|
|
+ if (res.status === "failure") {
|
|
|
+ _this.loading = false;
|
|
|
+ _this.exists = false;
|
|
|
+ } else {
|
|
|
+ _this.exists = true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ _this.socket.on("event:songs.next", data => {
|
|
|
+ _this.previousSong = _this.currentSong.songId
|
|
|
+ ? _this.currentSong
|
|
|
+ : null;
|
|
|
+ _this.currentSong = data.currentSong ? data.currentSong : {};
|
|
|
+ _this.startedAt = data.startedAt;
|
|
|
+ _this.paused = data.paused;
|
|
|
+ _this.timePaused = data.timePaused;
|
|
|
+ if (data.currentSong) {
|
|
|
+ _this.noSong = false;
|
|
|
+ if (_this.currentSong.artists)
|
|
|
+ _this.currentSong.artists = _this.currentSong.artists.join(
|
|
|
+ ", "
|
|
|
+ );
|
|
|
+ _this.simpleSong =
|
|
|
+ data.currentSong.likes === -1 &&
|
|
|
+ data.currentSong.dislikes === -1;
|
|
|
+ if (_this.simpleSong) _this.currentSong.skipDuration = 0;
|
|
|
+ if (!_this.playerReady) _this.youtubeReady();
|
|
|
+ else _this.playVideo();
|
|
|
+ _this.socket.emit(
|
|
|
+ "songs.getOwnSongRatings",
|
|
|
+ data.currentSong.songId,
|
|
|
+ song => {
|
|
|
+ if (_this.currentSong.songId === song.songId) {
|
|
|
+ _this.liked = song.liked;
|
|
|
+ _this.disliked = song.disliked;
|
|
|
}
|
|
|
- });
|
|
|
- } else {
|
|
|
- if (_this.playerReady) _this.player.pauseVideo();
|
|
|
- _this.noSong = true;
|
|
|
- }
|
|
|
+ }
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ if (_this.playerReady) _this.player.pauseVideo();
|
|
|
+ _this.noSong = true;
|
|
|
+ }
|
|
|
|
|
|
- let isInQueue = false;
|
|
|
- let userId = _this.$parent.userId;
|
|
|
- _this.songsList.forEach((queueSong) => {
|
|
|
- if (queueSong.requestedBy === userId) isInQueue = true;
|
|
|
- });
|
|
|
- if (!isInQueue && _this.privatePlaylistQueueSelected && (_this.automaticallyRequestedSongId !== _this.currentSong.songId || !_this.currentSong.songId)) {
|
|
|
- _this.addFirstPrivatePlaylistSongToQueue();
|
|
|
- }
|
|
|
+ let isInQueue = false;
|
|
|
+ const { userId } = _this.$parent;
|
|
|
+ _this.songsList.forEach(queueSong => {
|
|
|
+ if (queueSong.requestedBy === userId) isInQueue = true;
|
|
|
});
|
|
|
+ if (
|
|
|
+ !isInQueue &&
|
|
|
+ _this.privatePlaylistQueueSelected &&
|
|
|
+ (_this.automaticallyRequestedSongId !==
|
|
|
+ _this.currentSong.songId ||
|
|
|
+ !_this.currentSong.songId)
|
|
|
+ ) {
|
|
|
+ _this.addFirstPrivatePlaylistSongToQueue();
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
- _this.socket.on('event:stations.pause', data => {
|
|
|
- _this.pauseLocalStation();
|
|
|
- });
|
|
|
+ _this.socket.on("event:stations.pause", data => {
|
|
|
+ _this.pausedAt = data.pausedAt;
|
|
|
+ _this.pauseLocalStation();
|
|
|
+ });
|
|
|
|
|
|
- _this.socket.on('event:stations.resume', data => {
|
|
|
- _this.timePaused = data.timePaused;
|
|
|
- _this.resumeLocalStation();
|
|
|
- });
|
|
|
+ _this.socket.on("event:stations.resume", data => {
|
|
|
+ _this.timePaused = data.timePaused;
|
|
|
+ _this.resumeLocalStation();
|
|
|
+ });
|
|
|
|
|
|
- _this.socket.on('event:stations.remove', () => {
|
|
|
- location.href = '/';
|
|
|
- });
|
|
|
+ _this.socket.on("event:stations.remove", () => {
|
|
|
+ window.location.href = "/";
|
|
|
+ return true;
|
|
|
+ });
|
|
|
|
|
|
- _this.socket.on('event:song.like', data => {
|
|
|
- if (!this.noSong) {
|
|
|
- if (data.songId === _this.currentSong.songId) {
|
|
|
- _this.currentSong.dislikes = data.dislikes;
|
|
|
- _this.currentSong.likes = data.likes;
|
|
|
- }
|
|
|
+ _this.socket.on("event:song.like", data => {
|
|
|
+ if (!this.noSong) {
|
|
|
+ if (data.songId === _this.currentSong.songId) {
|
|
|
+ _this.currentSong.dislikes = data.dislikes;
|
|
|
+ _this.currentSong.likes = data.likes;
|
|
|
}
|
|
|
- });
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
- _this.socket.on('event:song.dislike', data => {
|
|
|
- if (!this.noSong) {
|
|
|
- if (data.songId === _this.currentSong.songId) {
|
|
|
- _this.currentSong.dislikes = data.dislikes;
|
|
|
- _this.currentSong.likes = data.likes;
|
|
|
- }
|
|
|
+ _this.socket.on("event:song.dislike", data => {
|
|
|
+ if (!this.noSong) {
|
|
|
+ if (data.songId === _this.currentSong.songId) {
|
|
|
+ _this.currentSong.dislikes = data.dislikes;
|
|
|
+ _this.currentSong.likes = data.likes;
|
|
|
}
|
|
|
- });
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
- _this.socket.on('event:song.unlike', data => {
|
|
|
- if (!this.noSong) {
|
|
|
- if (data.songId === _this.currentSong.songId) {
|
|
|
- _this.currentSong.dislikes = data.dislikes;
|
|
|
- _this.currentSong.likes = data.likes;
|
|
|
- }
|
|
|
+ _this.socket.on("event:song.unlike", data => {
|
|
|
+ if (!this.noSong) {
|
|
|
+ if (data.songId === _this.currentSong.songId) {
|
|
|
+ _this.currentSong.dislikes = data.dislikes;
|
|
|
+ _this.currentSong.likes = data.likes;
|
|
|
}
|
|
|
- });
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
- _this.socket.on('event:song.undislike', data => {
|
|
|
- if (!this.noSong) {
|
|
|
- if (data.songId === _this.currentSong.songId) {
|
|
|
- _this.currentSong.dislikes = data.dislikes;
|
|
|
- _this.currentSong.likes = data.likes;
|
|
|
- }
|
|
|
+ _this.socket.on("event:song.undislike", data => {
|
|
|
+ if (!this.noSong) {
|
|
|
+ if (data.songId === _this.currentSong.songId) {
|
|
|
+ _this.currentSong.dislikes = data.dislikes;
|
|
|
+ _this.currentSong.likes = data.likes;
|
|
|
}
|
|
|
- });
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
- _this.socket.on('event:song.newRatings', data => {
|
|
|
- if (!this.noSong) {
|
|
|
- if (data.songId === _this.currentSong.songId) {
|
|
|
- _this.liked = data.liked;
|
|
|
- _this.disliked = data.disliked;
|
|
|
- }
|
|
|
+ _this.socket.on("event:song.newRatings", data => {
|
|
|
+ if (!this.noSong) {
|
|
|
+ if (data.songId === _this.currentSong.songId) {
|
|
|
+ _this.liked = data.liked;
|
|
|
+ _this.disliked = data.disliked;
|
|
|
}
|
|
|
- });
|
|
|
-
|
|
|
- _this.socket.on('event:queue.update', queue => {
|
|
|
- if (this.type === 'community') this.songsList = queue;
|
|
|
- });
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
- _this.socket.on('event:song.voteSkipSong', () => {
|
|
|
- if (this.currentSong) this.currentSong.skipVotes++;
|
|
|
- });
|
|
|
+ _this.socket.on("event:queue.update", queue => {
|
|
|
+ if (this.type === "community") this.songsList = queue;
|
|
|
+ });
|
|
|
|
|
|
- _this.socket.on('event:privatePlaylist.selected', (playlistId) => {
|
|
|
- if (this.type === 'community') {
|
|
|
- this.station.privatePlaylist = playlistId;
|
|
|
- }
|
|
|
- });
|
|
|
+ _this.socket.on("event:song.voteSkipSong", () => {
|
|
|
+ if (this.currentSong) this.currentSong.skipVotes += 1;
|
|
|
+ });
|
|
|
|
|
|
- _this.socket.on('event:partyMode.updated', (partyMode) => {
|
|
|
- if (this.type === 'community') {
|
|
|
- this.station.partyMode = partyMode;
|
|
|
- }
|
|
|
- });
|
|
|
+ _this.socket.on("event:privatePlaylist.selected", playlistId => {
|
|
|
+ if (this.type === "community") {
|
|
|
+ this.station.privatePlaylist = playlistId;
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
- _this.socket.on('event:newOfficialPlaylist', (playlist) => {
|
|
|
- if (this.type === 'official') {
|
|
|
- this.songsList = playlist;
|
|
|
- }
|
|
|
- });
|
|
|
+ _this.socket.on("event:partyMode.updated", partyMode => {
|
|
|
+ if (this.type === "community") {
|
|
|
+ this.station.partyMode = partyMode;
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
- _this.socket.on('event:users.updated', users => {
|
|
|
- _this.users = users;
|
|
|
- });
|
|
|
+ _this.socket.on("event:newOfficialPlaylist", playlist => {
|
|
|
+ if (this.type === "official") {
|
|
|
+ this.songsList = playlist;
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
- _this.socket.on('event:userCount.updated', userCount => {
|
|
|
- _this.userCount = userCount;
|
|
|
- });
|
|
|
+ _this.socket.on("event:users.updated", users => {
|
|
|
+ _this.users = users;
|
|
|
+ });
|
|
|
|
|
|
- _this.socket.on('event:queueLockToggled', locked => {
|
|
|
- _this.station.locked = locked;
|
|
|
- });
|
|
|
+ _this.socket.on("event:userCount.updated", userCount => {
|
|
|
+ _this.userCount = userCount;
|
|
|
});
|
|
|
|
|
|
+ _this.socket.on("event:queueLockToggled", locked => {
|
|
|
+ _this.station.locked = locked;
|
|
|
+ });
|
|
|
+ });
|
|
|
|
|
|
+ if (JSON.parse(localStorage.getItem("muted"))) {
|
|
|
+ this.muted = true;
|
|
|
+ this.player.setVolume(0);
|
|
|
+ document.getElementById("volumeSlider").value = 0 * 100;
|
|
|
+ } else {
|
|
|
let volume = parseFloat(localStorage.getItem("volume"));
|
|
|
- volume = (typeof volume === "number" && !isNaN(volume)) ? volume : 20;
|
|
|
+ volume =
|
|
|
+ typeof volume === "number" && !Number.isNaN(volume)
|
|
|
+ ? volume
|
|
|
+ : 20;
|
|
|
localStorage.setItem("volume", volume);
|
|
|
- $("#volumeSlider").val(volume * 100);
|
|
|
- },
|
|
|
- components: {
|
|
|
- OfficialHeader,
|
|
|
- CommunityHeader,
|
|
|
- SongQueue,
|
|
|
- AddToPlaylist,
|
|
|
- EditPlaylist,
|
|
|
- CreatePlaylist,
|
|
|
- EditStation,
|
|
|
- Report,
|
|
|
- SongsListSidebar,
|
|
|
- PlaylistSidebar,
|
|
|
- UsersSidebar,
|
|
|
- MainFooter
|
|
|
+ document.getElementById("volumeSlider").value = volume * 100;
|
|
|
}
|
|
|
+ },
|
|
|
+ components: {
|
|
|
+ OfficialHeader,
|
|
|
+ CommunityHeader,
|
|
|
+ SongQueue,
|
|
|
+ AddToPlaylist,
|
|
|
+ EditPlaylist,
|
|
|
+ CreatePlaylist,
|
|
|
+ EditStation,
|
|
|
+ Report,
|
|
|
+ SongsListSidebar,
|
|
|
+ PlaylistSidebar,
|
|
|
+ UsersSidebar,
|
|
|
+ UserIdToUsername,
|
|
|
+ Z404
|
|
|
}
|
|
|
+};
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss">
|
|
|
- .no-song {
|
|
|
- color: #ff4545;
|
|
|
+.player-can-not-autoplay {
|
|
|
+ position: absolute;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ background: rgba(3, 169, 244, 0.95);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+
|
|
|
+ p {
|
|
|
+ color: white;
|
|
|
+ font-size: 26px;
|
|
|
text-align: center;
|
|
|
}
|
|
|
-
|
|
|
- #volumeSlider {
|
|
|
- padding: 0 15px;
|
|
|
- background: transparent;
|
|
|
- }
|
|
|
-
|
|
|
- .volume-slider-wrapper {
|
|
|
- margin-top: 0;
|
|
|
- position: relative;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- .material-icons { user-select: none; }
|
|
|
- }
|
|
|
-
|
|
|
- .material-icons { cursor: pointer; }
|
|
|
-
|
|
|
- .stationDisplayName {
|
|
|
- color: white !important;
|
|
|
+}
|
|
|
+
|
|
|
+.slide-enter-active,
|
|
|
+.slide-leave-active {
|
|
|
+ transition: all 0.3s ease;
|
|
|
+}
|
|
|
+.slide-enter,
|
|
|
+.slide-leave-to {
|
|
|
+ transform: translateX(300px);
|
|
|
+}
|
|
|
+
|
|
|
+.no-song {
|
|
|
+ color: #03a9f4;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+#volumeSlider {
|
|
|
+ padding: 0 15px;
|
|
|
+ background: transparent;
|
|
|
+}
|
|
|
+
|
|
|
+.volume-slider-wrapper {
|
|
|
+ margin-top: 0;
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ .material-icons {
|
|
|
+ user-select: none;
|
|
|
}
|
|
|
-
|
|
|
- .add-to-playlist {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.material-icons {
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.stationDisplayName {
|
|
|
+ color: white !important;
|
|
|
+}
|
|
|
+
|
|
|
+.add-to-playlist {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.slideout {
|
|
|
+ top: 50px;
|
|
|
+ height: 100%;
|
|
|
+ position: fixed;
|
|
|
+ right: 0;
|
|
|
+ width: 350px;
|
|
|
+ background-color: white;
|
|
|
+ box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16),
|
|
|
+ 0 2px 10px 0 rgba(0, 0, 0, 0.12);
|
|
|
+ .slideout-header {
|
|
|
+ text-align: center;
|
|
|
+ background-color: rgb(3, 169, 244) !important;
|
|
|
+ margin: 0;
|
|
|
+ padding-top: 5px;
|
|
|
+ padding-bottom: 7px;
|
|
|
+ color: white;
|
|
|
}
|
|
|
|
|
|
- .slideout {
|
|
|
- top: 50px;
|
|
|
+ .slideout-content {
|
|
|
height: 100%;
|
|
|
- position: fixed;
|
|
|
- right: 0;
|
|
|
- width: 350px;
|
|
|
- background-color: white;
|
|
|
- box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
|
|
|
- .slideout-header {
|
|
|
- text-align: center;
|
|
|
- background-color: rgb(255, 69, 69) !important;
|
|
|
- margin: 0;
|
|
|
- padding-top: 5px;
|
|
|
- padding-bottom: 7px;
|
|
|
- color: white;
|
|
|
- }
|
|
|
-
|
|
|
- .slideout-content {
|
|
|
- height: 100%;
|
|
|
- }
|
|
|
}
|
|
|
-
|
|
|
- .modal-large {
|
|
|
- width: 75%;
|
|
|
+}
|
|
|
+
|
|
|
+.modal-large {
|
|
|
+ width: 75%;
|
|
|
+}
|
|
|
+
|
|
|
+.station {
|
|
|
+ flex: 1 0 auto;
|
|
|
+ padding-top: 0.5vw;
|
|
|
+ transition: all 0.1s;
|
|
|
+ margin: 0 auto;
|
|
|
+ max-width: 100%;
|
|
|
+ width: 90%;
|
|
|
+
|
|
|
+ @media only screen and (min-width: 993px) {
|
|
|
+ width: 70%;
|
|
|
}
|
|
|
|
|
|
- .station {
|
|
|
- flex: 1 0 auto;
|
|
|
- padding-top: 0.5vw;
|
|
|
- transition: all 0.1s;
|
|
|
- margin: 0 auto;
|
|
|
- max-width: 100%;
|
|
|
- width: 90%;
|
|
|
-
|
|
|
- @media only screen and (min-width: 993px) {
|
|
|
- width: 70%;
|
|
|
- }
|
|
|
-
|
|
|
- @media only screen and (min-width: 601px) {
|
|
|
- width: 85%;
|
|
|
- }
|
|
|
+ @media only screen and (min-width: 601px) {
|
|
|
+ width: 85%;
|
|
|
+ }
|
|
|
|
|
|
- @media (min-width: 999px) {
|
|
|
- .mobile-only {
|
|
|
- display: none;
|
|
|
- }
|
|
|
- .desktop-only {
|
|
|
- display: block;
|
|
|
- }
|
|
|
+ @media (min-width: 999px) {
|
|
|
+ .mobile-only {
|
|
|
+ display: none;
|
|
|
}
|
|
|
- @media (max-width: 998px) {
|
|
|
- .mobile-only {
|
|
|
- display: block;
|
|
|
- }
|
|
|
- .desktop-only {
|
|
|
- display: none;
|
|
|
- visibility: hidden;
|
|
|
- }
|
|
|
+ .desktop-only {
|
|
|
+ display: block;
|
|
|
}
|
|
|
-
|
|
|
+ }
|
|
|
+ @media (max-width: 998px) {
|
|
|
.mobile-only {
|
|
|
- text-align: center;
|
|
|
+ display: block;
|
|
|
}
|
|
|
+ .desktop-only {
|
|
|
+ display: none;
|
|
|
+ visibility: hidden;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- .playlistCard {
|
|
|
- margin: 10px;
|
|
|
- position: relative;
|
|
|
- padding-bottom: calc(31.25% + 7px);
|
|
|
- height: 0;
|
|
|
- overflow-y: scroll;
|
|
|
-
|
|
|
- .title {
|
|
|
- background-color: rgb(255, 69, 69);
|
|
|
- text-align: center;
|
|
|
- padding: 10px;
|
|
|
- color: white;
|
|
|
- font-weight: 600;
|
|
|
- }
|
|
|
-
|
|
|
- .media { padding: 0 25px; }
|
|
|
-
|
|
|
- .media-content .content {
|
|
|
- min-height: 64px;
|
|
|
- max-height: 64px;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- }
|
|
|
-
|
|
|
- .content p strong { word-break: break-word; }
|
|
|
-
|
|
|
- .content p small { word-break: break-word; }
|
|
|
-
|
|
|
- .add-to-queue {
|
|
|
- width: 100%;
|
|
|
- margin-top: 25px;
|
|
|
- height: 40px;
|
|
|
- border-radius: 0;
|
|
|
- background: rgb(255, 69, 69);
|
|
|
- color: #fff !important;
|
|
|
- border: 0;
|
|
|
- &:active, &:focus { border: 0; }
|
|
|
- }
|
|
|
-
|
|
|
- .add-to-queue:focus { background: #029ce3; }
|
|
|
-
|
|
|
- .media-right { line-height: 64px; }
|
|
|
+ .mobile-only {
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
|
|
|
- .songTitle {
|
|
|
- word-wrap: break-word;
|
|
|
- overflow: hidden;
|
|
|
- text-overflow: ellipsis;
|
|
|
- display: -webkit-box;
|
|
|
- -webkit-box-orient: vertical;
|
|
|
- -webkit-line-clamp: 2;
|
|
|
- line-height: 20px;
|
|
|
- max-height: 40px;
|
|
|
- width: 100%;
|
|
|
- }
|
|
|
+ .playlistCard {
|
|
|
+ margin: 10px;
|
|
|
+ position: relative;
|
|
|
+ padding-bottom: calc(31.25% + 7px);
|
|
|
+ height: 0;
|
|
|
+ overflow-y: scroll;
|
|
|
|
|
|
+ .title {
|
|
|
+ background-color: rgb(3, 169, 244);
|
|
|
+ text-align: center;
|
|
|
+ padding: 10px;
|
|
|
+ color: white;
|
|
|
+ font-weight: 600;
|
|
|
}
|
|
|
|
|
|
- input[type=range] {
|
|
|
- -webkit-appearance: none;
|
|
|
- width: 100%;
|
|
|
- margin: 7.3px 0;
|
|
|
+ .media {
|
|
|
+ padding: 0 25px;
|
|
|
}
|
|
|
|
|
|
- input[type=range]:focus {
|
|
|
- outline: none;
|
|
|
+ .media-content .content {
|
|
|
+ min-height: 64px;
|
|
|
+ max-height: 64px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
}
|
|
|
|
|
|
- input[type=range]::-webkit-slider-runnable-track {
|
|
|
- width: 100%;
|
|
|
- height: 5.2px;
|
|
|
- cursor: pointer;
|
|
|
- box-shadow: 0;
|
|
|
- background: #c2c0c2;
|
|
|
- border-radius: 0;
|
|
|
- border: 0;
|
|
|
+ .content p strong {
|
|
|
+ word-break: break-word;
|
|
|
}
|
|
|
|
|
|
- input[type=range]::-webkit-slider-thumb {
|
|
|
- box-shadow: 0;
|
|
|
- border: 0;
|
|
|
- height: 19px;
|
|
|
- width: 19px;
|
|
|
- border-radius: 15px;
|
|
|
- background: #ff4545;
|
|
|
- cursor: pointer;
|
|
|
- -webkit-appearance: none;
|
|
|
- margin-top: -6.5px;
|
|
|
+ .content p small {
|
|
|
+ word-break: break-word;
|
|
|
}
|
|
|
|
|
|
- input[type=range]::-moz-range-track {
|
|
|
+ .add-to-queue {
|
|
|
width: 100%;
|
|
|
- height: 5.2px;
|
|
|
- cursor: pointer;
|
|
|
- box-shadow: 0;
|
|
|
- background: #c2c0c2;
|
|
|
+ margin-top: 25px;
|
|
|
+ height: 40px;
|
|
|
border-radius: 0;
|
|
|
+ background: rgb(3, 169, 244);
|
|
|
+ color: #fff !important;
|
|
|
border: 0;
|
|
|
+ &:active,
|
|
|
+ &:focus {
|
|
|
+ border: 0;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- input[type=range]::-moz-range-thumb {
|
|
|
- box-shadow: 0;
|
|
|
- border: 0;
|
|
|
- height: 19px;
|
|
|
- width: 19px;
|
|
|
- border-radius: 15px;
|
|
|
- background: #ff4545;
|
|
|
- cursor: pointer;
|
|
|
- -webkit-appearance: none;
|
|
|
- margin-top: -6.5px;
|
|
|
- }
|
|
|
-
|
|
|
- input[type=range]::-ms-track {
|
|
|
- width: 100%;
|
|
|
- height: 5.2px;
|
|
|
- cursor: pointer;
|
|
|
- box-shadow: 0;
|
|
|
- background: #c2c0c2;
|
|
|
- border-radius: 1.3px;
|
|
|
- }
|
|
|
-
|
|
|
- input[type=range]::-ms-fill-lower {
|
|
|
- background: #c2c0c2;
|
|
|
- border: 0;
|
|
|
- border-radius: 0;
|
|
|
- box-shadow: 0;
|
|
|
+ .add-to-queue:focus {
|
|
|
+ background: #029ce3;
|
|
|
}
|
|
|
|
|
|
- input[type=range]::-ms-fill-upper {
|
|
|
- background: #c2c0c2;
|
|
|
- border: 0;
|
|
|
- border-radius: 0;
|
|
|
- box-shadow: 0;
|
|
|
+ .media-right {
|
|
|
+ line-height: 64px;
|
|
|
}
|
|
|
|
|
|
- input[type=range]::-ms-thumb {
|
|
|
- box-shadow: 0;
|
|
|
- border: 0;
|
|
|
- height: 15px;
|
|
|
- width: 15px;
|
|
|
- border-radius: 15px;
|
|
|
- background: #ff4545;
|
|
|
- cursor: pointer;
|
|
|
- -webkit-appearance: none;
|
|
|
- margin-top: 1.5px;
|
|
|
- }
|
|
|
-
|
|
|
- .video-container {
|
|
|
- position: relative;
|
|
|
- padding-bottom: 56.25%;
|
|
|
- height: 0;
|
|
|
+ .songTitle {
|
|
|
+ word-wrap: break-word;
|
|
|
overflow: hidden;
|
|
|
-
|
|
|
- iframe {
|
|
|
- position: absolute;
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- }
|
|
|
- }
|
|
|
- .video-col {
|
|
|
- padding-right: 0.75rem;
|
|
|
- padding-left: 0.75rem;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .room-title {
|
|
|
- left: 50%;
|
|
|
- -webkit-transform: translateX(-50%);
|
|
|
- transform: translateX(-50%);
|
|
|
- font-size: 2.1em;
|
|
|
- }
|
|
|
-
|
|
|
- #ratings {
|
|
|
- span {
|
|
|
- font-size: 1.68rem;
|
|
|
- }
|
|
|
-
|
|
|
- i {
|
|
|
- color: #9e9e9e !important;
|
|
|
- cursor: pointer;
|
|
|
- transition: 0.1s color;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ display: -webkit-box;
|
|
|
+ -webkit-box-orient: vertical;
|
|
|
+ -webkit-line-clamp: 2;
|
|
|
+ line-height: 20px;
|
|
|
+ max-height: 40px;
|
|
|
+ width: 100%;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- #time-display {
|
|
|
- margin-top: 30px;
|
|
|
- float: right;
|
|
|
- }
|
|
|
-
|
|
|
- #thumbs_up:hover, #thumbs_up.liked {
|
|
|
- color: #87D37C !important;
|
|
|
- }
|
|
|
-
|
|
|
- #thumbs_down:hover, #thumbs_down.disliked {
|
|
|
- color: #EC644B !important;
|
|
|
- }
|
|
|
-
|
|
|
- #song-thumbnail {
|
|
|
- max-width: 100%;
|
|
|
- width: 85%;
|
|
|
- }
|
|
|
-
|
|
|
- .seeker-bar-container {
|
|
|
- position: relative;
|
|
|
- height: 7px;
|
|
|
- display: block;
|
|
|
+ input[type="range"] {
|
|
|
+ -webkit-appearance: none;
|
|
|
width: 100%;
|
|
|
- overflow: hidden;
|
|
|
- }
|
|
|
-
|
|
|
- .seeker-bar {
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- bottom: 0;
|
|
|
- position: absolute;
|
|
|
- }
|
|
|
-
|
|
|
- ul {
|
|
|
- list-style: none;
|
|
|
- margin: 0;
|
|
|
- display: block;
|
|
|
- }
|
|
|
-
|
|
|
- h1, h2, h3, h4, h5, h6 {
|
|
|
- font-weight: 400;
|
|
|
- line-height: 1.1;
|
|
|
- }
|
|
|
-
|
|
|
- h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
|
|
|
- font-weight: inherit;
|
|
|
- }
|
|
|
-
|
|
|
- h1 {
|
|
|
- font-size: 4.2rem;
|
|
|
- line-height: 110%;
|
|
|
- margin: 2.1rem 0 1.68rem 0;
|
|
|
- }
|
|
|
-
|
|
|
- h2 {
|
|
|
- font-size: 3.56rem;
|
|
|
- line-height: 110%;
|
|
|
- margin: 1.78rem 0 1.424rem 0;
|
|
|
+ margin: 7.3px 0;
|
|
|
}
|
|
|
|
|
|
- h3 {
|
|
|
- font-size: 2.92rem;
|
|
|
- line-height: 110%;
|
|
|
- margin: 1.46rem 0 1.168rem 0;
|
|
|
+ input[type="range"]:focus {
|
|
|
+ outline: none;
|
|
|
}
|
|
|
|
|
|
- h4 {
|
|
|
- font-size: 2.28rem;
|
|
|
- line-height: 110%;
|
|
|
- margin: 1.14rem 0 0.912rem 0;
|
|
|
- }
|
|
|
-
|
|
|
- h5 {
|
|
|
- font-size: 1.64rem;
|
|
|
- line-height: 110%;
|
|
|
- margin: 0.82rem 0 0.656rem 0;
|
|
|
- }
|
|
|
-
|
|
|
- h6 {
|
|
|
- font-size: 1rem;
|
|
|
- line-height: 110%;
|
|
|
- margin: 0.5rem 0 0.4rem 0;
|
|
|
+ input[type="range"]::-webkit-slider-runnable-track {
|
|
|
+ width: 100%;
|
|
|
+ height: 5.2px;
|
|
|
+ cursor: pointer;
|
|
|
+ box-shadow: 0;
|
|
|
+ background: #c2c0c2;
|
|
|
+ border-radius: 0;
|
|
|
+ border: 0;
|
|
|
}
|
|
|
|
|
|
- .thin {
|
|
|
- font-weight: 200;
|
|
|
+ input[type="range"]::-webkit-slider-thumb {
|
|
|
+ box-shadow: 0;
|
|
|
+ border: 0;
|
|
|
+ height: 19px;
|
|
|
+ width: 19px;
|
|
|
+ border-radius: 15px;
|
|
|
+ background: #03a9f4;
|
|
|
+ cursor: pointer;
|
|
|
+ -webkit-appearance: none;
|
|
|
+ margin-top: -6.5px;
|
|
|
}
|
|
|
|
|
|
- .left {
|
|
|
- float: left !important;
|
|
|
+ input[type="range"]::-moz-range-track {
|
|
|
+ width: 100%;
|
|
|
+ height: 5.2px;
|
|
|
+ cursor: pointer;
|
|
|
+ box-shadow: 0;
|
|
|
+ background: #c2c0c2;
|
|
|
+ border-radius: 0;
|
|
|
+ border: 0;
|
|
|
}
|
|
|
|
|
|
- .right {
|
|
|
- float: right !important;
|
|
|
+ input[type="range"]::-moz-range-thumb {
|
|
|
+ box-shadow: 0;
|
|
|
+ border: 0;
|
|
|
+ height: 19px;
|
|
|
+ width: 19px;
|
|
|
+ border-radius: 15px;
|
|
|
+ background: #03a9f4;
|
|
|
+ cursor: pointer;
|
|
|
+ -webkit-appearance: none;
|
|
|
+ margin-top: -6.5px;
|
|
|
}
|
|
|
|
|
|
- .light-blue {
|
|
|
- background-color: #ff4545 !important;
|
|
|
+ input[type="range"]::-ms-track {
|
|
|
+ width: 100%;
|
|
|
+ height: 5.2px;
|
|
|
+ cursor: pointer;
|
|
|
+ box-shadow: 0;
|
|
|
+ background: #c2c0c2;
|
|
|
+ border-radius: 1.3px;
|
|
|
}
|
|
|
|
|
|
- .white {
|
|
|
- background-color: #FFFFFF !important;
|
|
|
+ input[type="range"]::-ms-fill-lower {
|
|
|
+ background: #c2c0c2;
|
|
|
+ border: 0;
|
|
|
+ border-radius: 0;
|
|
|
+ box-shadow: 0;
|
|
|
}
|
|
|
|
|
|
- .btn-search {
|
|
|
- font-size: 14px;
|
|
|
+ input[type="range"]::-ms-fill-upper {
|
|
|
+ background: #c2c0c2;
|
|
|
+ border: 0;
|
|
|
+ border-radius: 0;
|
|
|
+ box-shadow: 0;
|
|
|
}
|
|
|
|
|
|
- .menu { padding: 0 10px; }
|
|
|
-
|
|
|
- .menu-list li a:hover { color: #000 !important; }
|
|
|
-
|
|
|
- .menu-list li {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
+ input[type="range"]::-ms-thumb {
|
|
|
+ box-shadow: 0;
|
|
|
+ border: 0;
|
|
|
+ height: 15px;
|
|
|
+ width: 15px;
|
|
|
+ border-radius: 15px;
|
|
|
+ background: #03a9f4;
|
|
|
+ cursor: pointer;
|
|
|
+ -webkit-appearance: none;
|
|
|
+ margin-top: 1.5px;
|
|
|
}
|
|
|
|
|
|
- .menu-list a {
|
|
|
- /*padding: 0 10px !important;*/
|
|
|
- }
|
|
|
+ .video-container {
|
|
|
+ position: relative;
|
|
|
+ padding-bottom: 56.25%;
|
|
|
+ height: 0;
|
|
|
+ overflow: hidden;
|
|
|
|
|
|
- .menu-list a:hover {
|
|
|
- background-color : transparent;
|
|
|
+ iframe {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- .icons-group { display: flex; }
|
|
|
-
|
|
|
- #like, #dislike {
|
|
|
- position: relative;
|
|
|
+ .video-col {
|
|
|
+ padding-right: 0.75rem;
|
|
|
+ padding-left: 0.75rem;
|
|
|
}
|
|
|
-
|
|
|
- .behind {
|
|
|
- z-index: -1;
|
|
|
+}
|
|
|
+
|
|
|
+.room-title {
|
|
|
+ left: 50%;
|
|
|
+ -webkit-transform: translateX(-50%);
|
|
|
+ transform: translateX(-50%);
|
|
|
+ font-size: 2.1em;
|
|
|
+}
|
|
|
+
|
|
|
+#ratings {
|
|
|
+ span {
|
|
|
+ font-size: 1.68rem;
|
|
|
}
|
|
|
|
|
|
- .behind:focus {
|
|
|
- z-index: 0;
|
|
|
+ i {
|
|
|
+ color: #9e9e9e !important;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: 0.1s color;
|
|
|
}
|
|
|
-
|
|
|
- .progress {
|
|
|
- width: 50px;
|
|
|
- animation: rotate 0.8s infinite linear;
|
|
|
- border: 8px solid #ff4545;
|
|
|
- border-right-color: transparent;
|
|
|
- height: 50px;
|
|
|
- position: absolute;
|
|
|
- top: 50%;
|
|
|
- left: 50%;
|
|
|
+}
|
|
|
+
|
|
|
+#time-display {
|
|
|
+ margin-top: 30px;
|
|
|
+ float: right;
|
|
|
+}
|
|
|
+
|
|
|
+#thumbs_up:hover,
|
|
|
+#thumbs_up.liked {
|
|
|
+ color: #87d37c !important;
|
|
|
+}
|
|
|
+
|
|
|
+#thumbs_down:hover,
|
|
|
+#thumbs_down.disliked {
|
|
|
+ color: #ec644b !important;
|
|
|
+}
|
|
|
+
|
|
|
+#song-thumbnail {
|
|
|
+ max-width: 100%;
|
|
|
+ width: 85%;
|
|
|
+}
|
|
|
+
|
|
|
+.seeker-bar-container {
|
|
|
+ position: relative;
|
|
|
+ height: 7px;
|
|
|
+ display: block;
|
|
|
+ width: 100%;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.seeker-bar {
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ bottom: 0;
|
|
|
+ position: absolute;
|
|
|
+}
|
|
|
+
|
|
|
+ul {
|
|
|
+ list-style: none;
|
|
|
+ margin: 0;
|
|
|
+ display: block;
|
|
|
+}
|
|
|
+
|
|
|
+h1,
|
|
|
+h2,
|
|
|
+h3,
|
|
|
+h4,
|
|
|
+h5,
|
|
|
+h6 {
|
|
|
+ font-weight: 400;
|
|
|
+ line-height: 1.1;
|
|
|
+}
|
|
|
+
|
|
|
+h1 a,
|
|
|
+h2 a,
|
|
|
+h3 a,
|
|
|
+h4 a,
|
|
|
+h5 a,
|
|
|
+h6 a {
|
|
|
+ font-weight: inherit;
|
|
|
+}
|
|
|
+
|
|
|
+h1 {
|
|
|
+ font-size: 4.2rem;
|
|
|
+ line-height: 110%;
|
|
|
+ margin: 2.1rem 0 1.68rem 0;
|
|
|
+}
|
|
|
+
|
|
|
+h2 {
|
|
|
+ font-size: 3.56rem;
|
|
|
+ line-height: 110%;
|
|
|
+ margin: 1.78rem 0 1.424rem 0;
|
|
|
+}
|
|
|
+
|
|
|
+h3 {
|
|
|
+ font-size: 2.92rem;
|
|
|
+ line-height: 110%;
|
|
|
+ margin: 1.46rem 0 1.168rem 0;
|
|
|
+}
|
|
|
+
|
|
|
+h4 {
|
|
|
+ font-size: 2.28rem;
|
|
|
+ line-height: 110%;
|
|
|
+ margin: 1.14rem 0 0.912rem 0;
|
|
|
+}
|
|
|
+
|
|
|
+h5 {
|
|
|
+ font-size: 1.64rem;
|
|
|
+ line-height: 110%;
|
|
|
+ margin: 0.82rem 0 0.656rem 0;
|
|
|
+}
|
|
|
+
|
|
|
+h6 {
|
|
|
+ font-size: 1rem;
|
|
|
+ line-height: 110%;
|
|
|
+ margin: 0.5rem 0 0.4rem 0;
|
|
|
+}
|
|
|
+
|
|
|
+.thin {
|
|
|
+ font-weight: 200;
|
|
|
+}
|
|
|
+
|
|
|
+.left {
|
|
|
+ float: left !important;
|
|
|
+}
|
|
|
+
|
|
|
+.right {
|
|
|
+ float: right !important;
|
|
|
+}
|
|
|
+
|
|
|
+.light-blue {
|
|
|
+ background-color: #03a9f4 !important;
|
|
|
+}
|
|
|
+
|
|
|
+.white {
|
|
|
+ background-color: #ffffff !important;
|
|
|
+}
|
|
|
+
|
|
|
+.btn-search {
|
|
|
+ font-size: 14px;
|
|
|
+}
|
|
|
+
|
|
|
+.menu {
|
|
|
+ padding: 0 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.menu-list li a:hover {
|
|
|
+ color: #000 !important;
|
|
|
+}
|
|
|
+
|
|
|
+.menu-list li {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+}
|
|
|
+
|
|
|
+.menu-list a {
|
|
|
+ /*padding: 0 10px !important;*/
|
|
|
+}
|
|
|
+
|
|
|
+.menu-list a:hover {
|
|
|
+ background-color: transparent;
|
|
|
+}
|
|
|
+
|
|
|
+.icons-group {
|
|
|
+ display: flex;
|
|
|
+}
|
|
|
+
|
|
|
+#like,
|
|
|
+#dislike {
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+.behind {
|
|
|
+ z-index: -1;
|
|
|
+}
|
|
|
+
|
|
|
+.behind:focus {
|
|
|
+ z-index: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.progress {
|
|
|
+ width: 50px;
|
|
|
+ animation: rotate 0.8s infinite linear;
|
|
|
+ border: 8px solid #03a9f4;
|
|
|
+ border-right-color: transparent;
|
|
|
+ height: 50px;
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ left: 50%;
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes rotate {
|
|
|
+ 0% {
|
|
|
+ transform: rotate(0deg);
|
|
|
}
|
|
|
-
|
|
|
- @keyframes rotate {
|
|
|
- 0% { transform: rotate(0deg); }
|
|
|
- 100% { transform: rotate(360deg); }
|
|
|
+ 100% {
|
|
|
+ transform: rotate(360deg);
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- .experimental {
|
|
|
- display: none !important;
|
|
|
- }
|
|
|
+.experimental {
|
|
|
+ display: none !important;
|
|
|
+}
|
|
|
</style>
|