fix logs merge with diff algorithm
This commit is contained in:
parent
aabbcd97fa
commit
e0f1ac498f
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,3 +1,4 @@
|
||||||
|
from collections import deque
|
||||||
import datetime
|
import datetime
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
import logging
|
import logging
|
||||||
|
@ -25,10 +26,14 @@ class LogQuerySet(models.QuerySet):
|
||||||
return self.filter(station=station) if id is None else \
|
return self.filter(station=station) if id is None else \
|
||||||
self.filter(station_id=id)
|
self.filter(station_id=id)
|
||||||
|
|
||||||
def at(self, date=None):
|
def today(self, date):
|
||||||
date = utils.date_or_default(date)
|
|
||||||
return self.filter(date__date=date)
|
return self.filter(date__date=date)
|
||||||
|
|
||||||
|
def after(self, date):
|
||||||
|
return self.filter(date__gte=date) \
|
||||||
|
if isinstance(date, tz.datetime) else \
|
||||||
|
self.filter(date__date__gte=date)
|
||||||
|
|
||||||
def on_air(self):
|
def on_air(self):
|
||||||
return self.filter(type=Log.Type.on_air)
|
return self.filter(type=Log.Type.on_air)
|
||||||
|
|
||||||
|
@ -100,13 +105,9 @@ class LogQuerySet(models.QuerySet):
|
||||||
}
|
}
|
||||||
|
|
||||||
def rel_obj(log, attr):
|
def rel_obj(log, attr):
|
||||||
attr_id = attr + '_id'
|
|
||||||
rel_id = log.get(attr + '_id')
|
rel_id = log.get(attr + '_id')
|
||||||
|
|
||||||
return rels[attr][rel_id] if rel_id else None
|
return rels[attr][rel_id] if rel_id else None
|
||||||
|
|
||||||
# make logs
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
Log(diffusion=rel_obj(log, 'diffusion'),
|
Log(diffusion=rel_obj(log, 'diffusion'),
|
||||||
sound=rel_obj(log, 'sound'),
|
sound=rel_obj(log, 'sound'),
|
||||||
|
@ -134,7 +135,7 @@ class LogQuerySet(models.QuerySet):
|
||||||
if os.path.exists(path) and not force:
|
if os.path.exists(path) and not force:
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
qs = self.station(station).at(date)
|
qs = self.station(station).today(date)
|
||||||
|
|
||||||
if not qs.exists():
|
if not qs.exists():
|
||||||
return 0
|
return 0
|
||||||
|
@ -241,6 +242,56 @@ class Log(models.Model):
|
||||||
"""
|
"""
|
||||||
return tz.localtime(self.date, tz.get_current_timezone())
|
return tz.localtime(self.date, tz.get_current_timezone())
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '#{} ({}, {}, {})'.format(
|
||||||
|
self.pk, self.get_type_display(),
|
||||||
|
self.source, self.local_date.strftime('%Y/%m/%d %H:%M%z'))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __list_append(cls, object_list, items):
|
||||||
|
object_list += [cls(obj) for obj in items]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def merge_diffusions(cls, logs, diffs):
|
||||||
|
diffs = deque(diffs.order_by('start'))
|
||||||
|
logs = list(logs.order_by('date'))
|
||||||
|
object_list = []
|
||||||
|
|
||||||
|
# +++ +++ ++ ++
|
||||||
|
# ---- ----- ----
|
||||||
|
while True:
|
||||||
|
if not len(diffs):
|
||||||
|
object_list += logs
|
||||||
|
break
|
||||||
|
|
||||||
|
if not len(logs):
|
||||||
|
object_list += diffs
|
||||||
|
break
|
||||||
|
|
||||||
|
diff = diffs.popleft()
|
||||||
|
|
||||||
|
# - takes all logs before diff happens
|
||||||
|
index = next((i for i, v in enumerate(logs)
|
||||||
|
if v.date >= diff.start), len(logs))
|
||||||
|
if index is not None and index > 0:
|
||||||
|
object_list += logs[:index]
|
||||||
|
logs = logs[index:]
|
||||||
|
|
||||||
|
# - add diff
|
||||||
|
object_list.append(diff)
|
||||||
|
|
||||||
|
# - last log while diff is running
|
||||||
|
# Using of greater allow last_log to log that starts with date
|
||||||
|
# equals to diff.end
|
||||||
|
index = next((i for i, v in enumerate(logs) if v.date > diff.end),
|
||||||
|
None)
|
||||||
|
if index is not None and index > 0:
|
||||||
|
object_list.append(logs[index-1])
|
||||||
|
logs = logs[index:]
|
||||||
|
|
||||||
|
return object_list
|
||||||
|
|
||||||
|
|
||||||
def print(self):
|
def print(self):
|
||||||
r = []
|
r = []
|
||||||
if self.diffusion:
|
if self.diffusion:
|
||||||
|
@ -252,7 +303,3 @@ class Log(models.Model):
|
||||||
logger.info('log %s: %s%s', str(self), self.comment or '',
|
logger.info('log %s: %s%s', str(self), self.comment or '',
|
||||||
' (' + ', '.join(r) + ')' if r else '')
|
' (' + ', '.join(r) + ')' if r else '')
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return '#{} ({}, {}, {})'.format(
|
|
||||||
self.pk, self.get_type_display(),
|
|
||||||
self.source, self.local_date.strftime('%Y/%m/%d %H:%M%z'))
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ def program_post_save(sender, instance, created, *args, **kwargs):
|
||||||
Clean-up later diffusions when a program becomes inactive
|
Clean-up later diffusions when a program becomes inactive
|
||||||
"""
|
"""
|
||||||
if not instance.active:
|
if not instance.active:
|
||||||
Diffusion.objects.program(instance).after().delete()
|
Diffusion.objects.program(instance).after(tz.now()).delete()
|
||||||
Episode.object.program(instance).filter(diffusion__isnull=True) \
|
Episode.object.program(instance).filter(diffusion__isnull=True) \
|
||||||
.delete()
|
.delete()
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ def schedule_post_save(sender, instance, created, *args, **kwargs):
|
||||||
today = tz.datetime.today()
|
today = tz.datetime.today()
|
||||||
delta = instance.normalize(today) - initial.normalize(today)
|
delta = instance.normalize(today) - initial.normalize(today)
|
||||||
|
|
||||||
qs = Diffusion.objects.program(instance.program).after()
|
qs = Diffusion.objects.program(instance.program).after(tz.now())
|
||||||
pks = [d.pk for d in qs if initial.match(d.date)]
|
pks = [d.pk for d in qs if initial.match(d.date)]
|
||||||
qs.filter(pk__in=pks).update(
|
qs.filter(pk__in=pks).update(
|
||||||
start=F('start') + delta,
|
start=F('start') + delta,
|
||||||
|
@ -86,7 +86,7 @@ def schedule_pre_delete(sender, instance, *args, **kwargs):
|
||||||
if not instance.program.sync:
|
if not instance.program.sync:
|
||||||
return
|
return
|
||||||
|
|
||||||
qs = Diffusion.objects.program(instance.program).after()
|
qs = Diffusion.objects.program(instance.program).after(tz.now())
|
||||||
pks = [d.pk for d in qs if instance.match(d.date)]
|
pks = [d.pk for d in qs if instance.match(d.date)]
|
||||||
qs.filter(pk__in=pks).delete()
|
qs.filter(pk__in=pks).delete()
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,11 @@ class Station(models.Model):
|
||||||
_("website's urls"), max_length=512, null=True, blank=True,
|
_("website's urls"), max_length=512, null=True, blank=True,
|
||||||
help_text=_('specify one url per line')
|
help_text=_('specify one url per line')
|
||||||
)
|
)
|
||||||
|
audio_streams = models.TextField(
|
||||||
|
_("audio streams"), max_length=2048, null=True, blank=True,
|
||||||
|
help_text=_("Audio streams urls used by station's player. One url "
|
||||||
|
"a line.")
|
||||||
|
)
|
||||||
|
|
||||||
objects = StationQuerySet.as_manager()
|
objects = StationQuerySet.as_manager()
|
||||||
|
|
||||||
|
|
|
@ -7147,15 +7147,25 @@ label.panel-block {
|
||||||
background-color: #fafafa;
|
background-color: #fafafa;
|
||||||
padding: 3rem 1.5rem 6rem; }
|
padding: 3rem 1.5rem 6rem; }
|
||||||
|
|
||||||
.navbar {
|
.is-fullwidth {
|
||||||
margin-bottom: 1em; }
|
width: 100%; }
|
||||||
|
|
||||||
.navbar.has-shadow {
|
.is-fixed-bottom {
|
||||||
box-shadow: 0em 0.05em 0.5em rgba(0, 0, 0, 0.1); }
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
border-radius: 0; }
|
||||||
|
|
||||||
|
.is-borderless {
|
||||||
|
border: none; }
|
||||||
|
|
||||||
|
.navbar + .container {
|
||||||
|
margin-top: 1em; }
|
||||||
|
|
||||||
|
.navbar.has-shadow, .navbar.is-fixed-bottom.has-shadow {
|
||||||
|
box-shadow: 0em 0em 1em rgba(0, 0, 0, 0.1); }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
|
|
||||||
.navbar-brand img {
|
.navbar-brand img {
|
||||||
min-height: 6em;
|
min-height: 6em;
|
||||||
}
|
}
|
||||||
|
@ -7200,7 +7210,3 @@ aside .media .subtitle {
|
||||||
aside .media .content {
|
aside .media .content {
|
||||||
display: none; }
|
display: none; }
|
||||||
|
|
||||||
/**[noscript="hidden"] {
|
|
||||||
display: none;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
|
@ -164,7 +164,7 @@
|
||||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./js */ \"./assets/js/index.js\");\n/* harmony import */ var _styles_scss__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./styles.scss */ \"./assets/styles.scss\");\n/* harmony import */ var _styles_scss__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_styles_scss__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var _noscript_scss__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./noscript.scss */ \"./assets/noscript.scss\");\n/* harmony import */ var _noscript_scss__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_noscript_scss__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var _vue__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./vue */ \"./assets/vue/index.js\");\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./assets/index.js?");
|
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _fortawesome_fontawesome_free_css_all_min_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @fortawesome/fontawesome-free/css/all.min.css */ \"./node_modules/@fortawesome/fontawesome-free/css/all.min.css\");\n/* harmony import */ var _fortawesome_fontawesome_free_css_all_min_css__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_fortawesome_fontawesome_free_css_all_min_css__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _fortawesome_fontawesome_free_css_fontawesome_min_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @fortawesome/fontawesome-free/css/fontawesome.min.css */ \"./node_modules/@fortawesome/fontawesome-free/css/fontawesome.min.css\");\n/* harmony import */ var _fortawesome_fontawesome_free_css_fontawesome_min_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_fortawesome_fontawesome_free_css_fontawesome_min_css__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var _js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./js */ \"./assets/js/index.js\");\n/* harmony import */ var _styles_scss__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./styles.scss */ \"./assets/styles.scss\");\n/* harmony import */ var _styles_scss__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_styles_scss__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var _vue__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./vue */ \"./assets/vue/index.js\");\n\n\n\n\n\n// import './noscript.scss';\n\n\n\n\n//# sourceURL=webpack:///./assets/index.js?");
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
|
@ -176,18 +176,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _js_
|
||||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
eval("/* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! vue */ \"./node_modules/vue/dist/vue.esm.browser.js\");\n/* harmony import */ var buefy__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! buefy */ \"./node_modules/buefy/dist/buefy.js\");\n/* harmony import */ var buefy__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(buefy__WEBPACK_IMPORTED_MODULE_1__);\n\n\n\nvue__WEBPACK_IMPORTED_MODULE_0__[\"default\"].use(buefy__WEBPACK_IMPORTED_MODULE_1___default.a);\n\nwindow.addEventListener('load', () => {\n var app = new vue__WEBPACK_IMPORTED_MODULE_0__[\"default\"]({\n el: '#app',\n delimiters: [ '[[', ']]' ],\n })\n});\n\n\n\n\n\n//# sourceURL=webpack:///./assets/js/index.js?");
|
eval("/* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! vue */ \"./node_modules/vue/dist/vue.esm.browser.js\");\n/* harmony import */ var buefy__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! buefy */ \"./node_modules/buefy/dist/buefy.js\");\n/* harmony import */ var buefy__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(buefy__WEBPACK_IMPORTED_MODULE_1__);\n\n\n\nvue__WEBPACK_IMPORTED_MODULE_0__[\"default\"].use(buefy__WEBPACK_IMPORTED_MODULE_1___default.a);\n\nvar app = null;\n\nfunction loadApp() {\n app = new vue__WEBPACK_IMPORTED_MODULE_0__[\"default\"]({\n el: '#app',\n delimiters: [ '[[', ']]' ],\n })\n}\n\n\nwindow.addEventListener('load', loadApp);\n\n\n\n\n//# sourceURL=webpack:///./assets/js/index.js?");
|
||||||
|
|
||||||
/***/ }),
|
|
||||||
|
|
||||||
/***/ "./assets/noscript.scss":
|
|
||||||
/*!******************************!*\
|
|
||||||
!*** ./assets/noscript.scss ***!
|
|
||||||
\******************************/
|
|
||||||
/*! no static exports found */
|
|
||||||
/***/ (function(module, exports, __webpack_require__) {
|
|
||||||
|
|
||||||
eval("// extracted by mini-css-extract-plugin\n\n//# sourceURL=webpack:///./assets/noscript.scss?");
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
|
@ -206,11 +195,89 @@ eval("// extracted by mini-css-extract-plugin\n\n//# sourceURL=webpack:///./asse
|
||||||
/*!*****************************!*\
|
/*!*****************************!*\
|
||||||
!*** ./assets/vue/index.js ***!
|
!*** ./assets/vue/index.js ***!
|
||||||
\*****************************/
|
\*****************************/
|
||||||
/*! exports provided: Tab, Tabs */
|
/*! exports provided: Player, Tab, Tabs */
|
||||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
eval("/* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! vue */ \"./node_modules/vue/dist/vue.esm.browser.js\");\n/* harmony import */ var _tab_vue__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./tab.vue */ \"./assets/vue/tab.vue\");\n/* harmony import */ var _tabs_vue__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./tabs.vue */ \"./assets/vue/tabs.vue\");\n\n\n\n\n\nvue__WEBPACK_IMPORTED_MODULE_0__[\"default\"].component('a-tab', _tab_vue__WEBPACK_IMPORTED_MODULE_1__[/* default */ \"a\"]);\nvue__WEBPACK_IMPORTED_MODULE_0__[\"default\"].component('a-tabs', _tabs_vue__WEBPACK_IMPORTED_MODULE_2__[/* default */ \"a\"]);\n\n\n\n\n\n\n//# sourceURL=webpack:///./assets/vue/index.js?");
|
eval("/* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! vue */ \"./node_modules/vue/dist/vue.esm.browser.js\");\n/* harmony import */ var _onAir_vue__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./onAir.vue */ \"./assets/vue/onAir.vue\");\n/* harmony import */ var _player_vue__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./player.vue */ \"./assets/vue/player.vue\");\n/* harmony import */ var _tab_vue__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./tab.vue */ \"./assets/vue/tab.vue\");\n/* harmony import */ var _tabs_vue__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./tabs.vue */ \"./assets/vue/tabs.vue\");\n\n\n\n\n\n\n\nvue__WEBPACK_IMPORTED_MODULE_0__[\"default\"].component('a-on-air', _onAir_vue__WEBPACK_IMPORTED_MODULE_5__[\"default\"]);\nvue__WEBPACK_IMPORTED_MODULE_0__[\"default\"].component('a-player', _player_vue__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\nvue__WEBPACK_IMPORTED_MODULE_0__[\"default\"].component('a-tab', _tab_vue__WEBPACK_IMPORTED_MODULE_3__[/* default */ \"a\"]);\nvue__WEBPACK_IMPORTED_MODULE_0__[\"default\"].component('a-tabs', _tabs_vue__WEBPACK_IMPORTED_MODULE_4__[/* default */ \"a\"]);\n\n\n\n\n\n\n//# sourceURL=webpack:///./assets/vue/index.js?");
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ "./assets/vue/onAir.vue":
|
||||||
|
/*!******************************!*\
|
||||||
|
!*** ./assets/vue/onAir.vue ***!
|
||||||
|
\******************************/
|
||||||
|
/*! no static exports found */
|
||||||
|
/*! exports used: default */
|
||||||
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
eval("/* harmony import */ var _onAir_vue_vue_type_template_id_0971e224___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./onAir.vue?vue&type=template&id=0971e224& */ \"./assets/vue/onAir.vue?vue&type=template&id=0971e224&\");\n/* harmony import */ var _onAir_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./onAir.vue?vue&type=script&lang=js& */ \"./assets/vue/onAir.vue?vue&type=script&lang=js&\");\n/* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"./node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\n\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__[/* default */ \"a\"])(\n _onAir_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _onAir_vue_vue_type_template_id_0971e224___WEBPACK_IMPORTED_MODULE_0__[/* render */ \"a\"],\n _onAir_vue_vue_type_template_id_0971e224___WEBPACK_IMPORTED_MODULE_0__[/* staticRenderFns */ \"b\"],\n false,\n null,\n null,\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"assets/vue/onAir.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack:///./assets/vue/onAir.vue?");
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ "./assets/vue/onAir.vue?vue&type=script&lang=js&":
|
||||||
|
/*!*******************************************************!*\
|
||||||
|
!*** ./assets/vue/onAir.vue?vue&type=script&lang=js& ***!
|
||||||
|
\*******************************************************/
|
||||||
|
/*! no static exports found */
|
||||||
|
/*! exports used: default */
|
||||||
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
eval("/* harmony import */ var _node_modules_vue_loader_lib_index_js_vue_loader_options_onAir_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../node_modules/vue-loader/lib??vue-loader-options!./onAir.vue?vue&type=script&lang=js& */ \"./node_modules/vue-loader/lib/index.js?!./assets/vue/onAir.vue?vue&type=script&lang=js&\");\n /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_vue_loader_lib_index_js_vue_loader_options_onAir_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[/* default */ \"a\"]); \n\n//# sourceURL=webpack:///./assets/vue/onAir.vue?");
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ "./assets/vue/onAir.vue?vue&type=template&id=0971e224&":
|
||||||
|
/*!*************************************************************!*\
|
||||||
|
!*** ./assets/vue/onAir.vue?vue&type=template&id=0971e224& ***!
|
||||||
|
\*************************************************************/
|
||||||
|
/*! exports provided: render, staticRenderFns */
|
||||||
|
/*! exports used: render, staticRenderFns */
|
||||||
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
eval("/* harmony import */ var _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_onAir_vue_vue_type_template_id_0971e224___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../node_modules/vue-loader/lib??vue-loader-options!./onAir.vue?vue&type=template&id=0971e224& */ \"./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/vue-loader/lib/index.js?!./assets/vue/onAir.vue?vue&type=template&id=0971e224&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"a\", function() { return _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_onAir_vue_vue_type_template_id_0971e224___WEBPACK_IMPORTED_MODULE_0__[\"a\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"b\", function() { return _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_onAir_vue_vue_type_template_id_0971e224___WEBPACK_IMPORTED_MODULE_0__[\"b\"]; });\n\n\n\n//# sourceURL=webpack:///./assets/vue/onAir.vue?");
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ "./assets/vue/player.vue":
|
||||||
|
/*!*******************************!*\
|
||||||
|
!*** ./assets/vue/player.vue ***!
|
||||||
|
\*******************************/
|
||||||
|
/*! no static exports found */
|
||||||
|
/*! exports used: default */
|
||||||
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
eval("/* harmony import */ var _player_vue_vue_type_template_id_2882cef8___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./player.vue?vue&type=template&id=2882cef8& */ \"./assets/vue/player.vue?vue&type=template&id=2882cef8&\");\n/* harmony import */ var _player_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./player.vue?vue&type=script&lang=js& */ \"./assets/vue/player.vue?vue&type=script&lang=js&\");\n/* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"./node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\n\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__[/* default */ \"a\"])(\n _player_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _player_vue_vue_type_template_id_2882cef8___WEBPACK_IMPORTED_MODULE_0__[/* render */ \"a\"],\n _player_vue_vue_type_template_id_2882cef8___WEBPACK_IMPORTED_MODULE_0__[/* staticRenderFns */ \"b\"],\n false,\n null,\n null,\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"assets/vue/player.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack:///./assets/vue/player.vue?");
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ "./assets/vue/player.vue?vue&type=script&lang=js&":
|
||||||
|
/*!********************************************************!*\
|
||||||
|
!*** ./assets/vue/player.vue?vue&type=script&lang=js& ***!
|
||||||
|
\********************************************************/
|
||||||
|
/*! no static exports found */
|
||||||
|
/*! exports used: default */
|
||||||
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
eval("/* harmony import */ var _node_modules_vue_loader_lib_index_js_vue_loader_options_player_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../node_modules/vue-loader/lib??vue-loader-options!./player.vue?vue&type=script&lang=js& */ \"./node_modules/vue-loader/lib/index.js?!./assets/vue/player.vue?vue&type=script&lang=js&\");\n /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_vue_loader_lib_index_js_vue_loader_options_player_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[/* default */ \"a\"]); \n\n//# sourceURL=webpack:///./assets/vue/player.vue?");
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ "./assets/vue/player.vue?vue&type=template&id=2882cef8&":
|
||||||
|
/*!**************************************************************!*\
|
||||||
|
!*** ./assets/vue/player.vue?vue&type=template&id=2882cef8& ***!
|
||||||
|
\**************************************************************/
|
||||||
|
/*! exports provided: render, staticRenderFns */
|
||||||
|
/*! exports used: render, staticRenderFns */
|
||||||
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
eval("/* harmony import */ var _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_player_vue_vue_type_template_id_2882cef8___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../node_modules/vue-loader/lib??vue-loader-options!./player.vue?vue&type=template&id=2882cef8& */ \"./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/vue-loader/lib/index.js?!./assets/vue/player.vue?vue&type=template&id=2882cef8&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"a\", function() { return _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_player_vue_vue_type_template_id_2882cef8___WEBPACK_IMPORTED_MODULE_0__[\"a\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"b\", function() { return _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_player_vue_vue_type_template_id_2882cef8___WEBPACK_IMPORTED_MODULE_0__[\"b\"]; });\n\n\n\n//# sourceURL=webpack:///./assets/vue/player.vue?");
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
|
@ -292,6 +359,32 @@ eval("/* harmony import */ var _node_modules_vue_loader_lib_loaders_templateLoad
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ "./node_modules/vue-loader/lib/index.js?!./assets/vue/onAir.vue?vue&type=script&lang=js&":
|
||||||
|
/*!*********************************************************************************************************!*\
|
||||||
|
!*** ./node_modules/vue-loader/lib??vue-loader-options!./assets/vue/onAir.vue?vue&type=script&lang=js& ***!
|
||||||
|
\*********************************************************************************************************/
|
||||||
|
/*! exports provided: default */
|
||||||
|
/*! exports used: default */
|
||||||
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
eval("//\n//\n//\n//\n//\n//\n//\n\n\n/* harmony default export */ __webpack_exports__[\"a\"] = ({\n props: {\n url: String,\n timeout: Number,\n },\n\n data() {\n return {\n // promise is set to null on destroy: this is used as check\n // for timeout to know wether to repeat itself or not.\n promise: null,\n // on air infos\n on_air: null\n }\n },\n\n\n methods: {\n fetch() {\n const promise = fetch(url).then(response =>\n reponse.ok ? response.json\n : Promise.reject(response)\n ).then(data => {\n this.on_air = data.results;\n return this.on_air\n })\n\n this.promise = promise;\n return promise;\n },\n\n refresh() {\n const promise = this.fetch();\n promise.then(data => {\n if(promise != this.promise)\n return;\n\n window.setTimeout(() => this.update(), this.timeout*1000)\n })\n },\n },\n\n mounted() {\n this.update()\n },\n\n destroyed() {\n this.promise = null;\n }\n\n});\n\n\n//# sourceURL=webpack:///./assets/vue/onAir.vue?./node_modules/vue-loader/lib??vue-loader-options");
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ "./node_modules/vue-loader/lib/index.js?!./assets/vue/player.vue?vue&type=script&lang=js&":
|
||||||
|
/*!**********************************************************************************************************!*\
|
||||||
|
!*** ./node_modules/vue-loader/lib??vue-loader-options!./assets/vue/player.vue?vue&type=script&lang=js& ***!
|
||||||
|
\**********************************************************************************************************/
|
||||||
|
/*! exports provided: State, default */
|
||||||
|
/*! exports used: default */
|
||||||
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
eval("/* unused harmony export State */\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\nconst State = {\n paused: 0,\n playing: 1,\n loading: 2,\n}\n\n/* harmony default export */ __webpack_exports__[\"a\"] = ({\n data() {\n return {\n state: State.paused,\n }\n },\n\n props: {\n onAir: String,\n src: String,\n },\n\n computed: {\n paused() { return this.state == State.paused; },\n playing() { return this.state == State.playing; },\n loading() { return this.state == State.loading; },\n },\n\n methods: {\n load(src) {\n const audio = this.$refs.audio;\n audio.src = src;\n audio.load()\n },\n\n play(src) {\n if(src)\n this.load(src);\n this.$refs.audio.play().catch(e => console.error(e))\n },\n\n pause() {\n this.$refs.audio.pause()\n },\n\n toggle() {\n if(this.paused)\n this.play()\n else\n this.pause()\n },\n\n onChange(event) {\n const audio = this.$refs.audio;\n this.state = audio.paused ? State.paused : State.playing;\n },\n },\n\n mounted() {\n this.load(this.src);\n }\n});\n\n\n\n//# sourceURL=webpack:///./assets/vue/player.vue?./node_modules/vue-loader/lib??vue-loader-options");
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
/***/ "./node_modules/vue-loader/lib/index.js?!./assets/vue/tab.vue?vue&type=script&lang=js&":
|
/***/ "./node_modules/vue-loader/lib/index.js?!./assets/vue/tab.vue?vue&type=script&lang=js&":
|
||||||
/*!*******************************************************************************************************!*\
|
/*!*******************************************************************************************************!*\
|
||||||
!*** ./node_modules/vue-loader/lib??vue-loader-options!./assets/vue/tab.vue?vue&type=script&lang=js& ***!
|
!*** ./node_modules/vue-loader/lib??vue-loader-options!./assets/vue/tab.vue?vue&type=script&lang=js& ***!
|
||||||
|
@ -318,6 +411,32 @@ eval("//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __w
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ "./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/vue-loader/lib/index.js?!./assets/vue/onAir.vue?vue&type=template&id=0971e224&":
|
||||||
|
/*!*******************************************************************************************************************************************************************************************!*\
|
||||||
|
!*** ./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options!./assets/vue/onAir.vue?vue&type=template&id=0971e224& ***!
|
||||||
|
\*******************************************************************************************************************************************************************************************/
|
||||||
|
/*! exports provided: render, staticRenderFns */
|
||||||
|
/*! exports used: render, staticRenderFns */
|
||||||
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
eval("/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"a\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"b\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"media\" })\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./assets/vue/onAir.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options");
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ "./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/vue-loader/lib/index.js?!./assets/vue/player.vue?vue&type=template&id=2882cef8&":
|
||||||
|
/*!********************************************************************************************************************************************************************************************!*\
|
||||||
|
!*** ./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options!./assets/vue/player.vue?vue&type=template&id=2882cef8& ***!
|
||||||
|
\********************************************************************************************************************************************************************************************/
|
||||||
|
/*! exports provided: render, staticRenderFns */
|
||||||
|
/*! exports used: render, staticRenderFns */
|
||||||
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
eval("/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"a\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"b\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"media\" }, [\n _c(\"div\", { staticClass: \"media-left\" }, [\n _c(\n \"div\",\n {\n staticClass: \"button is-size-4\",\n on: {\n click: function($event) {\n return _vm.toggle()\n }\n }\n },\n [\n _vm.playing\n ? _c(\"span\", { staticClass: \"fas fa-pause\" })\n : _c(\"span\", { staticClass: \"fas fa-play\" })\n ]\n ),\n _vm._v(\" \"),\n _c(\n \"audio\",\n {\n ref: \"audio\",\n on: {\n playing: _vm.onChange,\n ended: _vm.onChange,\n pause: _vm.onChange\n }\n },\n [_vm._t(\"sources\")],\n 2\n )\n ]),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"media-content\" })\n ])\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack:///./assets/vue/player.vue?./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options");
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
/***/ "./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/vue-loader/lib/index.js?!./assets/vue/tab.vue?vue&type=template&id=65401e0e&":
|
/***/ "./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/vue-loader/lib/index.js?!./assets/vue/tab.vue?vue&type=template&id=65401e0e&":
|
||||||
/*!*****************************************************************************************************************************************************************************************!*\
|
/*!*****************************************************************************************************************************************************************************************!*\
|
||||||
!*** ./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options!./assets/vue/tab.vue?vue&type=template&id=65401e0e& ***!
|
!*** ./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options!./assets/vue/tab.vue?vue&type=template&id=65401e0e& ***!
|
||||||
|
|
|
@ -1,5 +1,27 @@
|
||||||
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["vendor"],{
|
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["vendor"],{
|
||||||
|
|
||||||
|
/***/ "./node_modules/@fortawesome/fontawesome-free/css/all.min.css":
|
||||||
|
/*!********************************************************************!*\
|
||||||
|
!*** ./node_modules/@fortawesome/fontawesome-free/css/all.min.css ***!
|
||||||
|
\********************************************************************/
|
||||||
|
/*! no static exports found */
|
||||||
|
/***/ (function(module, exports, __webpack_require__) {
|
||||||
|
|
||||||
|
eval("// extracted by mini-css-extract-plugin\n\n//# sourceURL=webpack:///./node_modules/@fortawesome/fontawesome-free/css/all.min.css?");
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ "./node_modules/@fortawesome/fontawesome-free/css/fontawesome.min.css":
|
||||||
|
/*!****************************************************************************!*\
|
||||||
|
!*** ./node_modules/@fortawesome/fontawesome-free/css/fontawesome.min.css ***!
|
||||||
|
\****************************************************************************/
|
||||||
|
/*! no static exports found */
|
||||||
|
/***/ (function(module, exports, __webpack_require__) {
|
||||||
|
|
||||||
|
eval("// extracted by mini-css-extract-plugin\n\n//# sourceURL=webpack:///./node_modules/@fortawesome/fontawesome-free/css/fontawesome.min.css?");
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
/***/ "./node_modules/buefy/dist/buefy.js":
|
/***/ "./node_modules/buefy/dist/buefy.js":
|
||||||
/*!******************************************!*\
|
/*!******************************************!*\
|
||||||
!*** ./node_modules/buefy/dist/buefy.js ***!
|
!*** ./node_modules/buefy/dist/buefy.js ***!
|
||||||
|
|
|
@ -14,6 +14,7 @@ Context:
|
||||||
|
|
||||||
{% block assets %}
|
{% block assets %}
|
||||||
<link rel="stylesheet" type="text/css" href="{% static "aircox/main.css" %}"/>
|
<link rel="stylesheet" type="text/css" href="{% static "aircox/main.css" %}"/>
|
||||||
|
<link rel="stylesheet" type="text/css" href="{% static "aircox/vendor.css" %}"/>
|
||||||
<script src="{% static "aircox/main.js" %}"></script>
|
<script src="{% static "aircox/main.js" %}"></script>
|
||||||
<script src="{% static "aircox/vendor.js" %}"></script>
|
<script src="{% static "aircox/vendor.js" %}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -80,6 +81,9 @@ Context:
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
{% include "aircox/player.html" %}
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,5 +1,17 @@
|
||||||
{% load i18n %}
|
{% load i18n aircox %}
|
||||||
|
{% comment %}
|
||||||
|
Context objects:
|
||||||
|
- object: object to render
|
||||||
|
- hide_schedule: if true, hide the schedule
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% if object|is_diffusion %}
|
||||||
|
{% with object as diffusion %}
|
||||||
|
{% with diffusion.episode as object %}
|
||||||
|
{% include "aircox/episode_item.html" %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endwith %}
|
||||||
|
{% else %}
|
||||||
{% with object.track as track %}
|
{% with object.track as track %}
|
||||||
<span class="has-text-info is-size-5">♬</span>
|
<span class="has-text-info is-size-5">♬</span>
|
||||||
<span>{{ track.title }}</span>
|
<span>{{ track.title }}</span>
|
||||||
|
@ -8,4 +20,5 @@
|
||||||
{% if track.info %}(<i>{{ track.info }}</i>){% endif %}
|
{% if track.info %}(<i>{{ track.info }}</i>){% endif %}
|
||||||
</span>
|
</span>
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
|
@ -33,25 +33,18 @@
|
||||||
<table class="table is-striped is-hoverable is-fullwidth">
|
<table class="table is-striped is-hoverable is-fullwidth">
|
||||||
{% for object in object_list reversed %}
|
{% for object in object_list reversed %}
|
||||||
<tr>
|
<tr>
|
||||||
{% if object|is_diffusion %}
|
|
||||||
<td>
|
<td>
|
||||||
|
{% if object|is_diffusion %}
|
||||||
<time datetime="{{ object.start }}" title="{{ object.start }}">
|
<time datetime="{{ object.start }}" title="{{ object.start }}">
|
||||||
{{ object.start|date:"H:i" }} - {{ object.end|date:"H:i" }}
|
{{ object.start|date:"H:i" }} - {{ object.end|date:"H:i" }}
|
||||||
</time>
|
</time>
|
||||||
</td>
|
|
||||||
{% with object as diffusion %}
|
|
||||||
{% with diffusion.episode as object %}
|
|
||||||
<td>{% include "aircox/episode_item.html" %}</td>
|
|
||||||
{% endwith %}
|
|
||||||
{% endwith %}
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<td>
|
|
||||||
<time datetime="{{ object.date }}" title="{{ object.date }}">
|
<time datetime="{{ object.date }}" title="{{ object.date }}">
|
||||||
{{ object.date|date:"H:i" }}
|
{{ object.date|date:"H:i" }}
|
||||||
</time>
|
</time>
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>{% include "aircox/log_item.html" %}</td>
|
<td>{% include "aircox/log_item.html" %}</td>
|
||||||
{% endif %}
|
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -49,8 +49,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from django.urls import path, register_converter
|
from django.urls import include, path, register_converter
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from . import views, models
|
from . import views, models
|
||||||
|
@ -17,7 +17,13 @@ register_converter(WeekConverter, 'week')
|
||||||
# ]
|
# ]
|
||||||
|
|
||||||
|
|
||||||
|
api = [
|
||||||
|
path('on-air/', views.api.OnAirAPIView.as_view(), name='on-air'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
urls = [
|
urls = [
|
||||||
|
path('api/', include(api)),
|
||||||
# path('', views.PageDetailView.as_view(model=models.Article),
|
# path('', views.PageDetailView.as_view(model=models.Article),
|
||||||
# name='home'),
|
# name='home'),
|
||||||
path(_('articles/'),
|
path(_('articles/'),
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from . import api
|
||||||
|
|
||||||
from .article import ArticleListView
|
from .article import ArticleListView
|
||||||
from .base import BaseView
|
from .base import BaseView
|
||||||
from .episode import EpisodeDetailView, EpisodeListView, TimetableView
|
from .episode import EpisodeDetailView, EpisodeListView, TimetableView
|
||||||
|
|
|
@ -28,5 +28,10 @@ class BaseView(TemplateResponseMixin, ContextMixin):
|
||||||
kwargs.setdefault('station', self.station)
|
kwargs.setdefault('station', self.station)
|
||||||
kwargs.setdefault('cover', self.cover)
|
kwargs.setdefault('cover', self.cover)
|
||||||
kwargs.setdefault('show_side_nav', self.show_side_nav)
|
kwargs.setdefault('show_side_nav', self.show_side_nav)
|
||||||
|
|
||||||
|
if not 'audio_streams' in kwargs:
|
||||||
|
streams = self.station.audio_streams
|
||||||
|
streams = streams and streams.split('\n')
|
||||||
|
kwargs['audio_streams'] = streams
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
|
@ -7,66 +7,22 @@ from ..models import Diffusion, Log
|
||||||
from .base import BaseView
|
from .base import BaseView
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['BaseLogView', 'LogListView']
|
__all__ = ['BaseLogListView', 'LogListView']
|
||||||
|
|
||||||
|
|
||||||
class BaseLogView(ListView):
|
class BaseLogListView:
|
||||||
station = None
|
|
||||||
date = None
|
date = None
|
||||||
delta = None
|
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
# only get logs for tracks: log for diffusion will be retrieved
|
# only get logs for tracks: log for diffusion will be retrieved
|
||||||
# by the diffusions' queryset.
|
# by the diffusions' queryset.
|
||||||
return super().get_queryset().station(self.station).on_air() \
|
return super().get_queryset().on_air().filter(track__isnull=False)
|
||||||
.at(self.date).filter(track__isnull=False)
|
|
||||||
|
|
||||||
def get_diffusions_queryset(self):
|
def get_diffusions_queryset(self):
|
||||||
return Diffusion.objects.station(self.station).on_air() \
|
return Diffusion.objects.station(self.station).on_air()
|
||||||
.today(self.date)
|
|
||||||
|
|
||||||
def get_object_list(self, queryset):
|
|
||||||
diffs = deque(self.get_diffusions_queryset().order_by('start'))
|
|
||||||
logs = list(queryset.order_by('date'))
|
|
||||||
if not len(diffs):
|
|
||||||
return logs
|
|
||||||
|
|
||||||
object_list = []
|
|
||||||
diff = None
|
|
||||||
last_collision = None
|
|
||||||
|
|
||||||
# TODO/FIXME: multiple diffs at once - recheck the whole algorithm in
|
|
||||||
# detail -- however I barely see cases except when there are diff
|
|
||||||
# collision or the streamer is not working
|
|
||||||
for index, log in enumerate(logs):
|
|
||||||
# get next diff
|
|
||||||
if diff is None or diff.end < log.date:
|
|
||||||
diff = diffs.popleft() if len(diffs) else None
|
|
||||||
|
|
||||||
# no more diff that can collide: return list
|
|
||||||
if diff is None:
|
|
||||||
if last_collision and not object_list or \
|
|
||||||
object_list[-1] is not last_collision:
|
|
||||||
object_list.append(last_collision)
|
|
||||||
return object_list + logs[index:]
|
|
||||||
|
|
||||||
# diff colliding with log
|
|
||||||
if diff.start <= log.date:
|
|
||||||
if not object_list or object_list[-1] is not diff:
|
|
||||||
object_list.append(diff)
|
|
||||||
if log.date <= diff.end:
|
|
||||||
last_collision = log
|
|
||||||
else:
|
|
||||||
# add last colliding log: track
|
|
||||||
if last_collision is not None:
|
|
||||||
object_list.append(last_collision)
|
|
||||||
|
|
||||||
object_list.append(log)
|
|
||||||
last_collision = None
|
|
||||||
return object_list
|
|
||||||
|
|
||||||
|
|
||||||
class LogListView(BaseView, BaseLogView):
|
class LogListView(BaseView, BaseLogListView, ListView):
|
||||||
model = Log
|
model = Log
|
||||||
|
|
||||||
date = None
|
date = None
|
||||||
|
@ -80,6 +36,14 @@ class LogListView(BaseView, BaseLogView):
|
||||||
if 'date' in self.kwargs else today
|
if 'date' in self.kwargs else today
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
# only get logs for tracks: log for diffusion will be retrieved
|
||||||
|
# by the diffusions' queryset.
|
||||||
|
return super().get_queryset().today(self.date)
|
||||||
|
|
||||||
|
def get_diffusions_queryset(self):
|
||||||
|
return super().get_diffusions_queryset().today(self.date)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
today = datetime.date.today()
|
today = datetime.date.today()
|
||||||
max_date = min(max(self.date + datetime.timedelta(days=3),
|
max_date = min(max(self.date + datetime.timedelta(days=3),
|
||||||
|
@ -91,6 +55,9 @@ class LogListView(BaseView, BaseLogView):
|
||||||
dates=(date for date in (
|
dates=(date for date in (
|
||||||
max_date - datetime.timedelta(days=i)
|
max_date - datetime.timedelta(days=i)
|
||||||
for i in range(0, 7)) if date >= self.min_date),
|
for i in range(0, 7)) if date >= self.min_date),
|
||||||
object_list=self.get_object_list(self.object_list),
|
object_list=Log.merge_diffusions(self.object_list,
|
||||||
|
self.get_diffusions_queryset()),
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
import '@fortawesome/fontawesome-free/css/all.min.css';
|
||||||
|
import '@fortawesome/fontawesome-free/css/fontawesome.min.css';
|
||||||
|
|
||||||
import './js';
|
import './js';
|
||||||
import './styles.scss';
|
import './styles.scss';
|
||||||
import './noscript.scss';
|
// import './noscript.scss';
|
||||||
import './vue';
|
import './vue';
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,16 @@ import Buefy from 'buefy';
|
||||||
|
|
||||||
Vue.use(Buefy);
|
Vue.use(Buefy);
|
||||||
|
|
||||||
window.addEventListener('load', () => {
|
var app = null;
|
||||||
var app = new Vue({
|
|
||||||
|
function loadApp() {
|
||||||
|
app = new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
delimiters: [ '[[', ']]' ],
|
delimiters: [ '[[', ']]' ],
|
||||||
})
|
})
|
||||||
});
|
}
|
||||||
|
|
||||||
|
|
||||||
|
window.addEventListener('load', loadApp);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,17 +5,25 @@ $body-background-color: $light;
|
||||||
|
|
||||||
@import "~bulma/bulma";
|
@import "~bulma/bulma";
|
||||||
|
|
||||||
.navbar {
|
.is-fullwidth { width: 100%; }
|
||||||
margin-bottom: 1em;
|
.is-fixed-bottom {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
.is-borderless { border: none; }
|
||||||
|
|
||||||
|
|
||||||
|
.navbar + .container {
|
||||||
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar.has-shadow {
|
.navbar.has-shadow, .navbar.is-fixed-bottom.has-shadow {
|
||||||
box-shadow: 0em 0.05em 0.5em rgba(0,0,0,0.1);
|
box-shadow: 0em 0em 1em rgba(0,0,0,0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
|
|
||||||
.navbar-brand img {
|
.navbar-brand img {
|
||||||
min-height: 6em;
|
min-height: 6em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
import OnAir from './onAir.vue';
|
||||||
|
import Player from './player.vue';
|
||||||
import Tab from './tab.vue';
|
import Tab from './tab.vue';
|
||||||
import Tabs from './tabs.vue';
|
import Tabs from './tabs.vue';
|
||||||
|
|
||||||
|
Vue.component('a-on-air', OnAir);
|
||||||
|
Vue.component('a-player', Player);
|
||||||
Vue.component('a-tab', Tab);
|
Vue.component('a-tab', Tab);
|
||||||
Vue.component('a-tabs', Tabs);
|
Vue.component('a-tabs', Tabs);
|
||||||
|
|
||||||
export {Tab, Tabs};
|
export {Player, Tab, Tabs};
|
||||||
|
|
||||||
|
|
||||||
|
|
63
assets/vue/onAir.vue
Normal file
63
assets/vue/onAir.vue
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<template>
|
||||||
|
<div class="media">
|
||||||
|
<!-- TODO HERE -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
url: String,
|
||||||
|
timeout: Number,
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// promise is set to null on destroy: this is used as check
|
||||||
|
// for timeout to know wether to repeat itself or not.
|
||||||
|
promise: null,
|
||||||
|
// on air infos
|
||||||
|
on_air: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
fetch() {
|
||||||
|
const promise = fetch(url).then(response =>
|
||||||
|
reponse.ok ? response.json
|
||||||
|
: Promise.reject(response)
|
||||||
|
).then(data => {
|
||||||
|
this.on_air = data.results;
|
||||||
|
return this.on_air
|
||||||
|
})
|
||||||
|
|
||||||
|
this.promise = promise;
|
||||||
|
return promise;
|
||||||
|
},
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
const promise = this.fetch();
|
||||||
|
promise.then(data => {
|
||||||
|
if(promise != this.promise)
|
||||||
|
return;
|
||||||
|
|
||||||
|
window.setTimeout(() => this.update(), this.timeout*1000)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.update()
|
||||||
|
},
|
||||||
|
|
||||||
|
destroyed() {
|
||||||
|
this.promise = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
82
assets/vue/player.vue
Normal file
82
assets/vue/player.vue
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
<template>
|
||||||
|
<div class="media">
|
||||||
|
<div class="media-left">
|
||||||
|
<div class="button is-size-4" @click="toggle()">
|
||||||
|
<span class="fas fa-pause" v-if="playing"></span>
|
||||||
|
<span class="fas fa-play" v-else></span>
|
||||||
|
</div>
|
||||||
|
<audio ref="audio" @playing="onChange" @ended="onChange" @pause="onChange">
|
||||||
|
<slot name="sources"></slot>
|
||||||
|
</audio>
|
||||||
|
</div>
|
||||||
|
<div class="media-content">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export const State = {
|
||||||
|
paused: 0,
|
||||||
|
playing: 1,
|
||||||
|
loading: 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
state: State.paused,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
onAir: String,
|
||||||
|
src: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
paused() { return this.state == State.paused; },
|
||||||
|
playing() { return this.state == State.playing; },
|
||||||
|
loading() { return this.state == State.loading; },
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
load(src) {
|
||||||
|
const audio = this.$refs.audio;
|
||||||
|
audio.src = src;
|
||||||
|
audio.load()
|
||||||
|
},
|
||||||
|
|
||||||
|
play(src) {
|
||||||
|
if(src)
|
||||||
|
this.load(src);
|
||||||
|
this.$refs.audio.play().catch(e => console.error(e))
|
||||||
|
},
|
||||||
|
|
||||||
|
pause() {
|
||||||
|
this.$refs.audio.pause()
|
||||||
|
},
|
||||||
|
|
||||||
|
toggle() {
|
||||||
|
if(this.paused)
|
||||||
|
this.play()
|
||||||
|
else
|
||||||
|
this.pause()
|
||||||
|
},
|
||||||
|
|
||||||
|
onChange(event) {
|
||||||
|
const audio = this.$refs.audio;
|
||||||
|
this.state = audio.paused ? State.paused : State.playing;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.load(this.src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
11
notes.md
11
notes.md
|
@ -1,5 +1,16 @@
|
||||||
This file is used as a reminder, can be used as crappy documentation too.
|
This file is used as a reminder, can be used as crappy documentation too.
|
||||||
|
|
||||||
|
- player
|
||||||
|
- monitor interface
|
||||||
|
- statistics interface
|
||||||
|
- traduction
|
||||||
|
- hot reload
|
||||||
|
|
||||||
|
Améliorations:
|
||||||
|
- calendar dashboard
|
||||||
|
- accessibilité
|
||||||
|
- player: playlist
|
||||||
|
|
||||||
|
|
||||||
# for the 1.0
|
# for the 1.0
|
||||||
- logs:
|
- logs:
|
||||||
|
|
|
@ -56,13 +56,12 @@ module.exports = (env, argv) => Object({
|
||||||
sideEffects: false
|
sideEffects: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.scss$/,
|
test: /\.s?css$/,
|
||||||
use: [ { loader: MiniCssExtractPlugin.loader },
|
use: [ { loader: MiniCssExtractPlugin.loader },
|
||||||
{ loader: 'css-loader' },
|
{ loader: 'css-loader' },
|
||||||
{ loader: 'sass-loader' , options: { sourceMap: true }} ],
|
{ loader: 'sass-loader' , options: { sourceMap: true }} ],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// TODO: remove ttf eot svg
|
|
||||||
test: /\.(ttf|eot|svg|woff2?)$/,
|
test: /\.(ttf|eot|svg|woff2?)$/,
|
||||||
use: [{
|
use: [{
|
||||||
loader: 'file-loader',
|
loader: 'file-loader',
|
||||||
|
|
Loading…
Reference in New Issue
Block a user