make stream and restreamconfig listviews reactive with vuejs against restapi
This commit is contained in:
parent
2f5c5b20c5
commit
9b231f6859
|
@ -2,6 +2,8 @@
|
|||
static/css/font-awesome.min.css
|
||||
static/js/bootstrap.bundle.min.js
|
||||
static/js/jquery.min.js
|
||||
static/js/vue.min.js
|
||||
static/js/axios.min.js
|
||||
static/fonts
|
||||
|
||||
staticfiles
|
||||
|
|
15
Dockerfile
15
Dockerfile
|
@ -5,11 +5,6 @@ WORKDIR /app
|
|||
ENV PYTHONDONTWRITEBYTECODE 1
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
|
||||
# https://github.com/twbs/bootstrap/issues/30553 don't upgrade jquery to 3.5.0 yet
|
||||
ENV JQUERY_VERSION=3.4.1
|
||||
ENV BOOTSTRAP_VERSION=4.4.1
|
||||
ENV INTER_VERSION=3.13
|
||||
|
||||
# install required packages
|
||||
RUN apk add --no-cache postgresql-dev gcc python3-dev musl-dev gettext postgresql-client nginx supervisor
|
||||
|
||||
|
@ -28,14 +23,8 @@ RUN addgroup -S portier && adduser -S portier -G portier
|
|||
ADD --chown=portier:portier . /app
|
||||
|
||||
# add static external libraries for frontend
|
||||
RUN wget http://code.jquery.com/jquery-${JQUERY_VERSION}.min.js -O /app/static/js/jquery.min.js \
|
||||
&& wget https://stackpath.bootstrapcdn.com/bootstrap/${BOOTSTRAP_VERSION}/js/bootstrap.bundle.min.js -O /app/static/js/bootstrap.bundle.min.js \
|
||||
&& mkdir -p /tmp/inter /app/static/fonts \
|
||||
&& cd /tmp/inter && wget https://github.com/rsms/inter/releases/download/v${INTER_VERSION}/Inter-${INTER_VERSION}.zip \
|
||||
&& unzip Inter-${INTER_VERSION}.zip && mv /tmp/inter/Inter\ Web/* /app/static/fonts/ \
|
||||
&& cd - \
|
||||
&& rm -rf /tmp/inter \
|
||||
&& chown -R portier:portier /app/static/fonts/
|
||||
RUN ./fetch_frontend_libs.sh \
|
||||
&& chown -R portier:portier static/
|
||||
|
||||
# collect static files and compile localized strings
|
||||
RUN ./manage.py collectstatic --noinput --link
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
#!/bin/sh
|
||||
set -ex
|
||||
|
||||
JQUERY_VERSION=3.5.1
|
||||
BOOTSTRAP_VERSION=4.4.1
|
||||
INTER_VERSION=3.13
|
||||
VUE_VERSION=2.6.11
|
||||
AXIOS_VERSION=0.19.2
|
||||
|
||||
wget https://cdn.jsdelivr.net/npm/jquery@${JQUERY_VERSION}/dist/jquery.min.js -O static/js/jquery.min.js
|
||||
wget https://cdn.jsdelivr.net/npm/vue@${VUE_VERSION}/dist/vue.min.js -O static/js/vue.min.js
|
||||
wget https://cdn.jsdelivr.net/npm/axios@${AXIOS_VERSION}/dist/axios.min.js -O static/js/axios.min.js
|
||||
wget https://cdn.jsdelivr.net/npm/bootstrap@${BOOTSTRAP_VERSION}/dist/js/bootstrap.bundle.min.js -O static/js/bootstrap.bundle.min.js
|
||||
mkdir -p /tmp/inter static/fonts
|
||||
cd /tmp/inter && wget https://github.com/rsms/inter/releases/download/v${INTER_VERSION}/Inter-${INTER_VERSION}.zip
|
||||
unzip Inter-${INTER_VERSION}.zip
|
||||
cd -
|
||||
mv /tmp/inter/Inter\ Web/* static/fonts/
|
||||
rm -rf /tmp/inter
|
||||
|
|
@ -1,11 +1,37 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load fontawesome_5 %}
|
||||
|
||||
{% block 'content' %}
|
||||
<div class="jumbotron">
|
||||
<h1 class="display-4">Hello, world!</h1>
|
||||
<p class="leader">Nothing to see here yet. We're working on it, though!</p>
|
||||
<h1 class="display-4">Streaming Magic!</h1>
|
||||
<hr class="my-4">
|
||||
<p>Portier will be a pretty cool streaming platform, built by and for nerds. You are welcome to contribute, be it code, ideas, documentation, design or something else entirely.</p>
|
||||
<a class="btn btn-primary btn-lg" href="https://github.com/chaoswest-tv/portier" role="button">Code on GitHub</a>
|
||||
<p>Portier is a pretty cool streaming platform, built by and for nerds. You are welcome to contribute, be it code, ideas, documentation, design or something else entirely.</p>
|
||||
<a class="btn btn-primary btn" href="https://github.com/chaoswest-tv/portier" role="button">Code on GitHub</a>
|
||||
</div>
|
||||
<div class="card-deck">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h1 class="card-title text-center">{% fa5_icon 'check-double' %}</h1>
|
||||
<h4 class="card-subtitle text-center mb-2">Redundancy</h4>
|
||||
<hr class="mb-3">
|
||||
<p class="card-text">Send us multiple copies of your stream over different network connections. If one of your connections fails, we'll automatically fail over to the next available - within seconds!</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h1 class="card-title">{% fa5_icon 'code-branch' %}</h1>
|
||||
<h4 class="card-subtitle mb-2">Restreaming</h4>
|
||||
<hr class="mb-3">
|
||||
<p class="card-text">Any stream that's coming into our system can be restreamed to a new target. This works with most of the popular streaming platforms - even multiple at the same time.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h1 class="card-title text-center">{% fa5_icon 'hands-helping' %}</h1>
|
||||
<h4 class="card-subtitle text-center mb-2">Awesome Support</h4>
|
||||
<hr class="mb-3">
|
||||
<p class="card-text">Streaming is hard. Especially if you're not a techie. We have streaming-ninjas in our team that will happily support you every step of the way.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -20,7 +20,8 @@ class ApplicationViewSet(viewsets.ReadOnlyModelViewSet):
|
|||
class StreamSerializer(ObjectPermissionsAssignmentMixin, serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Stream
|
||||
fields = ['id', 'stream', 'name', 'application']
|
||||
fields = '__all__'
|
||||
read_only_fields = ['publish_counter']
|
||||
|
||||
def get_permissions_map(self, created):
|
||||
current_user = self.context['request'].user
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load bootstrap4 %}
|
||||
{% load fontawesome_5 %}
|
||||
{% load guardian_tags %}
|
||||
|
||||
{% block 'content' %}
|
||||
|
||||
|
@ -19,7 +19,15 @@
|
|||
</div>
|
||||
</div>
|
||||
<hr class="my-4">
|
||||
<table class="table">
|
||||
<div id="app">
|
||||
<div v-if="isLoading">
|
||||
<div class="my-4 text-center">
|
||||
<div class="spinner-border" role="status">
|
||||
<span class="sr-only">{% trans "loading..." %}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<table v-else class="table">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th scope="col">{% trans "name" %}</th>
|
||||
|
@ -28,27 +36,28 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for object in object_list %}
|
||||
{% get_obj_perms user for object as "obj_perms" %}
|
||||
{% if "view_restreamconfig" in obj_perms %}
|
||||
<tr>
|
||||
<th scope="row">{{ object.name }}</th>
|
||||
<td>
|
||||
{% if object.active %}
|
||||
<span class="text-success">{% fa5_icon 'check-circle' %}</span>
|
||||
{% else %}
|
||||
<span class="text-danger">{% fa5_icon 'times-circle' %}</span>
|
||||
{% endif %}
|
||||
<tr v-for="cfg in cfgs">
|
||||
<th scope="row">{% verbatim %}{{cfg.name}}{% endverbatim %}</th>
|
||||
<td v-if="cfg.active">
|
||||
<button v-on:click="toggleActive(cfg)" type="button" class="btn btn-outline-success">
|
||||
{% fa5_icon 'check-circle' %}
|
||||
</button>
|
||||
</td>
|
||||
<td v-else>
|
||||
<button v-on:click="toggleActive(cfg)" type="button" class="btn btn-outline-danger">
|
||||
{% fa5_icon 'times-circle' %}
|
||||
</button>
|
||||
</td>
|
||||
<td align="right">
|
||||
<a href="{% url 'restream:restreamconfig_detail' pk=object.pk %}" type="button" class="btn btn-sm btn-outline-primary">{% trans "details" %}</a>
|
||||
{% if "delete_restreamconfig" in obj_perms %}
|
||||
<a href="{% url 'restream:restreamconfig_delete' pk=object.pk %}" type="button" class="btn btn-sm btn-outline-danger">{% fa5_icon 'trash' %}</a>
|
||||
{% endif %}
|
||||
<a v-bind:href="detailLink(cfg.id)" type="button" class="btn btn-sm btn-outline-primary">{% trans "details" %}</a>
|
||||
<a v-bind:href="deleteLink(cfg.id)" type="button" class="btn btn-sm btn-outline-danger">{% fa5_icon 'trash' %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script src="{% static 'js/vue.min.js' %}"></script>
|
||||
<script src="{% static 'js/axios.min.js' %}"></script>
|
||||
<script src="{% static 'js/restreamconfig-list.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from django.urls import reverse_lazy
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.generic import ListView, DetailView, CreateView, DeleteView, UpdateView
|
||||
from guardian.decorators import permission_required_or_403
|
||||
|
@ -12,6 +13,7 @@ from . import forms
|
|||
@method_decorator(login_required, name='dispatch')
|
||||
@method_decorator(permission_required_or_403('restream.add_restreamconfig'),
|
||||
name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class RestreamConfigList(ListView):
|
||||
model = models.RestreamConfig
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load bootstrap4 %}
|
||||
{% load fontawesome_5 %}
|
||||
{% load guardian_tags %}
|
||||
|
@ -18,7 +19,15 @@
|
|||
</div>
|
||||
</div>
|
||||
<hr class="my-4">
|
||||
<table class="table">
|
||||
<div id="app">
|
||||
<div v-if="isLoading">
|
||||
<div class="my-4 text-center">
|
||||
<div class="spinner-border" role="status">
|
||||
<span class="sr-only">{% trans "loading..." %}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<table v-else class="table">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th scope="col">{% trans "name" %}</th>
|
||||
|
@ -27,27 +36,22 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for object in object_list %}
|
||||
{% get_obj_perms user for object as "obj_perms" %}
|
||||
{% if "view_stream" in obj_perms %}
|
||||
<tr>
|
||||
<th scope="ro">{{ object.name }}</th>
|
||||
<tr v-for="stream in streams">
|
||||
<th scope="row">{% verbatim %}{{ stream.name }}{% endverbatim %}</th>
|
||||
<td>
|
||||
{% if object.publish_counter > 0 %}
|
||||
<span class="text-success">{% fa5_icon 'check-circle' %}</span>
|
||||
{% else %}
|
||||
<span class="text-danger">{% fa5_icon 'times-circle' %}</span>
|
||||
{% endif %}
|
||||
<span v-if="isPublishing(stream)" class="text-success">{% fa5_icon 'check-circle' %} <span class="badge badge-success">{% verbatim %}{{ stream.publish_counter}}{% endverbatim %}</span></span>
|
||||
<span v-else class="text-danger">{% fa5_icon 'times-circle' %}</span>
|
||||
</td>
|
||||
<td align="right">
|
||||
<a href="{% url 'rtmp:stream_detail' pk=object.pk %}" type="button" class="btn btn-sm btn-outline-primary ">{% trans "details" %}</a>
|
||||
{% if "delete_stream" in obj_perms %}
|
||||
<a href="{% url 'rtmp:stream_delete' pk=object.pk %}" type="button" class="btn btn-sm btn-outline-danger">{% fa5_icon 'trash' %}</a>
|
||||
{% endif %}
|
||||
<a v-bind:href="detailLink(stream.id)" type="button" class="btn btn-sm btn-outline-primary">{% trans "details" %}</a>
|
||||
<a v-bind:href="deleteLink(stream.id)" type="button" class="btn btn-sm btn-outline-danger">{% fa5_icon 'trash' %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script src="{% static 'js/vue.min.js' %}"></script>
|
||||
<script src="{% static 'js/axios.min.js' %}"></script>
|
||||
<script src="{% static 'js/stream-list.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
var app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
cfgs: [],
|
||||
isLoading: true
|
||||
},
|
||||
methods: {
|
||||
detailLink(id) {
|
||||
return `${id}/`
|
||||
},
|
||||
deleteLink(id) {
|
||||
return `${id}/delete`
|
||||
},
|
||||
toggleActive(cfg) {
|
||||
axios
|
||||
.patch('/api/v1/restreamconfigs/' + cfg.id + '/', { active: !cfg.active })
|
||||
.then(response => {
|
||||
i = this.cfgs.findIndex((obj => obj.id == cfg.id))
|
||||
Vue.set(this.cfgs, i, response.data)
|
||||
}
|
||||
)
|
||||
},
|
||||
fetchData() {
|
||||
axios
|
||||
.get('/api/v1/restreamconfigs/')
|
||||
.then(response => {
|
||||
this.cfgs = response.data
|
||||
this.isLoading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
axios.defaults.xsrfCookieName = 'csrftoken'
|
||||
axios.defaults.xsrfHeaderName = 'X-CSRFTOKEN'
|
||||
this.fetchData()
|
||||
}
|
||||
})
|
|
@ -0,0 +1,34 @@
|
|||
var app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
streams: [],
|
||||
isLoading: true
|
||||
},
|
||||
methods: {
|
||||
isPublishing(stream) {
|
||||
return stream.publish_counter > 0
|
||||
},
|
||||
detailLink(id) {
|
||||
return `${id}/`
|
||||
},
|
||||
deleteLink(id) {
|
||||
return `${id}/delete`
|
||||
},
|
||||
fetchData() {
|
||||
axios
|
||||
.get('/api/v1/streams/')
|
||||
.then(response => {
|
||||
this.streams = response.data
|
||||
this.isLoading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
axios.defaults.xsrfCookieName = 'csrftoken'
|
||||
axios.defaults.xsrfHeaderName = 'X-CSRFTOKEN'
|
||||
this.fetchData()
|
||||
setInterval(function () {
|
||||
this.fetchData();
|
||||
}.bind(this), 5000);
|
||||
}
|
||||
})
|
Loading…
Reference in New Issue