one sound, one parent: Diffusion.sounds -> Sound.diffusion; player: playerStore, save pos when time update, single mode
This commit is contained in:
parent
ceecbcfa54
commit
2564ba298a
10
notes.md
10
notes.md
|
@ -11,8 +11,11 @@
|
|||
- (old) schedule.to_string unused? commented
|
||||
- write more tests
|
||||
- sounds:
|
||||
- inline admin
|
||||
- one sound, one diffusion?
|
||||
- check that a sound is available when uploading too
|
||||
- one sound, one diffusion
|
||||
-> update admin & inlining
|
||||
-> sound_monitor
|
||||
-> liquidsoap
|
||||
|
||||
- liquidsoap:
|
||||
- update rc's supervisor scripts
|
||||
|
@ -34,6 +37,7 @@
|
|||
- article list with the focus
|
||||
- similar articles (using tags)
|
||||
- calendar
|
||||
- tags: allow tags_url on all publications
|
||||
|
||||
- website:
|
||||
- diffusions:
|
||||
|
@ -42,10 +46,8 @@
|
|||
- print program's name in lists
|
||||
- player:
|
||||
- "listen" + "favorite" buttons made easy + automated
|
||||
- single mode / play next auto
|
||||
- mixcloud
|
||||
- seek bar
|
||||
- section for schedule as calendar
|
||||
- load complete week for a schedule?
|
||||
- finish that fucking website
|
||||
- list of played diffusions and tracks when non-stop;
|
||||
|
|
|
@ -25,7 +25,7 @@ class StreamInline(admin.TabularInline):
|
|||
extra = 1
|
||||
|
||||
class SoundDiffInline(admin.TabularInline):
|
||||
model = Diffusion.sounds.through
|
||||
model = Sound
|
||||
|
||||
# from suit.admin import SortableTabularInline, SortableModelAdmin
|
||||
#class TrackInline(SortableTabularInline):
|
||||
|
|
|
@ -104,15 +104,17 @@ class Sound(Nameable):
|
|||
"""
|
||||
A Sound is the representation of a sound file that can be either an excerpt
|
||||
or a complete archive of the related diffusion.
|
||||
|
||||
The podcasting and public access permissions of a Sound are managed through
|
||||
the related program info.
|
||||
"""
|
||||
class Type(IntEnum):
|
||||
other = 0x00,
|
||||
archive = 0x01,
|
||||
excerpt = 0x02,
|
||||
|
||||
diffusion = models.ForeignKey(
|
||||
'Diffusion',
|
||||
verbose_name = _('diffusion'),
|
||||
blank = True, null = True,
|
||||
)
|
||||
type = models.SmallIntegerField(
|
||||
verbose_name = _('type'),
|
||||
choices = [ (int(y), _(x)) for x,y in Type.__members__.items() ],
|
||||
|
@ -550,11 +552,6 @@ class Diffusion(models.Model):
|
|||
'Program',
|
||||
verbose_name = _('program'),
|
||||
)
|
||||
sounds = models.ManyToManyField(
|
||||
Sound,
|
||||
blank = True,
|
||||
verbose_name = _('sounds'),
|
||||
)
|
||||
# specific
|
||||
type = models.SmallIntegerField(
|
||||
verbose_name = _('type'),
|
||||
|
|
|
@ -60,13 +60,18 @@
|
|||
.playlists {
|
||||
}
|
||||
|
||||
.playlists ul:not([selected]) {
|
||||
display: none;
|
||||
}
|
||||
.playlists ul:not([selected]) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.playlists nav a.close {
|
||||
float: right;
|
||||
}
|
||||
.playlists nav > a.close,
|
||||
.playlists nav > label {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#player-single-mode + label[for="player-single-mode"]::after {
|
||||
content:"{% trans "single mode" %}";
|
||||
}
|
||||
|
||||
.playlist {
|
||||
margin: 0;
|
||||
|
@ -136,9 +141,8 @@
|
|||
</div>
|
||||
<div class="playlists">
|
||||
<nav>
|
||||
<a onclick="player.select_playlist()" class="close"
|
||||
title="{% trans "close" %}">✖</a>
|
||||
<!-- single mode -->
|
||||
<input type="checkbox" class="single" id="player-single-mode">
|
||||
<label for="player-single-mode"></label>
|
||||
</nav>
|
||||
</div>
|
||||
<div class='item on_air'>
|
||||
|
@ -148,6 +152,46 @@
|
|||
</div>
|
||||
|
||||
<script>
|
||||
playerStore = {
|
||||
// save data to localstorage, or remove it if data is null
|
||||
set: function(name, data) {
|
||||
name = 'player.' + name;
|
||||
if(data == undefined) {
|
||||
localStorage.removeItem(name);
|
||||
return;
|
||||
}
|
||||
localStorage.setItem(name, JSON.stringify(data))
|
||||
},
|
||||
|
||||
// load data from localstorage
|
||||
get: function(name) {
|
||||
try {
|
||||
name = 'player.' + name;
|
||||
var data = localStorage.getItem(name);
|
||||
if(data)
|
||||
return JSON.parse(data);
|
||||
}
|
||||
catch(e) { console.log(e, data); }
|
||||
},
|
||||
|
||||
// return true if the given item is stored
|
||||
exists: function(name) {
|
||||
name = 'player.' + name;
|
||||
return (localStorage.getItem(name) != null);
|
||||
},
|
||||
|
||||
// update a field in the stored data
|
||||
update: function(name, key, value) {
|
||||
data = this.get(name) || {};
|
||||
if(value)
|
||||
data[key] = value;
|
||||
else
|
||||
delete data[key];
|
||||
this.set(name, data);
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
// Create a Playlist:
|
||||
// * name: name of the playlist, used for container id and storage
|
||||
// * tab: text to put in the tab
|
||||
|
@ -185,7 +229,12 @@ Playlist.prototype = {
|
|||
items: undefined,
|
||||
|
||||
/// find an item in playlist
|
||||
find: function(item) {
|
||||
find: function(item, by_stream = false) {
|
||||
if(by_stream)
|
||||
return this.items.find(function(v) {
|
||||
return v.stream == item;
|
||||
});
|
||||
|
||||
return this.items.find(function(v) {
|
||||
return v.stream == item.stream;
|
||||
});
|
||||
|
@ -276,22 +325,16 @@ Playlist.prototype = {
|
|||
delete item.playlist;
|
||||
pl.push(item);
|
||||
}
|
||||
|
||||
if(pl.length)
|
||||
localStorage.setItem('playlist.' + this.name,
|
||||
JSON.stringify(pl))
|
||||
else
|
||||
localStorage.removeItem('playlist.' + this.name);
|
||||
playerStore.set('playlist.' + this.name, pl)
|
||||
},
|
||||
|
||||
/// Load playlist from local storage
|
||||
load: function() {
|
||||
var pl = localStorage.getItem('playlist.' + this.name);
|
||||
var pl = playerStore.get('playlist.' + this.name);
|
||||
if(pl)
|
||||
this.add_list(JSON.parse(pl));
|
||||
this.add_list(pl);
|
||||
},
|
||||
|
||||
|
||||
/// called by the player when the given item is unselected
|
||||
unselect: function(player, item) {
|
||||
this.tab.removeAttribute('active');
|
||||
|
@ -311,16 +354,6 @@ Playlist.prototype = {
|
|||
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();
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -330,12 +363,25 @@ player = {
|
|||
player: undefined,
|
||||
/// <audio> container
|
||||
audio: undefined,
|
||||
/// controls
|
||||
controls: undefined,
|
||||
|
||||
/// init player
|
||||
init: function(id) {
|
||||
this.player = document.getElementById(id);
|
||||
this.audio = this.player.querySelector('audio');
|
||||
this.controls = {
|
||||
single: this.player.querySelector('input.single'),
|
||||
}
|
||||
|
||||
// TODO: event on controls -> save info in storage
|
||||
|
||||
this.__init_audio();
|
||||
this.__init_playlists();
|
||||
this.load();
|
||||
},
|
||||
|
||||
__init_audio: function() {
|
||||
var self = this;
|
||||
this.audio.addEventListener('playing', function() {
|
||||
self.player.setAttribute('state', 'playing');
|
||||
|
@ -353,7 +399,19 @@ player = {
|
|||
self.player.removeAttribute('state');
|
||||
}, false);
|
||||
|
||||
this.__init_playlists()
|
||||
this.audio.addEventListener('timeupdate', function() {
|
||||
if(self.audio.seekable.length)
|
||||
playerStore.set('stream.' + self.item.stream + '.pos',
|
||||
self.audio.currentTime)
|
||||
}, false);
|
||||
|
||||
this.audio.addEventListener('ended', function() {
|
||||
playerStore.set('streams.' + self.item.stream + '.pos')
|
||||
|
||||
single = self.player.querySelector('input.single');
|
||||
if(!single.checked)
|
||||
self.next(true);
|
||||
}, false);
|
||||
},
|
||||
|
||||
__init_playlists: function() {
|
||||
|
@ -391,6 +449,27 @@ player = {
|
|||
this.update_on_air();
|
||||
},
|
||||
|
||||
load: function() {
|
||||
var data = playerStore.get('player');
|
||||
if(!data)
|
||||
return;
|
||||
|
||||
if(data.playlist)
|
||||
this.select_playlist(this[data.playlist]);
|
||||
if(data.stream) {
|
||||
item = this.playlist.find(data.stream, true);
|
||||
item && this.select(item, false);
|
||||
}
|
||||
this.controls.single.checked = data.single
|
||||
},
|
||||
|
||||
save: function() {
|
||||
playerStore.set('player', {
|
||||
'playlist': this.playlist.name,
|
||||
'stream': this.item && this.item.stream,
|
||||
'single': this.controls.single.checked,
|
||||
});
|
||||
},
|
||||
|
||||
/** player actions **/
|
||||
/// play a given item { title, src }
|
||||
|
@ -406,6 +485,16 @@ player = {
|
|||
}
|
||||
},
|
||||
|
||||
__ask_to_seek(item) {
|
||||
var key = 'stream.' + item.stream + '.pos'
|
||||
var pos = playerStore.get(key);
|
||||
if(!pos)
|
||||
return
|
||||
if(confirm("{% trans "restart from the last position?" %}"))
|
||||
this.audio.currentTime = Math.max(pos - 5, 0);
|
||||
playerStore.set(key);
|
||||
},
|
||||
|
||||
/// select the current track to play, and start playing it
|
||||
select: function(item, play = true) {
|
||||
var audio = this.audio;
|
||||
|
@ -416,7 +505,7 @@ player = {
|
|||
|
||||
audio.pause();
|
||||
audio.src = item.stream;
|
||||
audio.load()
|
||||
audio.load();
|
||||
|
||||
this.item = item;
|
||||
if(this.item && this.item.playlist)
|
||||
|
@ -425,8 +514,24 @@ player = {
|
|||
player.querySelectorAll('#simple-player .title')[0]
|
||||
.innerHTML = item.title;
|
||||
|
||||
if(play)
|
||||
if(play) {
|
||||
this.__ask_to_seek(item);
|
||||
this.play();
|
||||
}
|
||||
this.save();
|
||||
},
|
||||
|
||||
/// Select the next track in the current playlist, eventually play it
|
||||
next: function(play = true) {
|
||||
var playlist = this.playlist;
|
||||
var index = this.playlist.items.indexOf(this.item);
|
||||
console.log(index, this.item, this.playlist.items)
|
||||
if(index == -1)
|
||||
return;
|
||||
|
||||
index--;
|
||||
if(index >= 0)
|
||||
this.select(this.playlist.items[index], play);
|
||||
},
|
||||
|
||||
/// remove selection using the given selector.
|
||||
|
@ -442,14 +547,14 @@ player = {
|
|||
this.__unselect('.playlists nav .tab[selected]');
|
||||
this.__unselect('.playlists .playlist[selected]');
|
||||
|
||||
self.playlist = playlist
|
||||
this.playlist = playlist;
|
||||
if(playlist) {
|
||||
playlist.playlist.setAttribute('selected', 'true');
|
||||
playlist.tab.setAttribute('selected', 'true');
|
||||
}
|
||||
},
|
||||
|
||||
/** utility **/
|
||||
/** utility & actions **/
|
||||
/// update on air informations
|
||||
update_on_air: function() {
|
||||
part = Part('{% url "exp.player.on_air" %}').get()
|
||||
|
@ -506,7 +611,6 @@ player = {
|
|||
}
|
||||
}, true);
|
||||
|
||||
// TODO: remove "play" action
|
||||
if(!elm.parentNode)
|
||||
container.appendChild(elm);
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue
Block a user