actions & action button automatic generation; 'play' & 'listen' button on diffusions work
This commit is contained in:
parent
8ff67fe68a
commit
e971f3f0b5
|
@ -265,7 +265,7 @@ class List(Section):
|
|||
message_empty = _('nothing')
|
||||
paginate_by = 4
|
||||
|
||||
fields = [ 'date', 'time', 'image', 'title', 'content', 'info' ]
|
||||
fields = [ 'date', 'time', 'image', 'title', 'content', 'info', 'actions' ]
|
||||
image_size = '64x64'
|
||||
truncate = 16
|
||||
|
||||
|
|
|
@ -1,3 +1,77 @@
|
|||
/// Actions manager
|
||||
/// This class is used to register actions and to execute them when it is
|
||||
/// triggered by the user.
|
||||
var Actions = {
|
||||
registry: {},
|
||||
|
||||
/// Add an handler for a given action
|
||||
register: function(id, symbol, title, handler) {
|
||||
this.registry[id] = {
|
||||
symbol: symbol,
|
||||
title: title,
|
||||
handler: handler,
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/// Init an existing action HTML element
|
||||
init_action: function(item, action_id, data, url) {
|
||||
var action = this.registry[action_id];
|
||||
if(!action)
|
||||
return;
|
||||
item.title = action.title;
|
||||
item.innerHTML = (action.symbol || '') + '<label>' +
|
||||
action.title + '</label>';
|
||||
item.data = data;
|
||||
item.className = 'action';
|
||||
if(url)
|
||||
item.href = url;
|
||||
item.setAttribute('action', action_id);
|
||||
item.addEventListener('click', Actions.run, true);
|
||||
},
|
||||
|
||||
/// Add an action to the given item
|
||||
add_action: function(item, action_id, data, url) {
|
||||
var actions = item.querySelector('.actions');
|
||||
if(actions && actions.querySelector('[action="' + action_id + '"]'))
|
||||
return;
|
||||
|
||||
var item = document.createElement('a');
|
||||
this.init_action(item, action_id, data, url);
|
||||
actions.appendChild(item);
|
||||
},
|
||||
|
||||
/// Run an action from the given event -- ! this can be undefined
|
||||
run: function(event) {
|
||||
var item = event.target;
|
||||
var action = item.hasAttribute('action') &&
|
||||
item.getAttribute('action');
|
||||
if(!action)
|
||||
return;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
action = Actions.registry[action];
|
||||
if(!action)
|
||||
return
|
||||
|
||||
data = item.data || item.dataset;
|
||||
action.handler(data, item);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function(event) {
|
||||
var items = document.querySelectorAll('.action[action]');
|
||||
for(var i = 0; i < items.length; i++) {
|
||||
var item = items[i];
|
||||
var action_id = item.getAttribute('action');
|
||||
var data = item.dataset;
|
||||
Actions.init_action(item, action_id, data);
|
||||
}
|
||||
}, false);
|
||||
|
||||
|
||||
/// Small utility used to make XMLHttpRequests, and map results on objects.
|
||||
|
|
|
@ -4,63 +4,72 @@
|
|||
|
||||
{% with object|downcast as object %}
|
||||
<li {% if object.css_class %}class="{{ object.css_class }}"{% endif %}
|
||||
{% for k, v in object.attrs.items %}
|
||||
{{ k }} = "{{ v|addslashes }}"
|
||||
{% endfor %} >
|
||||
{% if object.url %}
|
||||
{% for k, v in object.attrs.items %}
|
||||
{{ k }} = "{{ v|addslashes }}"
|
||||
{% endfor %} >
|
||||
{% if object.url %}
|
||||
<a class="url" href="{{ object.url }}">
|
||||
{% endif %}
|
||||
{% if 'image' in list.fields and object.image %}
|
||||
<img class="image" src="{% thumbnail object.image list.image_size crop %}">
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if 'image' in list.fields and object.image %}
|
||||
<img class="image" src="{% thumbnail object.image list.image_size crop %}">
|
||||
{% endif %}
|
||||
|
||||
<div class="body">
|
||||
{% if 'title' in list.fields and object.title %}
|
||||
<h2 class="title">{{ object.title }}</h2>
|
||||
{% endif %}
|
||||
<div class="body">
|
||||
{% if 'title' in list.fields and object.title %}
|
||||
<h2 class="title">{{ object.title }}</h2>
|
||||
{% endif %}
|
||||
|
||||
{% if 'content' in list.fields and object.content %}
|
||||
<div class="content">
|
||||
{% if list.truncate %}
|
||||
{{ object.content|striptags|truncatewords:list.truncate }}
|
||||
{% else %}
|
||||
{{ object.content|striptags }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if 'content' in list.fields and object.content %}
|
||||
<div class="content">
|
||||
{% if list.truncate %}
|
||||
{{ object.content|striptags|truncatewords:list.truncate }}
|
||||
{% else %}
|
||||
{{ object.content|striptags }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="meta">
|
||||
{% if object.date and 'date' in list.fields or 'time' in list.fields %}
|
||||
<time datetime="{{ object.date }}">
|
||||
{% if 'date' in list.fields %}
|
||||
<span class="date">
|
||||
{{ object.date|date:'D. d F' }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if 'time' in list.fields %}
|
||||
<span class="time">
|
||||
{{ object.date|date:'H:i' }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</time>
|
||||
{% endif %}
|
||||
{% if object.author and 'author' in list.fields %}
|
||||
<span class="author">
|
||||
{{ object.author }}
|
||||
</span>
|
||||
{% endif %}
|
||||
<div class="meta">
|
||||
{% if object.date and 'date' in list.fields or 'time' in list.fields %}
|
||||
<time datetime="{{ object.date }}">
|
||||
{% if 'date' in list.fields %}
|
||||
<span class="date">
|
||||
{{ object.date|date:'D. d F' }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if 'time' in list.fields %}
|
||||
<span class="time">
|
||||
{{ object.date|date:'H:i' }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</time>
|
||||
{% endif %}
|
||||
{% if object.author and 'author' in list.fields %}
|
||||
<span class="author">
|
||||
{{ object.author }}
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
{% if object.info and 'info' in list.fields %}
|
||||
<span class="info">
|
||||
{{ object.info }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if object.info and 'info' in list.fields %}
|
||||
<span class="info">
|
||||
{{ object.info }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if object.url %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if object.actions and 'actions' in list.fields %}
|
||||
<div class="actions">
|
||||
{% for action_id,action_data in object.actions.items %}
|
||||
<a class="action" action="{{action_id}}"
|
||||
{% for k,v in action_data.items %}data-{{ k }}="{{ v }}"{% endfor %}></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if object.url %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endwith %}
|
||||
|
||||
|
|
|
@ -168,7 +168,6 @@ class PostListView(BaseView, ListView):
|
|||
self.list = sections.List(
|
||||
truncate = 32,
|
||||
paginate_by = 0,
|
||||
fields = ['date', 'time', 'image', 'title', 'content'],
|
||||
)
|
||||
else:
|
||||
self.list = self.list(paginate_by = 0)
|
||||
|
|
|
@ -64,7 +64,8 @@ class Monitor:
|
|||
playlist = diff.playlist
|
||||
if played_sounds:
|
||||
diff.played = [ sound.related_object.path
|
||||
for sound in sound_logs[0:len(playlist)] ]
|
||||
for sound in sound_logs[0:len(playlist)]
|
||||
if sound.type = program.Logs.Type.switch ]
|
||||
return diff
|
||||
|
||||
@classmethod
|
||||
|
@ -111,6 +112,13 @@ class Monitor:
|
|||
# playlist update
|
||||
if dealer.playlist != playlist:
|
||||
dealer.playlist = playlist
|
||||
if next_diff:
|
||||
cl.log(
|
||||
type = programs.Log.Type.load,
|
||||
source = dealer.id,
|
||||
date = now,
|
||||
related_object = next_diff
|
||||
)
|
||||
|
||||
# dealer.on when next_diff.start <= now
|
||||
if next_diff and not dealer.on and next_diff.start <= now:
|
||||
|
@ -118,18 +126,16 @@ class Monitor:
|
|||
for source in controller.streams.values():
|
||||
source.skip()
|
||||
cl.log(
|
||||
type = programs.Log.Type.play,
|
||||
source = dealer.id,
|
||||
date = now,
|
||||
comment = 'trigger diffusion to liquidsoap; '
|
||||
'skip other streams',
|
||||
related_object = next_diff,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def run_source (cl, source):
|
||||
"""
|
||||
Keep trace of played sounds on the given source. For the moment we only
|
||||
keep track of known sounds.
|
||||
Keep trace of played sounds on the given source.
|
||||
"""
|
||||
# TODO: repetition of the same sound out of an interval of time
|
||||
last_log = programs.Log.objects.filter(
|
||||
|
@ -150,16 +156,16 @@ class Monitor:
|
|||
return
|
||||
|
||||
sound = programs.Sound.objects.filter(path = on_air)
|
||||
if not sound:
|
||||
return
|
||||
|
||||
sound = sound[0]
|
||||
cl.log(
|
||||
source = source.id,
|
||||
date = tz.make_aware(tz.datetime.now()),
|
||||
comment = 'sound changed',
|
||||
related_object = sound or None,
|
||||
)
|
||||
kwargs = {
|
||||
'type': programs.Log.Type.play,
|
||||
'source': source.id,
|
||||
'date': tz.make_aware(tz.datetime.now()),
|
||||
}
|
||||
if sound:
|
||||
kwargs['related_object'] = sound[0]
|
||||
else:
|
||||
kwargs['comment'] = on_air
|
||||
cl.log(**kwargs)
|
||||
|
||||
|
||||
class Command (BaseCommand):
|
||||
|
|
|
@ -2,10 +2,10 @@ import os
|
|||
import socket
|
||||
import re
|
||||
import json
|
||||
import subprocess
|
||||
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from django.utils import timezone as tz
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from django.utils.text import slugify
|
||||
from django.conf import settings as main_settings
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
|
@ -130,7 +130,6 @@ class BaseSource:
|
|||
return self.update(metadata = r or {})
|
||||
|
||||
source = metadata.get('source') or ''
|
||||
# FIXME: self.program
|
||||
if hasattr(self, 'program') and self.program \
|
||||
and not source.startswith(self.id):
|
||||
return -1
|
||||
|
@ -145,20 +144,19 @@ class Source(BaseSource):
|
|||
metadata = None
|
||||
|
||||
def __init__(self, controller, program = None, is_dealer = None):
|
||||
station = controller.station
|
||||
if is_dealer:
|
||||
id, name = '{}_dealer'.format(station.slug), \
|
||||
id, name = '{}_dealer'.format(controller.id), \
|
||||
'Dealer'
|
||||
self.is_dealer = True
|
||||
else:
|
||||
id, name = '{}_stream_{}'.format(station.slug, program.id), \
|
||||
id, name = '{}_stream_{}'.format(controller.id, program.id), \
|
||||
program.name
|
||||
|
||||
super().__init__(controller, id, name)
|
||||
|
||||
self.program = program
|
||||
self.path = os.path.join(settings.AIRCOX_LIQUIDSOAP_MEDIA,
|
||||
station.slug,
|
||||
controller.id,
|
||||
self.id + '.m3u')
|
||||
if program:
|
||||
self.playlist_from_db()
|
||||
|
@ -237,10 +235,6 @@ class Master (BaseSource):
|
|||
"""
|
||||
A master Source based on a given station
|
||||
"""
|
||||
def __init__(self, controller):
|
||||
station = controller.station
|
||||
super().__init__(controller, station.slug, station.name)
|
||||
|
||||
def update(self, metadata = None):
|
||||
if metadata is not None:
|
||||
return super().update(metadata)
|
||||
|
@ -259,13 +253,12 @@ class Controller:
|
|||
path = None
|
||||
|
||||
connector = None
|
||||
station = None # the related station
|
||||
master = None # master source (station's source)
|
||||
master = None # master source
|
||||
dealer = None # dealer source
|
||||
streams = None # streams streams
|
||||
|
||||
# FIXME: used nowhere except in liquidsoap cli to get on air item but is not
|
||||
# correctly
|
||||
# correct
|
||||
@property
|
||||
def on_air(self):
|
||||
return self.master
|
||||
|
@ -294,10 +287,9 @@ class Controller:
|
|||
to the given station; We ensure the existence of the controller's
|
||||
files dir.
|
||||
"""
|
||||
self.id = station.slug
|
||||
self.id = slugify(station)
|
||||
self.name = station
|
||||
self.path = os.path.join(settings.AIRCOX_LIQUIDSOAP_MEDIA,
|
||||
slugify(station))
|
||||
self.path = os.path.join(settings.AIRCOX_LIQUIDSOAP_MEDIA, self.id)
|
||||
|
||||
self.outputs = models.Output.objects.all()
|
||||
|
||||
|
@ -360,6 +352,7 @@ class Controller:
|
|||
'log_script': log_script,
|
||||
}
|
||||
|
||||
# FIXME: remove this crappy thing
|
||||
data = render_to_string('aircox/liquidsoap/station.liq', context)
|
||||
data = re.sub(r'\s*\\\n', r'#\\n#', data)
|
||||
data = data.replace('\n', '')
|
||||
|
|
23
notes.md
23
notes.md
|
@ -1,11 +1,3 @@
|
|||
- sounds monitor: max_size of path, take in account
|
||||
- logs: archive functionnality + track stats for diffusions
|
||||
- debug/prod configuration
|
||||
|
||||
# TODO ajd
|
||||
- website/sections Diffusions/prepare\_object\_list -> sounds
|
||||
- players' buttons
|
||||
|
||||
|
||||
# TODO:
|
||||
- general:
|
||||
|
@ -14,6 +6,7 @@
|
|||
|
||||
- programs:
|
||||
- schedule changes -> update later diffusions according to the new schedule
|
||||
- stream disable -> remote control on liquidsoap
|
||||
- tests:
|
||||
- sound_monitor
|
||||
|
||||
|
@ -25,26 +18,34 @@
|
|||
- config generation and sound diffusion
|
||||
|
||||
- cms:
|
||||
- switch to abstract class and remove qcombine (or keep it smw else)?
|
||||
- empty content -> empty string
|
||||
- update documentation:
|
||||
- cms.script
|
||||
- cms.exposure; make it right, see nomenclature, + docstring
|
||||
- admin cms
|
||||
- sections:
|
||||
- calendar title update
|
||||
- article list with the focus
|
||||
-> set html attribute based on values that are public
|
||||
|
||||
- website:
|
||||
- render schedule does not get the correct list
|
||||
-> postlistview has not the same queryset as website/sections/schedule
|
||||
- diffusions:
|
||||
- filter sounds for undiffused diffusions
|
||||
- print sounds of diffusions
|
||||
- print program's name in lists
|
||||
- player:
|
||||
- "listen" + "favorite" buttons made easy + automated
|
||||
- mixcloud
|
||||
- seek bar
|
||||
- list of played diffusions and tracks when non-stop;
|
||||
|
||||
# Later todo
|
||||
- sounds monitor: max_size of path, take in account
|
||||
- logs: archive functionnality
|
||||
- track stats for diffusions
|
||||
- debug/prod configuration
|
||||
- player support diffusions with multiple archive files
|
||||
- view as grid
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ class SoundAdmin(NameableAdmin):
|
|||
fields = None
|
||||
list_display = ['id', 'name', 'duration', 'type', 'mtime', 'good_quality', 'removed']
|
||||
fieldsets = [
|
||||
(None, { 'fields': NameableAdmin.fields + ['path', 'type'] } ),
|
||||
(None, { 'fields': NameableAdmin.fields + ['path', 'type', 'diffusion'] } ),
|
||||
(None, { 'fields': ['embed', 'duration', 'mtime'] }),
|
||||
(None, { 'fields': ['removed', 'good_quality' ] } )
|
||||
]
|
||||
|
|
|
@ -81,8 +81,8 @@ class Track(Nameable):
|
|||
_('artist'),
|
||||
max_length = 128,
|
||||
)
|
||||
# position can be used to specify a position in seconds for non-
|
||||
# stop programs or a position in the playlist
|
||||
# position can be used to specify a position in seconds for stream
|
||||
# programs or a position in the playlist
|
||||
position = models.SmallIntegerField(
|
||||
default = 0,
|
||||
help_text=_('position in the playlist'),
|
||||
|
@ -172,7 +172,7 @@ class Sound(Nameable):
|
|||
# path = self._meta.get_field('path').path
|
||||
path = self.path.replace(main_settings.MEDIA_ROOT, '', 1)
|
||||
#path = self.path.replace(path, '', 1)
|
||||
return path
|
||||
return main_settings.MEDIA_URL + '/' + path
|
||||
|
||||
def file_exists(self):
|
||||
"""
|
||||
|
@ -684,8 +684,30 @@ class Diffusion(models.Model):
|
|||
|
||||
class Log(models.Model):
|
||||
"""
|
||||
Log a played sound start and stop, or a single message
|
||||
Log sounds and diffusions that are played in the streamer. It
|
||||
can also be used for other purposes.
|
||||
"""
|
||||
class Type(IntEnum):
|
||||
stop = 0x00
|
||||
"""
|
||||
Source has been stopped (only when there is no more sound)
|
||||
"""
|
||||
play = 0x01
|
||||
"""
|
||||
Source has been started/changed and is running related_object
|
||||
If no related_object is available, comment is used to designate
|
||||
the sound.
|
||||
"""
|
||||
load = 0x02
|
||||
"""
|
||||
Source starts to be preload related_object
|
||||
"""
|
||||
|
||||
type = models.SmallIntegerField(
|
||||
verbose_name = _('type'),
|
||||
choices = [ (int(y), _(x)) for x,y in Type.__members__.items() ],
|
||||
blank = True, null = True,
|
||||
)
|
||||
source = models.CharField(
|
||||
_('source'),
|
||||
max_length = 64,
|
||||
|
@ -693,10 +715,11 @@ class Log(models.Model):
|
|||
blank = True, null = True,
|
||||
)
|
||||
date = models.DateTimeField(
|
||||
'date',
|
||||
_('date'),
|
||||
auto_now_add=True,
|
||||
)
|
||||
comment = models.CharField(
|
||||
_('comment'),
|
||||
max_length = 512,
|
||||
blank = True, null = True,
|
||||
)
|
||||
|
|
|
@ -115,7 +115,17 @@ class Diffusions(sections.List):
|
|||
post.title = ': ' + post.title if post.title else \
|
||||
' // ' + post.related.start.strftime('%A %d %B')
|
||||
post.title = name + post.title
|
||||
|
||||
# sounds
|
||||
pl = post.related.get_archives()
|
||||
if pl:
|
||||
item = { 'title': post.title, 'stream': pl[0].url,
|
||||
'url': post.url() }
|
||||
post.actions = {
|
||||
'sound.play': item,
|
||||
'sound.mark': item,
|
||||
}
|
||||
|
||||
return object_list
|
||||
|
||||
def get_object_list(self):
|
||||
|
|
|
@ -99,10 +99,11 @@
|
|||
display: inline;
|
||||
}
|
||||
|
||||
.playlist .actions label,
|
||||
#playlist-live .actions,
|
||||
#playlist-recents .actions a.action[action="remove"],
|
||||
#playlist-marked .actions a.action[action="mark"],
|
||||
.playlist .actions a.action[action="play"],
|
||||
#playlist-marked .actions a.action[action="sound.mark"],
|
||||
.playlist .actions a.action[action="sound.play"],
|
||||
.playlist .actions a.url:not([href]),
|
||||
.playlist .actions a.url[href=""] {
|
||||
display: none;
|
||||
|
@ -113,10 +114,6 @@
|
|||
}
|
||||
</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>
|
||||
<div class="info"></div>
|
||||
|
@ -455,7 +452,7 @@ player = {
|
|||
return;
|
||||
|
||||
if(data.playlist)
|
||||
this.select_playlist(this[data.playlist]);
|
||||
this.select_playlist(this[data.selected_playlist]);
|
||||
if(data.stream) {
|
||||
item = this.playlist.find(data.stream, true);
|
||||
item && this.select(item, false);
|
||||
|
@ -465,7 +462,7 @@ player = {
|
|||
|
||||
save: function() {
|
||||
playerStore.set('player', {
|
||||
'playlist': this.playlist.name,
|
||||
'selected_playlist': this.__playlist && this.__playlist.name,
|
||||
'stream': this.item && this.item.stream,
|
||||
'single': this.controls.single.checked,
|
||||
});
|
||||
|
@ -477,12 +474,10 @@ player = {
|
|||
var player = this.player;
|
||||
var audio = this.audio;
|
||||
|
||||
if(audio.paused) {
|
||||
if(audio.paused)
|
||||
audio.play();
|
||||
}
|
||||
else {
|
||||
else
|
||||
audio.pause();
|
||||
}
|
||||
},
|
||||
|
||||
__ask_to_seek(item) {
|
||||
|
@ -523,17 +518,17 @@ player = {
|
|||
|
||||
/// Select the next track in the current playlist, eventually play it
|
||||
next: function(play = true) {
|
||||
var playlist = this.playlist;
|
||||
var playlist = this.__playlist;
|
||||
if(playlist == this.live)
|
||||
return
|
||||
|
||||
var index = this.playlist.items.indexOf(this.item);
|
||||
var index = this.__playlist.items.indexOf(this.item);
|
||||
if(index == -1)
|
||||
return;
|
||||
|
||||
index--;
|
||||
if(index >= 0)
|
||||
this.select(this.playlist.items[index], play);
|
||||
this.select(this.__playlist.items[index], play);
|
||||
},
|
||||
|
||||
/// remove selection using the given selector.
|
||||
|
@ -549,7 +544,7 @@ player = {
|
|||
this.__unselect('.playlists nav .tab[selected]');
|
||||
this.__unselect('.playlists .playlist[selected]');
|
||||
|
||||
this.playlist = playlist;
|
||||
this.__playlist = playlist;
|
||||
if(playlist) {
|
||||
playlist.playlist.setAttribute('selected', 'true');
|
||||
playlist.tab.setAttribute('selected', 'true');
|
||||
|
@ -574,51 +569,28 @@ player = {
|
|||
|
||||
/// 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);
|
||||
|
||||
if(!elm.parentNode)
|
||||
container.appendChild(elm);
|
||||
Actions.add_action(container, 'sound.mark', item);
|
||||
Actions.add_action(container, 'sound.play', item, item.stream);
|
||||
// TODO: remove from playlist
|
||||
},
|
||||
}
|
||||
|
||||
player.init('player')
|
||||
Actions.register('sound.mark', '★', '{% trans "add to my playlist" %}',
|
||||
function(item) {
|
||||
player.marked.add(item);
|
||||
}
|
||||
);
|
||||
|
||||
Actions.register('sound.play', '▶', '{% trans "listen" %}',
|
||||
function(item) {
|
||||
item = player.playlist.add(item);
|
||||
player.select_playlist(player.playlist);
|
||||
player.select(item, true);
|
||||
}
|
||||
);
|
||||
|
||||
player.init('player');
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user