remove Station model (to much trouble for few advantages); start new player; rename Post.detail_url to Post.url, same for ListItem; move Article into website app; add website.Sound post; work on lists;...
This commit is contained in:
parent
13bf57b401
commit
c3ae0e012c
|
@ -50,6 +50,9 @@ class MyModelPost(RelatedPost):
|
||||||
Note: it is possible to assign a function as a bounded value; in such case, the
|
Note: it is possible to assign a function as a bounded value; in such case, the
|
||||||
function will be called using arguments **(post, related_object)**.
|
function will be called using arguments **(post, related_object)**.
|
||||||
|
|
||||||
|
At rendering, the property *info* can be retrieved from the Post. It is however
|
||||||
|
not a field.
|
||||||
|
|
||||||
## Routes
|
## Routes
|
||||||
Routes are used to generate the URLs of the website. We provide some of the
|
Routes are used to generate the URLs of the website. We provide some of the
|
||||||
common routes: for the detail view of course, but also to select all posts or
|
common routes: for the detail view of course, but also to select all posts or
|
||||||
|
|
|
@ -91,7 +91,6 @@ def inject_inline(model, inline, prepend = False):
|
||||||
registry[model].inlines = inlines
|
registry[model].inlines = inlines
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(models.Article, PostAdmin)
|
|
||||||
admin.site.register(models.Comment, CommentAdmin)
|
admin.site.register(models.Comment, CommentAdmin)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -166,7 +166,10 @@ class Post (models.Model, Routable):
|
||||||
)
|
)
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
def detail_url(self):
|
def url(self):
|
||||||
|
"""
|
||||||
|
Return an url to the post detail view.
|
||||||
|
"""
|
||||||
return self.route_url(
|
return self.route_url(
|
||||||
routes.DetailRoute,
|
routes.DetailRoute,
|
||||||
pk = self.pk, slug = slugify(self.title)
|
pk = self.pk, slug = slugify(self.title)
|
||||||
|
@ -209,24 +212,6 @@ class Post (models.Model, Routable):
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
class Article (Post):
|
|
||||||
"""
|
|
||||||
Represent an article or a static page on the website.
|
|
||||||
"""
|
|
||||||
static_page = models.BooleanField(
|
|
||||||
_('static page'),
|
|
||||||
default = False,
|
|
||||||
)
|
|
||||||
focus = models.BooleanField(
|
|
||||||
_('article is focus'),
|
|
||||||
default = False,
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('Article')
|
|
||||||
verbose_name_plural = _('Articles')
|
|
||||||
|
|
||||||
|
|
||||||
class RelatedPostBase (models.base.ModelBase):
|
class RelatedPostBase (models.base.ModelBase):
|
||||||
"""
|
"""
|
||||||
Metaclass for RelatedPost children.
|
Metaclass for RelatedPost children.
|
||||||
|
@ -356,6 +341,7 @@ class RelatedPost (Post, metaclass = RelatedPostBase):
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
# FIXME: declare a binding only for init
|
||||||
class Relation:
|
class Relation:
|
||||||
"""
|
"""
|
||||||
Relation descriptor used to generate and manage the related object.
|
Relation descriptor used to generate and manage the related object.
|
||||||
|
|
|
@ -203,7 +203,7 @@ class ListItem:
|
||||||
date = None
|
date = None
|
||||||
image = None
|
image = None
|
||||||
info = None
|
info = None
|
||||||
detail_url = None
|
url = None
|
||||||
|
|
||||||
css_class = None
|
css_class = None
|
||||||
attrs = None
|
attrs = None
|
||||||
|
@ -222,8 +222,8 @@ class ListItem:
|
||||||
continue
|
continue
|
||||||
if hasattr(post, i) and not getattr(self, i):
|
if hasattr(post, i) and not getattr(self, i):
|
||||||
setattr(self, i, getattr(post, i))
|
setattr(self, i, getattr(post, i))
|
||||||
if not self.detail_url and hasattr(post, 'detail_url'):
|
if not self.url and hasattr(post, 'url'):
|
||||||
self.detail_url = post.detail_url()
|
self.url = post.url()
|
||||||
|
|
||||||
|
|
||||||
class List(Section):
|
class List(Section):
|
||||||
|
@ -243,6 +243,7 @@ class List(Section):
|
||||||
object_list = None
|
object_list = None
|
||||||
url = None
|
url = None
|
||||||
message_empty = _('nothing')
|
message_empty = _('nothing')
|
||||||
|
paginate_by = 4
|
||||||
|
|
||||||
fields = [ 'date', 'time', 'image', 'title', 'content', 'info' ]
|
fields = [ 'date', 'time', 'image', 'title', 'content', 'info' ]
|
||||||
image_size = '64x64'
|
image_size = '64x64'
|
||||||
|
@ -264,20 +265,32 @@ class List(Section):
|
||||||
def get_object_list(self):
|
def get_object_list(self):
|
||||||
return self.object_list
|
return self.object_list
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs):
|
def get_context_data(self, request, object=None, *args, **kwargs):
|
||||||
context = super().get_context_data(*args, **kwargs)
|
if request: self.request = request
|
||||||
|
if object: self.object = object
|
||||||
|
if kwargs: self.kwargs = kwargs
|
||||||
|
|
||||||
object_list = self.object_list or self.get_object_list()
|
object_list = self.object_list or self.get_object_list()
|
||||||
if not object_list and not self.message_empty:
|
if not object_list and not self.message_empty:
|
||||||
return
|
return
|
||||||
|
self.object_list = object_list
|
||||||
|
|
||||||
|
context = super().get_context_data(request, object, *args, **kwargs)
|
||||||
context.update({
|
context.update({
|
||||||
'base_template': 'aircox/cms/section.html',
|
'base_template': 'aircox/cms/section.html',
|
||||||
'list': self,
|
'list': self,
|
||||||
'object_list': object_list,
|
'object_list': object_list[:self.paginate_by]
|
||||||
|
if object_list and self.paginate_by else
|
||||||
|
object_list,
|
||||||
})
|
})
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
def need_url(self):
|
||||||
|
"""
|
||||||
|
Return True if there should be a pagination url
|
||||||
|
"""
|
||||||
|
return self.paginate_by and self.paginate_by < len(self.object_list)
|
||||||
|
|
||||||
|
|
||||||
class Comments(List):
|
class Comments(List):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
{% extends "admin/base.html" %}
|
|
||||||
|
|
||||||
{% block extrahead %}
|
|
||||||
{% include 'autocomplete_light/static.html' %}
|
|
||||||
<style>
|
|
||||||
|
|
||||||
/** autocomplete override **/
|
|
||||||
.autocomplete-light-widget .deck [data-value] .remove {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.autocomplete-light-widget .deck [data-value],
|
|
||||||
.autocomplete-light-widget .deck .choice {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-group .add-related,
|
|
||||||
.inline-group .add-related {
|
|
||||||
vertical-align: bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** suit **/
|
|
||||||
.controls textarea,
|
|
||||||
.controls .vTextField {
|
|
||||||
width: calc(100% - 10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** grappelli **/
|
|
||||||
.grp-autocomplete-wrapper-m2m:focus, .grp-autocomplete-wrapper-m2m.grp-state-focus,
|
|
||||||
.grp-autocomplete-wrapper-m2m {
|
|
||||||
background: rgba(255, 255, 255, 0.2);
|
|
||||||
border: none;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grp-autocomplete-wrapper-m2m ul.grp-repr li.grp-search {
|
|
||||||
background-color: #FDFDFD;
|
|
||||||
border: 1px solid #CCC;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grp-autocomplete-wrapper-m2m ul.grp-repr li {
|
|
||||||
float: none;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
{% for k, v in item.attrs.items %}
|
{% for k, v in item.attrs.items %}
|
||||||
{{ k }} = "{{ v|addslashes }}"
|
{{ k }} = "{{ v|addslashes }}"
|
||||||
{% endfor %} >
|
{% endfor %} >
|
||||||
{% if item.detail_url %}
|
{% if item.url %}
|
||||||
<a href="{{ item.detail_url }}">
|
<a href="{{ item.url }}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if 'image' in list.fields and item.image %}
|
{% if 'image' in list.fields and item.image %}
|
||||||
<img src="{% thumbnail item.image list.image_size crop %}">
|
<img src="{% thumbnail item.image list.image_size crop %}">
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% if item.detail_url %}
|
{% if item.url %}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% empty %}
|
{% empty %}
|
||||||
|
|
|
@ -16,7 +16,7 @@ def threads(post, sep = '/'):
|
||||||
posts.insert(0, post)
|
posts.insert(0, post)
|
||||||
|
|
||||||
return sep.join([
|
return sep.join([
|
||||||
'<a href="{}">{}</a>'.format(post.detail_url(), post.title)
|
'<a href="{}">{}</a>'.format(post.url(), post.title)
|
||||||
for post in posts if post.published
|
for post in posts if post.published
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
|
@ -118,10 +118,11 @@ class PostListView(PostBaseView, ListView):
|
||||||
if not self.list:
|
if not self.list:
|
||||||
self.list = sections.List(
|
self.list = sections.List(
|
||||||
truncate = 32,
|
truncate = 32,
|
||||||
|
paginate_by = 0,
|
||||||
fields = ['date', 'time', 'image', 'title', 'content'],
|
fields = ['date', 'time', 'image', 'title', 'content'],
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.list = self.list()
|
self.list = self.list(paginate_by = 0)
|
||||||
self.template_name = self.list.template_name
|
self.template_name = self.list.template_name
|
||||||
self.css_class = self.list.css_class
|
self.css_class = self.list.css_class
|
||||||
|
|
||||||
|
|
|
@ -51,8 +51,7 @@ class Website:
|
||||||
self.set_menu(menu)
|
self.set_menu(menu)
|
||||||
|
|
||||||
if self.comments_routes:
|
if self.comments_routes:
|
||||||
self.register_comments_routes()
|
self.register_comments()
|
||||||
|
|
||||||
|
|
||||||
def name_of_model(self, model):
|
def name_of_model(self, model):
|
||||||
"""
|
"""
|
||||||
|
@ -62,7 +61,7 @@ class Website:
|
||||||
if model is _model:
|
if model is _model:
|
||||||
return name
|
return name
|
||||||
|
|
||||||
def register_comments_routes(self):
|
def register_comments(self):
|
||||||
"""
|
"""
|
||||||
Register routes for comments, for the moment, only
|
Register routes for comments, for the moment, only
|
||||||
ThreadRoute
|
ThreadRoute
|
||||||
|
|
|
@ -3,6 +3,6 @@ import aircox.liquidsoap.models as models
|
||||||
|
|
||||||
@admin.register(models.Output)
|
@admin.register(models.Output)
|
||||||
class OutputAdmin (admin.ModelAdmin):
|
class OutputAdmin (admin.ModelAdmin):
|
||||||
list_display = ('id', 'type', 'station')
|
list_display = ('id', 'type')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ class Monitor:
|
||||||
# - preload next diffusion's tracks
|
# - preload next diffusion's tracks
|
||||||
args = {'start__gt': prev_diff.start } if prev_diff else {}
|
args = {'start__gt': prev_diff.start } if prev_diff else {}
|
||||||
next_diff = programs.Diffusion \
|
next_diff = programs.Diffusion \
|
||||||
.get(controller.station, now, now = True,
|
.get(now, now = True,
|
||||||
type = programs.Diffusion.Type.normal,
|
type = programs.Diffusion.Type.normal,
|
||||||
sounds__isnull = False,
|
sounds__isnull = False,
|
||||||
**args) \
|
**args) \
|
||||||
|
@ -194,30 +194,19 @@ class Command (BaseCommand):
|
||||||
help='write configuration and playlist'
|
help='write configuration and playlist'
|
||||||
)
|
)
|
||||||
|
|
||||||
group = parser.add_argument_group('selector')
|
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
'-s', '--station', type=int, action='append',
|
'-s', '--station', type=str,
|
||||||
help='select station(s) with this id'
|
default = 'aircox',
|
||||||
)
|
help='use this name as station name (default is "aircox")'
|
||||||
group.add_argument(
|
|
||||||
'-a', '--all', action='store_true',
|
|
||||||
help='select all stations'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def handle (self, *args, **options):
|
def handle (self, *args, **options):
|
||||||
# selector
|
|
||||||
stations = []
|
|
||||||
if options.get('all'):
|
|
||||||
stations = programs.Station.objects.filter(active = True)
|
|
||||||
elif options.get('station'):
|
|
||||||
stations = programs.Station.objects.filter(
|
|
||||||
id__in = options.get('station')
|
|
||||||
)
|
|
||||||
|
|
||||||
run = options.get('run')
|
run = options.get('run')
|
||||||
monitor = options.get('on_air') or options.get('monitor')
|
monitor = options.get('on_air') or options.get('monitor')
|
||||||
self.controllers = [ utils.Controller(station, connector = monitor)
|
self.controller = utils.Controller(
|
||||||
for station in stations ]
|
station = options.get('station'),
|
||||||
|
connector = monitor
|
||||||
|
)
|
||||||
|
|
||||||
# actions
|
# actions
|
||||||
if options.get('write') or run:
|
if options.get('write') or run:
|
||||||
|
|
|
@ -1,24 +1,19 @@
|
||||||
|
from enum import Enum, IntEnum
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||||
|
|
||||||
import aircox.programs.models as programs
|
|
||||||
|
|
||||||
|
|
||||||
class Output (models.Model):
|
class Output (models.Model):
|
||||||
# Note: we don't translate the names since it is project names.
|
# Note: we don't translate the names since it is project names.
|
||||||
Type = {
|
class Type(IntEnum):
|
||||||
'jack': 0x00,
|
jack = 0x00
|
||||||
'alsa': 0x01,
|
alsa = 0x01
|
||||||
'icecast': 0x02,
|
icecast = 0x02
|
||||||
}
|
|
||||||
|
|
||||||
station = models.ForeignKey(
|
|
||||||
programs.Station,
|
|
||||||
verbose_name = _('station'),
|
|
||||||
)
|
|
||||||
type = models.SmallIntegerField(
|
type = models.SmallIntegerField(
|
||||||
_('output type'),
|
_('output type'),
|
||||||
choices = [ (y, x) for x,y in Type.items() ],
|
choices = [ (int(y), _(x)) for x,y in Type.__members__.items() ],
|
||||||
blank = True, null = True
|
blank = True, null = True
|
||||||
)
|
)
|
||||||
settings = models.TextField(
|
settings = models.TextField(
|
||||||
|
|
|
@ -295,12 +295,11 @@ class Controller:
|
||||||
files dir.
|
files dir.
|
||||||
"""
|
"""
|
||||||
self.id = station.slug
|
self.id = station.slug
|
||||||
self.name = station.name
|
self.name = station
|
||||||
self.path = os.path.join(settings.AIRCOX_LIQUIDSOAP_MEDIA, station.slug)
|
self.path = os.path.join(settings.AIRCOX_LIQUIDSOAP_MEDIA,
|
||||||
|
slugify(station))
|
||||||
|
|
||||||
self.station = station
|
self.outputs = models.Output.objects.all()
|
||||||
self.station.controller = self
|
|
||||||
self.outputs = models.Output.objects.filter(station = station)
|
|
||||||
|
|
||||||
self.connector = connector and Connector(self.socket_path)
|
self.connector = connector and Connector(self.socket_path)
|
||||||
|
|
||||||
|
@ -310,8 +309,7 @@ class Controller:
|
||||||
source.id : source
|
source.id : source
|
||||||
for source in [
|
for source in [
|
||||||
Source(self, program)
|
Source(self, program)
|
||||||
for program in programs.Program.objects.filter(station = station,
|
for program in programs.Program.objects.filter(active = True)
|
||||||
active = True)
|
|
||||||
if program.stream_set.count()
|
if program.stream_set.count()
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -370,23 +368,3 @@ class Controller:
|
||||||
file.write(data)
|
file.write(data)
|
||||||
|
|
||||||
|
|
||||||
class Monitor:
|
|
||||||
"""
|
|
||||||
Monitor multiple controllers.
|
|
||||||
"""
|
|
||||||
controllers = None
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.controllers = {
|
|
||||||
controller.id : controller
|
|
||||||
for controller in [
|
|
||||||
Controller(station, True)
|
|
||||||
for station in programs.Station.objects.filter(active = True)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
for controller in self.controllers.values():
|
|
||||||
controller.update()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,8 @@ class StreamInline(admin.TabularInline):
|
||||||
model = Stream
|
model = Stream
|
||||||
extra = 1
|
extra = 1
|
||||||
|
|
||||||
|
class SoundDiffInline(admin.TabularInline):
|
||||||
|
model = Diffusion.sounds.through
|
||||||
|
|
||||||
# from suit.admin import SortableTabularInline, SortableModelAdmin
|
# from suit.admin import SortableTabularInline, SortableModelAdmin
|
||||||
#class TrackInline(SortableTabularInline):
|
#class TrackInline(SortableTabularInline):
|
||||||
|
@ -45,11 +47,11 @@ class NameableAdmin(admin.ModelAdmin):
|
||||||
@admin.register(Sound)
|
@admin.register(Sound)
|
||||||
class SoundAdmin(NameableAdmin):
|
class SoundAdmin(NameableAdmin):
|
||||||
fields = None
|
fields = None
|
||||||
list_display = ['id', 'name', 'duration', 'type', 'mtime', 'good_quality', 'removed', 'public']
|
list_display = ['id', 'name', 'duration', 'type', 'mtime', 'good_quality', 'removed']
|
||||||
fieldsets = [
|
fieldsets = [
|
||||||
(None, { 'fields': NameableAdmin.fields + ['path', 'type'] } ),
|
(None, { 'fields': NameableAdmin.fields + ['path', 'type'] } ),
|
||||||
(None, { 'fields': ['embed', 'duration', 'mtime'] }),
|
(None, { 'fields': ['embed', 'duration', 'mtime'] }),
|
||||||
(None, { 'fields': ['removed', 'good_quality', 'public' ] } )
|
(None, { 'fields': ['removed', 'good_quality' ] } )
|
||||||
]
|
]
|
||||||
readonly_fields = ('path', 'duration',)
|
readonly_fields = ('path', 'duration',)
|
||||||
|
|
||||||
|
@ -59,10 +61,6 @@ class StreamAdmin(admin.ModelAdmin):
|
||||||
list_display = ('id', 'program', 'delay', 'begin', 'end')
|
list_display = ('id', 'program', 'delay', 'begin', 'end')
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Station)
|
|
||||||
class StationAdmin(NameableAdmin):
|
|
||||||
fields = NameableAdmin.fields + [ 'active', 'public', 'fallback' ]
|
|
||||||
|
|
||||||
@admin.register(Program)
|
@admin.register(Program)
|
||||||
class ProgramAdmin(NameableAdmin):
|
class ProgramAdmin(NameableAdmin):
|
||||||
def schedule(self, obj):
|
def schedule(self, obj):
|
||||||
|
@ -113,8 +111,9 @@ class DiffusionAdmin(admin.ModelAdmin):
|
||||||
list_editable = ('type',)
|
list_editable = ('type',)
|
||||||
ordering = ('-start', 'id')
|
ordering = ('-start', 'id')
|
||||||
|
|
||||||
fields = ['type', 'start', 'end', 'initial', 'program', 'sounds']
|
fields = ['type', 'start', 'end', 'initial', 'program']
|
||||||
inlines = [ DiffusionInline ]
|
inlines = [ DiffusionInline, SoundDiffInline ]
|
||||||
|
exclude = ('sounds',)
|
||||||
|
|
||||||
|
|
||||||
def get_form(self, request, obj=None, **kwargs):
|
def get_form(self, request, obj=None, **kwargs):
|
||||||
|
|
|
@ -141,11 +141,6 @@ class Sound(Nameable):
|
||||||
default = False,
|
default = False,
|
||||||
help_text = _('sound\'s quality is okay')
|
help_text = _('sound\'s quality is okay')
|
||||||
)
|
)
|
||||||
public = models.BooleanField(
|
|
||||||
_('public'),
|
|
||||||
default = False,
|
|
||||||
help_text = _('sound\'s is accessible through the website')
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_mtime(self):
|
def get_mtime(self):
|
||||||
"""
|
"""
|
||||||
|
@ -417,32 +412,6 @@ class Schedule(models.Model):
|
||||||
verbose_name_plural = _('Schedules')
|
verbose_name_plural = _('Schedules')
|
||||||
|
|
||||||
|
|
||||||
class Station(Nameable):
|
|
||||||
"""
|
|
||||||
A Station regroup one or more programs (stream and normal), and is the top
|
|
||||||
element used to generate streams outputs and configuration.
|
|
||||||
"""
|
|
||||||
active = models.BooleanField(
|
|
||||||
_('active'),
|
|
||||||
default = True,
|
|
||||||
help_text = _('this station is active')
|
|
||||||
)
|
|
||||||
public = models.BooleanField(
|
|
||||||
_('public'),
|
|
||||||
default = True,
|
|
||||||
help_text = _('information are available to the public'),
|
|
||||||
)
|
|
||||||
fallback = models.FilePathField(
|
|
||||||
_('fallback song'),
|
|
||||||
match = r'(' + '|'.join(settings.AIRCOX_SOUND_FILE_EXT) \
|
|
||||||
.replace('.', r'\.') + ')$',
|
|
||||||
recursive = True,
|
|
||||||
blank = True, null = True,
|
|
||||||
help_text = _('use this song file if there is a problem and nothing is '
|
|
||||||
'played')
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Program(Nameable):
|
class Program(Nameable):
|
||||||
"""
|
"""
|
||||||
A Program can either be a Streamed or a Scheduled program.
|
A Program can either be a Streamed or a Scheduled program.
|
||||||
|
@ -456,10 +425,6 @@ class Program(Nameable):
|
||||||
Renaming a Program rename the corresponding directory to matches the new
|
Renaming a Program rename the corresponding directory to matches the new
|
||||||
name if it does not exists.
|
name if it does not exists.
|
||||||
"""
|
"""
|
||||||
station = models.ForeignKey(
|
|
||||||
Station,
|
|
||||||
verbose_name = _('station')
|
|
||||||
)
|
|
||||||
active = models.BooleanField(
|
active = models.BooleanField(
|
||||||
_('active'),
|
_('active'),
|
||||||
default = True,
|
default = True,
|
||||||
|
@ -621,7 +586,7 @@ class Diffusion(models.Model):
|
||||||
return r
|
return r
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get(cl, station = None, date = None,
|
def get(cl, date = None,
|
||||||
now = False, next = False, prev = False,
|
now = False, next = False, prev = False,
|
||||||
queryset = None,
|
queryset = None,
|
||||||
**filter_args):
|
**filter_args):
|
||||||
|
@ -637,9 +602,6 @@ class Diffusion(models.Model):
|
||||||
"""
|
"""
|
||||||
#FIXME: conflicts? ( + calling functions)
|
#FIXME: conflicts? ( + calling functions)
|
||||||
date = date_or_default(date)
|
date = date_or_default(date)
|
||||||
if station:
|
|
||||||
filter_args['program__station'] = station
|
|
||||||
|
|
||||||
if queryset is None:
|
if queryset is None:
|
||||||
queryset = cl.objects
|
queryset = cl.objects
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ class TrackInline(SortableTabularInline):
|
||||||
sortable = 'position'
|
sortable = 'position'
|
||||||
extra = 10
|
extra = 10
|
||||||
|
|
||||||
|
admin.site.register(models.Article, cms.PostAdmin)
|
||||||
admin.site.register(models.Program, cms.RelatedPostAdmin)
|
admin.site.register(models.Program, cms.RelatedPostAdmin)
|
||||||
admin.site.register(models.Diffusion, cms.RelatedPostAdmin)
|
admin.site.register(models.Diffusion, cms.RelatedPostAdmin)
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,38 @@
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger('aircox')
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||||
|
|
||||||
from aircox.cms.models import RelatedPost, Article
|
from aircox.cms.models import Post, RelatedPost
|
||||||
import aircox.programs.models as programs
|
import aircox.programs.models as programs
|
||||||
|
|
||||||
|
|
||||||
|
class Article (Post):
|
||||||
|
"""
|
||||||
|
Represent an article or a static page on the website.
|
||||||
|
"""
|
||||||
|
static_page = models.BooleanField(
|
||||||
|
_('static page'),
|
||||||
|
default = False,
|
||||||
|
)
|
||||||
|
focus = models.BooleanField(
|
||||||
|
_('article is focus'),
|
||||||
|
default = False,
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('Article')
|
||||||
|
verbose_name_plural = _('Articles')
|
||||||
|
|
||||||
|
|
||||||
class Program (RelatedPost):
|
class Program (RelatedPost):
|
||||||
url = models.URLField(_('website'), blank=True, null=True)
|
website = models.URLField(
|
||||||
|
_('website'),
|
||||||
|
blank=True, null=True
|
||||||
|
)
|
||||||
# rss = models.URLField()
|
# rss = models.URLField()
|
||||||
email = models.EmailField(
|
email = models.EmailField(
|
||||||
_('email'), blank=True, null=True,
|
_('email'), blank=True, null=True,
|
||||||
|
@ -20,6 +47,7 @@ class Program (RelatedPost):
|
||||||
rel_to_post = True
|
rel_to_post = True
|
||||||
auto_create = True
|
auto_create = True
|
||||||
|
|
||||||
|
|
||||||
class Diffusion (RelatedPost):
|
class Diffusion (RelatedPost):
|
||||||
class Relation:
|
class Relation:
|
||||||
model = programs.Diffusion
|
model = programs.Diffusion
|
||||||
|
@ -59,3 +87,52 @@ class Diffusion (RelatedPost):
|
||||||
return _('rerun of %(day)s') % {
|
return _('rerun of %(day)s') % {
|
||||||
'day': self.related.initial.start.strftime('%A %d/%m')
|
'day': self.related.initial.start.strftime('%A %d/%m')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Sound (RelatedPost):
|
||||||
|
"""
|
||||||
|
Publication concerning sound. In order to manage access of sound
|
||||||
|
files in the filesystem, we use permissions -- it is up to the
|
||||||
|
user to work select the correct groups and permissions.
|
||||||
|
"""
|
||||||
|
embed = models.TextField(
|
||||||
|
_('embedding code'),
|
||||||
|
blank=True, null=True,
|
||||||
|
help_text = _('HTML code used to embed a sound from an external '
|
||||||
|
'plateform'),
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
Embedding code if the file has been published on an external
|
||||||
|
plateform.
|
||||||
|
"""
|
||||||
|
|
||||||
|
auto_chmod = True
|
||||||
|
"""
|
||||||
|
change file permission depending on the "published" attribute.
|
||||||
|
"""
|
||||||
|
chmod_flags = (750, 700)
|
||||||
|
"""
|
||||||
|
chmod bit flags, for (not_published, published)
|
||||||
|
"""
|
||||||
|
class Relation:
|
||||||
|
model = programs.Sound
|
||||||
|
bindings = {
|
||||||
|
'date': 'mtime',
|
||||||
|
}
|
||||||
|
rel_to_post = True
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
if self.auto_chmod and not self.related.removed and \
|
||||||
|
os.path.exists(self.related.path):
|
||||||
|
try:
|
||||||
|
os.chmod(self.related.path,
|
||||||
|
self.chmod_flags[self.published])
|
||||||
|
except PermissionError as err:
|
||||||
|
logger.error(
|
||||||
|
'cannot set permission {} to file {}: {}'.format(
|
||||||
|
self.chmod_flags[self.published],
|
||||||
|
self.related.path, err
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,28 @@ import aircox.cms.sections as sections
|
||||||
import aircox.website.models as models
|
import aircox.website.models as models
|
||||||
|
|
||||||
|
|
||||||
|
class Player(sections.Section):
|
||||||
|
"""
|
||||||
|
Display a player that is cool.
|
||||||
|
"""
|
||||||
|
template_name = 'aircox/website/player.html'
|
||||||
|
live_streams = []
|
||||||
|
"""
|
||||||
|
A ListItem objects that display a list of available streams.
|
||||||
|
"""
|
||||||
|
#default_sounds
|
||||||
|
|
||||||
|
|
||||||
|
def get_context_data(self, *args, **kwargs):
|
||||||
|
context = super().get_context_data(*args, **kwargs)
|
||||||
|
|
||||||
|
context.update({
|
||||||
|
'live_streams': self.live_streams
|
||||||
|
})
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Diffusions(sections.List):
|
class Diffusions(sections.List):
|
||||||
"""
|
"""
|
||||||
Section that print diffusions. When rendering, if there is no post yet
|
Section that print diffusions. When rendering, if there is no post yet
|
||||||
|
@ -19,14 +41,19 @@ class Diffusions(sections.List):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.__dict__.update(kwargs)
|
|
||||||
|
|
||||||
def get_diffs(self, **filter_args):
|
def get_diffs(self, **filter_args):
|
||||||
qs = programs.Diffusion.objects.filter(
|
qs = programs.Diffusion.objects.filter(
|
||||||
type = programs.Diffusion.Type.normal
|
type = programs.Diffusion.Type.normal
|
||||||
)
|
)
|
||||||
if self.object:
|
if self.object:
|
||||||
qs = qs.filter(program = self.object.related)
|
object = self.object.related
|
||||||
|
if type(object) == programs.Program:
|
||||||
|
qs = qs.filter(program = object)
|
||||||
|
elif type(object) == programs.Diffusion:
|
||||||
|
if object.initial:
|
||||||
|
object = object.initial
|
||||||
|
qs = qs.filter(initial = object) | qs.filter(pk = object.pk)
|
||||||
if filter_args:
|
if filter_args:
|
||||||
qs = qs.filter(**filter_args).order_by('start')
|
qs = qs.filter(**filter_args).order_by('start')
|
||||||
|
|
||||||
|
@ -72,6 +99,9 @@ class Diffusions(sections.List):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def url(self):
|
def url(self):
|
||||||
|
if not self.need_url():
|
||||||
|
return
|
||||||
|
|
||||||
if self.object:
|
if self.object:
|
||||||
return models.Diffusion.route_url(routes.ThreadRoute,
|
return models.Diffusion.route_url(routes.ThreadRoute,
|
||||||
pk = self.object.id,
|
pk = self.object.id,
|
||||||
|
@ -114,6 +144,10 @@ class Playlist(sections.List):
|
||||||
for track in tracks ]
|
for track in tracks ]
|
||||||
|
|
||||||
|
|
||||||
|
class Sounds(sections.List):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Schedule(Diffusions):
|
class Schedule(Diffusions):
|
||||||
"""
|
"""
|
||||||
Render a list of diffusions in the form of a schedule
|
Render a list of diffusions in the form of a schedule
|
||||||
|
|
Loading…
Reference in New Issue
Block a user