1
0
Fork 0
mirror of https://github.com/fastogt/fastocloud_admin.git synced 2025-03-09 23:38:52 +00:00

Proxy stream

This commit is contained in:
topilski 2019-06-11 10:45:33 -04:00
parent 6203746a70
commit a2d932de68
15 changed files with 338 additions and 148 deletions

View file

@ -2,6 +2,7 @@
[Alexandr Topilski]
- User agents
- Subscribers
- Proxy streams
1.1.0 / June 6, 2019
[Alexandr Topilski]

View file

@ -2,14 +2,15 @@ from enum import IntEnum
class StreamType(IntEnum):
RELAY = 0
ENCODE = 1
TIMESHIFT_PLAYER = 2
TIMESHIFT_RECORDER = 3
CATCHUP = 4
TEST_LIFE = 5,
VOD_RELAY = 6,
VOD_ENCODE = 7
PROXY = 0,
RELAY = 1
ENCODE = 2
TIMESHIFT_PLAYER = 3
TIMESHIFT_RECORDER = 4
CATCHUP = 5
TEST_LIFE = 6,
VOD_RELAY = 7,
VOD_ENCODE = 8
@classmethod
def choices(cls):
@ -69,11 +70,6 @@ DEFAULT_LOCALE = 'en'
AVAILABLE_LOCALES = DEFAULT_LOCALE, 'ru'
AVAILABLE_LOCALES_PAIRS = [(DEFAULT_LOCALE, 'English'), ('ru', 'Russian')]
AVAILABLE_STREAM_TYPES_PAIRS = [(StreamType.RELAY, 'relay'), (StreamType.ENCODE, 'encode'),
(StreamType.TIMESHIFT_PLAYER, 'timeshift_player'),
(StreamType.TIMESHIFT_RECORDER, 'timeshift_record'), (StreamType.CATCHUP, 'catchup'),
(StreamType.TEST_LIFE, 'test_life'), (StreamType.VOD_RELAY, 'vod_relay'),
(StreamType.VOD_ENCODE, 'vod_encode')]
AVAILABLE_LOG_LEVELS_PAIRS = [(StreamLogLevel.LOG_LEVEL_EMERG, 'EVERG'), (StreamLogLevel.LOG_LEVEL_ALERT, 'ALERT'),
(StreamLogLevel.LOG_LEVEL_CRIT, 'CRITICAL'),
(StreamLogLevel.LOG_LEVEL_ERR, 'ERROR'),

View file

@ -55,7 +55,8 @@ class ActivateForm(FlaskForm):
class UploadM3uForm(FlaskForm):
AVAILABLE_STREAM_TYPES_FOR_UPLOAD = [(constants.StreamType.RELAY, 'Relay'), (constants.StreamType.ENCODE, 'Encode'),
AVAILABLE_STREAM_TYPES_FOR_UPLOAD = [(constants.StreamType.PROXY, 'Proxy'), (constants.StreamType.RELAY, 'Relay'),
(constants.StreamType.ENCODE, 'Encode'),
(constants.StreamType.CATCHUP, 'Catchup'),
(constants.StreamType.TEST_LIFE, 'Test life'),
(constants.StreamType.VOD_RELAY, 'Vod relay'),

View file

@ -1,9 +1,8 @@
from bson.objectid import ObjectId
from app.stream.stream_entry import Stream, EncodeStream, RelayStream, TimeshiftRecorderStream, CatchupStream, \
TimeshiftPlayerStream, TestLifeStream, make_encode_stream, make_relay_stream, make_timeshift_recorder_stream, \
make_catchup_stream, make_timeshift_player_stream, make_test_life_stream, make_vod_encode_stream, \
make_vod_relay_stream
from app.stream.stream_entry import IStream, ProxyStream, EncodeStream, RelayStream, TimeshiftRecorderStream, \
CatchupStream, \
TimeshiftPlayerStream, TestLifeStream, VodRelayStream, VodEncodeStream
from app.client.client_constants import ClientStatus
from app.service.service_entry import ServiceSettings
@ -216,29 +215,32 @@ class Service(IStreamHandler):
ServiceFields.VERSION: self.version, ServiceFields.UPTIME: self._uptime,
ServiceFields.TIMESTAMP: self._timestamp, ServiceFields.STATUS: self.status}
def make_relay_stream(self) -> RelayStream:
return make_relay_stream(self._settings)
def make_proxy_stream(self) -> ProxyStream:
return ProxyStream.make_stream(self._settings)
def make_vod_relay_stream(self) -> RelayStream:
return make_vod_relay_stream(self._settings)
def make_relay_stream(self) -> RelayStream:
return RelayStream.make_stream(self._settings)
def make_vod_relay_stream(self) -> VodRelayStream:
return VodRelayStream.make_stream(self._settings)
def make_encode_stream(self) -> EncodeStream:
return make_encode_stream(self._settings)
return EncodeStream.make_stream(self._settings)
def make_vod_encode_stream(self) -> EncodeStream:
return make_vod_encode_stream(self._settings)
return VodEncodeStream.make_stream(self._settings)
def make_timeshift_recorder_stream(self) -> TimeshiftRecorderStream:
return make_timeshift_recorder_stream(self._settings)
return TimeshiftRecorderStream.make_stream(self._settings)
def make_catchup_stream(self) -> CatchupStream:
return make_catchup_stream(self._settings)
return CatchupStream.make_stream(self._settings)
def make_timeshift_player_stream(self) -> TimeshiftPlayerStream:
return make_timeshift_player_stream(self._settings)
return TimeshiftPlayerStream.make_stream(self._settings)
def make_test_life_stream(self) -> TestLifeStream:
return make_test_life_stream(self._settings)
return TestLifeStream.make_stream(self._settings)
# handler
def on_stream_statistic_received(self, params: dict):
@ -303,7 +305,7 @@ class Service(IStreamHandler):
self._uptime = stats[ServiceFields.UPTIME]
self._timestamp = stats[ServiceFields.TIMESTAMP]
def __init_stream_runtime_fields(self, stream: Stream):
def __init_stream_runtime_fields(self, stream: IStream):
stream.set_server_settings(self._settings)
def __reload_from_db(self):

View file

@ -3,7 +3,7 @@ from mongoengine import Document, ListField, EmbeddedDocumentField, ReferenceFie
import app.constants as constants
from app.service.server_entry import ServerSettings
from app.stream.stream_entry import Stream
from app.stream.stream_entry import IStream
# #EXTM3U
@ -18,7 +18,7 @@ class UserPair(EmbeddedDocument):
class ServiceSettings(Document, ServerSettings):
meta = {'collection': 'services', 'auto_create_index': False}
streams = ListField(EmbeddedDocumentField(Stream), default=[])
streams = ListField(EmbeddedDocumentField(IStream), default=[])
users = ListField(EmbeddedDocumentField(UserPair), default=[])
subscribers = ListField(ReferenceField('Subscriber'), default=[])

View file

@ -37,7 +37,9 @@ class ServiceView(FlaskView):
m3u_parser.parse()
for file in m3u_parser.files:
if stream_type == constants.StreamType.RELAY:
if stream_type == constants.StreamType.PROXY:
stream = server.make_proxy_stream()
elif stream_type == constants.StreamType.RELAY:
stream = server.make_relay_stream()
stream.output.urls[0] = stream.generate_http_link()
elif stream_type == constants.StreamType.ENCODE:

View file

@ -105,22 +105,66 @@ class ChannelInfo:
ChannelInfo.AUDIO_ENABLE_FIELD: self.have_audio}
class Stream(EmbeddedDocument):
class IStream(EmbeddedDocument):
meta = {'allow_inheritance': True, 'auto_create_index': True}
id = ObjectIdField(required=True, default=ObjectId,
unique=True, primary_key=True)
unique=True, primary_key=True) #
name = StringField(default=constants.DEFAULT_STREAM_NAME, max_length=constants.MAX_STREAM_NAME_LENGTH,
min_length=constants.MIN_STREAM_NAME_LENGTH, required=True)
min_length=constants.MIN_STREAM_NAME_LENGTH, required=True) #
icon = StringField(default=constants.DEFAULT_STREAM_ICON_URL, max_length=constants.MAX_URL_LENGTH,
min_length=constants.MIN_URL_LENGTH, required=True)
min_length=constants.MIN_URL_LENGTH, required=True) #
group = StringField(default=constants.DEFAULT_STREAM_GROUP_TITLE, min_length=constants.MIN_STREAM_GROUP_TITLE,
max_length=constants.MAX_STREAM_GROUP_TITLE, required=False)
created_date = DateTimeField(default=datetime.now) # for inner use
output = EmbeddedDocumentField(OutputUrls, default=OutputUrls()) #
_settings = ServerSettings()
def __init__(self, *args, **kwargs):
super(IStream, self).__init__(*args, **kwargs)
def set_server_settings(self, settings: ServerSettings):
self._settings = settings
def get_type(self):
raise NotImplementedError('subclasses must override get_type()!')
def get_id(self) -> str:
return str(self.id)
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 to_front(self) -> dict:
return {StreamFields.NAME: self.name, StreamFields.ID: self.get_id(), StreamFields.TYPE: self.get_type()}
def fixup_output_urls(self):
return
class ProxyStream(IStream):
def __init__(self, *args, **kwargs):
super(ProxyStream, self).__init__(*args, **kwargs)
def get_type(self):
return constants.StreamType.PROXY
@classmethod
def make_stream(cls, settings: ServerSettings):
stream = cls()
stream._settings = settings
return stream
class HardwareStream(IStream):
log_level = IntField(default=constants.StreamLogLevel.LOG_LEVEL_INFO, required=True)
input = EmbeddedDocumentField(InputUrls, default=InputUrls())
output = EmbeddedDocumentField(OutputUrls, default=OutputUrls())
have_video = BooleanField(default=constants.DEFAULT_HAVE_VIDEO, required=True)
have_audio = BooleanField(default=constants.DEFAULT_HAVE_AUDIO, required=True)
audio_select = IntField(default=constants.INVALID_AUDIO_SELECT, required=True)
@ -139,13 +183,12 @@ class Stream(EmbeddedDocument):
_start_time = 0
_input_streams = str()
_output_streams = str()
_settings = ServerSettings()
def __init__(self, *args, **kwargs):
super(Stream, self).__init__(*args, **kwargs)
super(HardwareStream, self).__init__(*args, **kwargs)
def set_server_settings(self, settings: ServerSettings):
self._settings = settings
def get_type(self):
raise NotImplementedError('subclasses must override get_type()!')
def reset(self):
self._status = constants.StreamStatus.NEW
@ -172,11 +215,17 @@ class Stream(EmbeddedDocument):
self._output_streams = params[StreamFields.OUTPUT_STREAMS]
def to_front(self) -> dict:
return {StreamFields.NAME: self.name, StreamFields.ID: self.get_id(), StreamFields.TYPE: self.get_type(),
StreamFields.STATUS: self._status, StreamFields.CPU: self._cpu, StreamFields.TIMESTAMP: self._timestamp,
StreamFields.RSS: self._rss, StreamFields.LOOP_START_TIME: self._loop_start_time,
StreamFields.RESTARTS: self._restarts, StreamFields.START_TIME: self._start_time,
StreamFields.INPUT_STREAMS: self._input_streams, StreamFields.OUTPUT_STREAMS: self._output_streams}
front = super(HardwareStream, self).to_front()
front[StreamFields.STATUS] = self._status
front[StreamFields.CPU] = self._cpu
front[StreamFields.TIMESTAMP] = self._timestamp
front[StreamFields.RSS] = self._rss
front[StreamFields.LOOP_START_TIME] = self._loop_start_time
front[StreamFields.RESTARTS] = self._restarts
front[StreamFields.START_TIME] = self._start_time
front[StreamFields.INPUT_STREAMS] = self._input_streams
front[StreamFields.OUTPUT_STREAMS] = self._output_streams
return front
def config(self) -> dict:
conf = {
@ -199,13 +248,6 @@ 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())
@ -233,12 +275,6 @@ class Stream(EmbeddedDocument):
def get_have_audio(self):
return self.have_audio
def get_id(self) -> str:
return str(self.id)
def get_type(self):
raise NotImplementedError('subclasses must override get_type()!')
def get_loop(self):
return self.loop
@ -251,9 +287,6 @@ class Stream(EmbeddedDocument):
def get_auto_exit_time(self):
return self.auto_exit_time
def fixup_output_urls(self):
return
# private
def _generate_http_root_dir(self, oid: int):
return '{0}/{1}/{2}/{3}'.format(self._settings.hls_directory, self.get_type(), self.get_id(), oid)
@ -284,10 +317,9 @@ class Stream(EmbeddedDocument):
self.output.urls[idx] = self.generate_vod_link(filename)
class RelayStream(Stream):
class RelayStream(HardwareStream):
def __init__(self, *args, **kwargs):
super(RelayStream, self).__init__(*args, **kwargs)
# super(RelayStream, self).type = constants.StreamType.RELAY
video_parser = StringField(default=constants.DEFAULT_VIDEO_PARSER, required=True)
audio_parser = StringField(default=constants.DEFAULT_AUDIO_PARSER, required=True)
@ -310,8 +342,16 @@ class RelayStream(Stream):
def fixup_output_urls(self):
return self._fixup_http_output_urls()
@classmethod
def make_stream(cls, settings: ServerSettings):
stream = cls()
stream._settings = settings
stream.input = InputUrls(urls=[InputUrl(id=InputUrl.generate_id())])
stream.output = OutputUrls(urls=[OutputUrl(id=OutputUrl.generate_id())])
return stream
class EncodeStream(Stream):
class EncodeStream(HardwareStream):
def __init__(self, *args, **kwargs):
super(EncodeStream, self).__init__(*args, **kwargs)
@ -395,6 +435,14 @@ class EncodeStream(Stream):
def fixup_output_urls(self):
return self._fixup_http_output_urls()
@classmethod
def make_stream(cls, settings: ServerSettings):
stream = cls()
stream._settings = settings
stream.input = InputUrls(urls=[InputUrl(id=InputUrl.generate_id())])
stream.output = OutputUrls(urls=[OutputUrl(id=OutputUrl.generate_id())])
return stream
class TimeshiftRecorderStream(RelayStream):
def __init__(self, *args, **kwargs):
@ -422,6 +470,13 @@ class TimeshiftRecorderStream(RelayStream):
def fixup_output_urls(self):
return
@classmethod
def make_stream(cls, settings: ServerSettings):
stream = cls()
stream._settings = settings
stream.input = InputUrls(urls=[InputUrl(id=InputUrl.generate_id())])
return stream
class CatchupStream(TimeshiftRecorderStream):
def __init__(self, *args, **kwargs):
@ -432,6 +487,13 @@ class CatchupStream(TimeshiftRecorderStream):
def get_type(self):
return constants.StreamType.CATCHUP
@classmethod
def make_stream(cls, settings: ServerSettings):
stream = cls()
stream._settings = settings
stream.input = InputUrls(urls=[InputUrl(id=InputUrl.generate_id())])
return stream
class TimeshiftPlayerStream(RelayStream):
timeshift_dir = StringField(required=True) # FIXME default
@ -449,6 +511,14 @@ class TimeshiftPlayerStream(RelayStream):
conf[TIMESHIFT_DELAY] = self.timeshift_delay
return conf
@classmethod
def make_stream(cls, settings: ServerSettings):
stream = cls()
stream._settings = settings
stream.input = InputUrls(urls=[InputUrl(id=InputUrl.generate_id())])
stream.output = OutputUrls(urls=[OutputUrl(id=OutputUrl.generate_id())])
return stream
class TestLifeStream(RelayStream):
def __init__(self, *args, **kwargs):
@ -464,6 +534,14 @@ class TestLifeStream(RelayStream):
def fixup_output_urls(self):
return
@classmethod
def make_stream(cls, settings: ServerSettings):
stream = cls()
stream._settings = settings
stream.input = InputUrls(urls=[InputUrl(id=InputUrl.generate_id())])
stream.output = OutputUrls(urls=[OutputUrl(id=OutputUrl.generate_id(), uri=constants.DEFAULT_TEST_URL)])
return stream
class VodRelayStream(RelayStream):
def __init__(self, *args, **kwargs):
@ -481,6 +559,14 @@ class VodRelayStream(RelayStream):
def fixup_output_urls(self):
return self._fixup_vod_output_urls()
@classmethod
def make_stream(cls, settings: ServerSettings):
stream = cls()
stream._settings = settings
stream.input = InputUrls(urls=[InputUrl(id=InputUrl.generate_id())])
stream.output = OutputUrls(urls=[OutputUrl(id=OutputUrl.generate_id())])
return stream
class VodEncodeStream(EncodeStream):
def __init__(self, *args, **kwargs):
@ -498,66 +584,10 @@ class VodEncodeStream(EncodeStream):
def fixup_output_urls(self):
return self._fixup_vod_output_urls()
def make_relay_stream(settings: ServerSettings) -> RelayStream:
stream = RelayStream()
@classmethod
def make_stream(cls, settings: ServerSettings):
stream = cls()
stream._settings = settings
stream.input = InputUrls(urls=[InputUrl(id=InputUrl.generate_id())])
stream.output = OutputUrls(urls=[OutputUrl(id=OutputUrl.generate_id())])
return stream
# loop false, input file, output vod folder hls
def make_vod_relay_stream(settings: ServerSettings) -> VodRelayStream:
stream = VodRelayStream()
stream._settings = settings
stream.input = InputUrls(urls=[InputUrl(id=InputUrl.generate_id())])
stream.output = OutputUrls(urls=[OutputUrl(id=OutputUrl.generate_id())])
return stream
def make_encode_stream(settings: ServerSettings) -> EncodeStream:
stream = EncodeStream()
stream._settings = settings
stream.input = InputUrls(urls=[InputUrl(id=InputUrl.generate_id())])
stream.output = OutputUrls(urls=[OutputUrl(id=OutputUrl.generate_id())])
return stream
# loop false, input file, output vod folder hls
def make_vod_encode_stream(settings: ServerSettings) -> VodEncodeStream:
stream = VodEncodeStream()
stream._settings = settings
stream.input = InputUrls(urls=[InputUrl(id=InputUrl.generate_id())])
stream.output = OutputUrls(urls=[OutputUrl(id=OutputUrl.generate_id())])
return stream
def make_timeshift_recorder_stream(settings: ServerSettings) -> TimeshiftRecorderStream:
stream = TimeshiftRecorderStream()
stream._settings = settings
stream.input = InputUrls(urls=[InputUrl(id=InputUrl.generate_id())])
return stream
def make_catchup_stream(settings: ServerSettings) -> CatchupStream:
stream = CatchupStream()
stream._settings = settings
stream.input = InputUrls(urls=[InputUrl(id=InputUrl.generate_id())])
return stream
def make_timeshift_player_stream(settings: ServerSettings) -> TimeshiftPlayerStream:
stream = TimeshiftPlayerStream()
stream._settings = settings
stream.input = InputUrls(urls=[InputUrl(id=InputUrl.generate_id())])
stream.output = OutputUrls(urls=[OutputUrl(id=OutputUrl.generate_id())])
return stream
def make_test_life_stream(settings: ServerSettings) -> TestLifeStream:
stream = TestLifeStream()
stream._settings = settings
stream.input = InputUrls(urls=[InputUrl(id=InputUrl.generate_id())])
stream.output = OutputUrls(urls=[OutputUrl(id=OutputUrl.generate_id(), uri=constants.DEFAULT_TEST_URL)])
return stream

