Commit a64b5579 authored by Benoît Simonneaux's avatar Benoît Simonneaux

Initial commit.

parents
<html>
<head>
<meta charset="utf-8">
<title>Audio Stream</title>
</head>
<body>
<div>
<audio id="audio" controls>
</audio>
</div>
<script type="text/javascript">
var wsUri = "wss://api." + document.location.host + "/stream";
let audio = document.querySelector('audio');
let mimeCodec = 'audio/webm;codecs="opus"';
if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
let mediaSource = new MediaSource;
audio.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', sourceOpen);
} else {
console.error('Unsupported MIME type or codec: ', mimeCodec);
}
let blobs = [];
let first = 1;
let nothingToAppend = 1;
let sourceBuffer;
function launch() {
let time = Date.now();
let delay = 1000 - (time % 1000);
setTimeout(function() {
console.log(Date.now());
audio.play();
}, delay);
}
function appendNextBlob() {
if (first == 1) {
launch();
first = 0;
}
if (blobs.length) {
nothingToAppend = 0;
sourceBuffer.appendBuffer(blobs.shift());
}
else
nothingToAppend = 1;
}
function sourceOpen (_) {
//console.log(this.readyState); // open
let mediaSource = this;
sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
sourceBuffer.addEventListener('updateend', appendNextBlob, false);
WS_init();
};
function WS_init() {
let websocket = new WebSocket(wsUri);
websocket.binaryType = "arraybuffer";
websocket.onopen = function(evt) { WS_onOpen(evt) };
websocket.onclose = function(evt) { WS_onClose(evt) };
websocket.onmessage = function(evt) { WS_onMessage(evt) };
websocket.onerror = function(evt) { WS_onError(evt) };
}
function WS_onOpen(evt) {
console.log("[WS] Connected.");
}
function WS_onClose(evt) {
console.log("[WS] Disconnected.");
}
function WS_onMessage(evt)
{
console.log('[WS] Message :');
console.log(evt.data);
blobs.push(evt.data);
if(nothingToAppend == 1) {
sourceBuffer.appendBuffer(blobs.shift());
}
}
function WS_onError(evt) {
console.log("[WS] Error: " + evt.data);
}
</script>
</body>
</html>
\ No newline at end of file
/* fallback */
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: url('/fonts/material-icons.woff2') format('woff2');
}
.material-icons {
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 24px;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
-moz-font-feature-settings: 'liga';
-moz-osx-font-smoothing: grayscale;
}
This diff is collapsed.
html {
min-height: 100%
}
body {
margin: 0;
padding: 0;
min-height: 100%;
background-image: url("../img/background.jpg");
background-repeat: no-repeat;
background-position: top;
background-size: cover;
}
nav.bar .nav-wrapper {
display: flex;
}
nav.bar .breadcrumbs {
padding: 0 10px;
}
nav.bar .websocket-status {
width: 64px;
text-align: center;
}
nav.bar .websocket-status span {
width: 16px;
}
.sidenav .subtitle {
color: black;
text-align: center;
font-size: 20px;
}
.sidenav > .sidenav-top {
padding: 10px 20px;
text-align: center;
}
.sidenav > .sidenav-top .top-icon {
margin-bottom: 10px;
}
.sidenav > .sidenav-top .top-text {
font-size: 24px;
}
.sidenav > .sidenav-auth {
padding: 10px 20px;
}
.sidenav > .sidenav-auth .submit {
text-align: center;
}
.sidenav > .sidenav-auth .more {
padding: 5px 0;
text-align: center;
}
.sidenav > .sidenav-user .user-picture {
float: left;
width: 50px;
height: 50px;
margin: 0px 10px;
vertical-align: bottom;
}
.sidenav > .sidenav-user .user-username {
line-height: 50px;
margin: 0px 10px 0px 0px;
}
.search-bar {
position: relative;
margin: 0;
flex: 1;
}
.search-bar .prefix {
text-align: center;
top: 0;
}
.search-bar .input-field {
height: 100%;
}
.search-bar input[type="search"] {
margin: 0;
}
.search-bar .autocomplete, .search-bar .results {
color: black;
position: absolute;
background-color: white;
z-index: 10;
width: 100%;
line-height: normal;
}
.search-bar .autocomplete td, .search-bar .results td {
padding: 2px 5px;
}
.search-bar .autocomplete tr, .search-bar .results tr {
cursor: pointer;
}
.search-bar .results td.small {
width: 0px;
}
.search-bar .results img {
vertical-align: middle;
height: 50px;
}
@media (max-width: 992px) {
.search-bar {
margin: 0;
}
}
.container .title {
display: inline-block;
font-size: 32px;
font-weight: bold;
margin: 10px 0;
padding: 0 50px;
background-color: rgba(255, 255, 255, 0.7);
border-radius: 50px;
}
.container.room-list .rooms-container {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
align-items: stretch;
}
.container.room-list .rooms-container .room {
max-width: 300px;
margin: 10px 20px;
}
.container.room-list .rooms-container .room .owner {
font-weight: bold;
}
.container.room {
padding-top: 50px;
width: 100%;
}
.card .card-image .card-delete {
position: absolute;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.50);
}
.card .card-image .card-delete .btn-floating {
margin-top: -20px;
margin-left: -20px;
top: 50%;
left: 50%;
}
.playlist-container .card .card-content {
padding: 10px;
}
@media (max-width: 992px) {
.playlist_box {
margin-bottom: 50px;
}
}
.playlist_box {
width: 100%;
height: 390px;
overflow: hidden;
position: relative;
}
.playlist {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: -17px;
overflow-y: scroll;
border: none;
margin: 0;
}
.playlist li.collection-item {
background-color: rgba(3, 169, 244, 0.7);
position: relative;
color: white;
border-bottom: none;
margin-bottom: 1px;
font-size: 12px;
padding-right: 56px;
}
.playlist li.collection-item span.title {
font-size: 20px;
}
.playlist li.collection-item span.sender {
font-size: 14px;
}
.playlist li.collection-item .tools {
width: 24px;
text-align: right;
line-height: 0px;
}
.playlist li.collection-item.active {
background-color: rgba(2, 119, 189, 0.8);
}
.playlist li.collection-item.active span.title {
font-size: 20px;
font-weight: bold;
}
.playlist div.removeVideo {
color: red;
height: 20px;
width: 20px;
position: absolute;
top: 0px;
right: 0px;
display: none;
}
@media (max-width: 992px) {
.playlist {
right: 0;
overflow: auto;
}
}
.player_box {
text-align: center;
}
form[name="videoInputForm"] {
text-align: center;
}
form[name="videoInputForm"] input[type="text"] {
background-color: rgba(255, 255, 255, 0.8);
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>MusicProject</title>
<link href="css/material-icons.css" rel="stylesheet">
<link href="css/materialize.css" type="text/css" rel="stylesheet" />
<link href="css/style.css" type="text/css" rel="stylesheet" />
</head>
<body>
<nav class="bar blue">
<div class="nav-wrapper">
<!-- McDo <3 ! -->
<ul><li><a href="" data-target="sidenav-left" class="sidenav-trigger show-on-large">
<i class="material-icons mdi-navigation-menu">menu</i>
</a></li></ul>
<!--- Localization -->
<div class="breadcrumbs hide-on-med-and-down">
<a href="" class="breadcrumb" page="index">MusicProject</a>
<a href="" class="breadcrumb" page="roomlist">Salons</a>
</div>
<!--- Search Bar -->
<form class="search-bar" style="display: none;">
<div class="input-field">
<i class="material-icons prefix">search</i>
<input type="search" autocomplete="off" name="search" id="search-input">
<label for="search-input"></label>
<i class="material-icons">close</i>
</div>
<table class="autocomplete highlight" style="display: none;"></table>
<table class="results highlight" style="display: none;"></table>
</form>
</div>
</nav>
<ul id="sidenav-left" class="sidenav left-menu">
<div class="sidenav-top">
<div class="top-icon"><img src="img/icons/musical-note-blue.png" alt="♫"/></div>
<div class="top-text">MusicProject</div>
</div>
<div class="sidenav-auth">
<div class="signIn">
<div class="subtitle">Sign In</div>
<div class="input-field">
<input type="text" name="username" id="auth-signIn-local-username" />
<label for="auth-signIn-local-username">Username</label>
</div>
<div class="input-field">
<input type="password" name="password" id="auth-signIn-local-password" />
<label for="auth-signIn-local-password">Password</label>
</div>
<div class="submit">
<button class="btn waves-effect waves-light" type="submit" id="auth-signIn-local-submit">Sign In</button>
</div>
<div class="more">
<a href="" id="auth-signUp-display">Create an account</a>
</div>
</div>
<div class="signUp" style="display:none">
<div class="subtitle">Sign Up</div>
<div class="input-field">
<input type="text" name="username" id="auth-signUp-local-username" />
<label for="auth-signIn-local-username">Username</label>
</div>
<div class="input-field">
<input type="password" name="password" id="auth-signIn-local-password" />
<label for="auth-signIn-local-password">Password</label>
</div>
<div class="input-field">
<input type="password" name="password" id="auth-signIn-local-password-confirm" />
<label for="auth-signIn-local-password-confirm">Confirm your password</label>
</div>
<div class="submit">
<button class="btn waves-effect waves-light" type="submit" id="auth-signIn-local-submit">Sign Up</button>
</div>
<div class="more">
<a href="" id="auth-signIn-display">Sign in</a>
</div>
</div>
</div>
<div class="sidenav-user" style="display: none;">
<div class="row user-primary">
<div class="user-picture">
<img src="" alt="" class="circle responsive-img">
</div>
<div class="user-username"></div>
</div>
<ul class="action-list">
<li><a href=""> Salon Principal</a></li>
<li><a href="" class="grey-text"> Mes Salons</a></li>
<li><a href="" class="grey-text playlist-own"> Mes Playlists</a></li>
<li><div class="divider"></div></li>
<li><a href="" class="logout" id="auth-signOut">> Se Déconnecter</a></li>
</ul>
</div>
</ul>
<div class="container room-list">
<div class="title">Salons</div>
<div class="rooms-container popular-rooms">
</div>
</div>
<div class="container room" style="display: none;">
<div class="row">
<div class="col s12 m6 offset-m3 l4 push-l1 playlist_box">
<ul class="collection playlist"></ul>
</div>
<div class="col s12 l6 push-l1 player_box">
<audio id="audio"></audio>
</div>
</div>
</div>
<div class="container playlist-own" style="display: none;">
<div class="title">Mes Playlists</div>
<div class="playlist-container"></div>
<div class="fixed-action-btn">
<a class="btn-floating btn-large red">
<i class="large material-icons">mode_edit</i>
</a>
<ul>
<li><a class="btn-floating red btn-delete"><i class="material-icons">delete</i></a></li>
<li><a data-target="modal-playlist-create" class="btn-floating modal-trigger green"><i class="material-icons">add</i></a></li>
</ul>
</div>
</div>
<div class="container playlist-one" style="display: none;">
<div class="title"></div>
<div class="playlist-container">
<ul class="collection">
</ul>
</div>
</div>
<div id="modal-playlist-create" class="modal">
<div class="modal-content">
<h4>Créer une playlist</h4>
<div class="input-field">
<input type="text" name="username" id="playlist-create-name" />
<label for="playlist-create-name">Nom</label>
</div>
</div>
<div class="modal-footer">
<a class="modal-close waves-effect waves-green btn btn-flat">Annuler</a>
<a class="modal-close waves-effect waves-green btn btn-flat" id="playlist-create-submit">Créer</a>
</div>
</div>
<script src="js/lib/jquery-3.3.1.js" type="text/javascript"></script>
<script src="js/lib/materialize.js"></script>
<script src="js/lib/js.cookie.js" type="text/javascript"></script>
<script src="js/lib/utils.js" type="text/javascript"></script>
<script src="js/config.js" type="text/javascript"></script>
<script src="js/music.js" type="text/javascript"></script>
<script src="js/components/room.js" type="text/javascript"></script>
<script src="js/components/load.js" type="text/javascript"></script>
<script src="js/components/ws-ms.js" type="text/javascript"></script>
<script src="js/components/ws-ss.js" type="text/javascript"></script>
<script src="js/components/playlist.js" type="text/javascript"></script>
<script src="js/components/sidenav.js" type="text/javascript"></script>
<script src="js/components/sign.js" type="text/javascript"></script>
<script src="js/components/menu.js" type="text/javascript"></script>
<script src="js/components/search.js" type="text/javascript"></script>
<script src="js/components/breadcrumbs.js" type="text/javascript"></script>
</body>
</html>
\ No newline at end of file
$('.breadcrumbs').on('click', 'a', function(e) {
e.preventDefault();
if($(this).is(':last-child')) return;
if($(this).is(':first-child')) return;
$(this).nextAll().remove();
$('.search-bar').hide();
$('.content').hide();
$('.content.' + $(this).attr('page')).fadeIn();
});
function Breadcrumbs_Reset() {
let first = $('<a href="" class="breadcrumb" page="index">MusicProject</a>');
first.on('click', function(e) {
e.preventDefault();
Room_SwitchView_List();
});
$('.breadcrumbs').html(first);
}
function Breadcrumbs_Append(page, title, callback) {
let elmt = $('<a href="" class="breadcrumb">');
elmt.attr('page', page);
elmt.html(title);
elmt.on('click', function(e) {
e.preventDefault();
if($(this).is(':last-child'))
return ;
callback();
});
elmt.appendTo($('.breadcrumbs'));
}
\ No newline at end of file
function Content_DisplayRoomList() {
let content = $('<div class="content roomlist">');
}
$(document).ready(function(){
$('.fixed-action-btn').floatingActionButton();
$('.modal').modal();
$('.sidenav').sidenav();
});
$('.menu-left .logout').on('click', function(e) {
e.preventDefault();
FB.logout(function(response) {
Cookies.remove('sid');
location.reload();
});
});
$('.breadcrumb[page=roomlist]').on('click', function(e) {
e.preventDefault();
MWS_request({
type: 'room.quit',
success: function(data) {
console.log(data)
},
error: function(msg) {
console.log(msg);
}
});
});
$('.sidenav-user .action-list .playlist-own').on('click', function(e) {
e.preventDefault();
Sidenav_Close();
Playlist_GetOwn_Request(Playlist_GetOwn_Display);
});
$('#playlist-create-submit').on('click', function(e) {
e.preventDefault();
Playlist_Create_Request();
});
$('.container.playlist-own .fixed-action-btn .btn-delete').on('click', function(e) {
e.preventDefault();
$('.container.playlist-own .playlist-container .card-delete').toggle();
});
$('.container.playlist-own .playlist-container').on('click', '.card-delete a', function(e) {
let id = $(this).parents('.card').data('id');
Playlist_Destroy_Request(id);
});
function Playlist_GetOwn_Request(callback) {
MWS_request({
type: 'playlist.listOwn',
success: callback,
error: log
});
}
function Playlist_GetOwn_Display(data) {
Breadcrumbs_Reset();
Breadcrumbs_Append('playlist-own', 'Mes Playlists', () => { Playlist_GetOwn_Request(Playlist_GetOwn_Display); });
Search_Hide();
$('.container').hide();
$('.container.playlist-own .playlist-container').html('');
let row = $('<div class="row">');
$.each(data, function(index, playlist) {
let col = $('<div class="col s6 m4 l2">');
let card = $('<div>');
card.addClass('card');
card.attr('data-id', playlist.id);
let cardImage = $('<div>');
cardImage.addClass('card-image');
let cardImageImg = $('<img>');
cardImageImg.attr('src', '/img/wmp_cover.png');
cardImageImg.appendTo(cardImage);
let cardImageDestroy = $('<div>');
cardImageDestroy.addClass('card-delete');
cardImageDestroy.attr('style', 'display: none;');
cardImageDestroy.html('<a class="btn-floating red"><i class="material-icons">delete</i></a>');
cardImageDestroy.appendTo(cardImage);
cardImage.appendTo(card);
let cardContent = $('<div>');
cardContent.addClass('card-content');
let cardContentTitle = $('<a href="">');
cardContentTitle.addClass('playlist-title');
cardContentTitle.html(playlist.name);
cardContentTitle.appendTo(cardContent);