diff --git a/CHANGELOG b/CHANGELOG index 895d43a..d657806 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,7 @@ [Alexandr Topilski] - User agents - Subscribers +- Proxy streams 1.1.0 / June 6, 2019 [Alexandr Topilski] diff --git a/app/constants.py b/app/constants.py index 75f61a0..ff38248 100644 --- a/app/constants.py +++ b/app/constants.py @@ -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'), diff --git a/app/service/forms.py b/app/service/forms.py index b201921..640d902 100644 --- a/app/service/forms.py +++ b/app/service/forms.py @@ -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'), diff --git a/app/service/service.py b/app/service/service.py index 03a89b0..16ddfe2 100644 --- a/app/service/service.py +++ b/app/service/service.py @@ -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): diff --git a/app/service/service_entry.py b/app/service/service_entry.py index 348e766..205c872 100644 --- a/app/service/service_entry.py +++ b/app/service/service_entry.py @@ -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=[]) diff --git a/app/service/view.py b/app/service/view.py index c7d8cd6..48b5f45 100644 --- a/app/service/view.py +++ b/app/service/view.py @@ -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: diff --git a/app/stream/stream_entry.py b/app/stream/stream_entry.py index aecf015..2019864 100644 --- a/app/stream/stream_entry.py +++ b/app/stream/stream_entry.py @@ -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() - 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 + @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 diff --git a/app/stream/stream_forms.py b/app/stream/stream_forms.py index 9ae9208..0d6153e 100644 --- a/app/stream/stream_forms.py +++ b/app/stream/stream_forms.py @@ -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=[]) diff --git a/app/stream/view.py b/app/stream/view.py index 3dd99f3..61c9fcb 100644 --- a/app/stream/view.py +++ b/app/stream/view.py @@ -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(): diff --git a/app/templates/stream/proxy/add.html b/app/templates/stream/proxy/add.html new file mode 100644 index 0000000..058a934 --- /dev/null +++ b/app/templates/stream/proxy/add.html @@ -0,0 +1,4 @@ +{% extends 'stream/proxy/base.html' %} +{% block title %} +Add proxy stream +{% endblock %} \ No newline at end of file diff --git a/app/templates/stream/proxy/base.html b/app/templates/stream/proxy/base.html new file mode 100644 index 0000000..5c99937 --- /dev/null +++ b/app/templates/stream/proxy/base.html @@ -0,0 +1,47 @@ +{% from 'bootstrap/wtf.html' import form_field %} +{% macro render_bootstrap_field(field) %} +
+ +
+ {{ field(class='form-control')|safe }} +
+
+{% endmacro %} + +{% macro render_bootstrap_form(form) %} +
+ +
+ {{ form() }} +
+
+{% endmacro %} + +
+ + + +
diff --git a/app/templates/stream/proxy/edit.html b/app/templates/stream/proxy/edit.html new file mode 100644 index 0000000..b687508 --- /dev/null +++ b/app/templates/stream/proxy/edit.html @@ -0,0 +1,4 @@ +{% extends 'stream/proxy/base.html' %} +{% block title %} +Edit proxy stream +{% endblock %} \ No newline at end of file diff --git a/app/templates/stream/vod_relay/base.html b/app/templates/stream/vod_relay/base.html index 22de4e2..4acb165 100644 --- a/app/templates/stream/vod_relay/base.html +++ b/app/templates/stream/vod_relay/base.html @@ -32,6 +32,10 @@
{{ render_bootstrap_field(form.name) }}
+ {{ render_bootstrap_field(form.icon) }} +
+ {{ render_bootstrap_field(form.group) }} +
{{ render_bootstrap_field(form.audio_select) }}
{{ render_bootstrap_field(form.have_video) }} diff --git a/app/templates/user/dashboard.html b/app/templates/user/dashboard.html index d6e106c..5c42732 100644 --- a/app/templates/user/dashboard.html +++ b/app/templates/user/dashboard.html @@ -44,6 +44,7 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }} th.stream_actions { width: 26%; } + {{super()}} {% endblock %} @@ -246,6 +247,7 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }}
@@ -271,12 +273,13 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }} {% for rev in streams %} - {% if (rev.type != 6 and rev.type != 7) %} + {% if (rev.type != 7 and rev.type != 8 and rev.type != 0) %} {{ loop.index }} {{ rev.name }} - {{ ['RELAY', 'ENCODE', 'TIMESHIFT_PLAYER', 'TIMESHIFT_RECORDER', 'CATCHUP', + {{ ['PROXY','RELAY', 'ENCODE', 'TIMESHIFT_PLAYER', 'TIMESHIFT_RECORDER', + 'CATCHUP', 'TEST_LIFE', 'VOD_RELAY', 'VOD_ENCODE'][rev.type] }} @@ -411,12 +414,13 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }} {% for vod in streams %} - {% if (vod.type == 6 or vod.type == 7) %} + {% if (vod.type == 7 or vod.type == 8) %} {{ loop.index }} {{ vod.name }} - {{ ['RELAY', 'ENCODE', 'TIMESHIFT_PLAYER', 'TIMESHIFT_RECORDER', 'CATCHUP', + {{ ['PROXY','RELAY', 'ENCODE', 'TIMESHIFT_PLAYER', 'TIMESHIFT_RECORDER', + 'CATCHUP', 'TEST_LIFE', 'VOD_RELAY', 'VOD_ENCODE'][vod.type] }} @@ -482,6 +486,51 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }}
+
+
+ + + + + + + + + + + {% for proxy in streams %} + {% if (proxy.type == 0) %} + + + + + + + {% endif %} + {% endfor %} + +
#{% trans %}Name{% endtrans %}{% trans %}Type{% endtrans %}{% trans %}Actions{% endtrans %}
{{ loop.index }}{{ proxy.name }} + {{ ['PROXY','RELAY', 'ENCODE', 'TIMESHIFT_PLAYER', 'TIMESHIFT_RECORDER', + 'CATCHUP', + 'TEST_LIFE', + 'VOD_RELAY', 'VOD_ENCODE'][proxy.type] }} + + + +
+
+
+ +
+
@@ -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 }} } }); } + {% endblock %} diff --git a/scripts/test_life.py b/scripts/test_life.py index ca4dbc9..c9ecf5e 100755 --- a/scripts/test_life.py +++ b/scripts/test_life.py @@ -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)