View file

@ -5,12 +5,12 @@ from wtforms.validators import InputRequired, Length, NumberRange
from wtforms.fields import StringField, SubmitField, SelectField, IntegerField, FormField, BooleanField, FloatField
import app.constants as constants
from app.stream.stream_entry import Stream, RelayStream, EncodeStream, TimeshiftRecorderStream, CatchupStream, \
TimeshiftPlayerStream, TestLifeStream, VodRelayStream, VodEncodeStream
from app.stream.stream_entry import ProxyStream, HardwareStream, RelayStream, EncodeStream, TimeshiftRecorderStream, \
CatchupStream, TimeshiftPlayerStream, TestLifeStream, VodRelayStream, VodEncodeStream
from app.common_forms import InputUrlsForm, OutputUrlsForm, SizeForm, LogoForm, RationalForm
class StreamForm(FlaskForm):
class IStreamForm(FlaskForm):
name = StringField(lazy_gettext(u'Name:'),
validators=[InputRequired(),
Length(min=constants.MIN_STREAM_NAME_LENGTH, max=constants.MAX_STREAM_NAME_LENGTH)])
@ -18,8 +18,24 @@ class StreamForm(FlaskForm):
validators=[InputRequired(), Length(min=constants.MIN_URL_LENGTH, max=constants.MAX_URL_LENGTH)])
group = StringField(lazy_gettext(u'Group:'),
validators=[Length(min=constants.MIN_STREAM_GROUP_TITLE, max=constants.MAX_STREAM_GROUP_TITLE)])
input = FormField(InputUrlsForm, lazy_gettext(u'Input:'))
output = FormField(OutputUrlsForm, lazy_gettext(u'Output:'))
submit = SubmitField(lazy_gettext(u'Confirm'))
class ProxyStreamForm(IStreamForm):
def make_entry(self):
return self.update_entry(ProxyStream())
def update_entry(self, entry: ProxyStream):
entry.name = self.name.data
entry.icon = self.icon.data
entry.group = self.group.data
entry.output = self.output.get_data()
return entry
class HardwareStreamForm(IStreamForm):
input = FormField(InputUrlsForm, lazy_gettext(u'Input:'))
log_level = SelectField(lazy_gettext(u'Log level:'), validators=[],
choices=constants.AVAILABLE_LOG_LEVELS_PAIRS, coerce=constants.StreamLogLevel.coerce)
audio_select = IntegerField(lazy_gettext(u'Audio select:'),
@ -31,15 +47,12 @@ class StreamForm(FlaskForm):
restart_attempts = IntegerField(lazy_gettext(u'Max restart attempts and frozen:'),
validators=[NumberRange(1, 1000)])
auto_exit_time = IntegerField(lazy_gettext(u'Auto exit time:'), validators=[])
submit = SubmitField(lazy_gettext(u'Confirm'))
def make_entry(self):
return self.update_entry(Stream())
return self.update_entry(HardwareStream())
def update_entry(self, entry: Stream):
entry.name = self.name.data
def update_entry(self, entry: HardwareStream):
entry.input = self.input.get_data()
entry.output = self.output.get_data()
entry.audio_select = self.audio_select.data
entry.have_video = self.have_video.data
@ -52,7 +65,7 @@ class StreamForm(FlaskForm):
return entry
class RelayStreamForm(StreamForm):
class RelayStreamForm(HardwareStreamForm):
video_parser = SelectField(lazy_gettext(u'Video parser:'), validators=[],
choices=constants.AVAILABLE_VIDEO_PARSERS)
audio_parser = SelectField(lazy_gettext(u'Audio parser:'), validators=[],
@ -67,7 +80,7 @@ class RelayStreamForm(StreamForm):
return super(RelayStreamForm, self).update_entry(entry)
class EncodeStreamForm(StreamForm):
class EncodeStreamForm(HardwareStreamForm):
relay_video = BooleanField(lazy_gettext(u'Relay video:'), validators=[])
relay_audio = BooleanField(lazy_gettext(u'Relay audio:'), validators=[])
deinterlace = BooleanField(lazy_gettext(u'Deinterlace:'), validators=[])

View file

@ -6,8 +6,8 @@ from flask_login import login_required, current_user
import app.constants as constants
from app import get_runtime_stream_folder
from app.stream.stream_forms import EncodeStreamForm, RelayStreamForm, TimeshiftRecorderStreamForm, CatchupStreamForm, \
TimeshiftPlayerStreamForm, TestLifeStreamForm, VodEncodeStreamForm, VodRelayStreamForm
from app.stream.stream_forms import ProxyStreamForm, EncodeStreamForm, RelayStreamForm, TimeshiftRecorderStreamForm, \
CatchupStreamForm, TimeshiftPlayerStreamForm, TestLifeStreamForm, VodEncodeStreamForm, VodRelayStreamForm
# routes
@ -100,6 +100,21 @@ class StreamView(FlaskView):
# broadcast routes
@login_required
@route('/add/proxy', methods=['GET', 'POST'])
def add_proxy(self):
server = current_user.get_current_server()
if server:
stream = server.make_relay_stream()
form = ProxyStreamForm(obj=stream)
if request.method == 'POST' and form.validate_on_submit():
new_entry = form.make_entry()
server.add_stream(new_entry)
return jsonify(status='ok'), 200
return render_template('stream/proxy/add.html', form=form, feedback_dir=stream.generate_feedback_dir())
return jsonify(status='failed'), 404
@login_required
@route('/add/relay', methods=['GET', 'POST'])
def add_relay(self):
@ -234,7 +249,16 @@ class StreamView(FlaskView):
stream = server.find_stream_by_id(sid)
if stream:
type = stream.get_type()
if type == constants.StreamType.RELAY:
if type == constants.StreamType.PROXY:
form = ProxyStreamForm(obj=stream)
if request.method == 'POST' and form.validate_on_submit():
stream = form.update_entry(stream)
server.update_stream(stream)
return jsonify(status='ok'), 200
return render_template('stream/proxy/edit.html', form=form)
elif type == constants.StreamType.RELAY:
form = RelayStreamForm(obj=stream)
if request.method == 'POST' and form.validate_on_submit():

View file

@ -0,0 +1,4 @@
{% extends 'stream/proxy/base.html' %}
{% block title %}
Add proxy stream
{% endblock %}

View file

@ -0,0 +1,47 @@
{% 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="stream_entry_form" name="stream_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">&times;</span>
</button>
<h4 class="modal-title">
{% block title %}
{% endblock %}
</h4>
</div>
<div class="modal-body">
{{ form.hidden_tag() }}
<br>
{{ render_bootstrap_field(form.name) }}
<br>
{{ render_bootstrap_field(form.icon) }}
<br>
{{ render_bootstrap_field(form.group) }}
<br>
{{ render_bootstrap_form(form.output) }}
</div>
<div class="modal-footer">
{% block footer %}
<button type="button" class="btn btn-danger" data-dismiss="modal">{% trans %}Cancel{% endtrans %}</button>
{{ form_field(form.submit, class="btn btn-success") }}
{% endblock %}
</div>
</form>

View file

@ -0,0 +1,4 @@
{% extends 'stream/proxy/base.html' %}
{% block title %}
Edit proxy stream
{% endblock %}

View file

@ -32,6 +32,10 @@
<br>
{{ render_bootstrap_field(form.name) }}
<br>
{{ render_bootstrap_field(form.icon) }}
<br>
{{ render_bootstrap_field(form.group) }}
<br>
{{ render_bootstrap_field(form.audio_select) }}
<br>
{{ render_bootstrap_field(form.have_video) }}

View file

@ -44,6 +44,7 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }}
th.stream_actions {
width: 26%;
}
</style>
{{super()}}
{% endblock %}
@ -246,6 +247,7 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }}
<ul class="nav nav-tabs">
<li class="active"><a href="#streams" data-toggle="tab">Streams</a></li>
<li><a href="#vods" data-toggle="tab">Vods</a></li>
<li><a href="#proxy" data-toggle="tab">Proxy</a></li>
</ul>
</div>
<div class="panel-body">
@ -271,12 +273,13 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }}
</thead>
<tbody>
{% for rev in streams %}
{% if (rev.type != 6 and rev.type != 7) %}
{% if (rev.type != 7 and rev.type != 8 and rev.type != 0) %}
<tr id='{{ rev.id }}'>
<td>{{ loop.index }}</td>
<td>{{ rev.name }}</td>
<td>
{{ ['RELAY', 'ENCODE', 'TIMESHIFT_PLAYER', 'TIMESHIFT_RECORDER', 'CATCHUP',
{{ ['PROXY','RELAY', 'ENCODE', 'TIMESHIFT_PLAYER', 'TIMESHIFT_RECORDER',
'CATCHUP',
'TEST_LIFE',
'VOD_RELAY', 'VOD_ENCODE'][rev.type] }}
</td>
@ -411,12 +414,13 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }}
</thead>
<tbody>
{% for vod in streams %}
{% if (vod.type == 6 or vod.type == 7) %}
{% if (vod.type == 7 or vod.type == 8) %}
<tr id='{{ vod.id }}'>
<td>{{ loop.index }}</td>
<td>{{ vod.name }}</td>
<td>
{{ ['RELAY', 'ENCODE', 'TIMESHIFT_PLAYER', 'TIMESHIFT_RECORDER', 'CATCHUP',
{{ ['PROXY','RELAY', 'ENCODE', 'TIMESHIFT_PLAYER', 'TIMESHIFT_RECORDER',
'CATCHUP',
'TEST_LIFE',
'VOD_RELAY', 'VOD_ENCODE'][vod.type] }}
</td>
@ -482,6 +486,51 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }}
</button>
</div>
</div>
<div class="tab-pane fade" id="proxy">
<div class="row">
<table id='proxy_table' class="table">
<thead>
<tr>
<th class="stream_number">#</th>
<th class="stream_name">{% trans %}Name{% endtrans %}</th>
<th class="stream_type">{% trans %}Type{% endtrans %}</th>
<th class="stream_actions">{% trans %}Actions{% endtrans %}</th>
</tr>
</thead>
<tbody>
{% for proxy in streams %}
{% if (proxy.type == 0) %}
<tr id='{{ proxy.id }}'>
<td>{{ loop.index }}</td>
<td>{{ proxy.name }}</td>
<td>
{{ ['PROXY','RELAY', 'ENCODE', 'TIMESHIFT_PLAYER', 'TIMESHIFT_RECORDER',
'CATCHUP',
'TEST_LIFE',
'VOD_RELAY', 'VOD_ENCODE'][proxy.type] }}
</td>
<td>
<button type="submit" class="btn btn-success btn-xs"
onclick="edit_stream('{{ proxy.id }}')">
{% trans %}Edit{% endtrans %}
</button>
<button type="submit" class="btn btn-danger btn-xs"
onclick="remove_stream('{{ proxy.id }}')">
{% trans %}Remove{% endtrans %}
</button>
</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
</div>
<div class="row">
<button class="btn btn-warning btn-send col-md-12" onclick="add_proxy_stream()">
{% trans %}Add proxy{% endtrans %}
</button>
</div>
</div>
</div>
</div>
</div>
@ -701,6 +750,18 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }}
})
});
}
function add_proxy_stream() {
var url = "{{ url_for('StreamView:add_proxy') }}";
$.get(url, function(data) {
$('#stream_dialog .modal-content').html(data);
$('#stream_dialog').modal();
$('#submit').click(function(event) {
event.preventDefault();
add_stream_entry(url);
})
});
}
function edit_stream(sid) {
var url = "/stream/edit/" + sid;
$.get(url, function(data) {
@ -809,5 +870,6 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }}
}
});
}
</script>
{% endblock %}

View file

@ -6,7 +6,7 @@ from mongoengine import connect
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
from app.stream.stream_entry import make_test_life_stream
from app.stream.stream_entry import TestLifeStream
from app.service.service import ServiceSettings
from app.utils.m3u_parser import M3uParser
@ -26,7 +26,7 @@ if __name__ == '__main__':
m3u_parser.read_m3u(argv.uri)
m3u_parser.parse()
for file in m3u_parser.files:
stream = make_test_life_stream(service_settings.feedback_directory)
stream = TestLifeStream.make_stream(service_settings)
stream.input.urls[0].uri = file['link']
stream.name = '{0}({1})'.format(file['tvg-group'], file['title'])
service_settings.streams.append(stream)