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:
parent
6203746a70
commit
a2d932de68
15 changed files with 338 additions and 148 deletions
|
@ -2,6 +2,7 @@
|
||||||
[Alexandr Topilski]
|
[Alexandr Topilski]
|
||||||
- User agents
|
- User agents
|
||||||
- Subscribers
|
- Subscribers
|
||||||
|
- Proxy streams
|
||||||
|
|
||||||
1.1.0 / June 6, 2019
|
1.1.0 / June 6, 2019
|
||||||
[Alexandr Topilski]
|
[Alexandr Topilski]
|
||||||
|
|
|
@ -2,14 +2,15 @@ from enum import IntEnum
|
||||||
|
|
||||||
|
|
||||||
class StreamType(IntEnum):
|
class StreamType(IntEnum):
|
||||||
RELAY = 0
|
PROXY = 0,
|
||||||
ENCODE = 1
|
RELAY = 1
|
||||||
TIMESHIFT_PLAYER = 2
|
ENCODE = 2
|
||||||
TIMESHIFT_RECORDER = 3
|
TIMESHIFT_PLAYER = 3
|
||||||
CATCHUP = 4
|
TIMESHIFT_RECORDER = 4
|
||||||
TEST_LIFE = 5,
|
CATCHUP = 5
|
||||||
VOD_RELAY = 6,
|
TEST_LIFE = 6,
|
||||||
VOD_ENCODE = 7
|
VOD_RELAY = 7,
|
||||||
|
VOD_ENCODE = 8
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def choices(cls):
|
def choices(cls):
|
||||||
|
@ -69,11 +70,6 @@ DEFAULT_LOCALE = 'en'
|
||||||
AVAILABLE_LOCALES = DEFAULT_LOCALE, 'ru'
|
AVAILABLE_LOCALES = DEFAULT_LOCALE, 'ru'
|
||||||
AVAILABLE_LOCALES_PAIRS = [(DEFAULT_LOCALE, 'English'), ('ru', 'Russian')]
|
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'),
|
AVAILABLE_LOG_LEVELS_PAIRS = [(StreamLogLevel.LOG_LEVEL_EMERG, 'EVERG'), (StreamLogLevel.LOG_LEVEL_ALERT, 'ALERT'),
|
||||||
(StreamLogLevel.LOG_LEVEL_CRIT, 'CRITICAL'),
|
(StreamLogLevel.LOG_LEVEL_CRIT, 'CRITICAL'),
|
||||||
(StreamLogLevel.LOG_LEVEL_ERR, 'ERROR'),
|
(StreamLogLevel.LOG_LEVEL_ERR, 'ERROR'),
|
||||||
|
|
|
@ -55,7 +55,8 @@ class ActivateForm(FlaskForm):
|
||||||
|
|
||||||
|
|
||||||
class UploadM3uForm(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.CATCHUP, 'Catchup'),
|
||||||
(constants.StreamType.TEST_LIFE, 'Test life'),
|
(constants.StreamType.TEST_LIFE, 'Test life'),
|
||||||
(constants.StreamType.VOD_RELAY, 'Vod relay'),
|
(constants.StreamType.VOD_RELAY, 'Vod relay'),
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
from bson.objectid import ObjectId
|
from bson.objectid import ObjectId
|
||||||
|
|
||||||
from app.stream.stream_entry import Stream, EncodeStream, RelayStream, TimeshiftRecorderStream, CatchupStream, \
|
from app.stream.stream_entry import IStream, ProxyStream, EncodeStream, RelayStream, TimeshiftRecorderStream, \
|
||||||
TimeshiftPlayerStream, TestLifeStream, make_encode_stream, make_relay_stream, make_timeshift_recorder_stream, \
|
CatchupStream, \
|
||||||
make_catchup_stream, make_timeshift_player_stream, make_test_life_stream, make_vod_encode_stream, \
|
TimeshiftPlayerStream, TestLifeStream, VodRelayStream, VodEncodeStream
|
||||||
make_vod_relay_stream
|
|
||||||
from app.client.client_constants import ClientStatus
|
from app.client.client_constants import ClientStatus
|
||||||
|
|
||||||
from app.service.service_entry import ServiceSettings
|
from app.service.service_entry import ServiceSettings
|
||||||
|
@ -216,29 +215,32 @@ class Service(IStreamHandler):
|
||||||
ServiceFields.VERSION: self.version, ServiceFields.UPTIME: self._uptime,
|
ServiceFields.VERSION: self.version, ServiceFields.UPTIME: self._uptime,
|
||||||
ServiceFields.TIMESTAMP: self._timestamp, ServiceFields.STATUS: self.status}
|
ServiceFields.TIMESTAMP: self._timestamp, ServiceFields.STATUS: self.status}
|
||||||
|
|
||||||
def make_relay_stream(self) -> RelayStream:
|
def make_proxy_stream(self) -> ProxyStream:
|
||||||
return make_relay_stream(self._settings)
|
return ProxyStream.make_stream(self._settings)
|
||||||
|
|
||||||
def make_vod_relay_stream(self) -> RelayStream:
|
def make_relay_stream(self) -> RelayStream:
|
||||||
return make_vod_relay_stream(self._settings)
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
def make_test_life_stream(self) -> TestLifeStream:
|
||||||
return make_test_life_stream(self._settings)
|
return TestLifeStream.make_stream(self._settings)
|
||||||
|
|
||||||
# handler
|
# handler
|
||||||
def on_stream_statistic_received(self, params: dict):
|
def on_stream_statistic_received(self, params: dict):
|
||||||
|
@ -303,7 +305,7 @@ class Service(IStreamHandler):
|
||||||
self._uptime = stats[ServiceFields.UPTIME]
|
self._uptime = stats[ServiceFields.UPTIME]
|
||||||
self._timestamp = stats[ServiceFields.TIMESTAMP]
|
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)
|
stream.set_server_settings(self._settings)
|
||||||
|
|
||||||
def __reload_from_db(self):
|
def __reload_from_db(self):
|
||||||
|
|
|
@ -3,7 +3,7 @@ from mongoengine import Document, ListField, EmbeddedDocumentField, ReferenceFie
|
||||||
import app.constants as constants
|
import app.constants as constants
|
||||||
|
|
||||||
from app.service.server_entry import ServerSettings
|
from app.service.server_entry import ServerSettings
|
||||||
from app.stream.stream_entry import Stream
|
from app.stream.stream_entry import IStream
|
||||||
|
|
||||||
|
|
||||||
# #EXTM3U
|
# #EXTM3U
|
||||||
|
@ -18,7 +18,7 @@ class UserPair(EmbeddedDocument):
|
||||||
class ServiceSettings(Document, ServerSettings):
|
class ServiceSettings(Document, ServerSettings):
|
||||||
meta = {'collection': 'services', 'auto_create_index': False}
|
meta = {'collection': 'services', 'auto_create_index': False}
|
||||||
|
|
||||||
streams = ListField(EmbeddedDocumentField(Stream), default=[])
|
streams = ListField(EmbeddedDocumentField(IStream), default=[])
|
||||||
users = ListField(EmbeddedDocumentField(UserPair), default=[])
|
users = ListField(EmbeddedDocumentField(UserPair), default=[])
|
||||||
subscribers = ListField(ReferenceField('Subscriber'), default=[])
|
subscribers = ListField(ReferenceField('Subscriber'), default=[])
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,9 @@ class ServiceView(FlaskView):
|
||||||
m3u_parser.parse()
|
m3u_parser.parse()
|
||||||
|
|
||||||
for file in m3u_parser.files:
|
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 = server.make_relay_stream()
|
||||||
stream.output.urls[0] = stream.generate_http_link()
|
stream.output.urls[0] = stream.generate_http_link()
|
||||||
elif stream_type == constants.StreamType.ENCODE:
|
elif stream_type == constants.StreamType.ENCODE:
|
||||||
|
|
|
@ -105,22 +105,66 @@ class ChannelInfo:
|
||||||
ChannelInfo.AUDIO_ENABLE_FIELD: self.have_audio}
|
ChannelInfo.AUDIO_ENABLE_FIELD: self.have_audio}
|
||||||
|
|
||||||
|
|
||||||
class Stream(EmbeddedDocument):
|
class IStream(EmbeddedDocument):
|
||||||
meta = {'allow_inheritance': True, 'auto_create_index': True}
|
meta = {'allow_inheritance': True, 'auto_create_index': True}
|
||||||
|
|
||||||
id = ObjectIdField(required=True, default=ObjectId,
|
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,
|
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,
|
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,
|
group = StringField(default=constants.DEFAULT_STREAM_GROUP_TITLE, min_length=constants.MIN_STREAM_GROUP_TITLE,
|
||||||
max_length=constants.MAX_STREAM_GROUP_TITLE, required=False)
|
max_length=constants.MAX_STREAM_GROUP_TITLE, required=False)
|
||||||
created_date = DateTimeField(default=datetime.now) # for inner use
|
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)
|
log_level = IntField(default=constants.StreamLogLevel.LOG_LEVEL_INFO, required=True)
|
||||||
|
|
||||||
input = EmbeddedDocumentField(InputUrls, default=InputUrls())
|
input = EmbeddedDocumentField(InputUrls, default=InputUrls())
|
||||||
output = EmbeddedDocumentField(OutputUrls, default=OutputUrls())
|
|
||||||
have_video = BooleanField(default=constants.DEFAULT_HAVE_VIDEO, required=True)
|
have_video = BooleanField(default=constants.DEFAULT_HAVE_VIDEO, required=True)
|
||||||
have_audio = BooleanField(default=constants.DEFAULT_HAVE_AUDIO, required=True)
|
have_audio = BooleanField(default=constants.DEFAULT_HAVE_AUDIO, required=True)
|
||||||
audio_select = IntField(default=constants.INVALID_AUDIO_SELECT, required=True)
|
audio_select = IntField(default=constants.INVALID_AUDIO_SELECT, required=True)
|
||||||
|
@ -139,13 +183,12 @@ class Stream(EmbeddedDocument):
|
||||||
_start_time = 0
|
_start_time = 0
|
||||||
_input_streams = str()
|
_input_streams = str()
|
||||||
_output_streams = str()
|
_output_streams = str()
|
||||||
_settings = ServerSettings()
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(Stream, self).__init__(*args, **kwargs)
|
super(HardwareStream, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def set_server_settings(self, settings: ServerSettings):
|
def get_type(self):
|
||||||
self._settings = settings
|
raise NotImplementedError('subclasses must override get_type()!')
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self._status = constants.StreamStatus.NEW
|
self._status = constants.StreamStatus.NEW
|
||||||
|
@ -172,11 +215,17 @@ class Stream(EmbeddedDocument):
|
||||||
self._output_streams = params[StreamFields.OUTPUT_STREAMS]
|
self._output_streams = params[StreamFields.OUTPUT_STREAMS]
|
||||||
|
|
||||||
def to_front(self) -> dict:
|
def to_front(self) -> dict:
|
||||||
return {StreamFields.NAME: self.name, StreamFields.ID: self.get_id(), StreamFields.TYPE: self.get_type(),
|
front = super(HardwareStream, self).to_front()
|
||||||
StreamFields.STATUS: self._status, StreamFields.CPU: self._cpu, StreamFields.TIMESTAMP: self._timestamp,
|
front[StreamFields.STATUS] = self._status
|
||||||
StreamFields.RSS: self._rss, StreamFields.LOOP_START_TIME: self._loop_start_time,
|
front[StreamFields.CPU] = self._cpu
|
||||||
StreamFields.RESTARTS: self._restarts, StreamFields.START_TIME: self._start_time,
|
front[StreamFields.TIMESTAMP] = self._timestamp
|
||||||
StreamFields.INPUT_STREAMS: self._input_streams, StreamFields.OUTPUT_STREAMS: self._output_streams}
|
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:
|
def config(self) -> dict:
|
||||||
conf = {
|
conf = {
|
||||||
|
@ -199,13 +248,6 @@ class Stream(EmbeddedDocument):
|
||||||
conf[AUDIO_SELECT_FIELD] = audio_select
|
conf[AUDIO_SELECT_FIELD] = audio_select
|
||||||
return conf
|
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):
|
def generate_feedback_dir(self):
|
||||||
return '{0}/{1}/{2}'.format(self._settings.feedback_directory, self.get_type(), self.get_id())
|
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):
|
def get_have_audio(self):
|
||||||
return self.have_audio
|
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):
|
def get_loop(self):
|
||||||
return self.loop
|
return self.loop
|
||||||
|
|
||||||
|
@ -251,9 +287,6 @@ class Stream(EmbeddedDocument):
|
||||||
def get_auto_exit_time(self):
|
def get_auto_exit_time(self):
|
||||||
return self.auto_exit_time
|
return self.auto_exit_time
|
||||||
|
|
||||||
def fixup_output_urls(self):
|
|
||||||
return
|
|
||||||
|
|
||||||
# private
|
# private
|
||||||
def _generate_http_root_dir(self, oid: int):
|
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)
|
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)
|
self.output.urls[idx] = self.generate_vod_link(filename)
|
||||||
|
|
||||||
|
|
||||||
class RelayStream(Stream):
|
class RelayStream(HardwareStream):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(RelayStream, self).__init__(*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)
|
video_parser = StringField(default=constants.DEFAULT_VIDEO_PARSER, required=True)
|
||||||
audio_parser = StringField(default=constants.DEFAULT_AUDIO_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):
|
def fixup_output_urls(self):
|
||||||
return self._fixup_http_output_urls()
|
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):
|
def __init__(self, *args, **kwargs):
|
||||||
super(EncodeStream, self).__init__(*args, **kwargs)
|
super(EncodeStream, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
@ -395,6 +435,14 @@ class EncodeStream(Stream):
|
||||||
def fixup_output_urls(self):
|
def fixup_output_urls(self):
|
||||||
return self._fixup_http_output_urls()
|
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):
|
class TimeshiftRecorderStream(RelayStream):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -422,6 +470,13 @@ class TimeshiftRecorderStream(RelayStream):
|
||||||
def fixup_output_urls(self):
|
def fixup_output_urls(self):
|
||||||
return
|
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):
|
class CatchupStream(TimeshiftRecorderStream):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -432,6 +487,13 @@ class CatchupStream(TimeshiftRecorderStream):
|
||||||
def get_type(self):
|
def get_type(self):
|
||||||
return constants.StreamType.CATCHUP
|
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):
|
class TimeshiftPlayerStream(RelayStream):
|
||||||
timeshift_dir = StringField(required=True) # FIXME default
|
timeshift_dir = StringField(required=True) # FIXME default
|
||||||
|
@ -449,6 +511,14 @@ class TimeshiftPlayerStream(RelayStream):
|
||||||
conf[TIMESHIFT_DELAY] = self.timeshift_delay
|
conf[TIMESHIFT_DELAY] = self.timeshift_delay
|
||||||
return conf
|
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):
|
class TestLifeStream(RelayStream):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -464,6 +534,14 @@ class TestLifeStream(RelayStream):
|
||||||
def fixup_output_urls(self):
|
def fixup_output_urls(self):
|
||||||
return
|
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):
|
class VodRelayStream(RelayStream):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -481,6 +559,14 @@ class VodRelayStream(RelayStream):
|
||||||
def fixup_output_urls(self):
|
def fixup_output_urls(self):
|
||||||
return self._fixup_vod_output_urls()
|
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):
|
class VodEncodeStream(EncodeStream):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -498,66 +584,10 @@ class VodEncodeStream(EncodeStream):
|
||||||
def fixup_output_urls(self):
|
def fixup_output_urls(self):
|
||||||
return self._fixup_vod_output_urls()
|
return self._fixup_vod_output_urls()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
def make_relay_stream(settings: ServerSettings) -> RelayStream:
|
def make_stream(cls, settings: ServerSettings):
|
||||||
stream = RelayStream()
|
stream = cls()
|
||||||
stream._settings = settings
|
stream._settings = settings
|
||||||
stream.input = InputUrls(urls=[InputUrl(id=InputUrl.generate_id())])
|
stream.input = InputUrls(urls=[InputUrl(id=InputUrl.generate_id())])
|
||||||
stream.output = OutputUrls(urls=[OutputUrl(id=OutputUrl.generate_id())])
|
stream.output = OutputUrls(urls=[OutputUrl(id=OutputUrl.generate_id())])
|
||||||
return stream
|
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
|
|
||||||
|
|
|
@ -5,12 +5,12 @@ from wtforms.validators import InputRequired, Length, NumberRange
|
||||||
from wtforms.fields import StringField, SubmitField, SelectField, IntegerField, FormField, BooleanField, FloatField
|
from wtforms.fields import StringField, SubmitField, SelectField, IntegerField, FormField, BooleanField, FloatField
|
||||||
|
|
||||||
import app.constants as constants
|
import app.constants as constants
|
||||||
from app.stream.stream_entry import Stream, RelayStream, EncodeStream, TimeshiftRecorderStream, CatchupStream, \
|
from app.stream.stream_entry import ProxyStream, HardwareStream, RelayStream, EncodeStream, TimeshiftRecorderStream, \
|
||||||
TimeshiftPlayerStream, TestLifeStream, VodRelayStream, VodEncodeStream
|
CatchupStream, TimeshiftPlayerStream, TestLifeStream, VodRelayStream, VodEncodeStream
|
||||||
from app.common_forms import InputUrlsForm, OutputUrlsForm, SizeForm, LogoForm, RationalForm
|
from app.common_forms import InputUrlsForm, OutputUrlsForm, SizeForm, LogoForm, RationalForm
|
||||||
|
|
||||||
|
|
||||||
class StreamForm(FlaskForm):
|
class IStreamForm(FlaskForm):
|
||||||
name = StringField(lazy_gettext(u'Name:'),
|
name = StringField(lazy_gettext(u'Name:'),
|
||||||
validators=[InputRequired(),
|
validators=[InputRequired(),
|
||||||
Length(min=constants.MIN_STREAM_NAME_LENGTH, max=constants.MAX_STREAM_NAME_LENGTH)])
|
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)])
|
validators=[InputRequired(), Length(min=constants.MIN_URL_LENGTH, max=constants.MAX_URL_LENGTH)])
|
||||||
group = StringField(lazy_gettext(u'Group:'),
|
group = StringField(lazy_gettext(u'Group:'),
|
||||||
validators=[Length(min=constants.MIN_STREAM_GROUP_TITLE, max=constants.MAX_STREAM_GROUP_TITLE)])
|
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:'))
|
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=[],
|
log_level = SelectField(lazy_gettext(u'Log level:'), validators=[],
|
||||||
choices=constants.AVAILABLE_LOG_LEVELS_PAIRS, coerce=constants.StreamLogLevel.coerce)
|
choices=constants.AVAILABLE_LOG_LEVELS_PAIRS, coerce=constants.StreamLogLevel.coerce)
|
||||||
audio_select = IntegerField(lazy_gettext(u'Audio select:'),
|
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:'),
|
restart_attempts = IntegerField(lazy_gettext(u'Max restart attempts and frozen:'),
|
||||||
validators=[NumberRange(1, 1000)])
|
validators=[NumberRange(1, 1000)])
|
||||||
auto_exit_time = IntegerField(lazy_gettext(u'Auto exit time:'), validators=[])
|
auto_exit_time = IntegerField(lazy_gettext(u'Auto exit time:'), validators=[])
|
||||||
submit = SubmitField(lazy_gettext(u'Confirm'))
|
|
||||||
|
|
||||||
def make_entry(self):
|
def make_entry(self):
|
||||||
return self.update_entry(Stream())
|
return self.update_entry(HardwareStream())
|
||||||
|
|
||||||
def update_entry(self, entry: Stream):
|
def update_entry(self, entry: HardwareStream):
|
||||||
entry.name = self.name.data
|
|
||||||
entry.input = self.input.get_data()
|
entry.input = self.input.get_data()
|
||||||
entry.output = self.output.get_data()
|
|
||||||
|
|
||||||
entry.audio_select = self.audio_select.data
|
entry.audio_select = self.audio_select.data
|
||||||
entry.have_video = self.have_video.data
|
entry.have_video = self.have_video.data
|
||||||
|
@ -52,7 +65,7 @@ class StreamForm(FlaskForm):
|
||||||
return entry
|
return entry
|
||||||
|
|
||||||
|
|
||||||
class RelayStreamForm(StreamForm):
|
class RelayStreamForm(HardwareStreamForm):
|
||||||
video_parser = SelectField(lazy_gettext(u'Video parser:'), validators=[],
|
video_parser = SelectField(lazy_gettext(u'Video parser:'), validators=[],
|
||||||
choices=constants.AVAILABLE_VIDEO_PARSERS)
|
choices=constants.AVAILABLE_VIDEO_PARSERS)
|
||||||
audio_parser = SelectField(lazy_gettext(u'Audio parser:'), validators=[],
|
audio_parser = SelectField(lazy_gettext(u'Audio parser:'), validators=[],
|
||||||
|
@ -67,7 +80,7 @@ class RelayStreamForm(StreamForm):
|
||||||
return super(RelayStreamForm, self).update_entry(entry)
|
return super(RelayStreamForm, self).update_entry(entry)
|
||||||
|
|
||||||
|
|
||||||
class EncodeStreamForm(StreamForm):
|
class EncodeStreamForm(HardwareStreamForm):
|
||||||
relay_video = BooleanField(lazy_gettext(u'Relay video:'), validators=[])
|
relay_video = BooleanField(lazy_gettext(u'Relay video:'), validators=[])
|
||||||
relay_audio = BooleanField(lazy_gettext(u'Relay audio:'), validators=[])
|
relay_audio = BooleanField(lazy_gettext(u'Relay audio:'), validators=[])
|
||||||
deinterlace = BooleanField(lazy_gettext(u'Deinterlace:'), validators=[])
|
deinterlace = BooleanField(lazy_gettext(u'Deinterlace:'), validators=[])
|
||||||
|
|
|
@ -6,8 +6,8 @@ from flask_login import login_required, current_user
|
||||||
|
|
||||||
import app.constants as constants
|
import app.constants as constants
|
||||||
from app import get_runtime_stream_folder
|
from app import get_runtime_stream_folder
|
||||||
from app.stream.stream_forms import EncodeStreamForm, RelayStreamForm, TimeshiftRecorderStreamForm, CatchupStreamForm, \
|
from app.stream.stream_forms import ProxyStreamForm, EncodeStreamForm, RelayStreamForm, TimeshiftRecorderStreamForm, \
|
||||||
TimeshiftPlayerStreamForm, TestLifeStreamForm, VodEncodeStreamForm, VodRelayStreamForm
|
CatchupStreamForm, TimeshiftPlayerStreamForm, TestLifeStreamForm, VodEncodeStreamForm, VodRelayStreamForm
|
||||||
|
|
||||||
|
|
||||||
# routes
|
# routes
|
||||||
|
@ -100,6 +100,21 @@ class StreamView(FlaskView):
|
||||||
|
|
||||||
# broadcast routes
|
# 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
|
@login_required
|
||||||
@route('/add/relay', methods=['GET', 'POST'])
|
@route('/add/relay', methods=['GET', 'POST'])
|
||||||
def add_relay(self):
|
def add_relay(self):
|
||||||
|
@ -234,7 +249,16 @@ class StreamView(FlaskView):
|
||||||
stream = server.find_stream_by_id(sid)
|
stream = server.find_stream_by_id(sid)
|
||||||
if stream:
|
if stream:
|
||||||
type = stream.get_type()
|
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)
|
form = RelayStreamForm(obj=stream)
|
||||||
|
|
||||||
if request.method == 'POST' and form.validate_on_submit():
|
if request.method == 'POST' and form.validate_on_submit():
|
||||||
|
|
4
app/templates/stream/proxy/add.html
Normal file
4
app/templates/stream/proxy/add.html
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{% extends 'stream/proxy/base.html' %}
|
||||||
|
{% block title %}
|
||||||
|
Add proxy stream
|
||||||
|
{% endblock %}
|
47
app/templates/stream/proxy/base.html
Normal file
47
app/templates/stream/proxy/base.html
Normal 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">×</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>
|
4
app/templates/stream/proxy/edit.html
Normal file
4
app/templates/stream/proxy/edit.html
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{% extends 'stream/proxy/base.html' %}
|
||||||
|
{% block title %}
|
||||||
|
Edit proxy stream
|
||||||
|
{% endblock %}
|
|
@ -32,6 +32,10 @@
|
||||||
<br>
|
<br>
|
||||||
{{ render_bootstrap_field(form.name) }}
|
{{ render_bootstrap_field(form.name) }}
|
||||||
<br>
|
<br>
|
||||||
|
{{ render_bootstrap_field(form.icon) }}
|
||||||
|
<br>
|
||||||
|
{{ render_bootstrap_field(form.group) }}
|
||||||
|
<br>
|
||||||
{{ render_bootstrap_field(form.audio_select) }}
|
{{ render_bootstrap_field(form.audio_select) }}
|
||||||
<br>
|
<br>
|
||||||
{{ render_bootstrap_field(form.have_video) }}
|
{{ render_bootstrap_field(form.have_video) }}
|
||||||
|
|
|
@ -44,6 +44,7 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }}
|
||||||
th.stream_actions {
|
th.stream_actions {
|
||||||
width: 26%;
|
width: 26%;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
{{super()}}
|
{{super()}}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -246,6 +247,7 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }}
|
||||||
<ul class="nav nav-tabs">
|
<ul class="nav nav-tabs">
|
||||||
<li class="active"><a href="#streams" data-toggle="tab">Streams</a></li>
|
<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="#vods" data-toggle="tab">Vods</a></li>
|
||||||
|
<li><a href="#proxy" data-toggle="tab">Proxy</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
@ -271,12 +273,13 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }}
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for rev in streams %}
|
{% 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 }}'>
|
<tr id='{{ rev.id }}'>
|
||||||
<td>{{ loop.index }}</td>
|
<td>{{ loop.index }}</td>
|
||||||
<td>{{ rev.name }}</td>
|
<td>{{ rev.name }}</td>
|
||||||
<td>
|
<td>
|
||||||
{{ ['RELAY', 'ENCODE', 'TIMESHIFT_PLAYER', 'TIMESHIFT_RECORDER', 'CATCHUP',
|
{{ ['PROXY','RELAY', 'ENCODE', 'TIMESHIFT_PLAYER', 'TIMESHIFT_RECORDER',
|
||||||
|
'CATCHUP',
|
||||||
'TEST_LIFE',
|
'TEST_LIFE',
|
||||||
'VOD_RELAY', 'VOD_ENCODE'][rev.type] }}
|
'VOD_RELAY', 'VOD_ENCODE'][rev.type] }}
|
||||||
</td>
|
</td>
|
||||||
|
@ -411,12 +414,13 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }}
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for vod in streams %}
|
{% for vod in streams %}
|
||||||
{% if (vod.type == 6 or vod.type == 7) %}
|
{% if (vod.type == 7 or vod.type == 8) %}
|
||||||
<tr id='{{ vod.id }}'>
|
<tr id='{{ vod.id }}'>
|
||||||
<td>{{ loop.index }}</td>
|
<td>{{ loop.index }}</td>
|
||||||
<td>{{ vod.name }}</td>
|
<td>{{ vod.name }}</td>
|
||||||
<td>
|
<td>
|
||||||
{{ ['RELAY', 'ENCODE', 'TIMESHIFT_PLAYER', 'TIMESHIFT_RECORDER', 'CATCHUP',
|
{{ ['PROXY','RELAY', 'ENCODE', 'TIMESHIFT_PLAYER', 'TIMESHIFT_RECORDER',
|
||||||
|
'CATCHUP',
|
||||||
'TEST_LIFE',
|
'TEST_LIFE',
|
||||||
'VOD_RELAY', 'VOD_ENCODE'][vod.type] }}
|
'VOD_RELAY', 'VOD_ENCODE'][vod.type] }}
|
||||||
</td>
|
</td>
|
||||||
|
@ -482,6 +486,51 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</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) {
|
function edit_stream(sid) {
|
||||||
var url = "/stream/edit/" + sid;
|
var url = "/stream/edit/" + sid;
|
||||||
$.get(url, function(data) {
|
$.get(url, function(data) {
|
||||||
|
@ -809,5 +870,6 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -6,7 +6,7 @@ from mongoengine import connect
|
||||||
|
|
||||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
|
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.service.service import ServiceSettings
|
||||||
from app.utils.m3u_parser import M3uParser
|
from app.utils.m3u_parser import M3uParser
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ if __name__ == '__main__':
|
||||||
m3u_parser.read_m3u(argv.uri)
|
m3u_parser.read_m3u(argv.uri)
|
||||||
m3u_parser.parse()
|
m3u_parser.parse()
|
||||||
for file in m3u_parser.files:
|
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.input.urls[0].uri = file['link']
|
||||||
stream.name = '{0}({1})'.format(file['tvg-group'], file['title'])
|
stream.name = '{0}({1})'.format(file['tvg-group'], file['title'])
|
||||||
service_settings.streams.append(stream)
|
service_settings.streams.append(stream)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue