add notes.md; add actions, fix bugs

This commit is contained in:
bkfox 2016-06-19 23:46:19 +02:00
parent 9769e0e617
commit 23016a594f
3 changed files with 325 additions and 106 deletions

View File

@ -158,7 +158,7 @@ Part_.Selector.prototype = {
var dst = dst_qs[i];
if(this.attribute)
dst[this.attribute] = src[this.attribute];
dst[this.attribute] = src[this.attribute] || '';
else
dst.parentNode.replaceChild(src, dst);
}

52
notes.md Normal file
View File

@ -0,0 +1,52 @@
- sounds monitor: max_size of path, take in account
- logs: archive functionnality + track stats for diffusions
- debug/prod configuration
# TODO:
- programs:
- schedule:
- (old) schedule.to_string unused? commented
- check one week on two
- write more tests
- sounds:
- print sounds of diffusions
- inline admin
- one sound, one diffusion?
- liquidsoap:
- update rc's supervisor scripts
- check when a played sound has a temp blank
- cms:
- empty content -> empty string
- update documentation:
- cms.views
- cms.parts
- cms.script
- routes
- customized header depending on the list (e.g. thread -> link to thread parent)
- by tags
- different models
- admin cms
- content management -> do we use a markup language?
- sections:
- article list with the focus
- related articles
- website:
- diffusions:
- filter sounds for undiffused diffusions
- player:
- "listen" + "favorite" buttons made easy + automated
- single mode / play next auto
- mixcloud
- seek bar
- schedule as calendar?
- finish that fucking website
- list of played diffusions and tracks when non-stop;

View File

@ -20,27 +20,31 @@
padding: 0;
}
.player-button {
display: inline-block;
height: 1.2em;
width: 1.2em;
overflow: hidden;
cursor: pointer;
margin-right: 0.4em;
}
.player-button img {
height: inherit;
box-shadow: none;
.player-button {
display: inline-block;
width: 1.5em;
height: 1.5em;
overflow: hidden;
cursor: pointer;
margin-right: 0.4em;
text-align: center;
}
#player[play] .player-button img {
margin-left: -100%;
.player-button:after {
content: '▶';
}
#player[state="playing"] .player-button:after {
content: '▮▮';
}
#player[state="stalled"] .player-button:after {
content: '...';
}
#player .on_air {
padding: 0.2em;
margin: 0.2em 0em;
}
#player .on_air .title {
@ -68,10 +72,10 @@
margin: 0;
padding: 0;
height: 15em;
overflow-y: auto;
/*overflow-y: auto;*/
}
.playlist .item > * {
.playlist .item > *:not(.actions) {
display: inline;
margin: 0.2em;
vertical-align: middle;
@ -81,16 +85,40 @@
float: right;
}
.playlist .item a {
float: right;
.playlist .actions {
float: right;
}
.playlist .actions a.action {
display: inline;
}
#playlist-live .actions,
#playlist-recents .actions a.action[action="remove"],
#playlist-marked .actions a.action[action="mark"],
.playlist .actions a.action[action="play"],
.playlist .actions a.url:not([href]),
.playlist .actions a.url[href=""] {
display: none;
}
.playlist .action[action="remove"] {
float: right;
}
</style>
<div id="player">
<div class="actions sounds" style="display: none;">
<a class="action" action="mark" title="{% trans "mark this sound" %}"></a>
<a class="action" action="play" title="{% trans "play this sound" %}"></a>
</div>
<li class='item' style="display: none;">
<h2 class="title"></h2>
<a class="url">+</a>
<div class="info"></div>
<div class="actions">
<a class="url action" title="{% trans "more informations" %}"></a>
<a class="action" action="remove" title="{% trans "remove from the playlist" %}"></a>
</div>
</li>
<div class="player-box">
<div id="embed-player">
@ -98,30 +126,25 @@
<div id="simple-player">
<audio preload="metadata">
Your browser does not support the <code>audio</code> element.
{% for stream in live_streams %}
<source src="{{ stream.detail_url }}">
{% endfor %}
</audio>
<span class="player-button" onclick="player.play()">
<img src="{% static "aircox/website/player_button.png" %}"
alt="{% trans "play audio" %}">
</span>
<span class="player-button" onclick="player.play()"
title="{% trans "play/pause" %}"></span>
<h3 class="title"></h3>
<div class='item on_air'>
<h2 class="title"></h2>
<a class="url">+</a>
</div>
</div>
</div>
<div class="playlists">
<nav>
<a onclick="player.select_playlist()" class="close"
title="{% trans "close" %}">✖</a>
<!-- single mode -->
</nav>
</div>
<div class='item on_air'>
<h2 class="title"></h2>
<a class="url"></a>
</div>
</div>
<script>
@ -130,15 +153,16 @@
// * tab: text to put in the tab
// * items: list of items to append
// * store: store the playlist in localStorage
function Playlist(name, tab, items, store = false) {
function Playlist(player, name, tab, items, store = false) {
this.player = player;
this.name = name;
this.store = store;
var self = this;
this.playlist = document.createElement('ul');
this.playlist.setAttribute('id', 'playlist-' + name );
this.playlist.className = 'playlist list';
var self = this;
this.tab = document.createElement('a');
this.tab.addEventListener('click', function(event) {
player.select_playlist(self);
@ -160,67 +184,144 @@ function Playlist(name, tab, items, store = false) {
Playlist.prototype = {
items: undefined,
/// Create an item and add to the given container [ = this.playlist ]
add: function (info, container) {
var item = player.player.querySelector('.item');
item = item.cloneNode(true);
item.removeAttribute('style');
/// find an item in playlist
find: function(item) {
return this.items.find(function(v) {
return v.stream == item.stream;
});
},
if(container)
container.appendChild(item);
/// add an item to the playlist or container, if not in this playlist.
/// return the existing item or the newly created item.
add: function(item, container) {
var item_ = this.find(item);
if(item_)
return item_;
var player = this.player;
var elm = player.player.querySelector('.item').cloneNode(true);
elm.removeAttribute('style');
if(!container)
container = this.playlist;
if(container.childNodes.length)
container.insertBefore(elm, container.childNodes[0]);
else
this.playlist.appendChild(item);
container.appendChild(elm);
info.item = item;
item.info = info;
item.elm = elm;
item.playlist = this;
elm.item = item;
item.querySelector('.title').innerHTML = info.title;
item.querySelector('.url').href = info.url;
item.querySelector('.info').innerHTML = info.info || '';
elm.querySelector('.title').innerHTML = item.title || '';
elm.querySelector('.url').href = item.url || '';
elm.querySelector('.info').innerHTML = item.info || '';
if(info.class)
item.className += " " + info.class;
if(item.class)
elm.className += " " + item.class;
if(info.stream || info.embed)
item.addEventListener('click', function(event) {
if(event.target.className.indexOf('url') != -1)
return;
player.select(event.currentTarget.info);
event.preventDefault();
}, true);
this.items.push(info);
elm.addEventListener('click', function(event) {
if(event.currentTarget.tagName == 'A' ||
event.target.tagName == 'A')
return;
var item = event.currentTarget.item;
if(item.stream || item.embed)
player.select(item);
event.stopPropagation();
return true;
}, false);
if(item.embed || item.stream)
player.add_actions(item, elm);
this.items.push(item);
if(container == this.playlist && this.store)
this.save()
return item;
},
/// Add a list of items (optimized)
add_list: function (items) {
var container = document.createDocumentFragment();
for(var i = 0; i < items.length; i++)
this.add(items[i], container);
this.playlist.appendChild(container);
if(this.store)
this.save()
},
/// Add a list of items (optimized)
add_list: function (items) {
container = document.createDocumentFragment();
for(var i = 0; i < items.length; i++)
this.add(items[i], container);
this.playlist.appendChild(container);
/// remove an item from the playlist
remove: function(item) {
for(var i = 0; i < this.items.length; i++) {
var item_ = this.items[i];
if(item_.stream != item.stream)
continue;
item_.elm.parentNode.removeChild(item_.elm);
this.items.splice(i,1);
break;
}
if(this.store)
this.save()
},
/// Save a playlist to local storage
save: function() {
pl = []
for(var i = 0; i < this.items.length; i++)
info = Object.assign({}, this.items[i])
info.item = undefined;
pl.push(info);
var pl = [];
for(var i = 0; i < this.items.length; i++) {
var item = Object.assign({}, this.items[i])
delete item.elm;
delete item.playlist;
pl.push(item);
}
localStorage.setItem('playlist.' + self.name,
JSON.stringify(pl))
if(pl.length)
localStorage.setItem('playlist.' + this.name,
JSON.stringify(pl))
else
localStorage.removeItem('playlist.' + this.name);
},
/// Load playlist from local storage
load: function() {
pl = localStorage.getItem('playlist.' + self.name)
var pl = localStorage.getItem('playlist.' + this.name);
if(pl)
this.add_list(JSON.parse(pl));
},
/// called by the player when the given item is unselected
unselect: function(player, item) {
this.tab.removeAttribute('active');
if(item.elm)
item.elm.removeAttribute('selected');
var audio = this.player.audio;
if(this.store && !audio.ended) {
item.currentTime = audio.currentTime;
this.save();
}
},
/// called by the player when the given item is selected, in order to
/// prepare it.
select: function(player, item) {
this.tab.setAttribute('active', 'true');
if(item.elm)
item.elm.setAttribute('selected', 'true');
if(this.store && item.currentTime &&
confirm("{% trans "restart from last position?" %}"))
{
// FIXME: one track in multiple playlists
player.audio.currentTime = Math.max(item.currentTime - 5, 0);
}
item.currentTime = undefined;
this.save();
},
}
@ -234,10 +335,32 @@ player = {
init: function(id) {
this.player = document.getElementById(id);
this.audio = this.player.querySelector('audio');
var self = this;
this.audio.addEventListener('playing', function() {
self.player.setAttribute('state', 'playing');
}, false);
this.audio.addEventListener('pause', function() {
self.player.setAttribute('state', 'paused');
}, false);
this.audio.addEventListener('loadstart', function() {
self.player.setAttribute('state', 'stalled');
}, false);
this.audio.addEventListener('loadeddata', function() {
self.player.removeAttribute('state');
}, false);
this.__init_playlists()
},
__init_playlists: function() {
this.playlists = this.player.querySelector('.playlists');
this.live = new Playlist(
this.live = new Playlist(this,
'live',
"☰ {% trans "live" %}",
" {% trans "live" %}",
[ {% for sound in live_streams %}
{ title: "{{ sound.title }}",
url: "{{ sound.url }}",
@ -245,8 +368,8 @@ player = {
info: "{{ sound.info }}",
}, {% endfor %} ]
);
this.recents = new Playlist(
'recents', '{% trans "recents" %}',
this.recents = new Playlist(this,
'recents', '{% trans "recents" %}',
[ {% for sound in last_sounds %}
{ title: "{{ sound.title }}",
url: "{{ sound.url }}",
@ -258,30 +381,18 @@ player = {
info: "{{ sound.related.duration|date:"i:s" }}",
}, {% endfor %} ]
);
this.favorite = new Playlist(
'favorite', '♥ {% trans "favorites" %}', null, true);
this.playlist = new Playlist(
'playlist', ' {% trans "playlist" %}', null, true);
this.marked = new Playlist(this,
'marked', '★ {% trans "marked" %}', null, true);
this.playlist = new Playlist(this,
'playlist', ' {% trans "playlist" %}', null, true);
this.select(this.live.items[0], false)
this.select(this.live.items[0], false);
this.select_playlist(this.recents);
this.update_on_air();
},
/// update on air informations
update_on_air: function() {
part = Part('{% url "exp.player.on_air" %}').get()
.select({
title: '.title',
url: ['.url', 'href'],
})
.map(this.player.querySelector('.on_air'))
.send();
window.setTimeout(function() {
player.update_on_air();
}, 60000*5);
},
/** player actions **/
/// play a given item { title, src }
play: function() {
var player = this.player;
@ -289,11 +400,9 @@ player = {
if(audio.paused) {
audio.play();
player.setAttribute('play', 'true');
}
else {
audio.pause();
player.removeAttribute('play');
}
},
@ -302,23 +411,19 @@ player = {
var audio = this.audio;
var player = this.player;
if(this.item && this.item.playlist)
this.item.playlist.unselect(this, this.item);
audio.pause();
audio.src = item.stream;
audio.load()
if(this.item && this.item.item)
this.item.item.removeAttribute('selected')
this.item = item;
if(item.item)
console.log(item.item)
item.item.setAttribute('selected', 'true');
if(this.item && this.item.playlist)
this.item.playlist.select(this, this.item);
player.querySelectorAll('#simple-player .title')[0]
.innerHTML = item.title;
player.querySelectorAll('.playlists')[0]
.setAttribute('playlist', item.playlist);
if(play)
this.play();
@ -343,6 +448,68 @@ player = {
playlist.tab.setAttribute('selected', 'true');
}
},
/** utility **/
/// update on air informations
update_on_air: function() {
part = Part('{% url "exp.player.on_air" %}').get()
.select({
title: '.title',
url: ['.url', 'href'],
})
.map(this.player.querySelector('.on_air'))
.send();
window.setTimeout(function() {
player.update_on_air();
}, 60000*5);
},
/// add sound actions to a given element
add_actions: function(item, container) {
var player = this;
var actions = player.player.querySelector('.actions')
var elm = container.querySelector('.actions');
if(elm) {
var actions = actions.childNodes;
for(var i = 0; i < actions.length; i++)
elm.appendChild(actions[i].cloneNode(true));
}
else {
elm = elm.cloneNode(true);
elm.removeAttribute('style');
}
elm.addEventListener('click', function(event) {
var action = event.target.getAttribute('action');
if(!action)
return;
event.preventDefault();
event.stopImmediatePropagation();
switch(action) {
case 'mark':
player.marked.add(item);
break;
case 'play':
item = player.playlist.add(item);
player.select_playlist(player.playlist);
player.select(item, true);
break;
case 'remove':
item.playlist.remove(item);
break;
default:
return;
}
}, true);
// TODO: remove "play" action
if(!elm.parentNode)
container.appendChild(elm);
},
}
player.init('player')