mirror of
https://github.com/fastogt/fastocloud_admin.git
synced 2025-03-09 23:38:52 +00:00
Subscribers (#4)
* Subscribers start impl * Send subscribers to service * Add subscribers * Review * User agent
This commit is contained in:
parent
197173dd66
commit
df32bb51a7
22 changed files with 388 additions and 28 deletions
|
@ -1,3 +1,8 @@
|
|||
1.2.0 /
|
||||
[Alexandr Topilski]
|
||||
- User agents
|
||||
- Subscribers
|
||||
|
||||
1.1.0 / June 6, 2019
|
||||
[Alexandr Topilski]
|
||||
- User type
|
||||
|
|
|
@ -44,6 +44,7 @@ class Client:
|
|||
VODS_IN_DIRECTORY = 'vods_in_directory'
|
||||
VODS_DIRECTORY = 'vods_directory'
|
||||
STREAMS = 'streams'
|
||||
SUBSCRIBERS = 'subscribers'
|
||||
STREAM_ID = 'id'
|
||||
LICENSE_KEY = 'license_key'
|
||||
PATH = 'path'
|
||||
|
@ -126,8 +127,8 @@ class Client:
|
|||
self._send_request(command_id, Commands.PREPARE_SERVICE_COMMAND, command_args)
|
||||
|
||||
@is_active_decorator
|
||||
def sync_service(self, command_id: int, streams: list):
|
||||
command_args = {Client.STREAMS: streams}
|
||||
def sync_service(self, command_id: int, streams: list, subscribers: list):
|
||||
command_args = {Client.STREAMS: streams, Client.SUBSCRIBERS: subscribers}
|
||||
self._send_request(command_id, Commands.SYNC_SERVICE_COMMAND, command_args)
|
||||
|
||||
@is_active_decorator
|
||||
|
|
|
@ -19,7 +19,7 @@ class Url(EmbeddedDocument):
|
|||
|
||||
|
||||
class InputUrl(Url):
|
||||
pass
|
||||
user_agent = IntField(default=constants.UserAgent.GSTREAMER, required=True)
|
||||
|
||||
|
||||
class OutputUrl(Url):
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from wtforms import Form
|
||||
from flask_babel import lazy_gettext
|
||||
from wtforms.fields import StringField, FieldList, IntegerField, FormField, FloatField
|
||||
from wtforms.fields import StringField, FieldList, IntegerField, FormField, FloatField, SelectField
|
||||
from wtforms.validators import InputRequired, Length, NumberRange
|
||||
|
||||
import app.constants as constants
|
||||
|
@ -16,7 +16,9 @@ class UrlForm(Form):
|
|||
|
||||
|
||||
class InputUrlForm(UrlForm):
|
||||
pass
|
||||
user_agent = SelectField(lazy_gettext(u'User agent:'),
|
||||
validators=[InputRequired()],
|
||||
choices=constants.AVAILABLE_USER_AGENTS, coerce=constants.UserAgent.coerce)
|
||||
|
||||
|
||||
class InputUrlsForm(Form):
|
||||
|
@ -25,7 +27,7 @@ class InputUrlsForm(Form):
|
|||
def get_data(self) -> InputUrls:
|
||||
urls = InputUrls()
|
||||
for url in self.data['urls']:
|
||||
urls.urls.append(InputUrl(url['id'], url['uri']))
|
||||
urls.urls.append(InputUrl(url['id'], url['uri'], url['user_agent']))
|
||||
|
||||
return urls
|
||||
|
||||
|
|
|
@ -3,4 +3,4 @@ PUBLIC_CONFIG = {'site': {'title': 'FastoCloud', 'keywords': 'video,cloud,iptv,s
|
|||
'support': {'contact_email': 'support@fastogt.com',
|
||||
'contact_address': 'Republic of Belarus, Minsk, Stadionnay str. 5',
|
||||
'community_channel': 'https://discord.gg/cnUXsws'},
|
||||
'project': {'version': '1.1.0', 'version_type': 'release'}}
|
||||
'project': {'version': '1.2.0', 'version_type': 'release'}}
|
||||
|
|
|
@ -195,3 +195,22 @@ class Roles(IntEnum):
|
|||
|
||||
def __str__(self):
|
||||
return str(self.value)
|
||||
|
||||
|
||||
class UserAgent(IntEnum):
|
||||
GSTREAMER = 0
|
||||
VLC = 1
|
||||
|
||||
@classmethod
|
||||
def choices(cls):
|
||||
return [(choice, choice.name) for choice in cls]
|
||||
|
||||
@classmethod
|
||||
def coerce(cls, item):
|
||||
return cls(int(item)) if not isinstance(item, cls) else item
|
||||
|
||||
def __str__(self):
|
||||
return str(self.value)
|
||||
|
||||
|
||||
AVAILABLE_USER_AGENTS = [(UserAgent.GSTREAMER, 'GStreamer'), (UserAgent.VLC, 'VLC'), ]
|
||||
|
|
|
@ -13,6 +13,8 @@ class ServiceSettingsForm(FlaskForm):
|
|||
host = FormField(HostAndPortForm, lazy_gettext(u'Host:'), validators=[])
|
||||
http_host = FormField(HostAndPortForm, lazy_gettext(u'Http host:'), validators=[])
|
||||
vods_host = FormField(HostAndPortForm, lazy_gettext(u'Vods host:'), validators=[])
|
||||
subscribers_host = FormField(HostAndPortForm, lazy_gettext(u'Subscribers host:'), validators=[])
|
||||
bandwidth_host = FormField(HostAndPortForm, lazy_gettext(u'Bandwidth host:'), validators=[])
|
||||
|
||||
feedback_directory = StringField(lazy_gettext(u'Feedback directory:'), validators=[InputRequired()])
|
||||
timeshifts_directory = StringField(lazy_gettext(u'Timeshifts directory:'), validators=[InputRequired()])
|
||||
|
@ -32,6 +34,8 @@ class ServiceSettingsForm(FlaskForm):
|
|||
settings.host = self.host.get_data()
|
||||
settings.http_host = self.http_host.get_data()
|
||||
settings.vods_host = self.vods_host.get_data()
|
||||
settings.subscribers_host = self.subscribers_host.get_data()
|
||||
settings.bandwidth_host = self.bandwidth_host.get_data()
|
||||
|
||||
settings.feedback_directory = self.feedback_directory.data
|
||||
settings.timeshifts_directory = self.timeshifts_directory.data
|
||||
|
|
|
@ -24,6 +24,10 @@ class ServerSettings:
|
|||
DEFAULT_SERVICE_HTTP_PORT = 8000
|
||||
DEFAULT_SERVICE_VODS_HOST = 'localhost'
|
||||
DEFAULT_SERVICE_VODS_PORT = 7000
|
||||
DEFAULT_SERVICE_SUBSCRIBERS_HOST = 'localhost'
|
||||
DEFAULT_SERVICE_SUBSCRIBERS_PORT = 6000
|
||||
DEFAULT_SERVICE_BANDWIDTH_HOST = 'localhost'
|
||||
DEFAULT_SERVICE_BANDWIDTH_PORT = 5000
|
||||
|
||||
name = StringField(unique=True, default=DEFAULT_SERVICE_NAME, max_length=MAX_SERVICE_NAME_LENGTH,
|
||||
min_length=MIN_SERVICE_NAME_LENGTH)
|
||||
|
@ -32,6 +36,10 @@ class ServerSettings:
|
|||
port=DEFAULT_SERVICE_HTTP_PORT))
|
||||
vods_host = EmbeddedDocumentField(HostAndPort, default=HostAndPort(host=DEFAULT_SERVICE_VODS_HOST,
|
||||
port=DEFAULT_SERVICE_VODS_PORT))
|
||||
subscribers_host = EmbeddedDocumentField(HostAndPort, default=HostAndPort(host=DEFAULT_SERVICE_SUBSCRIBERS_HOST,
|
||||
port=DEFAULT_SERVICE_SUBSCRIBERS_PORT))
|
||||
bandwidth_host = EmbeddedDocumentField(HostAndPort, default=HostAndPort(host=DEFAULT_SERVICE_BANDWIDTH_HOST,
|
||||
port=DEFAULT_SERVICE_BANDWIDTH_PORT))
|
||||
|
||||
feedback_directory = StringField(default=DEFAULT_FEEDBACK_DIR_PATH)
|
||||
timeshifts_directory = StringField(default=DEFAULT_TIMESHIFTS_DIR_PATH)
|
||||
|
|
|
@ -54,7 +54,7 @@ class Service(IStreamHandler):
|
|||
self._settings = settings
|
||||
self.__reload_from_db()
|
||||
# other fields
|
||||
self._client = ServiceClient(self, settings)
|
||||
self._client = ServiceClient(settings.id, self, settings)
|
||||
self._host = host
|
||||
self._port = port
|
||||
self._socketio = socketio
|
||||
|
@ -69,7 +69,7 @@ class Service(IStreamHandler):
|
|||
return self._client.stop_service(delay)
|
||||
|
||||
def get_log_service(self):
|
||||
return self._client.get_log_service(self._host, self._port, self.id)
|
||||
return self._client.get_log_service(self._host, self._port)
|
||||
|
||||
def ping(self):
|
||||
return self._client.ping_service()
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from bson.objectid import ObjectId
|
||||
|
||||
from app.client.client import Client
|
||||
from app.client.client_handler import IClientHandler
|
||||
from app.client.json_rpc import Request, Response
|
||||
|
@ -11,6 +13,8 @@ import app.constants as constants
|
|||
class ServiceClient(IClientHandler):
|
||||
HTTP_HOST = 'http_host'
|
||||
VODS_HOST = 'vods_host'
|
||||
SUBSCRIBERS_HOST = 'subscribers_host'
|
||||
BANDWIDTH_HOST = 'bandwidth_host'
|
||||
VERSION = 'version'
|
||||
|
||||
@staticmethod
|
||||
|
@ -25,7 +29,8 @@ class ServiceClient(IClientHandler):
|
|||
def get_pipeline_stream_path(host: str, port: int, stream_id: str):
|
||||
return constants.DEFAULT_STREAM_PIPELINE_PATH_TEMPLATE_3SIS.format(host, port, stream_id)
|
||||
|
||||
def __init__(self, handler: IStreamHandler, settings: ServiceSettings):
|
||||
def __init__(self, sid: ObjectId, handler: IStreamHandler, settings: ServiceSettings):
|
||||
self.id = sid
|
||||
self._request_id = 0
|
||||
self._handler = handler
|
||||
self._service_settings = settings
|
||||
|
@ -50,8 +55,9 @@ class ServiceClient(IClientHandler):
|
|||
def stop_service(self, delay: int):
|
||||
return self._client.stop_service(self._gen_request_id(), delay)
|
||||
|
||||
def get_log_service(self, host: str, port: int, sid: str):
|
||||
return self._client.get_log_service(self._gen_request_id(), ServiceClient.get_log_service_path(host, port, sid))
|
||||
def get_log_service(self, host: str, port: int):
|
||||
return self._client.get_log_service(self._gen_request_id(),
|
||||
ServiceClient.get_log_service_path(host, port, str(self.id)))
|
||||
|
||||
def start_stream(self, config: dict):
|
||||
return self._client.start_stream(self._gen_request_id(), config)
|
||||
|
@ -74,7 +80,12 @@ class ServiceClient(IClientHandler):
|
|||
streams = []
|
||||
for stream in self._service_settings.streams:
|
||||
streams.append(stream.config())
|
||||
return self._client.sync_service(self._gen_request_id(), streams)
|
||||
|
||||
subscribers = []
|
||||
for subs in self._service_settings.subscribers:
|
||||
subscribers.append(subs.to_service(self.id))
|
||||
|
||||
return self._client.sync_service(self._gen_request_id(), streams, subscribers)
|
||||
|
||||
def get_http_host(self) -> str:
|
||||
return self._http_host
|
||||
|
@ -82,6 +93,12 @@ class ServiceClient(IClientHandler):
|
|||
def get_vods_host(self) -> str:
|
||||
return self._vods_host
|
||||
|
||||
def get_subscribers_host(self) -> str:
|
||||
return self._subscribers_host
|
||||
|
||||
def get_bandwidth_host(self) -> str:
|
||||
return self._bandwidth_host
|
||||
|
||||
def get_vods_in(self) -> list:
|
||||
return self._vods_in
|
||||
|
||||
|
@ -98,7 +115,9 @@ class ServiceClient(IClientHandler):
|
|||
self.sync_service()
|
||||
if self._handler:
|
||||
self._set_runtime_fields(resp.result[ServiceClient.HTTP_HOST],
|
||||
resp.result[ServiceClient.VODS_HOST], resp.result[ServiceClient.VERSION])
|
||||
resp.result[ServiceClient.VODS_HOST], resp.result[ServiceClient.VODS_HOST],
|
||||
resp.result[ServiceClient.SUBSCRIBERS_HOST],
|
||||
resp.result[ServiceClient.BANDWIDTH_HOST])
|
||||
self._handler.on_service_statistic_received(resp.result)
|
||||
|
||||
if req.method == Commands.PREPARE_SERVICE_COMMAND and resp.is_message():
|
||||
|
@ -127,9 +146,13 @@ class ServiceClient(IClientHandler):
|
|||
self._handler.on_client_state_changed(status)
|
||||
|
||||
# private
|
||||
def _set_runtime_fields(self, http_host=None, vods_host=None, version=None, vods_in=None):
|
||||
def _set_runtime_fields(self, http_host=None, vods_host=None, subscribers_host=None, bandwidth_host=None,
|
||||
version=None,
|
||||
vods_in=None):
|
||||
self._http_host = http_host
|
||||
self._vods_host = vods_host
|
||||
self._subscribers_host = subscribers_host
|
||||
self._bandwidth_host = bandwidth_host
|
||||
self._version = version
|
||||
self._vods_in = vods_in
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ class ServiceSettings(Document, ServerSettings):
|
|||
|
||||
streams = ListField(EmbeddedDocumentField(Stream), default=[])
|
||||
users = ListField(EmbeddedDocumentField(UserPair), default=[])
|
||||
subscribers = ListField(ReferenceField('Subscriber'), default=[])
|
||||
|
||||
def generate_playlist(self) -> str:
|
||||
result = '#EXTM3U\n'
|
||||
|
@ -47,4 +48,23 @@ class ServiceSettings(Document, ServerSettings):
|
|||
for user in self.users:
|
||||
if user.id == uid:
|
||||
self.users.remove(user)
|
||||
break
|
||||
self.save()
|
||||
|
||||
def add_subscriber(self, sid):
|
||||
self.subscribers.append(sid)
|
||||
self.save()
|
||||
|
||||
def remove_subscriber(self, sid):
|
||||
for subscriber in self.subscribers:
|
||||
if subscriber == sid:
|
||||
self.subscribers.remove(subscriber)
|
||||
break
|
||||
self.save()
|
||||
|
||||
def find_stream_settings_by_id(self, sid):
|
||||
for stream in self.streams:
|
||||
if stream.id == sid:
|
||||
return stream
|
||||
|
||||
return None
|
||||
|
|
|
@ -6,9 +6,11 @@ from flask_login import login_required, current_user
|
|||
|
||||
from app import get_runtime_folder
|
||||
from app.service.forms import ServiceSettingsForm, ActivateForm, UploadM3uForm, UserServerForm
|
||||
from app.subscriber.forms import SubscriberForm
|
||||
from app.service.service_entry import ServiceSettings, UserPair
|
||||
from app.utils.m3u_parser import M3uParser
|
||||
from app.home.user_loging_manager import User
|
||||
from app.subscriber.subscriber_entry import Subscriber
|
||||
import app.constants as constants
|
||||
|
||||
|
||||
|
@ -157,11 +159,25 @@ class ServiceView(FlaskView):
|
|||
|
||||
return render_template('service/user/add.html', form=form)
|
||||
|
||||
@login_required
|
||||
@route('/subscriber/add/<sid>', methods=['GET', 'POST'])
|
||||
def subscriber_add(self, sid):
|
||||
form = SubscriberForm(obj=Subscriber())
|
||||
if request.method == 'POST' and form.validate_on_submit():
|
||||
server = ServiceSettings.objects(id=sid).first()
|
||||
if server:
|
||||
new_entry = form.make_entry()
|
||||
new_entry.add_server(server)
|
||||
|
||||
server.add_subscriber(new_entry)
|
||||
return jsonify(status='ok'), 200
|
||||
|
||||
return render_template('service/subscriber/add.html', form=form)
|
||||
|
||||
@login_required
|
||||
@route('/add', methods=['GET', 'POST'])
|
||||
def add(self):
|
||||
model = ServiceSettings()
|
||||
form = ServiceSettingsForm(obj=model)
|
||||
form = ServiceSettingsForm(obj=ServiceSettings())
|
||||
if request.method == 'POST' and form.validate_on_submit():
|
||||
new_entry = form.make_entry()
|
||||
admin = UserPair(current_user.id, constants.Roles.ADMIN)
|
||||
|
|
|
@ -65,6 +65,46 @@ class StreamFields:
|
|||
TIMESTAMP = 'timestamp'
|
||||
|
||||
|
||||
class EpgInfo:
|
||||
ID_FIELD = 'id'
|
||||
URL_FIELD = 'url'
|
||||
TITLE_FIELD = 'display_name'
|
||||
ICON_FIELD = 'icon'
|
||||
PROGRAMS_FIELD = 'programs'
|
||||
|
||||
id = str
|
||||
url = str
|
||||
title = str
|
||||
icon = str
|
||||
programs = []
|
||||
|
||||
def __init__(self, eid: str, url: str, title: str, icon: str, programs=list()):
|
||||
self.id = eid
|
||||
self.url = url
|
||||
self.title = title
|
||||
self.icon = icon
|
||||
self.programs = programs
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {EpgInfo.ID_FIELD: self.id, EpgInfo.URL_FIELD: self.url, EpgInfo.TITLE_FIELD: self.title,
|
||||
EpgInfo.ICON_FIELD: self.icon, EpgInfo.PROGRAMS_FIELD: self.programs}
|
||||
|
||||
|
||||
class ChannelInfo:
|
||||
EPG_FIELD = 'epg'
|
||||
VIDEO_ENABLE_FIELD = 'video'
|
||||
AUDIO_ENABLE_FIELD = 'audio'
|
||||
|
||||
def __init__(self, epg: EpgInfo, have_video=True, have_audio=True):
|
||||
self.have_video = have_video
|
||||
self.have_audio = have_audio
|
||||
self.epg = epg
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {ChannelInfo.EPG_FIELD: self.epg.to_dict(), ChannelInfo.VIDEO_ENABLE_FIELD: self.have_video,
|
||||
ChannelInfo.AUDIO_ENABLE_FIELD: self.have_audio}
|
||||
|
||||
|
||||
class Stream(EmbeddedDocument):
|
||||
meta = {'allow_inheritance': True, 'auto_create_index': True}
|
||||
|
||||
|
@ -159,6 +199,13 @@ class Stream(EmbeddedDocument):
|
|||
conf[AUDIO_SELECT_FIELD] = audio_select
|
||||
return conf
|
||||
|
||||
def to_channel_info(self) -> [ChannelInfo]:
|
||||
ch = []
|
||||
for out in self.output.urls:
|
||||
epg = EpgInfo(self.get_id(), out.uri, self.name, self.icon)
|
||||
ch.append(ChannelInfo(epg))
|
||||
return ch
|
||||
|
||||
def generate_feedback_dir(self):
|
||||
return '{0}/{1}/{2}'.format(self._settings.feedback_directory, self.get_type(), self.get_id())
|
||||
|
||||
|
|
26
app/subscriber/forms.py
Normal file
26
app/subscriber/forms.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
from flask_wtf import FlaskForm
|
||||
from flask_babel import lazy_gettext
|
||||
|
||||
from wtforms.fields import StringField, PasswordField, SubmitField, SelectField
|
||||
from wtforms.validators import InputRequired, Length, Email
|
||||
|
||||
from app.subscriber.subscriber_entry import Subscriber
|
||||
|
||||
|
||||
class SubscriberForm(FlaskForm):
|
||||
AVAILABLE_COUNTRIES = [('UK', 'United kingdom'), ('RU', 'Russian'), ('BY', 'Belarus')]
|
||||
email = StringField(lazy_gettext(u'Email:'),
|
||||
validators=[InputRequired(), Email(message=lazy_gettext(u'Invalid email')), Length(max=30)])
|
||||
password = PasswordField(lazy_gettext(u'Password:'), validators=[InputRequired(), Length(min=4, max=80)])
|
||||
country = SelectField(lazy_gettext(u'Locale:'), coerce=str, validators=[InputRequired()],
|
||||
choices=AVAILABLE_COUNTRIES)
|
||||
apply = SubmitField(lazy_gettext(u'Apply'))
|
||||
|
||||
def make_entry(self):
|
||||
return self.update_entry(Subscriber())
|
||||
|
||||
def update_entry(self, subscriber: Subscriber):
|
||||
subscriber.email = self.email.data
|
||||
subscriber.password = Subscriber.make_md5_hash_from_password(self.password.data)
|
||||
subscriber.country = self.country.data
|
||||
return subscriber
|
105
app/subscriber/subscriber_entry.py
Normal file
105
app/subscriber/subscriber_entry.py
Normal file
|
@ -0,0 +1,105 @@
|
|||
from datetime import datetime
|
||||
from hashlib import md5
|
||||
from bson.objectid import ObjectId
|
||||
from enum import IntEnum
|
||||
|
||||
from mongoengine import Document, EmbeddedDocument, StringField, DateTimeField, IntField, ListField, ReferenceField, \
|
||||
PULL, ObjectIdField, EmbeddedDocumentField
|
||||
|
||||
from app.service.service_entry import ServiceSettings
|
||||
|
||||
|
||||
class Device(EmbeddedDocument):
|
||||
DEFAULT_DEVICE_NAME = 'Device'
|
||||
MIN_DEVICE_NAME_LENGTH = 3
|
||||
MAX_DEVICE_NAME_LENGTH = 32
|
||||
|
||||
meta = {'allow_inheritance': False, 'auto_create_index': True}
|
||||
id = ObjectIdField(required=True, default=ObjectId, unique=True, primary_key=True)
|
||||
created_date = DateTimeField(default=datetime.now)
|
||||
name = StringField(default=DEFAULT_DEVICE_NAME, min_length=MIN_DEVICE_NAME_LENGTH,
|
||||
max_length=MAX_DEVICE_NAME_LENGTH,
|
||||
required=True)
|
||||
|
||||
|
||||
class Subscriber(Document):
|
||||
ID_FIELD = "id"
|
||||
EMAIL_FIELD = "login"
|
||||
PASSWORD_FIELD = "password"
|
||||
STATUS_FIELD = "status"
|
||||
DEVICES_FIELD = "devices"
|
||||
STREAMS_FIELD = "channels"
|
||||
|
||||
class Status(IntEnum):
|
||||
NO_ACTIVE = 0
|
||||
ACTIVE = 1
|
||||
BANNED = 2
|
||||
|
||||
class Type(IntEnum):
|
||||
USER = 0,
|
||||
SUPPORT = 1
|
||||
|
||||
SUBSCRIBER_HASH_LENGHT = 32
|
||||
|
||||
meta = {'collection': 'subscribers', 'auto_create_index': False}
|
||||
|
||||
email = StringField(max_length=30, required=True)
|
||||
password = StringField(min_length=SUBSCRIBER_HASH_LENGHT, max_length=SUBSCRIBER_HASH_LENGHT, required=True)
|
||||
created_date = DateTimeField(default=datetime.now)
|
||||
status = IntField(default=Status.ACTIVE)
|
||||
type = IntField(default=Type.USER)
|
||||
country = StringField(min_length=2, max_length=3, required=True)
|
||||
servers = ListField(ReferenceField(ServiceSettings, reverse_delete_rule=PULL), default=[])
|
||||
devices = ListField(EmbeddedDocumentField(Device), default=[])
|
||||
streams = ListField(ObjectIdField(), default=[])
|
||||
|
||||
def add_server(self, server: ServiceSettings):
|
||||
self.servers.append(server)
|
||||
self.save()
|
||||
|
||||
def add_stream(self, stream):
|
||||
self.streams.append(stream.id)
|
||||
self.save()
|
||||
|
||||
def remove_stream(self, sid: ObjectId):
|
||||
for stream in self.streams:
|
||||
if stream == sid:
|
||||
self.streams.remove(stream)
|
||||
break
|
||||
self.save()
|
||||
|
||||
def to_service(self, sid: ObjectId) -> dict:
|
||||
for serv in self.servers:
|
||||
if serv.id == sid:
|
||||
devices = []
|
||||
for dev in self.devices:
|
||||
devices.append(str(dev.id))
|
||||
|
||||
streams = []
|
||||
for stream in self.streams:
|
||||
founded_stream = serv.find_stream_settings_by_id(stream)
|
||||
if founded_stream:
|
||||
channels = founded_stream.to_channel_info()
|
||||
for ch in channels:
|
||||
streams.append(ch.to_dict())
|
||||
|
||||
conf = {
|
||||
Subscriber.ID_FIELD: str(self.id), Subscriber.EMAIL_FIELD: self.email,
|
||||
Subscriber.PASSWORD_FIELD: self.password, Subscriber.STATUS_FIELD: self.status,
|
||||
Subscriber.DEVICES_FIELD: devices, Subscriber.STREAMS_FIELD: streams}
|
||||
return conf
|
||||
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
def make_md5_hash_from_password(password: str) -> str:
|
||||
m = md5()
|
||||
m.update(password.encode())
|
||||
return m.hexdigest()
|
||||
|
||||
@classmethod
|
||||
def md5_user(cls, email: str, password: str, country: str):
|
||||
return cls(email=email, password=Subscriber.make_md5_hash_from_password(password), country=country)
|
||||
|
||||
|
||||
Subscriber.register_delete_rule(ServiceSettings, "subscribers", PULL)
|
|
@ -38,6 +38,10 @@
|
|||
<br>
|
||||
{{ render_bootstrap_form(form.vods_host) }}
|
||||
<br>
|
||||
{{ render_bootstrap_form(form.subscribers_host) }}
|
||||
<br>
|
||||
{{ render_bootstrap_form(form.bandwidth_host) }}
|
||||
<br>
|
||||
{{ render_bootstrap_field(form.feedback_directory) }}
|
||||
<br>
|
||||
{{ render_bootstrap_field(form.timeshifts_directory) }}
|
||||
|
|
4
app/templates/service/subscriber/add.html
Normal file
4
app/templates/service/subscriber/add.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
{% extends 'service/subscriber/base.html' %}
|
||||
{% block title %}
|
||||
Add subscriber to server
|
||||
{% endblock %}
|
45
app/templates/service/subscriber/base.html
Normal file
45
app/templates/service/subscriber/base.html
Normal file
|
@ -0,0 +1,45 @@
|
|||
{% from 'bootstrap/wtf.html' import form_field %}
|
||||
{% macro render_bootstrap_field(field) %}
|
||||
<div class="row">
|
||||
<label class="col-md-4">{{ field.label }}</label>
|
||||
<div class="col-md-8">
|
||||
{{ field(class='form-control')|safe }}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_bootstrap_form(form) %}
|
||||
<div class="row">
|
||||
<label class="col-md-4">{{ form.label }}</label>
|
||||
<div class="col-md-8">
|
||||
{{ form() }}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
<form id="subscriber_entry_form" name="subscriber_entry_form" class="form" method="post">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">
|
||||
{% block title %}
|
||||
{% endblock %}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{{ form.hidden_tag() }}
|
||||
<br>
|
||||
{{ render_bootstrap_field(form.email) }}
|
||||
<br>
|
||||
{{ render_bootstrap_field(form.password) }}
|
||||
<br>
|
||||
{{ render_bootstrap_field(form.country) }}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
{% block footer %}
|
||||
<button type="button" class="btn btn-danger" data-dismiss="modal">{% trans %}Cancel{% endtrans %}</button>
|
||||
{{ form_field(form.apply, class="btn btn-success") }}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</form>
|
4
app/templates/service/subscriber/edit.html
Normal file
4
app/templates/service/subscriber/edit.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
{% extends 'service/subscriber/base.html' %}
|
||||
{% block title %}
|
||||
Edit subscriber
|
||||
{% endblock %}
|
|
@ -1,4 +1,4 @@
|
|||
{% extends 'service/base.html' %}
|
||||
{% extends 'service/user/base.html' %}
|
||||
{% block title %}
|
||||
Edit service
|
||||
{% endblock %}
|
|
@ -588,11 +588,12 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }}
|
|||
}
|
||||
|
||||
function edit_stream_entry(url) {
|
||||
var data_ser = $('#stream_entry_form').serialize();
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: "POST",
|
||||
dataType: 'json',
|
||||
data: $('#stream_entry_form').serialize(),
|
||||
data: data_ser,
|
||||
success: function (response) {
|
||||
console.log(response);
|
||||
$('#stream_dialog').modal('hide');
|
||||
|
@ -808,12 +809,5 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }}
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -74,6 +74,10 @@ Settings | {{ config['PUBLIC_CONFIG'].site.title }}
|
|||
onclick="add_user_to_server('{{ server.id }}')">
|
||||
{% trans %}Add user{% endtrans %}
|
||||
</button>
|
||||
<button type="submit" class="btn btn-success btn-xs"
|
||||
onclick="add_subscriber_to_server('{{ server.id }}')">
|
||||
{% trans %}Add subscriber{% endtrans %}
|
||||
</button>
|
||||
<button type="submit" class="btn btn-danger btn-xs"
|
||||
onclick="remove_server('{{ server.id }}')">
|
||||
{% trans %}Remove{% endtrans %}
|
||||
|
@ -155,6 +159,37 @@ Settings | {{ config['PUBLIC_CONFIG'].site.title }}
|
|||
}
|
||||
|
||||
|
||||
function add_subscriber_to_server_entry(url) {
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: "POST",
|
||||
dataType: 'json',
|
||||
data: $('#subscriber_entry_form').serialize(),
|
||||
success: function (response) {
|
||||
console.log(response);
|
||||
$('#service_dialog').modal('hide');
|
||||
window.location.reload();
|
||||
},
|
||||
error: function (error) {
|
||||
console.error(error);
|
||||
$('#service_dialog .modal-content').html(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function add_subscriber_to_server(sid) {
|
||||
var url = "/service/subscriber/add/" + sid;
|
||||
$.get(url, function(data) {
|
||||
$('#service_dialog .modal-content').html(data);
|
||||
$('#service_dialog').modal();
|
||||
|
||||
$('#apply').click(function(event) {
|
||||
event.preventDefault();
|
||||
add_subscriber_to_server_entry(url);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function add_server() {
|
||||
var url = "{{ url_for('ServiceView:add') }}";
|
||||
$.get(url, function(data) {
|
||||
|
@ -215,7 +250,5 @@ Settings | {{ config['PUBLIC_CONFIG'].site.title }}
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue