add fancy frontend player
This commit is contained in:
43
Cargo.lock
generated
43
Cargo.lock
generated
@@ -689,6 +689,30 @@ version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
|
||||
|
||||
[[package]]
|
||||
name = "maud"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8156733e27020ea5c684db5beac5d1d611e1272ab17901a49466294b84fc217e"
|
||||
dependencies = [
|
||||
"axum-core",
|
||||
"http",
|
||||
"itoa",
|
||||
"maud_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maud_macros"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7261b00f3952f617899bc012e3dbd56e4f0110a038175929fa5d18e5a19913ca"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mediatype"
|
||||
version = "0.20.0"
|
||||
@@ -800,6 +824,7 @@ dependencies = [
|
||||
"axum",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"maud",
|
||||
"reqwest",
|
||||
"serde_json",
|
||||
"stream-download",
|
||||
@@ -835,6 +860,18 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2-diagnostics"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn"
|
||||
version = "0.11.8"
|
||||
@@ -1482,6 +1519,12 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.1"
|
||||
|
@@ -13,4 +13,5 @@ async-stream = "0.3"
|
||||
serde_json = "1"
|
||||
tracing = "0.1"
|
||||
stream-download = "0.22"
|
||||
chrono = "0.4.41"
|
||||
chrono = "0.4"
|
||||
maud = { version = "0.27", features = ["axum"] }
|
||||
|
96
src/main.rs
96
src/main.rs
@@ -1,16 +1,108 @@
|
||||
mod state;
|
||||
mod streamer;
|
||||
|
||||
use axum::{routing::get, Router};
|
||||
use axum::{response::IntoResponse, routing::get, Router};
|
||||
use maud::{html, Markup, DOCTYPE};
|
||||
use reqwest::header;
|
||||
use state::AppState;
|
||||
use std::sync::Arc;
|
||||
|
||||
async fn index() -> Markup {
|
||||
html! {
|
||||
(DOCTYPE)
|
||||
html lang="en" {
|
||||
head {
|
||||
meta charset="utf-8";
|
||||
meta name="viewport" content="user-scalable=no";
|
||||
title { "Ö1 Morgenjournal" }
|
||||
link rel="stylesheet" href="/styles.css";
|
||||
}
|
||||
body {
|
||||
// Top Info
|
||||
div #title {
|
||||
span #track {}
|
||||
div #timer { "0:00" }
|
||||
div #duration { "0:00" }
|
||||
}
|
||||
|
||||
// Controls
|
||||
div .controlsOuter {
|
||||
div .controlsInner {
|
||||
div #loading {}
|
||||
div .btn #playBtn {}
|
||||
div .btn #pauseBtn {}
|
||||
div .btn #prevBtn {}
|
||||
div .btn #nextBtn {}
|
||||
}
|
||||
div .btn #playlistBtn {}
|
||||
div .btn #volumeBtn {}
|
||||
}
|
||||
|
||||
// Progress
|
||||
div #waveform {}
|
||||
div #bar {}
|
||||
div #progress {}
|
||||
|
||||
// Playlist
|
||||
div #playlist {
|
||||
div #list {}
|
||||
}
|
||||
|
||||
// Volume
|
||||
div #volume .fadeout {
|
||||
div #barFull .bar {}
|
||||
div #barEmpty .bar {}
|
||||
div #sliderBtn {}
|
||||
}
|
||||
|
||||
// Scripts
|
||||
script src="/howler.core.min.js" {}
|
||||
script src="/siriwave.js" {}
|
||||
script src="/player.js" {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn styles() -> impl IntoResponse {
|
||||
(
|
||||
[(header::CONTENT_TYPE, "text/css")],
|
||||
include_str!("../static/styles.css"),
|
||||
)
|
||||
}
|
||||
|
||||
async fn howler() -> impl IntoResponse {
|
||||
(
|
||||
[(header::CONTENT_TYPE, "text/javascript")],
|
||||
include_str!("../static/howler.core.min.js").to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
async fn player() -> impl IntoResponse {
|
||||
(
|
||||
[(header::CONTENT_TYPE, "text/javascript")],
|
||||
include_str!("../static/player.js").to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
async fn siriwave() -> impl IntoResponse {
|
||||
(
|
||||
[(header::CONTENT_TYPE, "text/javascript")],
|
||||
include_str!("../static/siriwave.js").to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let state = Arc::new(AppState::new());
|
||||
|
||||
let app = Router::new()
|
||||
.route("/", get(streamer::stream_handler))
|
||||
.route("/", get(index))
|
||||
.route("/stream", get(streamer::stream_handler))
|
||||
.route("/howler.core.min.js", get(howler))
|
||||
.route("/styles.css", get(styles))
|
||||
.route("/player.js", get(player))
|
||||
.route("/siriwave.js", get(siriwave))
|
||||
.with_state(state);
|
||||
|
||||
println!("Streaming server running on http://localhost:3029");
|
||||
|
2
static/howler.core.min.js
vendored
Normal file
2
static/howler.core.min.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/*! howler.js v2.2.4 | (c) 2013-2020, James Simpson of GoldFire Studios | MIT License | howlerjs.com */
|
||||
!function(){"use strict";var e=function(){this.init()};e.prototype={init:function(){var e=this||n;return e._counter=1e3,e._html5AudioPool=[],e.html5PoolSize=10,e._codecs={},e._howls=[],e._muted=!1,e._volume=1,e._canPlayEvent="canplaythrough",e._navigator="undefined"!=typeof window&&window.navigator?window.navigator:null,e.masterGain=null,e.noAudio=!1,e.usingWebAudio=!0,e.autoSuspend=!0,e.ctx=null,e.autoUnlock=!0,e._setup(),e},volume:function(e){var o=this||n;if(e=parseFloat(e),o.ctx||_(),void 0!==e&&e>=0&&e<=1){if(o._volume=e,o._muted)return o;o.usingWebAudio&&o.masterGain.gain.setValueAtTime(e,n.ctx.currentTime);for(var t=0;t<o._howls.length;t++)if(!o._howls[t]._webAudio)for(var r=o._howls[t]._getSoundIds(),a=0;a<r.length;a++){var u=o._howls[t]._soundById(r[a]);u&&u._node&&(u._node.volume=u._volume*e)}return o}return o._volume},mute:function(e){var o=this||n;o.ctx||_(),o._muted=e,o.usingWebAudio&&o.masterGain.gain.setValueAtTime(e?0:o._volume,n.ctx.currentTime);for(var t=0;t<o._howls.length;t++)if(!o._howls[t]._webAudio)for(var r=o._howls[t]._getSoundIds(),a=0;a<r.length;a++){var u=o._howls[t]._soundById(r[a]);u&&u._node&&(u._node.muted=!!e||u._muted)}return o},stop:function(){for(var e=this||n,o=0;o<e._howls.length;o++)e._howls[o].stop();return e},unload:function(){for(var e=this||n,o=e._howls.length-1;o>=0;o--)e._howls[o].unload();return e.usingWebAudio&&e.ctx&&void 0!==e.ctx.close&&(e.ctx.close(),e.ctx=null,_()),e},codecs:function(e){return(this||n)._codecs[e.replace(/^x-/,"")]},_setup:function(){var e=this||n;if(e.state=e.ctx?e.ctx.state||"suspended":"suspended",e._autoSuspend(),!e.usingWebAudio)if("undefined"!=typeof Audio)try{var o=new Audio;void 0===o.oncanplaythrough&&(e._canPlayEvent="canplay")}catch(n){e.noAudio=!0}else e.noAudio=!0;try{var o=new Audio;o.muted&&(e.noAudio=!0)}catch(e){}return e.noAudio||e._setupCodecs(),e},_setupCodecs:function(){var e=this||n,o=null;try{o="undefined"!=typeof Audio?new Audio:null}catch(n){return e}if(!o||"function"!=typeof o.canPlayType)return e;var t=o.canPlayType("audio/mpeg;").replace(/^no$/,""),r=e._navigator?e._navigator.userAgent:"",a=r.match(/OPR\/(\d+)/g),u=a&&parseInt(a[0].split("/")[1],10)<33,d=-1!==r.indexOf("Safari")&&-1===r.indexOf("Chrome"),i=r.match(/Version\/(.*?) /),_=d&&i&&parseInt(i[1],10)<15;return e._codecs={mp3:!(u||!t&&!o.canPlayType("audio/mp3;").replace(/^no$/,"")),mpeg:!!t,opus:!!o.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/,""),ogg:!!o.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),oga:!!o.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),wav:!!(o.canPlayType('audio/wav; codecs="1"')||o.canPlayType("audio/wav")).replace(/^no$/,""),aac:!!o.canPlayType("audio/aac;").replace(/^no$/,""),caf:!!o.canPlayType("audio/x-caf;").replace(/^no$/,""),m4a:!!(o.canPlayType("audio/x-m4a;")||o.canPlayType("audio/m4a;")||o.canPlayType("audio/aac;")).replace(/^no$/,""),m4b:!!(o.canPlayType("audio/x-m4b;")||o.canPlayType("audio/m4b;")||o.canPlayType("audio/aac;")).replace(/^no$/,""),mp4:!!(o.canPlayType("audio/x-mp4;")||o.canPlayType("audio/mp4;")||o.canPlayType("audio/aac;")).replace(/^no$/,""),weba:!(_||!o.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/,"")),webm:!(_||!o.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/,"")),dolby:!!o.canPlayType('audio/mp4; codecs="ec-3"').replace(/^no$/,""),flac:!!(o.canPlayType("audio/x-flac;")||o.canPlayType("audio/flac;")).replace(/^no$/,"")},e},_unlockAudio:function(){var e=this||n;if(!e._audioUnlocked&&e.ctx){e._audioUnlocked=!1,e.autoUnlock=!1,e._mobileUnloaded||44100===e.ctx.sampleRate||(e._mobileUnloaded=!0,e.unload()),e._scratchBuffer=e.ctx.createBuffer(1,1,22050);var o=function(n){for(;e._html5AudioPool.length<e.html5PoolSize;)try{var t=new Audio;t._unlocked=!0,e._releaseHtml5Audio(t)}catch(n){e.noAudio=!0;break}for(var r=0;r<e._howls.length;r++)if(!e._howls[r]._webAudio)for(var a=e._howls[r]._getSoundIds(),u=0;u<a.length;u++){var d=e._howls[r]._soundById(a[u]);d&&d._node&&!d._node._unlocked&&(d._node._unlocked=!0,d._node.load())}e._autoResume();var i=e.ctx.createBufferSource();i.buffer=e._scratchBuffer,i.connect(e.ctx.destination),void 0===i.start?i.noteOn(0):i.start(0),"function"==typeof e.ctx.resume&&e.ctx.resume(),i.onended=function(){i.disconnect(0),e._audioUnlocked=!0,document.removeEventListener("touchstart",o,!0),document.removeEventListener("touchend",o,!0),document.removeEventListener("click",o,!0),document.removeEventListener("keydown",o,!0);for(var n=0;n<e._howls.length;n++)e._howls[n]._emit("unlock")}};return document.addEventListener("touchstart",o,!0),document.addEventListener("touchend",o,!0),document.addEventListener("click",o,!0),document.addEventListener("keydown",o,!0),e}},_obtainHtml5Audio:function(){var e=this||n;if(e._html5AudioPool.length)return e._html5AudioPool.pop();var o=(new Audio).play();return o&&"undefined"!=typeof Promise&&(o instanceof Promise||"function"==typeof o.then)&&o.catch(function(){console.warn("HTML5 Audio pool exhausted, returning potentially locked audio object.")}),new Audio},_releaseHtml5Audio:function(e){var o=this||n;return e._unlocked&&o._html5AudioPool.push(e),o},_autoSuspend:function(){var e=this;if(e.autoSuspend&&e.ctx&&void 0!==e.ctx.suspend&&n.usingWebAudio){for(var o=0;o<e._howls.length;o++)if(e._howls[o]._webAudio)for(var t=0;t<e._howls[o]._sounds.length;t++)if(!e._howls[o]._sounds[t]._paused)return e;return e._suspendTimer&&clearTimeout(e._suspendTimer),e._suspendTimer=setTimeout(function(){if(e.autoSuspend){e._suspendTimer=null,e.state="suspending";var n=function(){e.state="suspended",e._resumeAfterSuspend&&(delete e._resumeAfterSuspend,e._autoResume())};e.ctx.suspend().then(n,n)}},3e4),e}},_autoResume:function(){var e=this;if(e.ctx&&void 0!==e.ctx.resume&&n.usingWebAudio)return"running"===e.state&&"interrupted"!==e.ctx.state&&e._suspendTimer?(clearTimeout(e._suspendTimer),e._suspendTimer=null):"suspended"===e.state||"running"===e.state&&"interrupted"===e.ctx.state?(e.ctx.resume().then(function(){e.state="running";for(var n=0;n<e._howls.length;n++)e._howls[n]._emit("resume")}),e._suspendTimer&&(clearTimeout(e._suspendTimer),e._suspendTimer=null)):"suspending"===e.state&&(e._resumeAfterSuspend=!0),e}};var n=new e,o=function(e){var n=this;if(!e.src||0===e.src.length)return void console.error("An array of source files must be passed with any new Howl.");n.init(e)};o.prototype={init:function(e){var o=this;return n.ctx||_(),o._autoplay=e.autoplay||!1,o._format="string"!=typeof e.format?e.format:[e.format],o._html5=e.html5||!1,o._muted=e.mute||!1,o._loop=e.loop||!1,o._pool=e.pool||5,o._preload="boolean"!=typeof e.preload&&"metadata"!==e.preload||e.preload,o._rate=e.rate||1,o._sprite=e.sprite||{},o._src="string"!=typeof e.src?e.src:[e.src],o._volume=void 0!==e.volume?e.volume:1,o._xhr={method:e.xhr&&e.xhr.method?e.xhr.method:"GET",headers:e.xhr&&e.xhr.headers?e.xhr.headers:null,withCredentials:!(!e.xhr||!e.xhr.withCredentials)&&e.xhr.withCredentials},o._duration=0,o._state="unloaded",o._sounds=[],o._endTimers={},o._queue=[],o._playLock=!1,o._onend=e.onend?[{fn:e.onend}]:[],o._onfade=e.onfade?[{fn:e.onfade}]:[],o._onload=e.onload?[{fn:e.onload}]:[],o._onloaderror=e.onloaderror?[{fn:e.onloaderror}]:[],o._onplayerror=e.onplayerror?[{fn:e.onplayerror}]:[],o._onpause=e.onpause?[{fn:e.onpause}]:[],o._onplay=e.onplay?[{fn:e.onplay}]:[],o._onstop=e.onstop?[{fn:e.onstop}]:[],o._onmute=e.onmute?[{fn:e.onmute}]:[],o._onvolume=e.onvolume?[{fn:e.onvolume}]:[],o._onrate=e.onrate?[{fn:e.onrate}]:[],o._onseek=e.onseek?[{fn:e.onseek}]:[],o._onunlock=e.onunlock?[{fn:e.onunlock}]:[],o._onresume=[],o._webAudio=n.usingWebAudio&&!o._html5,void 0!==n.ctx&&n.ctx&&n.autoUnlock&&n._unlockAudio(),n._howls.push(o),o._autoplay&&o._queue.push({event:"play",action:function(){o.play()}}),o._preload&&"none"!==o._preload&&o.load(),o},load:function(){var e=this,o=null;if(n.noAudio)return void e._emit("loaderror",null,"No audio support.");"string"==typeof e._src&&(e._src=[e._src]);for(var r=0;r<e._src.length;r++){var u,d;if(e._format&&e._format[r])u=e._format[r];else{if("string"!=typeof(d=e._src[r])){e._emit("loaderror",null,"Non-string found in selected audio sources - ignoring.");continue}u=/^data:audio\/([^;,]+);/i.exec(d),u||(u=/\.([^.]+)$/.exec(d.split("?",1)[0])),u&&(u=u[1].toLowerCase())}if(u||console.warn('No file extension was found. Consider using the "format" property or specify an extension.'),u&&n.codecs(u)){o=e._src[r];break}}return o?(e._src=o,e._state="loading","https:"===window.location.protocol&&"http:"===o.slice(0,5)&&(e._html5=!0,e._webAudio=!1),new t(e),e._webAudio&&a(e),e):void e._emit("loaderror",null,"No codec support for selected audio sources.")},play:function(e,o){var t=this,r=null;if("number"==typeof e)r=e,e=null;else{if("string"==typeof e&&"loaded"===t._state&&!t._sprite[e])return null;if(void 0===e&&(e="__default",!t._playLock)){for(var a=0,u=0;u<t._sounds.length;u++)t._sounds[u]._paused&&!t._sounds[u]._ended&&(a++,r=t._sounds[u]._id);1===a?e=null:r=null}}var d=r?t._soundById(r):t._inactiveSound();if(!d)return null;if(r&&!e&&(e=d._sprite||"__default"),"loaded"!==t._state){d._sprite=e,d._ended=!1;var i=d._id;return t._queue.push({event:"play",action:function(){t.play(i)}}),i}if(r&&!d._paused)return o||t._loadQueue("play"),d._id;t._webAudio&&n._autoResume();var _=Math.max(0,d._seek>0?d._seek:t._sprite[e][0]/1e3),s=Math.max(0,(t._sprite[e][0]+t._sprite[e][1])/1e3-_),l=1e3*s/Math.abs(d._rate),c=t._sprite[e][0]/1e3,f=(t._sprite[e][0]+t._sprite[e][1])/1e3;d._sprite=e,d._ended=!1;var p=function(){d._paused=!1,d._seek=_,d._start=c,d._stop=f,d._loop=!(!d._loop&&!t._sprite[e][2])};if(_>=f)return void t._ended(d);var m=d._node;if(t._webAudio){var v=function(){t._playLock=!1,p(),t._refreshBuffer(d);var e=d._muted||t._muted?0:d._volume;m.gain.setValueAtTime(e,n.ctx.currentTime),d._playStart=n.ctx.currentTime,void 0===m.bufferSource.start?d._loop?m.bufferSource.noteGrainOn(0,_,86400):m.bufferSource.noteGrainOn(0,_,s):d._loop?m.bufferSource.start(0,_,86400):m.bufferSource.start(0,_,s),l!==1/0&&(t._endTimers[d._id]=setTimeout(t._ended.bind(t,d),l)),o||setTimeout(function(){t._emit("play",d._id),t._loadQueue()},0)};"running"===n.state&&"interrupted"!==n.ctx.state?v():(t._playLock=!0,t.once("resume",v),t._clearTimer(d._id))}else{var h=function(){m.currentTime=_,m.muted=d._muted||t._muted||n._muted||m.muted,m.volume=d._volume*n.volume(),m.playbackRate=d._rate;try{var r=m.play();if(r&&"undefined"!=typeof Promise&&(r instanceof Promise||"function"==typeof r.then)?(t._playLock=!0,p(),r.then(function(){t._playLock=!1,m._unlocked=!0,o?t._loadQueue():t._emit("play",d._id)}).catch(function(){t._playLock=!1,t._emit("playerror",d._id,"Playback was unable to start. This is most commonly an issue on mobile devices and Chrome where playback was not within a user interaction."),d._ended=!0,d._paused=!0})):o||(t._playLock=!1,p(),t._emit("play",d._id)),m.playbackRate=d._rate,m.paused)return void t._emit("playerror",d._id,"Playback was unable to start. This is most commonly an issue on mobile devices and Chrome where playback was not within a user interaction.");"__default"!==e||d._loop?t._endTimers[d._id]=setTimeout(t._ended.bind(t,d),l):(t._endTimers[d._id]=function(){t._ended(d),m.removeEventListener("ended",t._endTimers[d._id],!1)},m.addEventListener("ended",t._endTimers[d._id],!1))}catch(e){t._emit("playerror",d._id,e)}};"data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA"===m.src&&(m.src=t._src,m.load());var y=window&&window.ejecta||!m.readyState&&n._navigator.isCocoonJS;if(m.readyState>=3||y)h();else{t._playLock=!0,t._state="loading";var g=function(){t._state="loaded",h(),m.removeEventListener(n._canPlayEvent,g,!1)};m.addEventListener(n._canPlayEvent,g,!1),t._clearTimer(d._id)}}return d._id},pause:function(e){var n=this;if("loaded"!==n._state||n._playLock)return n._queue.push({event:"pause",action:function(){n.pause(e)}}),n;for(var o=n._getSoundIds(e),t=0;t<o.length;t++){n._clearTimer(o[t]);var r=n._soundById(o[t]);if(r&&!r._paused&&(r._seek=n.seek(o[t]),r._rateSeek=0,r._paused=!0,n._stopFade(o[t]),r._node))if(n._webAudio){if(!r._node.bufferSource)continue;void 0===r._node.bufferSource.stop?r._node.bufferSource.noteOff(0):r._node.bufferSource.stop(0),n._cleanBuffer(r._node)}else isNaN(r._node.duration)&&r._node.duration!==1/0||r._node.pause();arguments[1]||n._emit("pause",r?r._id:null)}return n},stop:function(e,n){var o=this;if("loaded"!==o._state||o._playLock)return o._queue.push({event:"stop",action:function(){o.stop(e)}}),o;for(var t=o._getSoundIds(e),r=0;r<t.length;r++){o._clearTimer(t[r]);var a=o._soundById(t[r]);a&&(a._seek=a._start||0,a._rateSeek=0,a._paused=!0,a._ended=!0,o._stopFade(t[r]),a._node&&(o._webAudio?a._node.bufferSource&&(void 0===a._node.bufferSource.stop?a._node.bufferSource.noteOff(0):a._node.bufferSource.stop(0),o._cleanBuffer(a._node)):isNaN(a._node.duration)&&a._node.duration!==1/0||(a._node.currentTime=a._start||0,a._node.pause(),a._node.duration===1/0&&o._clearSound(a._node))),n||o._emit("stop",a._id))}return o},mute:function(e,o){var t=this;if("loaded"!==t._state||t._playLock)return t._queue.push({event:"mute",action:function(){t.mute(e,o)}}),t;if(void 0===o){if("boolean"!=typeof e)return t._muted;t._muted=e}for(var r=t._getSoundIds(o),a=0;a<r.length;a++){var u=t._soundById(r[a]);u&&(u._muted=e,u._interval&&t._stopFade(u._id),t._webAudio&&u._node?u._node.gain.setValueAtTime(e?0:u._volume,n.ctx.currentTime):u._node&&(u._node.muted=!!n._muted||e),t._emit("mute",u._id))}return t},volume:function(){var e,o,t=this,r=arguments;if(0===r.length)return t._volume;if(1===r.length||2===r.length&&void 0===r[1]){t._getSoundIds().indexOf(r[0])>=0?o=parseInt(r[0],10):e=parseFloat(r[0])}else r.length>=2&&(e=parseFloat(r[0]),o=parseInt(r[1],10));var a;if(!(void 0!==e&&e>=0&&e<=1))return a=o?t._soundById(o):t._sounds[0],a?a._volume:0;if("loaded"!==t._state||t._playLock)return t._queue.push({event:"volume",action:function(){t.volume.apply(t,r)}}),t;void 0===o&&(t._volume=e),o=t._getSoundIds(o);for(var u=0;u<o.length;u++)(a=t._soundById(o[u]))&&(a._volume=e,r[2]||t._stopFade(o[u]),t._webAudio&&a._node&&!a._muted?a._node.gain.setValueAtTime(e,n.ctx.currentTime):a._node&&!a._muted&&(a._node.volume=e*n.volume()),t._emit("volume",a._id));return t},fade:function(e,o,t,r){var a=this;if("loaded"!==a._state||a._playLock)return a._queue.push({event:"fade",action:function(){a.fade(e,o,t,r)}}),a;e=Math.min(Math.max(0,parseFloat(e)),1),o=Math.min(Math.max(0,parseFloat(o)),1),t=parseFloat(t),a.volume(e,r);for(var u=a._getSoundIds(r),d=0;d<u.length;d++){var i=a._soundById(u[d]);if(i){if(r||a._stopFade(u[d]),a._webAudio&&!i._muted){var _=n.ctx.currentTime,s=_+t/1e3;i._volume=e,i._node.gain.setValueAtTime(e,_),i._node.gain.linearRampToValueAtTime(o,s)}a._startFadeInterval(i,e,o,t,u[d],void 0===r)}}return a},_startFadeInterval:function(e,n,o,t,r,a){var u=this,d=n,i=o-n,_=Math.abs(i/.01),s=Math.max(4,_>0?t/_:t),l=Date.now();e._fadeTo=o,e._interval=setInterval(function(){var r=(Date.now()-l)/t;l=Date.now(),d+=i*r,d=Math.round(100*d)/100,d=i<0?Math.max(o,d):Math.min(o,d),u._webAudio?e._volume=d:u.volume(d,e._id,!0),a&&(u._volume=d),(o<n&&d<=o||o>n&&d>=o)&&(clearInterval(e._interval),e._interval=null,e._fadeTo=null,u.volume(o,e._id),u._emit("fade",e._id))},s)},_stopFade:function(e){var o=this,t=o._soundById(e);return t&&t._interval&&(o._webAudio&&t._node.gain.cancelScheduledValues(n.ctx.currentTime),clearInterval(t._interval),t._interval=null,o.volume(t._fadeTo,e),t._fadeTo=null,o._emit("fade",e)),o},loop:function(){var e,n,o,t=this,r=arguments;if(0===r.length)return t._loop;if(1===r.length){if("boolean"!=typeof r[0])return!!(o=t._soundById(parseInt(r[0],10)))&&o._loop;e=r[0],t._loop=e}else 2===r.length&&(e=r[0],n=parseInt(r[1],10));for(var a=t._getSoundIds(n),u=0;u<a.length;u++)(o=t._soundById(a[u]))&&(o._loop=e,t._webAudio&&o._node&&o._node.bufferSource&&(o._node.bufferSource.loop=e,e&&(o._node.bufferSource.loopStart=o._start||0,o._node.bufferSource.loopEnd=o._stop,t.playing(a[u])&&(t.pause(a[u],!0),t.play(a[u],!0)))));return t},rate:function(){var e,o,t=this,r=arguments;if(0===r.length)o=t._sounds[0]._id;else if(1===r.length){var a=t._getSoundIds(),u=a.indexOf(r[0]);u>=0?o=parseInt(r[0],10):e=parseFloat(r[0])}else 2===r.length&&(e=parseFloat(r[0]),o=parseInt(r[1],10));var d;if("number"!=typeof e)return d=t._soundById(o),d?d._rate:t._rate;if("loaded"!==t._state||t._playLock)return t._queue.push({event:"rate",action:function(){t.rate.apply(t,r)}}),t;void 0===o&&(t._rate=e),o=t._getSoundIds(o);for(var i=0;i<o.length;i++)if(d=t._soundById(o[i])){t.playing(o[i])&&(d._rateSeek=t.seek(o[i]),d._playStart=t._webAudio?n.ctx.currentTime:d._playStart),d._rate=e,t._webAudio&&d._node&&d._node.bufferSource?d._node.bufferSource.playbackRate.setValueAtTime(e,n.ctx.currentTime):d._node&&(d._node.playbackRate=e);var _=t.seek(o[i]),s=(t._sprite[d._sprite][0]+t._sprite[d._sprite][1])/1e3-_,l=1e3*s/Math.abs(d._rate);!t._endTimers[o[i]]&&d._paused||(t._clearTimer(o[i]),t._endTimers[o[i]]=setTimeout(t._ended.bind(t,d),l)),t._emit("rate",d._id)}return t},seek:function(){var e,o,t=this,r=arguments;if(0===r.length)t._sounds.length&&(o=t._sounds[0]._id);else if(1===r.length){var a=t._getSoundIds(),u=a.indexOf(r[0]);u>=0?o=parseInt(r[0],10):t._sounds.length&&(o=t._sounds[0]._id,e=parseFloat(r[0]))}else 2===r.length&&(e=parseFloat(r[0]),o=parseInt(r[1],10));if(void 0===o)return 0;if("number"==typeof e&&("loaded"!==t._state||t._playLock))return t._queue.push({event:"seek",action:function(){t.seek.apply(t,r)}}),t;var d=t._soundById(o);if(d){if(!("number"==typeof e&&e>=0)){if(t._webAudio){var i=t.playing(o)?n.ctx.currentTime-d._playStart:0,_=d._rateSeek?d._rateSeek-d._seek:0;return d._seek+(_+i*Math.abs(d._rate))}return d._node.currentTime}var s=t.playing(o);s&&t.pause(o,!0),d._seek=e,d._ended=!1,t._clearTimer(o),t._webAudio||!d._node||isNaN(d._node.duration)||(d._node.currentTime=e);var l=function(){s&&t.play(o,!0),t._emit("seek",o)};if(s&&!t._webAudio){var c=function(){t._playLock?setTimeout(c,0):l()};setTimeout(c,0)}else l()}return t},playing:function(e){var n=this;if("number"==typeof e){var o=n._soundById(e);return!!o&&!o._paused}for(var t=0;t<n._sounds.length;t++)if(!n._sounds[t]._paused)return!0;return!1},duration:function(e){var n=this,o=n._duration,t=n._soundById(e);return t&&(o=n._sprite[t._sprite][1]/1e3),o},state:function(){return this._state},unload:function(){for(var e=this,o=e._sounds,t=0;t<o.length;t++)o[t]._paused||e.stop(o[t]._id),e._webAudio||(e._clearSound(o[t]._node),o[t]._node.removeEventListener("error",o[t]._errorFn,!1),o[t]._node.removeEventListener(n._canPlayEvent,o[t]._loadFn,!1),o[t]._node.removeEventListener("ended",o[t]._endFn,!1),n._releaseHtml5Audio(o[t]._node)),delete o[t]._node,e._clearTimer(o[t]._id);var a=n._howls.indexOf(e);a>=0&&n._howls.splice(a,1);var u=!0;for(t=0;t<n._howls.length;t++)if(n._howls[t]._src===e._src||e._src.indexOf(n._howls[t]._src)>=0){u=!1;break}return r&&u&&delete r[e._src],n.noAudio=!1,e._state="unloaded",e._sounds=[],e=null,null},on:function(e,n,o,t){var r=this,a=r["_on"+e];return"function"==typeof n&&a.push(t?{id:o,fn:n,once:t}:{id:o,fn:n}),r},off:function(e,n,o){var t=this,r=t["_on"+e],a=0;if("number"==typeof n&&(o=n,n=null),n||o)for(a=0;a<r.length;a++){var u=o===r[a].id;if(n===r[a].fn&&u||!n&&u){r.splice(a,1);break}}else if(e)t["_on"+e]=[];else{var d=Object.keys(t);for(a=0;a<d.length;a++)0===d[a].indexOf("_on")&&Array.isArray(t[d[a]])&&(t[d[a]]=[])}return t},once:function(e,n,o){var t=this;return t.on(e,n,o,1),t},_emit:function(e,n,o){for(var t=this,r=t["_on"+e],a=r.length-1;a>=0;a--)r[a].id&&r[a].id!==n&&"load"!==e||(setTimeout(function(e){e.call(this,n,o)}.bind(t,r[a].fn),0),r[a].once&&t.off(e,r[a].fn,r[a].id));return t._loadQueue(e),t},_loadQueue:function(e){var n=this;if(n._queue.length>0){var o=n._queue[0];o.event===e&&(n._queue.shift(),n._loadQueue()),e||o.action()}return n},_ended:function(e){var o=this,t=e._sprite;if(!o._webAudio&&e._node&&!e._node.paused&&!e._node.ended&&e._node.currentTime<e._stop)return setTimeout(o._ended.bind(o,e),100),o;var r=!(!e._loop&&!o._sprite[t][2]);if(o._emit("end",e._id),!o._webAudio&&r&&o.stop(e._id,!0).play(e._id),o._webAudio&&r){o._emit("play",e._id),e._seek=e._start||0,e._rateSeek=0,e._playStart=n.ctx.currentTime;var a=1e3*(e._stop-e._start)/Math.abs(e._rate);o._endTimers[e._id]=setTimeout(o._ended.bind(o,e),a)}return o._webAudio&&!r&&(e._paused=!0,e._ended=!0,e._seek=e._start||0,e._rateSeek=0,o._clearTimer(e._id),o._cleanBuffer(e._node),n._autoSuspend()),o._webAudio||r||o.stop(e._id,!0),o},_clearTimer:function(e){var n=this;if(n._endTimers[e]){if("function"!=typeof n._endTimers[e])clearTimeout(n._endTimers[e]);else{var o=n._soundById(e);o&&o._node&&o._node.removeEventListener("ended",n._endTimers[e],!1)}delete n._endTimers[e]}return n},_soundById:function(e){for(var n=this,o=0;o<n._sounds.length;o++)if(e===n._sounds[o]._id)return n._sounds[o];return null},_inactiveSound:function(){var e=this;e._drain();for(var n=0;n<e._sounds.length;n++)if(e._sounds[n]._ended)return e._sounds[n].reset();return new t(e)},_drain:function(){var e=this,n=e._pool,o=0,t=0;if(!(e._sounds.length<n)){for(t=0;t<e._sounds.length;t++)e._sounds[t]._ended&&o++;for(t=e._sounds.length-1;t>=0;t--){if(o<=n)return;e._sounds[t]._ended&&(e._webAudio&&e._sounds[t]._node&&e._sounds[t]._node.disconnect(0),e._sounds.splice(t,1),o--)}}},_getSoundIds:function(e){var n=this;if(void 0===e){for(var o=[],t=0;t<n._sounds.length;t++)o.push(n._sounds[t]._id);return o}return[e]},_refreshBuffer:function(e){var o=this;return e._node.bufferSource=n.ctx.createBufferSource(),e._node.bufferSource.buffer=r[o._src],e._panner?e._node.bufferSource.connect(e._panner):e._node.bufferSource.connect(e._node),e._node.bufferSource.loop=e._loop,e._loop&&(e._node.bufferSource.loopStart=e._start||0,e._node.bufferSource.loopEnd=e._stop||0),e._node.bufferSource.playbackRate.setValueAtTime(e._rate,n.ctx.currentTime),o},_cleanBuffer:function(e){var o=this,t=n._navigator&&n._navigator.vendor.indexOf("Apple")>=0;if(!e.bufferSource)return o;if(n._scratchBuffer&&e.bufferSource&&(e.bufferSource.onended=null,e.bufferSource.disconnect(0),t))try{e.bufferSource.buffer=n._scratchBuffer}catch(e){}return e.bufferSource=null,o},_clearSound:function(e){/MSIE |Trident\//.test(n._navigator&&n._navigator.userAgent)||(e.src="data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA")}};var t=function(e){this._parent=e,this.init()};t.prototype={init:function(){var e=this,o=e._parent;return e._muted=o._muted,e._loop=o._loop,e._volume=o._volume,e._rate=o._rate,e._seek=0,e._paused=!0,e._ended=!0,e._sprite="__default",e._id=++n._counter,o._sounds.push(e),e.create(),e},create:function(){var e=this,o=e._parent,t=n._muted||e._muted||e._parent._muted?0:e._volume;return o._webAudio?(e._node=void 0===n.ctx.createGain?n.ctx.createGainNode():n.ctx.createGain(),e._node.gain.setValueAtTime(t,n.ctx.currentTime),e._node.paused=!0,e._node.connect(n.masterGain)):n.noAudio||(e._node=n._obtainHtml5Audio(),e._errorFn=e._errorListener.bind(e),e._node.addEventListener("error",e._errorFn,!1),e._loadFn=e._loadListener.bind(e),e._node.addEventListener(n._canPlayEvent,e._loadFn,!1),e._endFn=e._endListener.bind(e),e._node.addEventListener("ended",e._endFn,!1),e._node.src=o._src,e._node.preload=!0===o._preload?"auto":o._preload,e._node.volume=t*n.volume(),e._node.load()),e},reset:function(){var e=this,o=e._parent;return e._muted=o._muted,e._loop=o._loop,e._volume=o._volume,e._rate=o._rate,e._seek=0,e._rateSeek=0,e._paused=!0,e._ended=!0,e._sprite="__default",e._id=++n._counter,e},_errorListener:function(){var e=this;e._parent._emit("loaderror",e._id,e._node.error?e._node.error.code:0),e._node.removeEventListener("error",e._errorFn,!1)},_loadListener:function(){var e=this,o=e._parent;o._duration=Math.ceil(10*e._node.duration)/10,0===Object.keys(o._sprite).length&&(o._sprite={__default:[0,1e3*o._duration]}),"loaded"!==o._state&&(o._state="loaded",o._emit("load"),o._loadQueue()),e._node.removeEventListener(n._canPlayEvent,e._loadFn,!1)},_endListener:function(){var e=this,n=e._parent;n._duration===1/0&&(n._duration=Math.ceil(10*e._node.duration)/10,n._sprite.__default[1]===1/0&&(n._sprite.__default[1]=1e3*n._duration),n._ended(e)),e._node.removeEventListener("ended",e._endFn,!1)}};var r={},a=function(e){var n=e._src;if(r[n])return e._duration=r[n].duration,void i(e);if(/^data:[^;]+;base64,/.test(n)){for(var o=atob(n.split(",")[1]),t=new Uint8Array(o.length),a=0;a<o.length;++a)t[a]=o.charCodeAt(a);d(t.buffer,e)}else{var _=new XMLHttpRequest;_.open(e._xhr.method,n,!0),_.withCredentials=e._xhr.withCredentials,_.responseType="arraybuffer",e._xhr.headers&&Object.keys(e._xhr.headers).forEach(function(n){_.setRequestHeader(n,e._xhr.headers[n])}),_.onload=function(){var n=(_.status+"")[0];if("0"!==n&&"2"!==n&&"3"!==n)return void e._emit("loaderror",null,"Failed loading audio file with status: "+_.status+".");d(_.response,e)},_.onerror=function(){e._webAudio&&(e._html5=!0,e._webAudio=!1,e._sounds=[],delete r[n],e.load())},u(_)}},u=function(e){try{e.send()}catch(n){e.onerror()}},d=function(e,o){var t=function(){o._emit("loaderror",null,"Decoding audio data failed.")},a=function(e){e&&o._sounds.length>0?(r[o._src]=e,i(o,e)):t()};"undefined"!=typeof Promise&&1===n.ctx.decodeAudioData.length?n.ctx.decodeAudioData(e).then(a).catch(t):n.ctx.decodeAudioData(e,a,t)},i=function(e,n){n&&!e._duration&&(e._duration=n.duration),0===Object.keys(e._sprite).length&&(e._sprite={__default:[0,1e3*e._duration]}),"loaded"!==e._state&&(e._state="loaded",e._emit("load"),e._loadQueue())},_=function(){if(n.usingWebAudio){try{"undefined"!=typeof AudioContext?n.ctx=new AudioContext:"undefined"!=typeof webkitAudioContext?n.ctx=new webkitAudioContext:n.usingWebAudio=!1}catch(e){n.usingWebAudio=!1}n.ctx||(n.usingWebAudio=!1);var e=/iP(hone|od|ad)/.test(n._navigator&&n._navigator.platform),o=n._navigator&&n._navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/),t=o?parseInt(o[1],10):null;if(e&&t&&t<9){var r=/safari/.test(n._navigator&&n._navigator.userAgent.toLowerCase());n._navigator&&!r&&(n.usingWebAudio=!1)}n.usingWebAudio&&(n.masterGain=void 0===n.ctx.createGain?n.ctx.createGainNode():n.ctx.createGain(),n.masterGain.gain.setValueAtTime(n._muted?0:n._volume,n.ctx.currentTime),n.masterGain.connect(n.ctx.destination)),n._setup()}};"function"==typeof define&&define.amd&&define([],function(){return{Howler:n,Howl:o}}),"undefined"!=typeof exports&&(exports.Howler=n,exports.Howl=o),"undefined"!=typeof global?(global.HowlerGlobal=e,global.Howler=n,global.Howl=o,global.Sound=t):"undefined"!=typeof window&&(window.HowlerGlobal=e,window.Howler=n,window.Howl=o,window.Sound=t)}();
|
405
static/player.js
Normal file
405
static/player.js
Normal file
@@ -0,0 +1,405 @@
|
||||
/*!
|
||||
* Howler.js Audio Player Demo
|
||||
* howlerjs.com
|
||||
*
|
||||
* (c) 2013-2020, James Simpson of GoldFire Studios
|
||||
* goldfirestudios.com
|
||||
*
|
||||
* MIT License
|
||||
*/
|
||||
|
||||
// Cache references to DOM elements.
|
||||
var elms = ['track', 'timer', 'duration', 'playBtn', 'pauseBtn', 'prevBtn', 'nextBtn', 'playlistBtn', 'volumeBtn', 'progress', 'bar', 'wave', 'loading', 'playlist', 'list', 'volume', 'barEmpty', 'barFull', 'sliderBtn'];
|
||||
elms.forEach(function(elm) {
|
||||
window[elm] = document.getElementById(elm);
|
||||
});
|
||||
|
||||
/**
|
||||
* Player class containing the state of our playlist and where we are in it.
|
||||
* Includes all methods for playing, skipping, updating the display, etc.
|
||||
* @param {Array} playlist Array of objects with playlist song details ({title, file, howl}).
|
||||
*/
|
||||
var Player = function(playlist) {
|
||||
this.playlist = playlist;
|
||||
this.index = 0;
|
||||
|
||||
// Display the title of the first track.
|
||||
track.innerHTML = '1. ' + playlist[0].title;
|
||||
|
||||
// Setup the playlist display.
|
||||
playlist.forEach(function(song) {
|
||||
var div = document.createElement('div');
|
||||
div.className = 'list-song';
|
||||
div.innerHTML = song.title;
|
||||
div.onclick = function() {
|
||||
player.skipTo(playlist.indexOf(song));
|
||||
};
|
||||
list.appendChild(div);
|
||||
});
|
||||
};
|
||||
Player.prototype = {
|
||||
/**
|
||||
* Play a song in the playlist.
|
||||
* @param {Number} index Index of the song in the playlist (leave empty to play the first or current).
|
||||
*/
|
||||
play: function(index) {
|
||||
var self = this;
|
||||
var sound;
|
||||
|
||||
index = typeof index === 'number' ? index : self.index;
|
||||
var data = self.playlist[index];
|
||||
|
||||
// If we already loaded this track, use the current one.
|
||||
// Otherwise, setup and load a new Howl.
|
||||
if (data.howl) {
|
||||
sound = data.howl;
|
||||
} else {
|
||||
sound = data.howl = new Howl({
|
||||
src: [ './' + data.file ],
|
||||
html5: true, // Force to HTML5 so that the audio can stream in (best for large files).
|
||||
onplay: function() {
|
||||
// Display the duration.
|
||||
duration.innerHTML = self.formatTime(Math.round(sound.duration()));
|
||||
|
||||
// Start updating the progress of the track.
|
||||
requestAnimationFrame(self.step.bind(self));
|
||||
|
||||
// Start the wave animation if we have already loaded
|
||||
wave.container.style.display = 'block';
|
||||
bar.style.display = 'none';
|
||||
pauseBtn.style.display = 'block';
|
||||
},
|
||||
onload: function() {
|
||||
// Start the wave animation.
|
||||
wave.container.style.display = 'block';
|
||||
bar.style.display = 'none';
|
||||
loading.style.display = 'none';
|
||||
},
|
||||
onend: function() {
|
||||
// Stop the wave animation.
|
||||
wave.container.style.display = 'none';
|
||||
bar.style.display = 'block';
|
||||
self.skip('next');
|
||||
},
|
||||
onpause: function() {
|
||||
// Stop the wave animation.
|
||||
wave.container.style.display = 'none';
|
||||
bar.style.display = 'block';
|
||||
},
|
||||
onstop: function() {
|
||||
// Stop the wave animation.
|
||||
wave.container.style.display = 'none';
|
||||
bar.style.display = 'block';
|
||||
},
|
||||
onseek: function() {
|
||||
// Start updating the progress of the track.
|
||||
requestAnimationFrame(self.step.bind(self));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Begin playing the sound.
|
||||
sound.play();
|
||||
|
||||
// Update the track display.
|
||||
track.innerHTML = (index + 1) + '. ' + data.title;
|
||||
|
||||
// Show the pause button.
|
||||
if (sound.state() === 'loaded') {
|
||||
playBtn.style.display = 'none';
|
||||
pauseBtn.style.display = 'block';
|
||||
} else {
|
||||
loading.style.display = 'block';
|
||||
playBtn.style.display = 'none';
|
||||
pauseBtn.style.display = 'none';
|
||||
}
|
||||
|
||||
// Keep track of the index we are currently playing.
|
||||
self.index = index;
|
||||
},
|
||||
|
||||
/**
|
||||
* Pause the currently playing track.
|
||||
*/
|
||||
pause: function() {
|
||||
var self = this;
|
||||
|
||||
// Get the Howl we want to manipulate.
|
||||
var sound = self.playlist[self.index].howl;
|
||||
|
||||
// Puase the sound.
|
||||
sound.pause();
|
||||
|
||||
// Show the play button.
|
||||
playBtn.style.display = 'block';
|
||||
pauseBtn.style.display = 'none';
|
||||
},
|
||||
|
||||
/**
|
||||
* Skip to the next or previous track.
|
||||
* @param {String} direction 'next' or 'prev'.
|
||||
*/
|
||||
skip: function(direction) {
|
||||
var self = this;
|
||||
|
||||
// Get the next track based on the direction of the track.
|
||||
var index = 0;
|
||||
if (direction === 'prev') {
|
||||
index = self.index - 1;
|
||||
if (index < 0) {
|
||||
index = self.playlist.length - 1;
|
||||
}
|
||||
} else {
|
||||
index = self.index + 1;
|
||||
if (index >= self.playlist.length) {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
self.skipTo(index);
|
||||
},
|
||||
|
||||
/**
|
||||
* Skip to a specific track based on its playlist index.
|
||||
* @param {Number} index Index in the playlist.
|
||||
*/
|
||||
skipTo: function(index) {
|
||||
var self = this;
|
||||
|
||||
// Stop the current track.
|
||||
if (self.playlist[self.index].howl) {
|
||||
self.playlist[self.index].howl.stop();
|
||||
}
|
||||
|
||||
// Reset progress.
|
||||
progress.style.width = '0%';
|
||||
|
||||
// Play the new track.
|
||||
self.play(index);
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the volume and update the volume slider display.
|
||||
* @param {Number} val Volume between 0 and 1.
|
||||
*/
|
||||
volume: function(val) {
|
||||
var self = this;
|
||||
|
||||
// Update the global volume (affecting all Howls).
|
||||
Howler.volume(val);
|
||||
|
||||
// Update the display on the slider.
|
||||
var barWidth = (val * 90) / 100;
|
||||
barFull.style.width = (barWidth * 100) + '%';
|
||||
sliderBtn.style.left = (window.innerWidth * barWidth + window.innerWidth * 0.05 - 25) + 'px';
|
||||
},
|
||||
|
||||
/**
|
||||
* Seek to a new position in the currently playing track.
|
||||
* @param {Number} per Percentage through the song to skip.
|
||||
*/
|
||||
seek: function(per) {
|
||||
var self = this;
|
||||
|
||||
// Get the Howl we want to manipulate.
|
||||
var sound = self.playlist[self.index].howl;
|
||||
|
||||
// Convert the percent into a seek position.
|
||||
if (sound.playing()) {
|
||||
sound.seek(sound.duration() * per);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The step called within requestAnimationFrame to update the playback position.
|
||||
*/
|
||||
step: function() {
|
||||
var self = this;
|
||||
|
||||
// Get the Howl we want to manipulate.
|
||||
var sound = self.playlist[self.index].howl;
|
||||
|
||||
// Determine our current seek position.
|
||||
var seek = sound.seek() || 0;
|
||||
timer.innerHTML = self.formatTime(Math.round(seek));
|
||||
progress.style.width = (((seek / sound.duration()) * 100) || 0) + '%';
|
||||
|
||||
// If the sound is still playing, continue stepping.
|
||||
if (sound.playing()) {
|
||||
requestAnimationFrame(self.step.bind(self));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle the playlist display on/off.
|
||||
*/
|
||||
togglePlaylist: function() {
|
||||
var self = this;
|
||||
var display = (playlist.style.display === 'block') ? 'none' : 'block';
|
||||
|
||||
setTimeout(function() {
|
||||
playlist.style.display = display;
|
||||
}, (display === 'block') ? 0 : 500);
|
||||
playlist.className = (display === 'block') ? 'fadein' : 'fadeout';
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle the volume display on/off.
|
||||
*/
|
||||
toggleVolume: function() {
|
||||
var self = this;
|
||||
var display = (volume.style.display === 'block') ? 'none' : 'block';
|
||||
|
||||
setTimeout(function() {
|
||||
volume.style.display = display;
|
||||
}, (display === 'block') ? 0 : 500);
|
||||
volume.className = (display === 'block') ? 'fadein' : 'fadeout';
|
||||
},
|
||||
|
||||
/**
|
||||
* Format the time from seconds to M:SS.
|
||||
* @param {Number} secs Seconds to format.
|
||||
* @return {String} Formatted time.
|
||||
*/
|
||||
formatTime: function(secs) {
|
||||
var minutes = Math.floor(secs / 60) || 0;
|
||||
var seconds = (secs - minutes * 60) || 0;
|
||||
|
||||
return minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
|
||||
}
|
||||
};
|
||||
|
||||
// Setup our new audio player class and pass it the playlist.
|
||||
var player = new Player([
|
||||
{
|
||||
title: 'Ö1 Morgenjournal',
|
||||
file: 'stream',
|
||||
howl: null
|
||||
}
|
||||
]);
|
||||
|
||||
// Bind our player controls.
|
||||
playBtn.addEventListener('click', function() {
|
||||
player.play();
|
||||
});
|
||||
pauseBtn.addEventListener('click', function() {
|
||||
player.pause();
|
||||
});
|
||||
prevBtn.addEventListener('click', function() {
|
||||
var sound = player.playlist[player.index].howl;
|
||||
|
||||
if (sound && sound.playing()) {
|
||||
var currentSeek = sound.seek() || 0;
|
||||
var newSeek = currentSeek - 10; // Subtract 10 seconds
|
||||
var duration = sound.duration();
|
||||
|
||||
// Make sure we don't seek beyond the end of the track
|
||||
if (newSeek > 0) {
|
||||
sound.seek(newSeek);
|
||||
} else {
|
||||
// If seeking would go past the beginning, go to the beginning
|
||||
sound.seek(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
nextBtn.addEventListener('click', function() {
|
||||
var sound = player.playlist[player.index].howl;
|
||||
|
||||
if (sound && sound.playing()) {
|
||||
var currentSeek = sound.seek() || 0;
|
||||
var newSeek = currentSeek + 10; // Add 10 seconds
|
||||
var duration = sound.duration();
|
||||
|
||||
// Make sure we don't seek beyond the end of the track
|
||||
if (newSeek < duration) {
|
||||
sound.seek(newSeek);
|
||||
} else {
|
||||
// If seeking would go past the end, go to the end
|
||||
sound.seek(duration - 0.1); // Leave a small buffer to avoid issues
|
||||
}
|
||||
}
|
||||
});
|
||||
waveform.addEventListener('click', function(event) {
|
||||
player.seek(event.clientX / window.innerWidth);
|
||||
});
|
||||
playlistBtn.addEventListener('click', function() {
|
||||
player.togglePlaylist();
|
||||
});
|
||||
playlist.addEventListener('click', function() {
|
||||
player.togglePlaylist();
|
||||
});
|
||||
volumeBtn.addEventListener('click', function() {
|
||||
player.toggleVolume();
|
||||
});
|
||||
volume.addEventListener('click', function() {
|
||||
player.toggleVolume();
|
||||
});
|
||||
|
||||
// Setup the event listeners to enable dragging of volume slider.
|
||||
barEmpty.addEventListener('click', function(event) {
|
||||
var per = event.layerX / parseFloat(barEmpty.scrollWidth);
|
||||
player.volume(per);
|
||||
});
|
||||
sliderBtn.addEventListener('mousedown', function() {
|
||||
window.sliderDown = true;
|
||||
});
|
||||
sliderBtn.addEventListener('touchstart', function() {
|
||||
window.sliderDown = true;
|
||||
});
|
||||
volume.addEventListener('mouseup', function() {
|
||||
window.sliderDown = false;
|
||||
});
|
||||
volume.addEventListener('touchend', function() {
|
||||
window.sliderDown = false;
|
||||
});
|
||||
|
||||
var move = function(event) {
|
||||
if (window.sliderDown) {
|
||||
var x = event.clientX || event.touches[0].clientX;
|
||||
var startX = window.innerWidth * 0.05;
|
||||
var layerX = x - startX;
|
||||
var per = Math.min(1, Math.max(0, layerX / parseFloat(barEmpty.scrollWidth)));
|
||||
player.volume(per);
|
||||
}
|
||||
};
|
||||
|
||||
volume.addEventListener('mousemove', move);
|
||||
volume.addEventListener('touchmove', move);
|
||||
|
||||
// Setup the "waveform" animation.
|
||||
var wave = new SiriWave({
|
||||
container: waveform,
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight * 0.3,
|
||||
cover: true,
|
||||
speed: 0.03,
|
||||
amplitude: 0.7,
|
||||
frequency: 2
|
||||
});
|
||||
wave.start();
|
||||
|
||||
// Update the height of the wave animation.
|
||||
// These are basically some hacks to get SiriWave.js to do what we want.
|
||||
var resize = function() {
|
||||
var height = window.innerHeight * 0.3;
|
||||
var width = window.innerWidth;
|
||||
wave.height = height;
|
||||
wave.height_2 = height / 2;
|
||||
wave.MAX = wave.height_2 - 4;
|
||||
wave.width = width;
|
||||
wave.width_2 = width / 2;
|
||||
wave.width_4 = width / 4;
|
||||
wave.canvas.height = height;
|
||||
wave.canvas.width = width;
|
||||
wave.container.style.margin = -(height / 2) + 'px auto';
|
||||
|
||||
// Update the position of the slider.
|
||||
var sound = player.playlist[player.index].howl;
|
||||
if (sound) {
|
||||
var vol = sound.volume();
|
||||
var barWidth = (vol * 0.9);
|
||||
sliderBtn.style.left = (window.innerWidth * barWidth + window.innerWidth * 0.05 - 25) + 'px';
|
||||
}
|
||||
};
|
||||
window.addEventListener('resize', resize);
|
||||
resize();
|
148
static/siriwave.js
Normal file
148
static/siriwave.js
Normal file
@@ -0,0 +1,148 @@
|
||||
/* Modified from https://github.com/CaffeinaLab/SiriWaveJS */
|
||||
|
||||
(function() {
|
||||
|
||||
function SiriWave(opt) {
|
||||
opt = opt || {};
|
||||
|
||||
this.phase = 0;
|
||||
this.run = false;
|
||||
|
||||
// UI vars
|
||||
|
||||
this.ratio = opt.ratio || window.devicePixelRatio || 1;
|
||||
|
||||
this.width = this.ratio * (opt.width || 320);
|
||||
this.width_2 = this.width / 2;
|
||||
this.width_4 = this.width / 4;
|
||||
|
||||
this.height = this.ratio * (opt.height || 100);
|
||||
this.height_2 = this.height / 2;
|
||||
|
||||
this.MAX = (this.height_2) - 4;
|
||||
|
||||
// Constructor opt
|
||||
|
||||
this.amplitude = opt.amplitude || 1;
|
||||
this.speed = opt.speed || 0.2;
|
||||
this.frequency = opt.frequency || 6;
|
||||
this.color = (function hex2rgb(hex){
|
||||
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
|
||||
hex = hex.replace(shorthandRegex, function(m,r,g,b) { return r + r + g + g + b + b; });
|
||||
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||
return result ?
|
||||
parseInt(result[1],16).toString()+','+parseInt(result[2], 16).toString()+','+parseInt(result[3], 16).toString()
|
||||
: null;
|
||||
})(opt.color || '#fff') || '255,255,255';
|
||||
|
||||
// Canvas
|
||||
|
||||
this.canvas = document.createElement('canvas');
|
||||
this.canvas.width = this.width;
|
||||
this.canvas.height = this.height;
|
||||
if (opt.cover) {
|
||||
this.canvas.style.width = this.canvas.style.height = '100%';
|
||||
} else {
|
||||
this.canvas.style.width = (this.width / this.ratio) + 'px';
|
||||
this.canvas.style.height = (this.height / this.ratio) + 'px';
|
||||
};
|
||||
|
||||
this.container = opt.container || document.body;
|
||||
this.container.appendChild(this.canvas);
|
||||
|
||||
this.ctx = this.canvas.getContext('2d');
|
||||
|
||||
// Start
|
||||
|
||||
if (opt.autostart) {
|
||||
this.start();
|
||||
}
|
||||
}
|
||||
|
||||
SiriWave.prototype._GATF_cache = {};
|
||||
SiriWave.prototype._globAttFunc = function(x) {
|
||||
if (SiriWave.prototype._GATF_cache[x] == null) {
|
||||
SiriWave.prototype._GATF_cache[x] = Math.pow(4/(4+Math.pow(x,4)), 4);
|
||||
}
|
||||
return SiriWave.prototype._GATF_cache[x];
|
||||
};
|
||||
|
||||
SiriWave.prototype._xpos = function(i) {
|
||||
return this.width_2 + i * this.width_4;
|
||||
};
|
||||
|
||||
SiriWave.prototype._ypos = function(i, attenuation) {
|
||||
var att = (this.MAX * this.amplitude) / attenuation;
|
||||
return this.height_2 + this._globAttFunc(i) * att * Math.sin(this.frequency * i - this.phase);
|
||||
};
|
||||
|
||||
SiriWave.prototype._drawLine = function(attenuation, color, width){
|
||||
this.ctx.moveTo(0,0);
|
||||
this.ctx.beginPath();
|
||||
this.ctx.strokeStyle = color;
|
||||
this.ctx.lineWidth = width || 1;
|
||||
|
||||
var i = -2;
|
||||
while ((i += 0.01) <= 2) {
|
||||
var y = this._ypos(i, attenuation);
|
||||
if (Math.abs(i) >= 1.90) y = this.height_2;
|
||||
this.ctx.lineTo(this._xpos(i), y);
|
||||
}
|
||||
|
||||
this.ctx.stroke();
|
||||
};
|
||||
|
||||
SiriWave.prototype._clear = function() {
|
||||
this.ctx.globalCompositeOperation = 'destination-out';
|
||||
this.ctx.fillRect(0, 0, this.width, this.height);
|
||||
this.ctx.globalCompositeOperation = 'source-over';
|
||||
};
|
||||
|
||||
SiriWave.prototype._draw = function() {
|
||||
if (this.run === false) return;
|
||||
|
||||
this.phase = (this.phase + Math.PI*this.speed) % (2*Math.PI);
|
||||
|
||||
this._clear();
|
||||
this._drawLine(-2, 'rgba(' + this.color + ',0.1)');
|
||||
this._drawLine(-6, 'rgba(' + this.color + ',0.2)');
|
||||
this._drawLine(4, 'rgba(' + this.color + ',0.4)');
|
||||
this._drawLine(2, 'rgba(' + this.color + ',0.6)');
|
||||
this._drawLine(1, 'rgba(' + this.color + ',1)', 1.5);
|
||||
|
||||
if (window.requestAnimationFrame) {
|
||||
requestAnimationFrame(this._draw.bind(this));
|
||||
return;
|
||||
};
|
||||
setTimeout(this._draw.bind(this), 20);
|
||||
};
|
||||
|
||||
/* API */
|
||||
|
||||
SiriWave.prototype.start = function() {
|
||||
this.phase = 0;
|
||||
this.run = true;
|
||||
this._draw();
|
||||
};
|
||||
|
||||
SiriWave.prototype.stop = function() {
|
||||
this.phase = 0;
|
||||
this.run = false;
|
||||
};
|
||||
|
||||
SiriWave.prototype.setSpeed = function(v) {
|
||||
this.speed = v;
|
||||
};
|
||||
|
||||
SiriWave.prototype.setNoise = SiriWave.prototype.setAmplitude = function(v) {
|
||||
this.amplitude = Math.max(Math.min(v, 1), 0);
|
||||
};
|
||||
|
||||
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(function(){ return SiriWave; });
|
||||
return;
|
||||
};
|
||||
window.SiriWave = SiriWave;
|
||||
|
||||
})();
|
331
static/styles.css
Normal file
331
static/styles.css
Normal file
@@ -0,0 +1,331 @@
|
||||
/* https://howlerjs.com/assets/howler.js/examples/player/styles.css */
|
||||
html {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: #bb71f3;
|
||||
background: -moz-linear-gradient(-45deg, #bb71f3 0%, #3d4d91 100%);
|
||||
background: -webkit-linear-gradient(-45deg, #bb71f3 0%, #3d4d91 100%);
|
||||
background: linear-gradient(135deg, #bb71f3 0%, #3d4d91 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#bb71f3', endColorstr='#3d4d91', GradientType=1);
|
||||
font-family: "Helvetica Neue", "Futura", "Trebuchet MS", Arial;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-tap-highlight-color: rgba(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
/* Top Info */
|
||||
#title {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: 3%;
|
||||
line-height: 34px;
|
||||
height: 34px;
|
||||
text-align: center;
|
||||
font-size: 34px;
|
||||
opacity: 0.9;
|
||||
font-weight: 300;
|
||||
color: #fff;
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.33);
|
||||
}
|
||||
#timer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 3%;
|
||||
text-align: left;
|
||||
font-size: 26px;
|
||||
opacity: 0.9;
|
||||
font-weight: 300;
|
||||
color: #fff;
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.33);
|
||||
}
|
||||
#duration {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 3%;
|
||||
text-align: right;
|
||||
font-size: 26px;
|
||||
opacity: 0.5;
|
||||
font-weight: 300;
|
||||
color: #fff;
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.33);
|
||||
}
|
||||
|
||||
/* Controls */
|
||||
.controlsOuter {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
bottom: 3%;
|
||||
}
|
||||
.controlsInner {
|
||||
position: absolute;
|
||||
width: 340px;
|
||||
height: 70px;
|
||||
left: 50%;
|
||||
margin: 0 -170px;
|
||||
}
|
||||
.btn {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
opacity: 0.9;
|
||||
-webkit-filter: drop-shadow(1px 1px 2px rgba(0, 0, 0, 0.33));
|
||||
filter: drop-shadow(1px 1px 2px rgba(0, 0, 0, 0.33));
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.btn:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
#playBtn {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEUAAABGCAYAAACaGVmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QThERThENUFDNkJEMTFFNTk2RTBDMTRBMkYyNjVGMzQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QThERThENUJDNkJEMTFFNTk2RTBDMTRBMkYyNjVGMzQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBOERFOEQ1OEM2QkQxMUU1OTZFMEMxNEEyRjI2NUYzNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBOERFOEQ1OUM2QkQxMUU1OTZFMEMxNEEyRjI2NUYzNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PnFM7yIAAAFWSURBVHja7NRJDoMwDEDRcPL2ZoVeLEUtCyoCZPBsf8mS10+Wp5zzK6X0XGdJ0bdpRcnbPgfOESUFzjmKe5wrFLc4NSjucFpQ3OD0oJjHGUExiwOBYg4HEsUMDgaKehxMFLU4FCjqcChR1OBwoIjH4UQRiyMBRRyOJBQxOBJR2HEko7DhaEAhx9GEQoajEQUdRzMKGo4FFHAcSyhgOBZRhnEso3TjeEBpxvGEUo3jEeUWxzPKKU6gFHACpYATKMfegbLDWOcRl/Jr2X7JHI+2gOEZ5RTDI8othieUagwPKM0YllG6MSyiDGNYQgHDsIACjqEZBQ1DIwo6hiYUMgwNKOQYklHYMCSisGNIQhGDIQFFHAYnilgMDhTxGJQoajAoUNRhYKKoxcBAUY8BiWIGAwLFHMYIilmMHhTzGC0objBqUNxhXKG4xSihuMfYo8yB8d9HgAEATyefBJWpw70AAAAASUVORK5CYII=');
|
||||
width: 69px;
|
||||
height: 70px;
|
||||
left: 50%;
|
||||
margin: auto -34.5px;
|
||||
}
|
||||
#pauseBtn {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEUAAABGCAYAAACaGVmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MDE4MjkzRjNDNzI0MTFFNTk2RTBDMTRBMkYyNjVGMzQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MDE4MjkzRjRDNzI0MTFFNTk2RTBDMTRBMkYyNjVGMzQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBOERFOEQ1Q0M2QkQxMUU1OTZFMEMxNEEyRjI2NUYzNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowMTgyOTNGMkM3MjQxMUU1OTZFMEMxNEEyRjI2NUYzNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pne5d1AAAAB8SURBVHja7NCxCQAwAALBuP/QZoEUlinua0G4tD1j83Asv/4GChQoUKBAgQIFChQoUKBAgQIFChQoUKBAgQIFChQoUKBAgQIFChQoUKBAgQIFChQoUKBAgQIFChQoUKBAgQIFChQoUKBAgQIFChQoUKBAgQIFCpRXV4ABAOgpXYTDYwEwAAAAAElFTkSuQmCC');
|
||||
width: 69px;
|
||||
height: 70px;
|
||||
left: 50%;
|
||||
margin: auto -34.5px;
|
||||
display: none;
|
||||
}
|
||||
#prevBtn {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MDE4MjkzRkJDNzI0MTFFNTk2RTBDMTRBMkYyNjVGMzQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MDE4MjkzRkNDNzI0MTFFNTk2RTBDMTRBMkYyNjVGMzQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowMTgyOTNGOUM3MjQxMUU1OTZFMEMxNEEyRjI2NUYzNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowMTgyOTNGQUM3MjQxMUU1OTZFMEMxNEEyRjI2NUYzNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PndeAH8AAACtSURBVHja7NXJCcAgEAVQEevK0lk6M1aW6CGQQBBn+ephBv7F0wNn8Vd7OWCWnOjd2FpzYk7K2cJAxFEA78cwA6I3porohWlCoDEkBArDQmhjRAgtjApCilFFcDEQBAcTUYinKLcJCqFinGEMYxjDgDFpJkzZwDsSRf2mE4ni9gwEJW1gVZTWNKmgtEdbhELtGRYKvfRIqF4buAnV+xxUUaNu0y9q9KH8oG4BBgDjdpk0jMoz1gAAAABJRU5ErkJggg==');
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
margin: -17.5px auto;
|
||||
}
|
||||
#nextBtn {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MDE4MjkzRjdDNzI0MTFFNTk2RTBDMTRBMkYyNjVGMzQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MDE4MjkzRjhDNzI0MTFFNTk2RTBDMTRBMkYyNjVGMzQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowMTgyOTNGNUM3MjQxMUU1OTZFMEMxNEEyRjI2NUYzNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowMTgyOTNGNkM3MjQxMUU1OTZFMEMxNEEyRjI2NUYzNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PiFbkXsAAACvSURBVHja7NZBDoAgDARA9GPiz/yZ8DKsxgMHY1rYBQ5sshdCwiQcWpdSOqWb1BGrSn6RiTJjmKhiDANVjUGiYBgECo6pQdEwJSg6xoJqhtGgVFkeETZBekhjdqZ6g4H5QnXH5Cg/CkadiZmYiZkYdtYGb8QRMDdi144C1jfFd0AG69RG7jNB6nsvV3+IZhgNgo6xIGiYEgQcU4OAYRCIagwSUYxhIMwYJsKEuQQYABTs+qTMuF1rAAAAAElFTkSuQmCC');
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
margin: -17.5px auto;
|
||||
}
|
||||
#playlistBtn {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MUVBQzMwNzRDNzI0MTFFNTk2RTBDMTRBMkYyNjVGMzQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MUVBQzMwNzVDNzI0MTFFNTk2RTBDMTRBMkYyNjVGMzQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDoxRUFDMzA3MkM3MjQxMUU1OTZFMEMxNEEyRjI2NUYzNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDoxRUFDMzA3M0M3MjQxMUU1OTZFMEMxNEEyRjI2NUYzNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PnWBPa0AAADGSURBVHjaYvz//z/DYAFMDIMIjDpm1DHDwjH/0fDugXAMCxK7AojPAnEHmpo0IFaisTsqIEECLPT+Q4ALlL0bihmQ+LQGYLsYoSXwf2iovAdiYyjbdaCiaQ+S2FkopjtgHK2bCESTC5r4+4GIKuQEjAz2DGQCHhTlDAtaLtoDjSJkoATN7iMzmjrRxO+NljODsZzpwBJNs0bLGShwhToCvWHVQYes7YruGJiFgljKmpEbTa5YKsrRcmbUMaOOGXUMNQBAgAEAm2XFJrn+jrsAAAAASUVORK5CYII=');
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
top: 50%;
|
||||
left: 3%;
|
||||
margin: -17.5px auto;
|
||||
}
|
||||
#volumeBtn {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MUVBQzMwNzBDNzI0MTFFNTk2RTBDMTRBMkYyNjVGMzQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MUVBQzMwNzFDNzI0MTFFNTk2RTBDMTRBMkYyNjVGMzQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDoxRUFDMzA2RUM3MjQxMUU1OTZFMEMxNEEyRjI2NUYzNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDoxRUFDMzA2RkM3MjQxMUU1OTZFMEMxNEEyRjI2NUYzNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PtAZ+FgAAAHRSURBVHja7FjRbYMwEIWqC7CCO4IzgjMCHYEV6AjuCHSEMAKMkIwAI5ARXFt9rox1phDA5KMnPSGwOZ4fx/mOVCmVPIu9JE9ke5GRGpejyeQanUapkXlj5lzEIMM1GqjRAyG1eNCLCeAVyDQq9WOdRoHrDeDP7YCM8reGiNQYQER6D6DIGHDc02xFJsfqjF00GDEnRCaBegp+RmMp8oyYERcmAAvMvWl8aLSBuQ2O58D4Ff7eqJiZa4MTF1OYUsZAwF9BKaOw0tsf6pjx+wwVXWU41Pwk1DF2opQRK7+skDIyECMlrv8GfoztwMaW9K7XTqKMujeZV8SQma31eO0iNpkW8L/a3t02Yu7aITL8CDL2S2VE/jqsnnHJ3I8urnpKldhkOEFmdB6TTO7kFveVRScjoExLqBWVTIbsax765RFh7n74GoFMiQefCLXcbWFEhs/MFfeFZGon9fsx1I78PVDPyA3qmXyqnhEzV1lgRT124/rBSq+Dj/Pa7sDUPVesrEGRvUSZMlQ/rSmgCqc7qBZ0B7aQ37RVsb2QJOIp1DcNUDXbg4wFw2qV06j5ZCqQYSE/yYZ1LxVPvjJ86v50p/8ztr96X3JT+v+zKGDfAgwAZkMcn0x+AsMAAAAASUVORK5CYII=');
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
top: 50%;
|
||||
right: 3%;
|
||||
margin: -17.5px auto;
|
||||
}
|
||||
|
||||
/* Progress */
|
||||
#waveform {
|
||||
width: 100%;
|
||||
height: 30%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
margin: -15% auto;
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
#waveform:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
#bar {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.33);
|
||||
opacity: 0.9;
|
||||
}
|
||||
#progress {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
/* Loading */
|
||||
#loading {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin: -35px;
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
background-color: #fff;
|
||||
border-radius: 100%;
|
||||
-webkit-animation: sk-scaleout 1.0s infinite ease-in-out;
|
||||
animation: sk-scaleout 1.0s infinite ease-in-out;
|
||||
display: none;
|
||||
}
|
||||
@-webkit-keyframes sk-scaleout {
|
||||
0% { -webkit-transform: scale(0) }
|
||||
100% {
|
||||
-webkit-transform: scale(1.0);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes sk-scaleout {
|
||||
0% {
|
||||
-webkit-transform: scale(0);
|
||||
transform: scale(0);
|
||||
} 100% {
|
||||
-webkit-transform: scale(1.0);
|
||||
transform: scale(1.0);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Plylist */
|
||||
#playlist {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: none;
|
||||
}
|
||||
#list {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
-webkit-box-pack: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
.list-song {
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
font-size: 50px;
|
||||
line-height: 120px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.33);
|
||||
}
|
||||
.list-song:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Volume */
|
||||
#volume {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
touch-action: none;
|
||||
-webkit-user-select: none;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
display: none;
|
||||
}
|
||||
.bar {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 5%;
|
||||
margin: -5px auto;
|
||||
height: 10px;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.33);
|
||||
}
|
||||
#barEmpty {
|
||||
width: 90%;
|
||||
opacity: 0.5;
|
||||
box-shadow: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
#barFull {
|
||||
width: 90%;
|
||||
}
|
||||
#sliderBtn {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 93.25%;
|
||||
margin: -25px auto;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.33);
|
||||
border-radius: 25px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Fade-In */
|
||||
.fadeout {
|
||||
webkit-animation: fadeout 0.5s;
|
||||
-ms-animation: fadeout 0.5s;
|
||||
animation: fadeout 0.5s;
|
||||
}
|
||||
.fadein {
|
||||
webkit-animation: fadein 0.5s;
|
||||
-ms-animation: fadein 0.5s;
|
||||
animation: fadein 0.5s;
|
||||
}
|
||||
@keyframes fadein {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
@-webkit-keyframes fadein {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
@-ms-keyframes fadein {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
@keyframes fadeout {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
}
|
||||
@-webkit-keyframes fadeout {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
}
|
||||
@-ms-keyframes fadeout {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
}
|
Reference in New Issue
Block a user