1
0
mirror of https://github.com/fHDHR/fHDHR_NextPVR.git synced 2025-12-06 16:06:59 -05:00

Compare commits

..

No commits in common. "0de184c2428aedaf97314d276f62143da0a122be" and "13faf0845e88c7a62c9d21fb1f27623cd66b50b9" have entirely different histories.

145 changed files with 2211 additions and 2752 deletions

View File

@ -6,7 +6,7 @@
"config_web": true "config_web": true
}, },
"method":{ "method":{
"value": "none", "value": "blocks",
"config_file": true, "config_file": true,
"config_web": true "config_web": true
}, },

View File

@ -15,6 +15,26 @@
"config_file": true, "config_file": true,
"config_web": true "config_web": true
}, },
"reporting_manufacturer":{
"value": "BoronDust",
"config_file": true,
"config_web": true
},
"reporting_model":{
"value": "fHDHR",
"config_file": true,
"config_web": true
},
"reporting_firmware_ver":{
"value": "20201001",
"config_file": true,
"config_web": true
},
"reporting_tuner_type":{
"value": "Antenna",
"config_file": true,
"config_web": true
},
"device_auth":{ "device_auth":{
"value": "fHDHR", "value": "fHDHR",
"config_file": true, "config_file": true,
@ -34,6 +54,16 @@
"value": "fHDHR", "value": "fHDHR",
"config_file": true, "config_file": true,
"config_web": true "config_web": true
},
"tuner_count":{
"value": 4,
"config_file": true,
"config_web": true
},
"reporting_firmware_name":{
"value": "fHDHR",
"config_file": true,
"config_web": true
} }
} }
} }

View File

@ -15,6 +15,11 @@
"config_file": false, "config_file": false,
"config_web": false "config_web": false
}, },
"dictpopname":{
"value": "fHDHR",
"config_file": false,
"config_web": false
},
"reponame":{ "reponame":{
"value": "fHDHR", "value": "fHDHR",
"config_file": false, "config_file": false,

View File

@ -0,0 +1,9 @@
{
"rmg":{
"enabled":{
"value": true,
"config_file": true,
"config_web": false
}
}
}

View File

@ -1,28 +1,25 @@
# coding=utf-8 # coding=utf-8
from .originwrapper import OriginServiceWrapper
from .device import fHDHR_Device from .device import fHDHR_Device
from .api import fHDHR_API_URLs from .api import fHDHR_API_URLs
import fHDHR.tools import fHDHR.tools
fHDHR_VERSION = "v0.6.0-beta" fHDHR_VERSION = "v0.6.0-beta"
class fHDHR_INT_OBJ(): class fHDHR_INT_OBJ():
def __init__(self, settings, logger, db, plugins): def __init__(self, settings, logger, db):
self.version = fHDHR_VERSION self.version = fHDHR_VERSION
self.config = settings self.config = settings
self.logger = logger self.logger = logger
self.db = db self.db = db
self.plugins = plugins
self.web = fHDHR.tools.WebReq() self.web = fHDHR.tools.WebReq()
for plugin_name in list(self.plugins.plugins.keys()):
self.plugins.plugins[plugin_name].plugin_utils.web = self.web
self.api = fHDHR_API_URLs(settings, self.web) self.api = fHDHR_API_URLs(settings, self.web)
for plugin_name in list(self.plugins.plugins.keys()):
self.plugins.plugins[plugin_name].plugin_utils.api = self.api
self.threads = {} self.threads = {}
@ -30,11 +27,11 @@ class fHDHR_INT_OBJ():
class fHDHR_OBJ(): class fHDHR_OBJ():
def __init__(self, settings, logger, db, plugins): def __init__(self, settings, logger, db, plugins):
self.fhdhr = fHDHR_INT_OBJ(settings, logger, db, plugins) self.fhdhr = fHDHR_INT_OBJ(settings, logger, db)
self.fhdhr.origins = fHDHR.origins.Origins(self.fhdhr) self.originwrapper = OriginServiceWrapper(self.fhdhr, plugins.origin)
self.device = fHDHR_Device(self.fhdhr, self.fhdhr.origins) self.device = fHDHR_Device(self.fhdhr, self.originwrapper, plugins)
def __getattr__(self, name): def __getattr__(self, name):
''' will only get called for undefined attributes ''' ''' will only get called for undefined attributes '''

View File

@ -7,8 +7,6 @@ from fHDHR import fHDHR_VERSION, fHDHR_OBJ
import fHDHR.exceptions import fHDHR.exceptions
import fHDHR.config import fHDHR.config
import fHDHR.logger import fHDHR.logger
import fHDHR.plugins
import fHDHR.origins
from fHDHR.db import fHDHRdb from fHDHR.db import fHDHRdb
ERR_CODE = 1 ERR_CODE = 1
@ -27,10 +25,10 @@ def build_args_parser():
return parser.parse_args() return parser.parse_args()
def get_configuration(args, script_dir, fHDHR_web): def get_configuration(args, script_dir, plugins, fHDHR_web):
if not os.path.isfile(args.cfg): if not os.path.isfile(args.cfg):
raise fHDHR.exceptions.ConfigurationNotFound(filename=args.cfg) raise fHDHR.exceptions.ConfigurationNotFound(filename=args.cfg)
return fHDHR.config.Config(args.cfg, script_dir, fHDHR_web) return fHDHR.config.Config(args.cfg, script_dir, plugins, fHDHR_web)
def run(settings, logger, db, script_dir, fHDHR_web, plugins): def run(settings, logger, db, script_dir, fHDHR_web, plugins):
@ -66,41 +64,23 @@ def run(settings, logger, db, script_dir, fHDHR_web, plugins):
return ERR_CODE return ERR_CODE
def start(args, script_dir, fHDHR_web): def start(args, script_dir, fHDHR_web, plugins):
"""Get Configuration for fHDHR and start""" """Get Configuration for fHDHR and start"""
try: try:
settings = get_configuration(args, script_dir, fHDHR_web) settings = get_configuration(args, script_dir, plugins, fHDHR_web)
except fHDHR.exceptions.ConfigurationError as e: except fHDHR.exceptions.ConfigurationError as e:
print(e) print(e)
return ERR_CODE_NO_RESTART return ERR_CODE_NO_RESTART
# Find Plugins and import their default configs
plugins = fHDHR.plugins.PluginsHandler(settings)
# Apply User Configuration
settings.user_config()
settings.config_verification()
# Setup Logging
logger = fHDHR.logger.Logger(settings) logger = fHDHR.logger.Logger(settings)
# Setup Database
db = fHDHRdb(settings) db = fHDHRdb(settings)
# Setup Plugins
plugins.load_plugins(logger, db)
plugins.setup()
settings.config_verification_plugins()
if not len([x for x in list(plugins.plugins.keys()) if plugins.plugins[x].type == "origin"]):
print("No Origin Plugins found.")
return ERR_CODE
return run(settings, logger, db, script_dir, fHDHR_web, plugins) return run(settings, logger, db, script_dir, fHDHR_web, plugins)
def main(script_dir, fHDHR_web): def main(script_dir, fHDHR_web, plugins):
"""fHDHR run script entry point""" """fHDHR run script entry point"""
print("Loading fHDHR %s" % fHDHR_VERSION) print("Loading fHDHR %s" % fHDHR_VERSION)
@ -109,7 +89,7 @@ def main(script_dir, fHDHR_web):
try: try:
args = build_args_parser() args = build_args_parser()
while True: while True:
returned_code = start(args, script_dir, fHDHR_web) returned_code = start(args, script_dir, fHDHR_web, plugins)
if returned_code not in ["restart"]: if returned_code not in ["restart"]:
return returned_code return returned_code
except KeyboardInterrupt: except KeyboardInterrupt:

View File

@ -13,28 +13,29 @@ from fHDHR.tools import isint, isfloat, is_arithmetic, is_docker
class Config(): class Config():
def __init__(self, filename, script_dir, fHDHR_web): def __init__(self, filename, script_dir, plugins, fHDHR_web):
self.plugins = plugins
self.fHDHR_web = fHDHR_web self.fHDHR_web = fHDHR_web
self.internal = {} self.internal = {}
self.conf_default = {} self.conf_default = {}
self.dict = {} self.dict = {}
self.internal["versions"] = {}
self.config_file = filename self.config_file = filename
self.core_setup(script_dir) self.core_setup(script_dir)
self.plugins_setup()
self.user_config()
self.config_verification()
def core_setup(self, script_dir): def core_setup(self, script_dir):
data_dir = pathlib.Path(script_dir).joinpath('data') data_dir = pathlib.Path(script_dir).joinpath('data')
internal_plugins_dir = pathlib.Path(script_dir).joinpath('plugins')
fHDHR_web_dir = pathlib.Path(script_dir).joinpath('fHDHR_web') fHDHR_web_dir = pathlib.Path(script_dir).joinpath('fHDHR_web')
www_dir = pathlib.Path(fHDHR_web_dir).joinpath('www_dir') www_dir = pathlib.Path(fHDHR_web_dir).joinpath('www_dir')
self.internal["paths"] = { self.internal["paths"] = {
"script_dir": script_dir, "script_dir": script_dir,
"data_dir": data_dir, "data_dir": data_dir,
"plugins_dir": [internal_plugins_dir],
"cache_dir": pathlib.Path(data_dir).joinpath('cache'), "cache_dir": pathlib.Path(data_dir).joinpath('cache'),
"internal_config": pathlib.Path(data_dir).joinpath('internal_config'), "internal_config": pathlib.Path(data_dir).joinpath('internal_config'),
"fHDHR_web_dir": fHDHR_web_dir, "fHDHR_web_dir": fHDHR_web_dir,
@ -53,61 +54,68 @@ class Config():
if str(file_item_path).endswith("_conf.json"): if str(file_item_path).endswith("_conf.json"):
self.read_json_config(file_item_path) self.read_json_config(file_item_path)
self.dict["epg"]["valid_methods"] = {None: {}} self.dict["epg"]["valid_methods"] = ["origin", "blocks", None]
self.dict["origins"] = {}
self.dict["origins"]["valid_methods"] = {} self.dict["streaming"]["valid_methods"] = ["direct"]
self.dict["streaming"]["valid_methods"] = {"direct": {}}
self.dict["plugin_web_paths"] = {}
self.load_versions() self.load_versions()
def register_web_path(self, name, path, plugin_dict_name): def plugins_setup(self):
self.dict["plugin_web_paths"][name.lower()] = {
"name": name,
"namespace": name.lower(),
"path": path,
"plugin": plugin_dict_name
}
def register_valid_origin_method(self, method_item): # Load Origin Paths
self.dict["origins"]["valid_methods"][method_item.lower()] = { origin_dir = [self.plugins.plugin_dict[x]["PATH"] for x in list(self.plugins.plugin_dict.keys()) if self.plugins.plugin_dict[x]["TYPE"] == "origin"][0]
"name": method_item, self.internal["paths"]["origin"] = origin_dir
"namespace": method_item.lower(), self.internal["paths"]["origin_web"] = pathlib.Path(origin_dir).joinpath('origin_web')
}
def register_valid_streaming_method(self, method_item, plugin_dict_name): # Load Plugin Conf
self.dict["streaming"]["valid_methods"][method_item.lower()] = { for dir_type in ["alt_epg", "origin", "alt_stream"]:
"name": method_item, if dir_type == "origin":
"namespace": method_item.lower(), dir_tops = [self.internal["paths"]["origin"]]
"plugin": plugin_dict_name elif dir_type in ["alt_stream", "alt_epg"]:
} dir_tops = [self.plugins.plugin_dict[x]["PATH"] for x in list(self.plugins.plugin_dict.keys()) if self.plugins.plugin_dict[x]["TYPE"] == dir_type]
for top_dir in dir_tops:
def register_valid_epg_method(self, method_item, plugin_dict_name): for file_item in os.listdir(top_dir):
self.dict["epg"]["valid_methods"][method_item.lower()] = { file_item_path = pathlib.Path(top_dir).joinpath(file_item)
"name": method_item, if file_item_path.is_dir():
"namespace": method_item.lower(), for sub_file_item in os.listdir(file_item_path):
"plugin": plugin_dict_name sub_file_item_path = pathlib.Path(file_item_path).joinpath(sub_file_item)
} if str(sub_file_item_path).endswith("_conf.json"):
self.read_json_config(sub_file_item_path)
def register_version(self, item_name, item_version, item_type): else:
self.internal["versions"][item_name] = { if str(file_item_path).endswith("_conf.json"):
"name": item_name,
"version": item_version,
"type": item_type
}
def import_conf_json(self, file_item_path):
self.read_json_config(file_item_path) self.read_json_config(file_item_path)
# Rename the Origin conf section
self.dict["origin"] = self.dict.pop(self.dict["main"]["dictpopname"])
# Get Pltuin Version
for plugin_item in list(self.plugins.plugin_dict.keys()):
self.internal["versions"][plugin_item] = self.plugins.plugin_dict[plugin_item]["VERSION"]
# Run Plugin Setup Checks
for plugin_item in list(self.plugins.plugin_dict.keys()):
try:
eval("self.plugins.%s_Setup(self)" % self.plugins.plugin_dict[plugin_item]["NAME"].upper())
except AttributeError:
pass
self.dict["epg"]["valid_methods"].extend([self.plugins.plugin_dict[x]["NAME"] for x in list(self.plugins.plugin_dict.keys()) if self.plugins.plugin_dict[x]["TYPE"] == "alt_epg"])
self.dict["streaming"]["valid_methods"].extend([self.plugins.plugin_dict[x]["NAME"] for x in list(self.plugins.plugin_dict.keys()) if self.plugins.plugin_dict[x]["TYPE"] == "alt_stream"])
def register_version(self, item_name, item_version):
self.internal["versions"][item_name] = item_version
def load_versions(self): def load_versions(self):
self.register_version("fHDHR", fHDHR_VERSION, "fHDHR") self.internal["versions"] = {}
self.register_version("fHDHR_web", self.fHDHR_web.fHDHR_web_VERSION, "fHDHR")
self.register_version("Python", sys.version, "env") self.internal["versions"]["fHDHR"] = fHDHR_VERSION
self.internal["versions"]["fHDHR_web"] = self.fHDHR_web.fHDHR_web_VERSION
self.internal["versions"]["Python"] = sys.version
opersystem = platform.system() opersystem = platform.system()
self.register_version("Operating System", opersystem, "env") self.internal["versions"]["Operating System"] = opersystem
if opersystem in ["Linux", "Darwin"]: if opersystem in ["Linux", "Darwin"]:
# Linux/Mac # Linux/Mac
if os.getuid() == 0 or os.geteuid() == 0: if os.getuid() == 0 or os.geteuid() == 0:
@ -120,19 +128,23 @@ class Config():
print("Uncommon Operating System, use at your own risk.") print("Uncommon Operating System, use at your own risk.")
isdocker = is_docker() isdocker = is_docker()
self.register_version("Docker", isdocker, "env") self.internal["versions"]["Docker"] = isdocker
def user_config(self): def user_config(self):
print("Loading Configuration File: %s" % self.config_file) print("Loading Configuration File: %s" % self.config_file)
self.read_ini_config(self.config_file) self.read_ini_config(self.config_file)
def config_verification_plugins(self): def config_verification(self):
required_missing = {} required_missing = {}
# create dict and combine items # create dict and combine items
for config_section in list(self.conf_default.keys()): for config_section in list(self.conf_default.keys()):
for config_item in list(self.conf_default[config_section].keys()): for config_item in list(self.conf_default[config_section].keys()):
if self.conf_default[config_section][config_item]["required"]: if self.conf_default[config_section][config_item]["required"]:
if not self.dict[config_section][config_item]: config_section_name = config_section
if config_section == self.dict["main"]["dictpopname"]:
config_section_name = "origin"
if not self.dict[config_section_name][config_item]:
if config_section not in list(required_missing.keys()): if config_section not in list(required_missing.keys()):
required_missing[config_section] = [] required_missing[config_section] = []
required_missing[config_section].append(config_item) required_missing[config_section].append(config_item)
@ -144,25 +156,19 @@ class Config():
self.dict["epg"]["method"] = [self.dict["epg"]["method"]] self.dict["epg"]["method"] = [self.dict["epg"]["method"]]
epg_methods = [] epg_methods = []
for epg_method in self.dict["epg"]["method"]: for epg_method in self.dict["epg"]["method"]:
if epg_method in list(self.dict["epg"]["valid_methods"].keys()): if epg_method == self.dict["main"]["dictpopname"] or epg_method == "origin":
epg_methods.append(epg_method) epg_methods.append("origin")
elif epg_method in list(self.dict["origins"]["valid_methods"].keys()): elif epg_method in ["None"]:
raise fHDHR.exceptions.ConfigurationError("Invalid EPG Method. Exiting...")
elif epg_method in self.dict["epg"]["valid_methods"]:
epg_methods.append(epg_method) epg_methods.append(epg_method)
else: else:
raise fHDHR.exceptions.ConfigurationError("Invalid EPG Method. Exiting...") raise fHDHR.exceptions.ConfigurationError("Invalid EPG Method. Exiting...")
if self.dict["epg"]["method"]:
self.dict["epg"]["def_method"] = self.dict["epg"]["method"][0] self.dict["epg"]["def_method"] = self.dict["epg"]["method"][0]
else:
self.dict["epg"]["def_method"] = None
if self.dict["streaming"]["method"] not in self.dict["streaming"]["valid_methods"]:
raise fHDHR.exceptions.ConfigurationError("Invalid stream type. Exiting...")
def config_verification(self):
if not self.dict["main"]["uuid"]: if not self.dict["main"]["uuid"]:
self.dict["main"]["uuid"] = ''.join(random.choice("hijklmnopqrstuvwxyz") for i in range(8)) self.dict["main"]["uuid"] = ''.join(random.choice("hijklmnopqrstuvwxyz") for i in range(8))
self.write('uuid', self.dict["main"]["uuid"], 'main') self.write('main', 'uuid', self.dict["main"]["uuid"])
if self.dict["main"]["cache_dir"]: if self.dict["main"]["cache_dir"]:
if not pathlib.Path(self.dict["main"]["cache_dir"]).is_dir(): if not pathlib.Path(self.dict["main"]["cache_dir"]).is_dir():
@ -177,6 +183,9 @@ class Config():
self.dict["database"]["path"] = pathlib.Path(cache_dir).joinpath('fhdhr.db') self.dict["database"]["path"] = pathlib.Path(cache_dir).joinpath('fhdhr.db')
if self.dict["streaming"]["method"] not in self.dict["streaming"]["valid_methods"]:
raise fHDHR.exceptions.ConfigurationError("Invalid stream type. Exiting...")
if not self.dict["fhdhr"]["discovery_address"] and self.dict["fhdhr"]["address"] != "0.0.0.0": if not self.dict["fhdhr"]["discovery_address"] and self.dict["fhdhr"]["address"] != "0.0.0.0":
self.dict["fhdhr"]["discovery_address"] = self.dict["fhdhr"]["address"] self.dict["fhdhr"]["discovery_address"] = self.dict["fhdhr"]["address"]
if not self.dict["fhdhr"]["discovery_address"] or self.dict["fhdhr"]["discovery_address"] == "0.0.0.0": if not self.dict["fhdhr"]["discovery_address"] or self.dict["fhdhr"]["discovery_address"] == "0.0.0.0":
@ -254,9 +263,11 @@ class Config():
import_val = False import_val = False
if import_val: if import_val:
if each_section == self.dict["main"]["dictpopname"]:
each_section = "origin"
self.dict[each_section.lower()][each_key.lower()] = each_val self.dict[each_section.lower()][each_key.lower()] = each_val
def write(self, key, value, section): def write(self, section, key, value):
if not value: if not value:
value = None value = None
@ -273,6 +284,8 @@ class Config():
elif isinstance(value, list): elif isinstance(value, list):
",".join(value) ",".join(value)
if section == self.dict["main"]["dictpopname"]:
section = "origin"
self.dict[section][key] = value self.dict[section][key] = value
config_handler = configparser.ConfigParser() config_handler = configparser.ConfigParser()

View File

@ -32,10 +32,28 @@ MYSQL_TABLE_ARGS = {'mysql_engine': 'InnoDB',
'mysql_collate': 'utf8mb4_unicode_ci'} 'mysql_collate': 'utf8mb4_unicode_ci'}
class PluginValues(BASE): class ChannelValues(BASE):
__tablename__ = 'plugin_values' __tablename__ = 'channel_values'
__table_args__ = MYSQL_TABLE_ARGS __table_args__ = MYSQL_TABLE_ARGS
pluginitem = Column(String(255), primary_key=True) channel = Column(String(255), primary_key=True)
namespace = Column(String(255), primary_key=True)
key = Column(String(255), primary_key=True)
value = Column(Text())
class ProgramValues(BASE):
__tablename__ = 'program_values'
__table_args__ = MYSQL_TABLE_ARGS
program = Column(String(255), primary_key=True)
namespace = Column(String(255), primary_key=True)
key = Column(String(255), primary_key=True)
value = Column(Text())
class CacheValues(BASE):
__tablename__ = 'cache_values'
__table_args__ = MYSQL_TABLE_ARGS
cacheitem = Column(String(255), primary_key=True)
namespace = Column(String(255), primary_key=True) namespace = Column(String(255), primary_key=True)
key = Column(String(255), primary_key=True) key = Column(String(255), primary_key=True)
value = Column(Text()) value = Column(Text())
@ -130,6 +148,198 @@ class fHDHRdb(object):
def get_uri(self): def get_uri(self):
return self.url return self.url
# Channel Values
def set_channel_value(self, channel, key, value, namespace='default'):
channel = channel.lower()
value = json.dumps(value, ensure_ascii=False)
session = self.ssession()
try:
result = session.query(ChannelValues) \
.filter(ChannelValues.channel == channel)\
.filter(ChannelValues.namespace == namespace)\
.filter(ChannelValues.key == key) \
.one_or_none()
# ChannelValues exists, update
if result:
result.value = value
session.commit()
# DNE - Insert
else:
new_channelvalue = ChannelValues(channel=channel, namespace=namespace, key=key, value=value)
session.add(new_channelvalue)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def get_channel_value(self, channel, key, namespace='default'):
channel = channel.lower()
session = self.ssession()
try:
result = session.query(ChannelValues) \
.filter(ChannelValues.channel == channel)\
.filter(ChannelValues.namespace == namespace)\
.filter(ChannelValues.key == key) \
.one_or_none()
if result is not None:
result = result.value
return _deserialize(result)
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def delete_channel_value(self, channel, key, namespace='default'):
channel = channel.lower()
session = self.ssession()
try:
result = session.query(ChannelValues) \
.filter(ChannelValues.channel == channel)\
.filter(ChannelValues.namespace == namespace)\
.filter(ChannelValues.key == key) \
.one_or_none()
# ChannelValues exists, delete
if result:
session.delete(result)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
# Program Values
def set_program_value(self, program, key, value, namespace='default'):
program = program.lower()
value = json.dumps(value, ensure_ascii=False)
session = self.ssession()
try:
result = session.query(ProgramValues) \
.filter(ProgramValues.program == program)\
.filter(ProgramValues.namespace == namespace)\
.filter(ProgramValues.key == key) \
.one_or_none()
# ProgramValue exists, update
if result:
result.value = value
session.commit()
# DNE - Insert
else:
new_programvalue = ProgramValues(program=program, namespace=namespace, key=key, value=value)
session.add(new_programvalue)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def get_program_value(self, program, key, namespace='default'):
program = program.lower()
session = self.ssession()
try:
result = session.query(ProgramValues) \
.filter(ProgramValues.program == program)\
.filter(ProgramValues.namespace == namespace)\
.filter(ProgramValues.key == key) \
.one_or_none()
if result is not None:
result = result.value
return _deserialize(result)
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def delete_program_value(self, program, key, namespace='default'):
program = program.lower()
session = self.ssession()
try:
result = session.query(ProgramValues) \
.filter(ProgramValues.program == program)\
.filter(ProgramValues.namespace == namespace)\
.filter(ProgramValues.key == key) \
.one_or_none()
# ProgramValue exists, delete
if result:
session.delete(result)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
# Cache Values
def set_cacheitem_value(self, cacheitem, key, value, namespace='default'):
cacheitem = cacheitem.lower()
value = json.dumps(value, ensure_ascii=False)
session = self.ssession()
try:
result = session.query(CacheValues) \
.filter(CacheValues.cacheitem == cacheitem)\
.filter(CacheValues.namespace == namespace)\
.filter(CacheValues.key == key) \
.one_or_none()
# ProgramValue exists, update
if result:
result.value = value
session.commit()
# DNE - Insert
else:
new_cacheitemvalue = CacheValues(cacheitem=cacheitem, namespace=namespace, key=key, value=value)
session.add(new_cacheitemvalue)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def get_cacheitem_value(self, cacheitem, key, namespace='default'):
cacheitem = cacheitem.lower()
session = self.ssession()
try:
result = session.query(CacheValues) \
.filter(CacheValues.cacheitem == cacheitem)\
.filter(CacheValues.namespace == namespace)\
.filter(CacheValues.key == key) \
.one_or_none()
if result is not None:
result = result.value
return _deserialize(result)
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def delete_cacheitem_value(self, cacheitem, key, namespace='default'):
cacheitem = cacheitem.lower()
session = self.ssession()
try:
result = session.query(CacheValues) \
.filter(CacheValues.cacheitem == cacheitem)\
.filter(CacheValues.namespace == namespace)\
.filter(CacheValues.key == key) \
.one_or_none()
# ProgramValue exists, delete
if result:
session.delete(result)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
# fHDHR Values # fHDHR Values
def set_fhdhr_value(self, item, key, value, namespace='default'): def set_fhdhr_value(self, item, key, value, namespace='default'):
@ -148,8 +358,8 @@ class fHDHRdb(object):
session.commit() session.commit()
# DNE - Insert # DNE - Insert
else: else:
new_pluginitemvalue = fHDHRValues(item=item, namespace=namespace, key=key, value=value) new_cacheitemvalue = fHDHRValues(item=item, namespace=namespace, key=key, value=value)
session.add(new_pluginitemvalue) session.add(new_cacheitemvalue)
session.commit() session.commit()
except SQLAlchemyError: except SQLAlchemyError:
session.rollback() session.rollback()
@ -193,67 +403,3 @@ class fHDHRdb(object):
raise raise
finally: finally:
session.close() session.close()
# Plugin Values
def set_plugin_value(self, pluginitem, key, value, namespace='default'):
pluginitem = pluginitem.lower()
value = json.dumps(value, ensure_ascii=False)
session = self.ssession()
try:
result = session.query(PluginValues) \
.filter(PluginValues.pluginitem == pluginitem)\
.filter(PluginValues.namespace == namespace)\
.filter(PluginValues.key == key) \
.one_or_none()
# ProgramValue exists, update
if result:
result.value = value
session.commit()
# DNE - Insert
else:
new_pluginitemvalue = PluginValues(pluginitem=pluginitem, namespace=namespace, key=key, value=value)
session.add(new_pluginitemvalue)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def get_plugin_value(self, pluginitem, key, namespace='default'):
pluginitem = pluginitem.lower()
session = self.ssession()
try:
result = session.query(PluginValues) \
.filter(PluginValues.pluginitem == pluginitem)\
.filter(PluginValues.namespace == namespace)\
.filter(PluginValues.key == key) \
.one_or_none()
if result is not None:
result = result.value
return _deserialize(result)
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def delete_plugin_value(self, pluginitem, key, namespace='default'):
pluginitem = pluginitem.lower()
session = self.ssession()
try:
result = session.query(PluginValues) \
.filter(PluginValues.pluginitem == pluginitem)\
.filter(PluginValues.namespace == namespace)\
.filter(PluginValues.key == key) \
.one_or_none()
# ProgramValue exists, delete
if result:
session.delete(result)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()

View File

@ -3,32 +3,21 @@ from .epg import EPG
from .tuners import Tuners from .tuners import Tuners
from .images import imageHandler from .images import imageHandler
from .ssdp import SSDPServer from .ssdp import SSDPServer
from .cluster import fHDHR_Cluster
class fHDHR_Device(): class fHDHR_Device():
def __init__(self, fhdhr, origins): def __init__(self, fhdhr, originwrapper, plugins):
self.fhdhr = fhdhr
self.channels = Channels(fhdhr, origins) self.channels = Channels(fhdhr, originwrapper)
self.epg = EPG(fhdhr, self.channels, origins) self.epg = EPG(fhdhr, self.channels, originwrapper, plugins)
self.tuners = Tuners(fhdhr, self.epg, self.channels) self.tuners = Tuners(fhdhr, self.epg, self.channels, plugins)
self.images = imageHandler(fhdhr, self.epg) self.images = imageHandler(fhdhr, self.epg)
self.ssdp = SSDPServer(fhdhr) self.ssdp = SSDPServer(fhdhr)
self.interfaces = {} self.cluster = fHDHR_Cluster(fhdhr, self.ssdp)
for plugin_name in list(self.fhdhr.plugins.plugins.keys()):
if self.fhdhr.plugins.plugins[plugin_name].manifest["type"] == "interface":
method = self.fhdhr.plugins.plugins[plugin_name].name.lower()
plugin_utils = self.fhdhr.plugins.plugins[plugin_name].plugin_utils
plugin_utils.channels = self.channels
plugin_utils.epg = self.epg
plugin_utils.tuners = self.tuners
plugin_utils.images = self.images
plugin_utils.ssdp = self.ssdp
self.interfaces[method] = self.fhdhr.plugins.plugins[plugin_name].Plugin_OBJ(fhdhr, plugin_utils)

View File

@ -8,111 +8,57 @@ from .chan_ident import Channel_IDs
class Channels(): class Channels():
def __init__(self, fhdhr, origins): def __init__(self, fhdhr, originwrapper):
self.fhdhr = fhdhr self.fhdhr = fhdhr
self.origins = origins self.origin = originwrapper
self.id_system = Channel_IDs(fhdhr, origins) self.id_system = Channel_IDs(fhdhr)
self.list = {} self.list = {}
for origin in list(self.origins.origins_dict.keys()):
self.list[origin] = {}
self.get_db_channels() self.get_db_channels()
def get_channel_obj(self, keyfind, valfind, origin=None): def get_channel_obj(self, keyfind, valfind):
if origin:
origin = origin.lower()
if keyfind == "number": if keyfind == "number":
matches = [self.list[origin][x].dict["id"] for x in list(self.list[origin].keys()) if self.list[origin][x].number == valfind] return next(self.list[fhdhr_id] for fhdhr_id in [x["id"] for x in self.get_channels()] if self.list[fhdhr_id].number == valfind) or None
else: else:
matches = [self.list[origin][x].dict["id"] for x in list(self.list[origin].keys()) if self.list[origin][x].dict[keyfind] == valfind] return next(self.list[fhdhr_id] for fhdhr_id in [x["id"] for x in self.get_channels()] if self.list[fhdhr_id].dict[keyfind] == valfind) or None
if len(matches):
return self.list[origin][matches[0]] def get_channel_list(self, keyfind):
else:
matches = []
for origin in list(self.list.keys()):
if keyfind == "number": if keyfind == "number":
matches = [self.list[origin][x].dict["id"] for x in list(self.list[origin].keys()) if self.list[origin][x].number == valfind] return [self.list[x].number for x in [x["id"] for x in self.get_channels()]]
else: else:
matches = [self.list[origin][x].dict["id"] for x in list(self.list[origin].keys()) if self.list[origin][x].dict[keyfind] == valfind] return [self.list[x].dict[keyfind] for x in [x["id"] for x in self.get_channels()]]
if len(matches):
return self.list[origin][matches[0]]
if len(matches):
return self.list[origin][matches[0]]
return None
def get_channel_list(self, keyfind, origin=None): def set_channel_status(self, keyfind, valfind, updatedict):
if origin: self.get_channel_obj(keyfind, valfind).set_status(updatedict)
if keyfind == "number":
return [self.list[origin][x].number for x in [x["id"] for x in self.get_channels(origin)]]
else:
return [self.list[origin][x].dict[keyfind] for x in [x["id"] for x in self.get_channels(origin)]]
else:
matches = []
for origin in list(self.list.keys()):
if keyfind == "number":
next_match = [self.list[origin][x].number for x in [x["id"] for x in self.get_channels(origin)]]
else:
next_match = [self.list[origin][x].dict[keyfind] for x in [x["id"] for x in self.get_channels(origin)]]
if len(next_match):
matches.append(next_match)
return matches[0]
def get_channel_dict(self, keyfind, valfind, origin=None): def set_channel_enablement_all(self, enablement):
chan_obj = self.get_channel_obj(keyfind, valfind, origin) for fhdhr_id in [x["id"] for x in self.get_channels()]:
if chan_obj: self.list[fhdhr_id].set_enablement(enablement)
return chan_obj.dict
return None
def set_channel_status(self, keyfind, valfind, updatedict, origin): def set_channel_enablement(self, keyfind, valfind, enablement):
self.get_channel_obj(keyfind, valfind, origin).set_status(updatedict) self.get_channel_obj(keyfind, valfind).set_enablement(enablement)
def set_channel_enablement_all(self, enablement, origin): def set_channel_favorite(self, keyfind, valfind, enablement):
for fhdhr_id in [x["id"] for x in self.get_channels(origin)]: self.get_channel_obj(keyfind, valfind).set_favorite(enablement)
self.list[fhdhr_id].set_enablement(enablement, origin)
def set_channel_enablement(self, keyfind, valfind, enablement, origin): def get_db_channels(self):
self.get_channel_obj(keyfind, valfind, origin).set_enablement(enablement) self.fhdhr.logger.info("Checking for Channel information stored in the database.")
channel_ids = self.fhdhr.db.get_fhdhr_value("channels", "list") or []
def set_channel_favorite(self, keyfind, valfind, enablement, origin):
self.get_channel_obj(keyfind, valfind, origin).set_favorite(enablement)
def get_db_channels(self, origin=None):
if not origin:
origins_list = list(self.list.keys())
else:
origins_list = origin.lower()
if isinstance(origins_list, str):
origins_list = [origins_list]
for origin in origins_list:
self.fhdhr.logger.info("Checking for %s Channel information stored in the database." % origin)
channel_ids = self.fhdhr.db.get_fhdhr_value("channels", "list", origin) or []
if len(channel_ids): if len(channel_ids):
self.fhdhr.logger.info("Found %s existing channels in the database." % str(len(channel_ids))) self.fhdhr.logger.info("Found %s existing channels in the database." % str(len(channel_ids)))
for channel_id in channel_ids: for channel_id in channel_ids:
channel_obj = Channel(self.fhdhr, self.id_system, origin=origin, channel_id=channel_id) channel_obj = Channel(self.fhdhr, self.id_system, channel_id=channel_id)
channel_id = channel_obj.dict["id"] channel_id = channel_obj.dict["id"]
self.list[origin][channel_id] = channel_obj self.list[channel_id] = channel_obj
def save_db_channels(self, origin=None): def save_db_channels(self):
if not origin: channel_ids = [x["id"] for x in self.get_channels()]
origins_list = list(self.list.keys()) self.fhdhr.db.set_fhdhr_value("channels", "list", channel_ids)
else:
origins_list = origin.lower()
if isinstance(origins_list, str): def get_channels(self, forceupdate=False):
origins_list = [origins_list]
for origin in origins_list:
channel_ids = [self.list[origin][x].dict["id"] for x in list(self.list[origin].keys())]
self.fhdhr.db.set_fhdhr_value("channels", "list", channel_ids, origin)
def get_channels(self, origin=None, forceupdate=False):
"""Pull Channels from origin. """Pull Channels from origin.
Output a list. Output a list.
@ -120,31 +66,18 @@ class Channels():
Don't pull more often than 12 hours. Don't pull more often than 12 hours.
""" """
if not origin: if not len(list(self.list.keys())):
origins_list = list(self.list.keys()) self.get_db_channels()
else:
origins_list = origin.lower().lower()
if isinstance(origins_list, str):
origins_list = [origins_list]
return_chan_list = []
for origin in origins_list:
if not len(list(self.list[origin].keys())):
self.get_db_channels(origin=origin)
if not forceupdate: if not forceupdate:
return_chan_list.extend([self.list[origin][x].dict for x in list(self.list[origin].keys())]) return [self.list[x].dict for x in list(self.list.keys())]
else: channel_origin_id_list = [str(self.list[x].dict["origin_id"]) for x in list(self.list.keys())]
channel_origin_id_list = [str(self.list[origin][x].dict["origin_id"]) for x in list(self.list[origin].keys())] self.fhdhr.logger.info("Performing Channel Scan.")
self.fhdhr.logger.info("Performing Channel Scan for %s." % origin) channel_dict_list = self.origin.get_channels()
self.fhdhr.logger.info("Found %s channels for %s." % (len(channel_dict_list), self.fhdhr.config.dict["main"]["servicename"]))
channel_dict_list = self.origins.origins_dict[origin].get_channels()
self.fhdhr.logger.info("Found %s channels for %s." % (len(channel_dict_list), origin))
self.fhdhr.logger.info("Performing Channel Import, This can take some time, Please wait.") self.fhdhr.logger.info("Performing Channel Import, This can take some time, Please wait.")
@ -155,29 +88,31 @@ class Channels():
chan_existing = str(channel_info["id"]) in channel_origin_id_list chan_existing = str(channel_info["id"]) in channel_origin_id_list
if chan_existing: if chan_existing:
channel_obj = self.get_channel_obj("origin_id", channel_info["id"], origin) channel_obj = self.get_channel_obj("origin_id", channel_info["id"])
else: else:
channel_obj = Channel(self.fhdhr, self.id_system, origin, origin_id=channel_info["id"]) channel_obj = Channel(self.fhdhr, self.id_system, origin_id=channel_info["id"])
channel_id = channel_obj.dict["id"] channel_id = channel_obj.dict["id"]
channel_obj.basics(channel_info) channel_obj.basics(channel_info)
if not chan_existing: if not chan_existing:
self.list[origin][channel_id] = channel_obj self.list[channel_id] = channel_obj
newchan += 1 newchan += 1
self.fhdhr.logger.info("%s Channel Import took %s" % (origin, humanized_time(time.time() - chan_scan_start))) self.fhdhr.logger.info("Channel Import took %s" % humanized_time(time.time() - chan_scan_start))
if not newchan: if not newchan:
newchan = "no" newchan = "no"
self.fhdhr.logger.info("Found %s NEW channels for %s." % (newchan, origin)) self.fhdhr.logger.info("Found %s NEW channels." % newchan)
self.fhdhr.logger.info("Total %s Channel Count: %s" % (origin, len(self.list[origin].keys()))) self.fhdhr.logger.info("Total Channel Count: %s" % len(self.list.keys()))
self.save_db_channels(origin=origin) self.save_db_channels()
self.fhdhr.db.set_fhdhr_value("channels", "scanned_time", time.time(), origin) self.fhdhr.db.set_fhdhr_value("channels", "scanned_time", time.time())
return_chan_list.extend([self.list[origin][x].dict for x in list(self.list[origin].keys())])
return return_chan_list return [self.list[x].dict for x in list(self.list.keys())]
def get_channel_stream(self, stream_args, origin): def get_channel_stream(self, stream_args):
return self.origins.origins_dict[origin].get_channel_stream(self.get_channel_dict("number", stream_args["channel"]), stream_args) return self.origin.get_channel_stream(self.get_channel_dict("number", stream_args["channel"]), stream_args)
def get_channel_dict(self, keyfind, valfind):
return self.get_channel_obj(keyfind, valfind).dict

View File

@ -2,32 +2,31 @@ import uuid
class Channel_IDs(): class Channel_IDs():
def __init__(self, fhdhr, origins): def __init__(self, fhdhr):
self.fhdhr = fhdhr self.fhdhr = fhdhr
self.origins = origins
def get(self, origin_id, origin): def get(self, origin_id):
existing_ids = self.fhdhr.db.get_fhdhr_value("channels", "list", origin) or [] existing_ids = self.fhdhr.db.get_fhdhr_value("channels", "list") or []
existing_channel_info = [self.fhdhr.db.get_fhdhr_value(channel_id, "dict", origin) or {} for channel_id in existing_ids] existing_channel_info = [self.fhdhr.db.get_channel_value(channel_id, "dict") or {} for channel_id in existing_ids]
for existing_channel in existing_channel_info: for existing_channel in existing_channel_info:
if existing_channel["origin_id"] == origin_id: if existing_channel["origin_id"] == origin_id:
return existing_channel["id"] return existing_channel["id"]
return self.assign(origin) return self.assign()
def assign(self, origin): def assign(self):
existing_ids = self.fhdhr.db.get_fhdhr_value("channels", "list", origin) or [] existing_ids = self.fhdhr.db.get_fhdhr_value("channels", "list") or []
channel_id = None channel_id = None
while not channel_id: while not channel_id:
unique_id = str(uuid.uuid4()) unique_id = str(uuid.uuid4())
if str(unique_id) not in existing_ids: if str(unique_id) not in existing_ids:
channel_id = str(unique_id) channel_id = str(unique_id)
existing_ids.append(channel_id) existing_ids.append(channel_id)
self.fhdhr.db.set_fhdhr_value("channels", "list", existing_ids, origin) self.fhdhr.db.set_fhdhr_value("channels", "list", existing_ids)
return channel_id return channel_id
def get_number(self, channel_id, origin): def get_number(self, channel_id):
existing_ids = self.fhdhr.db.get_fhdhr_value("channels", "list", origin) or [] existing_ids = self.fhdhr.db.get_fhdhr_value("channels", "list") or []
existing_channel_info = [self.fhdhr.db.get_fhdhr_value(channel_id, "dict", origin) or {} for channel_id in existing_ids] existing_channel_info = [self.fhdhr.db.get_channel_value(channel_id, "dict") or {} for channel_id in existing_ids]
cnumber = [existing_channel["number"] for existing_channel in existing_channel_info if existing_channel["id"] == channel_id] or None cnumber = [existing_channel["number"] for existing_channel in existing_channel_info if existing_channel["id"] == channel_id] or None
if cnumber: if cnumber:
return cnumber return cnumber

View File

@ -3,23 +3,22 @@ import time
class Channel(): class Channel():
def __init__(self, fhdhr, id_system, origin, origin_id=None, channel_id=None): def __init__(self, fhdhr, id_system, origin_id=None, channel_id=None):
self.fhdhr = fhdhr self.fhdhr = fhdhr
self.origin = origin
self.id_system = id_system self.id_system = id_system
if not channel_id: if not channel_id:
if origin_id: if origin_id:
channel_id = id_system.get(origin_id, origin) channel_id = id_system.get(origin_id)
else: else:
channel_id = id_system.assign(origin) channel_id = id_system.assign()
self.channel_id = channel_id self.channel_id = channel_id
self.dict = self.fhdhr.db.get_fhdhr_value(str(channel_id), "dict", self.origin) or self.default_dict self.dict = self.fhdhr.db.get_channel_value(str(channel_id), "dict") or self.default_dict
self.verify_dict() self.verify_dict()
self.fhdhr.db.set_fhdhr_value(self.dict["id"], "dict", self.dict, self.origin) self.fhdhr.db.set_channel_value(self.dict["id"], "dict", self.dict)
@property @property
def number(self): def number(self):
@ -97,9 +96,9 @@ class Channel():
self.dict["tags"] = self.dict["origin_tags"] self.dict["tags"] = self.dict["origin_tags"]
if "number" not in list(channel_info.keys()): if "number" not in list(channel_info.keys()):
channel_info["number"] = self.id_system.get_number(channel_info["id"], self.origin) channel_info["number"] = self.id_system.get_number(channel_info["id"])
elif not channel_info["number"]: elif not channel_info["number"]:
channel_info["number"] = self.id_system.get_number(channel_info["id"], self.origin) channel_info["number"] = self.id_system.get_number(channel_info["id"])
self.dict["origin_number"] = str(channel_info["number"]) self.dict["origin_number"] = str(channel_info["number"])
if not self.dict["number"]: if not self.dict["number"]:
self.dict["number"] = self.dict["origin_number"].split(".")[0] self.dict["number"] = self.dict["origin_number"].split(".")[0]
@ -129,7 +128,7 @@ class Channel():
if "created" not in list(self.dict.keys()): if "created" not in list(self.dict.keys()):
self.dict["created"] = time.time() self.dict["created"] = time.time()
self.fhdhr.db.set_fhdhr_value(self.dict["id"], "dict", self.dict, self.origin) self.fhdhr.db.set_channel_value(self.dict["id"], "dict", self.dict)
@property @property
def default_dict(self): def default_dict(self):
@ -145,37 +144,64 @@ class Channel():
} }
def destroy(self): def destroy(self):
self.fhdhr.db.delete_fhdhr_value(self.dict["id"], "dict", self.origin) self.fhdhr.db.delete_channel_value(self.dict["id"], "dict")
channel_ids = self.fhdhr.db.get_fhdhr_value("channels", "list") or [] channel_ids = self.fhdhr.db.get_fhdhr_value("channels", "list") or []
if self.dict["id"] in channel_ids: if self.dict["id"] in channel_ids:
channel_ids.remove(self.dict["id"]) channel_ids.remove(self.dict["id"])
self.fhdhr.db.set_fhdhr_value("channels", "list", channel_ids, self.origin) self.fhdhr.db.set_fhdhr_value("channels", "list", channel_ids)
def set_status(self, updatedict): def set_status(self, updatedict):
for key in list(updatedict.keys()): for key in list(updatedict.keys()):
if key == "number": if key == "number":
updatedict[key] = str(updatedict[key]) updatedict[key] = str(updatedict[key])
self.dict[key] = updatedict[key] self.dict[key] = updatedict[key]
self.fhdhr.db.set_fhdhr_value(self.dict["id"], "dict", self.dict, self.origin) self.fhdhr.db.set_channel_value(self.dict["id"], "dict", self.dict)
@property
def lineup_dict(self):
return {
'GuideNumber': self.number,
'GuideName': self.dict['name'],
'Tags': ",".join(self.dict['tags']),
'URL': self.hdhr_stream_url,
'HD': self.dict["HD"],
"Favorite": self.dict["favorite"],
}
@property @property
def generic_image_url(self): def generic_image_url(self):
return "/api/images?method=generate&type=channel&message=%s" % self.number return "/api/images?method=generate&type=channel&message=%s" % self.number
@property @property
def api_stream_url(self): def hdhr_stream_url(self):
return '/api/tuners?method=%s&channel=%s&origin=%s' % (self.fhdhr.config.dict["streaming"]["method"], self.dict["id"], self.origin) return '/auto/%s' % self.hdhr_stream_ident
@property @property
def api_m3u_url(self): def hdhr_stream_ident(self):
return '/api/m3u?method=get&channel=%s&origin=%s' % (self.dict["id"], self.origin) return 'v%s' % self.number
@property
def rmg_stream_url(self):
return "/devices/%s/media/%s" % (self.fhdhr.config.dict["main"]["uuid"], self.rmg_stream_ident)
@property
def rmg_stream_ident(self):
return "id://%s" % self.number
@property
def api_stream_url(self):
return '/api/tuners?method=%s&channel=%s' % (self.fhdhr.config.dict["streaming"]["method"], self.number)
@property
def m3u_url(self):
return '/api/m3u?method=get&channel=%s' % self.number
def set_favorite(self, enablement): def set_favorite(self, enablement):
if enablement == "+": if enablement == "+":
self.dict["favorite"] = 1 self.dict["favorite"] = 1
elif enablement == "-": elif enablement == "+":
self.dict["favorite"] = 0 self.dict["favorite"] = 0
self.fhdhr.db.set_fhdhr_value(self.dict["id"], "info", self.dict, self.origin) self.fhdhr.db.set_channel_value(self.dict["id"], "info", self.dict)
def set_enablement(self, enablement): def set_enablement(self, enablement):
if enablement == "disable": if enablement == "disable":
@ -187,7 +213,7 @@ class Channel():
self.dict["enabled"] = False self.dict["enabled"] = False
else: else:
self.dict["enabled"] = True self.dict["enabled"] = True
self.fhdhr.db.set_fhdhr_value(self.dict["id"], "info", self.dict, self.origin) self.fhdhr.db.set_channel_value(self.dict["id"], "info", self.dict)
def __getattr__(self, name): def __getattr__(self, name):
''' will only get called for undefined attributes ''' ''' will only get called for undefined attributes '''

158
fHDHR/device/cluster.py Normal file
View File

@ -0,0 +1,158 @@
from collections import OrderedDict
class fHDHR_Cluster():
def __init__(self, fhdhr, ssdp):
self.fhdhr = fhdhr
self.ssdp = ssdp
self.friendlyname = self.fhdhr.config.dict["fhdhr"]["friendlyname"]
if self.fhdhr.config.dict["fhdhr"]["discovery_address"]:
self.startup_sync()
def cluster(self):
return self.fhdhr.db.get_fhdhr_value("cluster", "dict") or self.default_cluster()
def get_cluster_dicts_web(self):
fhdhr_list = self.cluster()
locations = []
for location in list(fhdhr_list.keys()):
item_dict = {
"base_url": fhdhr_list[location]["base_url"],
"name": fhdhr_list[location]["name"]
}
if item_dict["base_url"] != self.fhdhr.api.base:
locations.append(item_dict)
if len(locations):
locations = sorted(locations, key=lambda i: i['name'])
return locations
else:
return None
def get_list(self):
cluster = self.fhdhr.db.get_fhdhr_value("cluster", "dict") or self.default_cluster()
return_dict = {}
for location in list(cluster.keys()):
if location != self.fhdhr.api.base:
return_dict[location] = {
"Joined": True
}
detected_list = self.ssdp.detect_method.get()
for location in detected_list:
if location not in list(cluster.keys()):
return_dict[location] = {
"Joined": False
}
return_dict = OrderedDict(sorted(return_dict.items()))
return return_dict
def default_cluster(self):
defdict = {}
defdict[self.fhdhr.api.base] = {
"base_url": self.fhdhr.api.base,
"name": self.friendlyname
}
return defdict
def startup_sync(self):
self.fhdhr.logger.info("Syncronizing with Cluster.")
cluster = self.fhdhr.db.get_fhdhr_value("cluster", "dict") or self.default_cluster()
if not len(list(cluster.keys())):
self.fhdhr.logger.info("No Cluster Found.")
else:
self.fhdhr.logger.info("Found %s clustered services." % str(len(list(cluster.keys()))))
for location in list(cluster.keys()):
if location != self.fhdhr.api.base:
self.fhdhr.logger.debug("Checking Cluster Syncronization information from %s." % location)
sync_url = "%s/api/cluster?method=get" % location
try:
sync_open = self.fhdhr.web.session.get(sync_url)
retrieved_cluster = sync_open.json()
if self.fhdhr.api.base not in list(retrieved_cluster.keys()):
return self.leave()
except self.fhdhr.web.exceptions.ConnectionError:
self.fhdhr.logger.error("Unreachable: %s" % location)
def leave(self):
self.fhdhr.logger.info("Leaving cluster.")
self.fhdhr.db.set_fhdhr_value("cluster", "dict", self.default_cluster())
def disconnect(self):
cluster = self.fhdhr.db.get_fhdhr_value("cluster", "dict") or self.default_cluster()
for location in list(cluster.keys()):
if location != self.fhdhr.api.base:
self.fhdhr.logger.info("Informing %s that I am departing the Cluster." % location)
sync_url = "%s/api/cluster?method=del&location=%s" % (location, self.fhdhr.api.base)
try:
self.fhdhr.web.session.get(sync_url)
except self.fhdhr.web.exceptions.ConnectionError:
self.fhdhr.logger.error("Unreachable: %s" % location)
self.leave()
def sync(self, location):
sync_url = "%s/api/cluster?method=get" % location
try:
sync_open = self.fhdhr.web.session.get(sync_url)
self.fhdhr.db.set_fhdhr_value("cluster", "dict", sync_open.json())
except self.fhdhr.web.exceptions.ConnectionError:
self.fhdhr.logger.error("Unreachable: %s" % location)
def push_sync(self):
cluster = self.fhdhr.db.get_fhdhr_value("cluster", "dict") or self.default_cluster()
for location in list(cluster.keys()):
if location != self.fhdhr.api.base:
sync_url = "%s/api/cluster?method=sync&location=%s" % (location, self.fhdhr.api.base_quoted)
try:
self.fhdhr.web.session.get(sync_url)
except self.fhdhr.web.exceptions.ConnectionError:
self.fhdhr.logger.error("Unreachable: %s" % location)
def add(self, location):
cluster = self.fhdhr.db.get_fhdhr_value("cluster", "dict") or self.default_cluster()
if location not in list(cluster.keys()):
self.fhdhr.logger.info("Adding %s to cluster." % location)
cluster[location] = {"base_url": location}
location_info_url = "%s/hdhr/discover.json" % location
try:
location_info_req = self.fhdhr.web.session.get(location_info_url)
except self.fhdhr.web.exceptions.ConnectionError:
self.fhdhr.logger.error("Unreachable: %s" % location)
del cluster[location]
self.fhdhr.db.set_fhdhr_value("cluster", "dict", cluster)
return
location_info = location_info_req.json()
cluster[location]["name"] = location_info["FriendlyName"]
cluster_info_url = "%s/api/cluster?method=get" % location
try:
cluster_info_req = self.fhdhr.web.session.get(cluster_info_url)
except self.fhdhr.web.exceptions.ConnectionError:
self.fhdhr.logger.error("Unreachable: %s" % location)
del cluster[location]
self.fhdhr.db.set_fhdhr_value("cluster", "dict", cluster)
return
cluster_info = cluster_info_req.json()
for cluster_key in list(cluster_info.keys()):
if cluster_key not in list(cluster.keys()):
cluster[cluster_key] = cluster_info[cluster_key]
self.fhdhr.db.set_fhdhr_value("cluster", "dict", cluster)
self.push_sync()
def remove(self, location):
cluster = self.fhdhr.db.get_fhdhr_value("cluster", "dict") or self.default_cluster()
if location in list(cluster.keys()):
self.fhdhr.logger.info("Removing %s from cluster." % location)
del cluster[location]
sync_url = "%s/api/cluster?method=leave" % location
try:
self.fhdhr.web.session.get(sync_url)
except self.fhdhr.web.exceptions.ConnectionError:
self.fhdhr.logger.error("Unreachable: %s" % location)
self.push_sync()
self.fhdhr.db.set_fhdhr_value("cluster", "dict", cluster)

View File

@ -9,19 +9,23 @@ from .blocks import blocksEPG
class EPG(): class EPG():
def __init__(self, fhdhr, channels, origins): def __init__(self, fhdhr, channels, originwrapper, plugins):
self.fhdhr = fhdhr self.fhdhr = fhdhr
self.origins = origins self.origin = originwrapper
self.channels = channels self.channels = channels
self.plugins = plugins
self.epgdict = {} self.epgdict = {}
self.epg_methods = self.fhdhr.config.dict["epg"]["method"] or [] self.epg_methods = self.fhdhr.config.dict["epg"]["method"]
self.valid_epg_methods = [x for x in list(self.fhdhr.config.dict["epg"]["valid_methods"].keys()) if x and x not in [None, "None"]] self.valid_epg_methods = [x for x in self.fhdhr.config.dict["epg"]["valid_methods"] if x and x not in [None, "None"]]
self.blocks = blocksEPG(self.fhdhr, self.channels, self.origins, None) self.blocks = blocksEPG(self.fhdhr, self.channels)
self.epg_handling = {} self.epg_handling = {
"origin": self.origin,
"blocks": self.blocks,
}
self.epg_method_selfadd() self.epg_method_selfadd()
self.def_method = self.fhdhr.config.dict["epg"]["def_method"] self.def_method = self.fhdhr.config.dict["epg"]["def_method"]
@ -40,14 +44,16 @@ class EPG():
def clear_epg_cache(self, method=None): def clear_epg_cache(self, method=None):
if not method: if not method:
if not self.def_method:
return
if method not in self.valid_epg_methods:
if not self.def_method:
return
method = self.def_method method = self.def_method
if (method == self.fhdhr.config.dict["main"]["dictpopname"] or
method not in self.fhdhr.config.dict["epg"]["valid_methods"]):
method = "origin"
self.fhdhr.logger.info("Clearing %s EPG cache." % method) epgtypename = method
if method in [self.fhdhr.config.dict["main"]["dictpopname"], "origin"]:
epgtypename = self.fhdhr.config.dict["main"]["dictpopname"]
self.fhdhr.logger.info("Clearing %s EPG cache." % epgtypename)
if hasattr(self.epg_handling[method], 'clear_cache'): if hasattr(self.epg_handling[method], 'clear_cache'):
self.epg_handling[method].clear_cache() self.epg_handling[method].clear_cache()
@ -60,17 +66,11 @@ class EPG():
def whats_on_now(self, channel_number, method=None, chan_obj=None, chan_dict=None): def whats_on_now(self, channel_number, method=None, chan_obj=None, chan_dict=None):
nowtime = time.time() nowtime = time.time()
epgdict = self.get_epg(method) epgdict = self.get_epg(method)
if channel_number not in list(epgdict.keys()): try:
epgdict[channel_number] = { listings = epgdict[channel_number]["listing"]
"callsign": "", except KeyError:
"name": "", listings = []
"number": str(channel_number), for listing in listings:
"id": "",
"thumbnail": "",
"listing": []
}
for listing in epgdict[channel_number]["listing"]:
for time_item in ["time_start", "time_end"]: for time_item in ["time_start", "time_end"]:
time_value = listing[time_item] time_value = listing[time_item]
if str(time_value).endswith("+00:00"): if str(time_value).endswith("+00:00"):
@ -90,19 +90,16 @@ class EPG():
def whats_on_allchans(self, method=None): def whats_on_allchans(self, method=None):
if not method: if not method:
if not self.def_method:
return
method = self.def_method
if method not in self.valid_epg_methods:
if not self.def_method:
return
method = self.def_method method = self.def_method
if (method == self.fhdhr.config.dict["main"]["dictpopname"] or
method not in self.fhdhr.config.dict["epg"]["valid_methods"]):
method = "origin"
channel_guide_dict = {} channel_guide_dict = {}
epgdict = self.get_epg(method) epgdict = self.get_epg(method)
epgdict = epgdict.copy() epgdict = epgdict.copy()
for c in list(epgdict.keys()): for c in list(epgdict.keys()):
if method in [origin for origin in list(self.origins.origins_dict.keys())]: if method in ["blocks", "origin", self.fhdhr.config.dict["main"]["dictpopname"]]:
chan_obj = self.channels.get_channel_obj("origin_id", epgdict[c]["id"]) chan_obj = self.channels.get_channel_obj("origin_id", epgdict[c]["id"])
channel_number = chan_obj.number channel_number = chan_obj.number
epgdict[channel_number] = epgdict.pop(c) epgdict[channel_number] = epgdict.pop(c)
@ -122,13 +119,10 @@ class EPG():
def get_epg(self, method=None): def get_epg(self, method=None):
if not method: if not method:
if not self.def_method:
return
method = self.def_method
if method not in self.valid_epg_methods:
if not self.def_method:
return
method = self.def_method method = self.def_method
if (method == self.fhdhr.config.dict["main"]["dictpopname"] or
method not in self.fhdhr.config.dict["epg"]["valid_methods"]):
method = "origin"
if method in list(self.epgdict.keys()): if method in list(self.epgdict.keys()):
return self.epgdict[method] return self.epgdict[method]
@ -159,28 +153,27 @@ class EPG():
return next(item for item in event_list if item["id"] == event_id) or None return next(item for item in event_list if item["id"] == event_id) or None
def epg_method_selfadd(self): def epg_method_selfadd(self):
for plugin_name in list(self.fhdhr.plugins.plugins.keys()): new_epgtype_list = [self.plugins.plugin_dict[x]["NAME"] for x in list(self.plugins.plugin_dict.keys()) if self.plugins.plugin_dict[x]["TYPE"] == "alt_epg"]
if self.fhdhr.plugins.plugins[plugin_name].type == "alt_epg": for method in new_epgtype_list:
method = self.fhdhr.plugins.plugins[plugin_name].name.lower() self.epg_handling[method] = eval("self.plugins.%sEPG(self.fhdhr, self.channels)" % method)
self.epg_handling[method] = self.fhdhr.plugins.plugins[plugin_name].Plugin_OBJ(self.channels, self.fhdhr.plugins.plugins[plugin_name].plugin_utils)
for origin in list(self.origins.origins_dict.keys()):
if origin.lower() not in list(self.epg_handling.keys()):
self.epg_handling[origin.lower()] = blocksEPG(self.fhdhr, self.channels, self.origins, origin)
self.fhdhr.config.register_valid_epg_method(origin, "Blocks")
self.valid_epg_methods.append(origin.lower())
def update(self, method=None): def update(self, method=None):
if not method: if (not method or
if not self.def_method: method not in self.fhdhr.config.dict["epg"]["valid_methods"]):
return
method = self.def_method
if method not in self.valid_epg_methods:
if not self.def_method:
return
method = self.def_method method = self.def_method
self.fhdhr.logger.info("Updating %s EPG cache." % method) if method == self.fhdhr.config.dict["main"]["dictpopname"]:
method = "origin"
epgtypename = method
if method in [self.fhdhr.config.dict["main"]["dictpopname"], "origin"]:
epgtypename = self.fhdhr.config.dict["main"]["dictpopname"]
self.fhdhr.logger.info("Updating %s EPG cache." % epgtypename)
if method == 'origin':
programguide = self.epg_handling['origin'].update_epg(self.channels)
else:
programguide = self.epg_handling[method].update_epg() programguide = self.epg_handling[method].update_epg()
# sort the channel listings by time stamp # sort the channel listings by time stamp
@ -197,7 +190,7 @@ class EPG():
clean_prog_guide[cnum] = programguide[cnum].copy() clean_prog_guide[cnum] = programguide[cnum].copy()
clean_prog_guide[cnum]["listing"] = [] clean_prog_guide[cnum]["listing"] = []
if method in [origin for origin in list(self.origins.origins_dict.keys())]: if method in ["blocks", "origin", self.fhdhr.config.dict["main"]["dictpopname"]]:
chan_obj = self.channels.get_channel_obj("origin_id", programguide[cnum]["id"]) chan_obj = self.channels.get_channel_obj("origin_id", programguide[cnum]["id"])
else: else:
chan_obj = None chan_obj = None
@ -258,10 +251,10 @@ class EPG():
programguide = clean_prog_guide.copy() programguide = clean_prog_guide.copy()
# if a stock method, generate Blocks EPG for missing channels # if a stock method, generate Blocks EPG for missing channels
if method in [origin for origin in list(self.origins.origins_dict.keys())]: if method in ["blocks", "origin", self.fhdhr.config.dict["main"]["dictpopname"]]:
timestamps = self.blocks.timestamps timestamps = self.blocks.timestamps
for fhdhr_id in [x["id"] for x in self.channels.get_channels(method)]: for fhdhr_id in [x["id"] for x in self.channels.get_channels()]:
chan_obj = self.channels.get_channel_obj("id", fhdhr_id, method) chan_obj = self.channels.list[fhdhr_id]
if str(chan_obj.number) not in list(programguide.keys()): if str(chan_obj.number) not in list(programguide.keys()):
programguide[str(chan_obj.number)] = chan_obj.epgdict programguide[str(chan_obj.number)] = chan_obj.epgdict
clean_prog_dicts = self.blocks.empty_channel_epg(timestamps, chan_obj=chan_obj) clean_prog_dicts = self.blocks.empty_channel_epg(timestamps, chan_obj=chan_obj)
@ -292,7 +285,7 @@ class EPG():
self.epgdict[method] = sorted_chan_guide self.epgdict[method] = sorted_chan_guide
self.fhdhr.db.set_fhdhr_value("epg_dict", method, programguide) self.fhdhr.db.set_fhdhr_value("epg_dict", method, programguide)
self.fhdhr.db.set_fhdhr_value("update_time", method, time.time()) self.fhdhr.db.set_fhdhr_value("update_time", method, time.time())
self.fhdhr.logger.info("Wrote %s EPG cache. %s Programs for %s Channels" % (method, total_programs, total_channels)) self.fhdhr.logger.info("Wrote %s EPG cache. %s Programs for %s Channels" % (epgtypename, total_programs, total_channels))
def start(self): def start(self):
self.fhdhr.logger.info("EPG Update Thread Starting") self.fhdhr.logger.info("EPG Update Thread Starting")

View File

@ -3,19 +3,18 @@ import datetime
class blocksEPG(): class blocksEPG():
def __init__(self, fhdhr, channels, origins, origin): def __init__(self, fhdhr, channels):
self.fhdhr = fhdhr self.fhdhr = fhdhr
self.channels = channels self.channels = channels
self.origins = origins
self.origin = origin
def update_epg(self): def update_epg(self):
programguide = {} programguide = {}
timestamps = self.timestamps timestamps = self.timestamps
for fhdhr_id in [x["id"] for x in self.channels.get_channels(self.origin)]: for fhdhr_id in [x["id"] for x in self.channels.get_channels()]:
chan_obj = self.channels.get_channel_obj("id", fhdhr_id, self.origin) chan_obj = self.channels.list[fhdhr_id]
if str(chan_obj.number) not in list(programguide.keys()): if str(chan_obj.number) not in list(programguide.keys()):
programguide[str(chan_obj.number)] = chan_obj.epgdict programguide[str(chan_obj.number)] = chan_obj.epgdict

View File

@ -4,20 +4,22 @@ import struct
import time import time
import threading import threading
from .ssdp_detect import fHDHR_Detect
from .rmg_ssdp import RMG_SSDP
from .hdhr_ssdp import HDHR_SSDP
class SSDPServer(): class SSDPServer():
def __init__(self, fhdhr): def __init__(self, fhdhr):
self.fhdhr = fhdhr self.fhdhr = fhdhr
self.ssdp_handling = {} self.detect_method = fHDHR_Detect(fhdhr)
self.methods = [x for x in list(self.fhdhr.plugins.plugins.keys()) if self.fhdhr.plugins.plugins[x].type == "ssdp"]
if (self.fhdhr.config.dict["fhdhr"]["discovery_address"] and
self.fhdhr.config.dict["ssdp"]["enabled"] and
len(self.methods)):
self.fhdhr.threads["ssdp"] = threading.Thread(target=self.run) self.fhdhr.threads["ssdp"] = threading.Thread(target=self.run)
if (self.fhdhr.config.dict["fhdhr"]["discovery_address"] and
self.fhdhr.config.dict["ssdp"]["enabled"]):
self.setup_ssdp() self.setup_ssdp()
self.sock.bind((self.bind_address, 1900)) self.sock.bind((self.bind_address, 1900))
@ -27,18 +29,12 @@ class SSDPServer():
self.max_age = int(fhdhr.config.dict["ssdp"]["max_age"]) self.max_age = int(fhdhr.config.dict["ssdp"]["max_age"])
self.age_time = None self.age_time = None
self.ssdp_method_selfadd() self.rmg_ssdp = RMG_SSDP(fhdhr, self.broadcast_ip, self.max_age)
self.hdhr_ssdp = HDHR_SSDP(fhdhr, self.broadcast_ip, self.max_age)
self.do_alive() self.do_alive()
self.m_search() self.m_search()
def ssdp_method_selfadd(self):
for plugin_name in list(self.fhdhr.plugins.plugins.keys()):
if self.fhdhr.plugins.plugins[plugin_name].type == "ssdp":
method = self.fhdhr.plugins.plugins[plugin_name].name.lower()
plugin_utils = self.fhdhr.plugins.plugins[plugin_name].plugin_utils
self.ssdp_handling[method] = self.fhdhr.plugins.plugins[plugin_name].Plugin_OBJ(self.fhdhr, plugin_utils, self.broadcast_ip, self.max_age)
def start(self): def start(self):
self.fhdhr.logger.info("SSDP Server Starting") self.fhdhr.logger.info("SSDP Server Starting")
self.fhdhr.threads["ssdp"].start() self.fhdhr.threads["ssdp"].start()
@ -66,22 +62,21 @@ class SSDPServer():
if send_alive: if send_alive:
self.fhdhr.logger.info("Sending Alive message to network.") self.fhdhr.logger.info("Sending Alive message to network.")
self.do_notify(self.broadcast_address_tuple) self.do_notify(self.broadcase_address_tuple)
self.age_time = time.time() self.age_time = time.time()
def do_notify(self, address): def do_notify(self, address):
notify_list = [] notify_list = []
for ssdp_handler in list(self.ssdp_handling.keys()):
if self.ssdp_handling[ssdp_handler].enabled and hasattr(self.ssdp_handling[ssdp_handler], 'notify'): hdhr_notify = self.hdhr_ssdp.get()
notify_data = self.ssdp_handling[ssdp_handler].notify notify_list.append(hdhr_notify)
if isinstance(notify_data, list):
notify_list.extend(notify_data) if self.fhdhr.config.dict["rmg"]["enabled"]:
else: rmg_notify = self.rmg_ssdp.get()
notify_list.append(notify_data) notify_list.append(rmg_notify)
for notifydata in notify_list: for notifydata in notify_list:
notifydata = notifydata.encode("utf-8")
self.fhdhr.logger.debug("Created {}".format(notifydata)) self.fhdhr.logger.debug("Created {}".format(notifydata))
try: try:
@ -108,10 +103,6 @@ class SSDPServer():
headers = [x.split(':', 1) for x in lines] headers = [x.split(':', 1) for x in lines]
headers = dict(map(lambda x: (x[0].lower(), x[1]), headers)) headers = dict(map(lambda x: (x[0].lower(), x[1]), headers))
for ssdp_handler in list(self.ssdp_handling.keys()):
if self.ssdp_handling[ssdp_handler].enabled and hasattr(self.ssdp_handling[ssdp_handler], 'on_recv'):
self.ssdp_handling[ssdp_handler].on_recv(headers, cmd, list(self.ssdp_handling.keys()))
if cmd[0] == 'M-SEARCH' and cmd[1] == '*': if cmd[0] == 'M-SEARCH' and cmd[1] == '*':
# SSDP discovery # SSDP discovery
self.fhdhr.logger.debug("Received qualifying M-SEARCH from {}".format(address)) self.fhdhr.logger.debug("Received qualifying M-SEARCH from {}".format(address))
@ -119,14 +110,26 @@ class SSDPServer():
self.do_notify(address) self.do_notify(address)
if cmd[0] == 'NOTIFY' and cmd[1] == '*': elif cmd[0] == 'NOTIFY' and cmd[1] == '*':
# SSDP presence
self.fhdhr.logger.debug("NOTIFY data: {}".format(headers)) self.fhdhr.logger.debug("NOTIFY data: {}".format(headers))
try:
if headers["server"].startswith("fHDHR"):
savelocation = headers["location"].split("/device.xml")[0]
if savelocation.endswith("/hdhr"):
savelocation = savelocation.replace("/hdhr", '')
elif savelocation.endswith("/rmg"):
savelocation = savelocation.replace("/rmg", '')
if savelocation != self.fhdhr.api.base:
self.detect_method.set(savelocation)
except KeyError:
return
else: else:
self.fhdhr.logger.debug('Unknown SSDP command %s %s' % (cmd[0], cmd[1])) self.fhdhr.logger.debug('Unknown SSDP command %s %s' % (cmd[0], cmd[1]))
def m_search(self): def m_search(self):
data = self.msearch_payload data = self.msearch_payload
self.sock.sendto(data, self.broadcast_address_tuple) self.sock.sendto(data, self.broadcase_address_tuple)
def create_msearch_payload(self): def create_msearch_payload(self):
@ -173,7 +176,7 @@ class SSDPServer():
if self.proto == "ipv4": if self.proto == "ipv4":
self.af_type = socket.AF_INET self.af_type = socket.AF_INET
self.broadcast_ip = "239.255.255.250" self.broadcast_ip = "239.255.255.250"
self.broadcast_address_tuple = (self.broadcast_ip, 1900) self.broadcase_address_tuple = (self.broadcast_ip, 1900)
self.bind_address = "0.0.0.0" self.bind_address = "0.0.0.0"
elif self.proto == "ipv6": elif self.proto == "ipv6":
self.af_type = socket.AF_INET6 self.af_type = socket.AF_INET6

View File

@ -1,31 +1,30 @@
class Plugin_OBJ(): class HDHR_SSDP():
def __init__(self, fhdhr, plugin_utils, broadcast_ip, max_age): def __init__(self, fhdhr, broadcast_ip, max_age):
self.fhdhr = fhdhr self.fhdhr = fhdhr
self.ssdp_content = None
self.broadcast_ip = broadcast_ip self.broadcast_ip = broadcast_ip
self.device_xml_path = '/hdhr/device.xml' self.device_xml_path = '/hdhr/device.xml'
self.cable_schema = "urn:schemas-opencable-com:service:Security:1" self.cable_schema = "urn:schemas-opencable-com:service:Security:1"
self.ota_schema = "urn:schemas-upnp-org:device:MediaServer:1" self.ota_schema = "urn:schemas-upnp-org:device:MediaServer:1"
if self.fhdhr.config.dict["hdhr"]["reporting_tuner_type"].lower() == "antenna": if self.fhdhr.config.dict["fhdhr"]["reporting_tuner_type"].lower() == "antenna":
self.schema = self.ota_schema self.schema = self.ota_schema
elif self.fhdhr.config.dict["hdhr"]["reporting_tuner_type"].lower() == "cable": elif self.fhdhr.config.dict["fhdhr"]["reporting_tuner_type"].lower() == "cable":
self.schema = self.cable_schema self.schema = self.cable_schema
else: else:
self.schema = self.ota_schema self.schema = self.ota_schema
self.max_age = max_age self.max_age = max_age
@property def get(self):
def enabled(self): if self.ssdp_content:
return self.fhdhr.config.dict["hdhr"]["enabled"] return self.ssdp_content.encode("utf-8")
@property
def notify(self):
data = '' data = ''
data_command = "NOTIFY * HTTP/1.1" data_command = "NOTIFY * HTTP/1.1"
@ -46,4 +45,5 @@ class Plugin_OBJ():
data += "%s:%s\r\n" % (data_key, data_dict[data_key]) data += "%s:%s\r\n" % (data_key, data_dict[data_key])
data += "\r\n" data += "\r\n"
return data self.ssdp_content = data
return data.encode("utf-8")

View File

@ -0,0 +1,49 @@
class RMG_SSDP():
def __init__(self, fhdhr, broadcast_ip, max_age):
self.fhdhr = fhdhr
self.ssdp_content = None
self.broadcast_ip = broadcast_ip
self.device_xml_path = '/rmg/device.xml'
self.cable_schema = "urn:schemas-opencable-com:service:Security:1"
self.ota_schema = "urn:schemas-upnp-org:device-1-0"
if self.fhdhr.config.dict["fhdhr"]["reporting_tuner_type"].lower() == "antenna":
self.schema = self.ota_schema
elif self.fhdhr.config.dict["fhdhr"]["reporting_tuner_type"].lower() == "cable":
self.schema = self.cable_schema
else:
self.schema = self.ota_schema
self.max_age = max_age
def get(self):
if self.ssdp_content:
return self.ssdp_content.encode("utf-8")
data = ''
data_command = "NOTIFY * HTTP/1.1"
data_dict = {
"HOST": "%s:%s" % ("239.255.255.250", 1900),
"NT": self.schema,
"NTS": "ssdp:alive",
"USN": 'uuid:%s::%s' % (self.fhdhr.config.dict["main"]["uuid"], self.schema),
"SERVER": 'fHDHR/%s UPnP/1.0' % self.fhdhr.version,
"LOCATION": "%s%s" % (self.fhdhr.api.base, self.device_xml_path),
"AL": "%s%s" % (self.fhdhr.api.base, self.device_xml_path),
"Cache-Control:max-age=": self.max_age
}
data += "%s\r\n" % data_command
for data_key in list(data_dict.keys()):
data += "%s:%s\r\n" % (data_key, data_dict[data_key])
data += "\r\n"
self.ssdp_content = data
return data.encode("utf-8")

View File

@ -0,0 +1,16 @@
class fHDHR_Detect():
def __init__(self, fhdhr):
self.fhdhr = fhdhr
self.fhdhr.db.delete_fhdhr_value("ssdp_detect", "list")
def set(self, location):
detect_list = self.fhdhr.db.get_fhdhr_value("ssdp_detect", "list") or []
if location not in detect_list:
detect_list.append(location)
self.fhdhr.db.set_fhdhr_value("ssdp_detect", "list", detect_list)
def get(self):
return self.fhdhr.db.get_fhdhr_value("ssdp_detect", "list") or []

View File

@ -7,117 +7,93 @@ from .tuner import Tuner
class Tuners(): class Tuners():
def __init__(self, fhdhr, epg, channels): def __init__(self, fhdhr, epg, channels, plugins):
self.fhdhr = fhdhr self.fhdhr = fhdhr
self.channels = channels self.channels = channels
self.plugins = plugins
self.epg = epg self.epg = epg
self.max_tuners = int(self.fhdhr.config.dict["fhdhr"]["tuner_count"])
self.tuners = {} self.tuners = {}
for origin in list(self.fhdhr.origins.origins_dict.keys()):
self.tuners[origin] = {}
max_tuners = int(self.fhdhr.origins.origins_dict[origin].tuners) self.fhdhr.logger.info("Creating %s tuners." % str(self.max_tuners))
self.fhdhr.logger.info("Creating %s tuners for %s." % (max_tuners, origin)) for i in range(0, self.max_tuners):
self.tuners[str(i)] = Tuner(fhdhr, i, epg, plugins)
for i in range(0, max_tuners): def get_available_tuner(self):
self.tuners[origin][str(i)] = Tuner(fhdhr, i, epg, origin) return next(tunernum for tunernum in list(self.tuners.keys()) if not self.tuners[tunernum].tuner_lock.locked()) or None
self.alt_stream_handlers = {} def get_scanning_tuner(self):
return next(tunernum for tunernum in list(self.tuners.keys()) if self.tuners[tunernum].status["status"] == "Scanning") or None
def alt_stream_methods_selfadd(self): def stop_tuner_scan(self):
for plugin_name in list(self.fhdhr.plugins.plugins.keys()): tunernum = self.get_scanning_tuner()
if self.fhdhr.plugins.plugins[plugin_name].type == "alt_stream":
method = self.fhdhr.plugins.plugins[plugin_name].name
self.alt_stream_handlers[method] = self.fhdhr.plugins.plugins[plugin_name]
def get_available_tuner(self, origin):
return next(tunernum for tunernum in list(self.tuners[origin].keys()) if not self.tuners[origin][tunernum].tuner_lock.locked()) or None
def get_scanning_tuner(self, origin):
return next(tunernum for tunernum in list(self.tuners[origin].keys()) if self.tuners[origin][tunernum].status["status"] == "Scanning") or None
def stop_tuner_scan(self, origin):
tunernum = self.get_scanning_tuner(origin)
if tunernum: if tunernum:
self.tuners[origin][str(tunernum)].close() self.tuners[str(tunernum)].close()
def tuner_scan(self, origin="all"): def tuner_scan(self):
"""Temporarily use a tuner for a scan""" """Temporarily use a tuner for a scan"""
if not self.available_tuner_count():
if origin == "all":
origins = list(self.tuners.keys())
else:
origins = [origin]
for origin in origins:
if not self.available_tuner_count(origin):
raise TunerError("805 - All Tuners In Use") raise TunerError("805 - All Tuners In Use")
tunernumber = self.get_available_tuner(origin) tunernumber = self.get_available_tuner()
self.tuners[str(tunernumber)].channel_scan(origin) self.tuners[str(tunernumber)].channel_scan()
if not tunernumber: if not tunernumber:
raise TunerError("805 - All Tuners In Use") raise TunerError("805 - All Tuners In Use")
def tuner_grab(self, tuner_number, origin, channel_number): def tuner_grab(self, tuner_number, channel_number):
if str(tuner_number) not in list(self.tuners[origin].keys()): if str(tuner_number) not in list(self.tuners.keys()):
self.fhdhr.logger.error("Tuner %s does not exist for %s." % (tuner_number, origin)) self.fhdhr.logger.error("Tuner %s does not exist." % str(tuner_number))
raise TunerError("806 - Tune Failed") raise TunerError("806 - Tune Failed")
# TunerError will raise if unavailable # TunerError will raise if unavailable
self.tuners[origin][str(tuner_number)].grab(origin, channel_number) self.tuners[str(tuner_number)].grab(channel_number)
return tuner_number return tuner_number
def first_available(self, origin, channel_number, dograb=True): def first_available(self, channel_number, dograb=True):
if not self.available_tuner_count(origin): if not self.available_tuner_count():
raise TunerError("805 - All Tuners In Use") raise TunerError("805 - All Tuners In Use")
tunernumber = self.get_available_tuner(origin) tunernumber = self.get_available_tuner()
if not tunernumber: if not tunernumber:
raise TunerError("805 - All Tuners In Use") raise TunerError("805 - All Tuners In Use")
else: else:
self.tuners[origin][str(tunernumber)].grab(origin, channel_number) self.tuners[str(tunernumber)].grab(channel_number)
return tunernumber return tunernumber
def tuner_close(self, tunernum, origin): def tuner_close(self, tunernum):
self.tuners[origin][str(tunernum)].close() self.tuners[str(tunernum)].close()
def status(self, origin=None): def status(self):
all_status = {} all_status = {}
if origin: for tunernum in list(self.tuners.keys()):
for tunernum in list(self.tuners[origin].keys()): all_status[tunernum] = self.tuners[str(tunernum)].get_status()
all_status[tunernum] = self.tuners[origin][str(tunernum)].get_status()
else:
for origin in list(self.tuners.keys()):
all_status[origin] = {}
for tunernum in list(self.tuners[origin].keys()):
all_status[origin][tunernum] = self.tuners[origin][str(tunernum)].get_status()
return all_status return all_status
def available_tuner_count(self, origin): def available_tuner_count(self):
available_tuners = 0 available_tuners = 0
for tunernum in list(self.tuners[origin].keys()): for tunernum in list(self.tuners.keys()):
if not self.tuners[origin][str(tunernum)].tuner_lock.locked(): if not self.tuners[str(tunernum)].tuner_lock.locked():
available_tuners += 1 available_tuners += 1
return available_tuners return available_tuners
def inuse_tuner_count(self, origin): def inuse_tuner_count(self):
inuse_tuners = 0 inuse_tuners = 0
for tunernum in list(self.tuners[origin].keys()): for tunernum in list(self.tuners.keys()):
if self.tuners[origin][str(tunernum)].tuner_lock.locked(): if self.tuners[str(tunernum)].tuner_lock.locked():
inuse_tuners += 1 inuse_tuners += 1
return inuse_tuners return inuse_tuners
def get_stream_info(self, stream_args): def get_stream_info(self, stream_args):
stream_info = self.channels.get_channel_stream(stream_args, stream_args["origin"]) stream_info = self.channels.get_channel_stream(stream_args)
if not stream_info: if not stream_info:
raise TunerError("806 - Tune Failed") raise TunerError("806 - Tune Failed")
@ -167,14 +143,10 @@ class Tuners():
while True: while True:
self.fhdhr.logger.info("Opening m3u8 for reading %s" % m3u8_url) self.fhdhr.logger.info("Opening m3u8 for reading %s" % m3u8_url)
try:
if stream_args["stream_info"]["headers"]: if stream_args["stream_info"]["headers"]:
videoUrlM3u = m3u8.load(m3u8_url, headers=stream_args["stream_info"]["headers"]) videoUrlM3u = m3u8.load(m3u8_url, headers=stream_args["stream_info"]["headers"])
else: else:
videoUrlM3u = m3u8.load(m3u8_url) videoUrlM3u = m3u8.load(m3u8_url)
except Exception as e:
self.fhdhr.logger.info("m3u8 load error: %s" % e)
return m3u8_url
if len(videoUrlM3u.playlists): if len(videoUrlM3u.playlists):
self.fhdhr.logger.info("%s m3u8 varients found" % len(videoUrlM3u.playlists)) self.fhdhr.logger.info("%s m3u8 varients found" % len(videoUrlM3u.playlists))

View File

@ -6,18 +6,20 @@ from .direct_m3u8_stream import Direct_M3U8_Stream
class Stream(): class Stream():
def __init__(self, fhdhr, stream_args, tuner): def __init__(self, fhdhr, stream_args, tuner, plugins):
self.fhdhr = fhdhr self.fhdhr = fhdhr
self.stream_args = stream_args self.stream_args = stream_args
self.plugins = plugins
if stream_args["method"] == "direct": if stream_args["method"] == "direct":
if self.stream_args["true_content_type"].startswith(tuple(["application/", "text/"])): if self.stream_args["true_content_type"].startswith(tuple(["application/", "text/"])):
self.method = Direct_M3U8_Stream(fhdhr, stream_args, tuner) self.method = Direct_M3U8_Stream(fhdhr, stream_args, tuner)
else: else:
self.method = Direct_Stream(fhdhr, stream_args, tuner) self.method = Direct_Stream(fhdhr, stream_args, tuner)
else: else:
plugin_name = self.fhdhr.config.dict["streaming"]["valid_methods"][stream_args["method"]]["plugin"]
self.method = self.fhdhr.plugins.plugins[plugin_name].Plugin_OBJ(self.fhdhr.plugins.plugins[plugin_name].plugin_utils, stream_args, tuner) self.method = eval("self.plugins.%s_Stream(fhdhr, stream_args, tuner)" % stream_args["method"].upper())
def get(self): def get(self):
return self.method.get() return self.method.get()

View File

@ -34,15 +34,10 @@ class Direct_M3U8_Stream():
while self.tuner.tuner_lock.locked(): while self.tuner.tuner_lock.locked():
try:
if self.stream_args["stream_info"]["headers"]: if self.stream_args["stream_info"]["headers"]:
playlist = m3u8.load(self.stream_args["stream_info"]["url"], headers=self.stream_args["stream_info"]["headers"]) playlist = m3u8.load(self.stream_args["stream_info"]["url"], headers=self.stream_args["stream_info"]["headers"])
else: else:
playlist = m3u8.load(self.stream_args["stream_info"]["url"]) playlist = m3u8.load(self.stream_args["stream_info"]["url"])
except Exception as e:
self.fhdhr.logger.info("Connection Closed: %s" % e)
self.tuner.close()
return None
segments = playlist.segments segments = playlist.segments

View File

@ -8,22 +8,22 @@ from .stream import Stream
class Tuner(): class Tuner():
def __init__(self, fhdhr, inum, epg, origin): def __init__(self, fhdhr, inum, epg, plugins):
self.fhdhr = fhdhr self.fhdhr = fhdhr
self.plugins = plugins
self.number = inum self.number = inum
self.origin = origin
self.epg = epg self.epg = epg
self.tuner_lock = threading.Lock() self.tuner_lock = threading.Lock()
self.set_off_status() self.set_off_status()
self.chanscan_url = "/api/channels?method=scan" self.chanscan_url = "/api/channels?method=scan"
self.close_url = "/api/tuners?method=close&tuner=%s&origin=%s" % (self.number, self.origin) self.close_url = "/api/tuners?method=close&tuner=%s" % str(self.number)
def channel_scan(self, origin, grabbed=False): def channel_scan(self, grabbed=False):
if self.tuner_lock.locked() and not grabbed: if self.tuner_lock.locked() and not grabbed:
self.fhdhr.logger.error("%s Tuner #%s is not available." % (self.origin, self.number)) self.fhdhr.logger.error("Tuner #%s is not available." % str(self.number))
raise TunerError("804 - Tuner In Use") raise TunerError("804 - Tuner In Use")
if self.status["status"] == "Scanning": if self.status["status"] == "Scanning":
@ -33,16 +33,14 @@ class Tuner():
if not grabbed: if not grabbed:
self.tuner_lock.acquire() self.tuner_lock.acquire()
self.status["status"] = "Scanning" self.status["status"] = "Scanning"
self.status["origin"] = origin self.fhdhr.logger.info("Tuner #%s Performing Channel Scan." % str(self.number))
self.status["time_start"] = datetime.datetime.utcnow()
self.fhdhr.logger.info("Tuner #%s Performing Channel Scan for %s origin." % (self.number, origin))
chanscan = threading.Thread(target=self.runscan, args=(origin,)) chanscan = threading.Thread(target=self.runscan)
chanscan.start() chanscan.start()
def runscan(self, origin): def runscan(self):
self.fhdhr.api.get("%s&origin=%s" % (self.chanscan_url, origin)) self.fhdhr.api.get(self.chanscan_url)
self.fhdhr.logger.info("Requested Channel Scan for %s origin Complete." % origin) self.fhdhr.logger.info("Requested Channel Scan Complete.")
self.close() self.close()
self.fhdhr.api.get(self.close_url) self.fhdhr.api.get(self.close_url)
@ -50,15 +48,13 @@ class Tuner():
if "downloaded" in list(self.status.keys()): if "downloaded" in list(self.status.keys()):
self.status["downloaded"] += bytes_count self.status["downloaded"] += bytes_count
def grab(self, origin, channel_number): def grab(self, channel_number):
if self.tuner_lock.locked(): if self.tuner_lock.locked():
self.fhdhr.logger.error("Tuner #%s is not available." % self.number) self.fhdhr.logger.error("Tuner #%s is not available." % self.number)
raise TunerError("804 - Tuner In Use") raise TunerError("804 - Tuner In Use")
self.tuner_lock.acquire() self.tuner_lock.acquire()
self.status["status"] = "Acquired" self.status["status"] = "Acquired"
self.status["origin"] = origin
self.status["channel"] = channel_number self.status["channel"] = channel_number
self.status["time_start"] = datetime.datetime.utcnow()
self.fhdhr.logger.info("Tuner #%s Acquired." % str(self.number)) self.fhdhr.logger.info("Tuner #%s Acquired." % str(self.number))
def close(self): def close(self):
@ -69,22 +65,19 @@ class Tuner():
def get_status(self): def get_status(self):
current_status = self.status.copy() current_status = self.status.copy()
current_status["epg"] = {} if current_status["status"] == "Active":
if current_status["status"] in ["Acquired", "Active", "Scanning"]: current_status["Play Time"] = str(
current_status["running_time"] = str(
humanized_time( humanized_time(
int((datetime.datetime.utcnow() - current_status["time_start"]).total_seconds()))) int((datetime.datetime.utcnow() - current_status["time_start"]).total_seconds())))
current_status["time_start"] = str(current_status["time_start"]) current_status["time_start"] = str(current_status["time_start"])
if current_status["status"] in ["Active"]: current_status["epg"] = self.epg.whats_on_now(current_status["channel"])
if current_status["origin"] in self.epg.epg_methods:
current_status["epg"] = self.epg.whats_on_now(current_status["channel"], method=current_status["origin"])
return current_status return current_status
def set_off_status(self): def set_off_status(self):
self.status = {"status": "Inactive"} self.status = {"status": "Inactive"}
def get_stream(self, stream_args, tuner): def get_stream(self, stream_args, tuner):
stream = Stream(self.fhdhr, stream_args, tuner) stream = Stream(self.fhdhr, stream_args, tuner, self.plugins)
return stream.get() return stream.get()
def set_status(self, stream_args): def set_status(self, stream_args):
@ -95,7 +88,6 @@ class Tuner():
"clients_id": [], "clients_id": [],
"method": stream_args["method"], "method": stream_args["method"],
"accessed": [stream_args["accessed"]], "accessed": [stream_args["accessed"]],
"origin": stream_args["origin"],
"channel": stream_args["channel"], "channel": stream_args["channel"],
"proxied_url": stream_args["stream_info"]["url"], "proxied_url": stream_args["stream_info"]["url"],
"time_start": datetime.datetime.utcnow(), "time_start": datetime.datetime.utcnow(),

View File

@ -1,45 +0,0 @@
import fHDHR.exceptions
class Origin_StandIN():
def __init__(self):
self.setup_success = False
def get_channels(self):
return []
def get_channel_stream(self, chandict, stream_args):
return None
class Origins():
def __init__(self, fhdhr):
self.fhdhr = fhdhr
self.origins_dict = {}
self.origin_selfadd()
for plugin_name in list(self.fhdhr.plugins.plugins.keys()):
if self.fhdhr.plugins.plugins[plugin_name].manifest["tagged_mod"] and self.fhdhr.plugins.plugins[plugin_name].manifest["tagged_mod_type"] == "origin":
self.fhdhr.plugins.plugins[plugin_name].plugin_utils.origin = self.origins_dict[self.fhdhr.plugins.plugins[plugin_name].manifest["tagged_mod"].lower()]
@property
def valid_origins(self):
return [origin for origin in list(self.origins_dict.keys())]
def origin_selfadd(self):
for plugin_name in list(self.fhdhr.plugins.plugins.keys()):
if self.fhdhr.plugins.plugins[plugin_name].type == "origin":
method = self.fhdhr.plugins.plugins[plugin_name].name.lower()
try:
plugin_utils = self.fhdhr.plugins.plugins[plugin_name].plugin_utils
self.origins_dict[method] = self.fhdhr.plugins.plugins[plugin_name].Plugin_OBJ(plugin_utils)
self.fhdhr.logger.info("%s Setup Success" % method)
self.origins_dict[method].setup_success = True
except fHDHR.exceptions.OriginSetupError as e:
self.fhdhr.logger.error(e)
self.origins_dict[method] = Origin_StandIN()
if not hasattr(self.origins_dict[method], 'tuners'):
self.origins_dict[method].tuners = 4

View File

@ -0,0 +1,62 @@
from .origin_channels_standin import OriginChannels_StandIN
from .origin_epg_standin import OriginEPG_StandIN
import fHDHR.exceptions
class OriginServiceWrapper():
def __init__(self, fhdhr, origin):
self.fhdhr = fhdhr
self.origin = origin
self.servicename = fhdhr.config.dict["main"]["servicename"]
self.setup_success = None
self.setup()
def setup(self):
if self.origin:
try:
self.originservice = self.origin.OriginService(self.fhdhr)
self.setup_success = True
self.fhdhr.logger.info("%s Setup Success" % self.servicename)
except fHDHR.exceptions.OriginSetupError as e:
self.originservice = None
self.fhdhr.logger.error(e)
self.setup_success = False
if self.setup_success:
self.channels = self.origin.OriginChannels(self.fhdhr, self.originservice)
self.epg = self.origin.OriginEPG(self.fhdhr)
else:
self.channels = OriginChannels_StandIN()
self.epg = OriginEPG_StandIN()
else:
self.originservice = None
self.channels = OriginChannels_StandIN()
self.epg = OriginEPG_StandIN()
def get_channels(self):
return self.channels.get_channels()
def get_channel_stream(self, chandict, stream_args):
return self.channels.get_channel_stream(chandict, stream_args)
def update_epg(self, channels):
return self.epg.update_epg(channels)
def __getattr__(self, name):
''' will only get called for undefined attributes '''
if hasattr(self.fhdhr, name):
return eval("self.fhdhr.%s" % name)
if hasattr(self.originservice, name):
return eval("self.originservice.%s" % name)
elif hasattr(self.channels, name):
return eval("self.channels.%s" % name)
elif hasattr(self.epg, name):
return eval("self.epg.%s" % name)
else:
raise AttributeError(name)

View File

@ -0,0 +1,11 @@
class OriginChannels_StandIN():
def __init__(self):
pass
def get_channels(self):
return []
def get_channel_stream(self, chandict, stream_args):
return None

View File

@ -0,0 +1,8 @@
class OriginEPG_StandIN():
def __init__(self):
pass
def update_epg(self, channels):
return {}

View File

@ -1,250 +0,0 @@
import os
import imp
import json
class Plugin_DB():
def __init__(self, db, name):
self._db = db
self.name = name
self.namespace = name.lower()
# fhdhr
def set_fhdhr_value(self, pluginitem, key, value, namespace="default"):
print("%s plugin is not allowed write access to fhdhr db namespaces." % self.name)
return
def get_fhdhr_value(self, pluginitem, key, namespace="default"):
return self._db.get_fhdhr_value(pluginitem, key, namespace=namespace.lower())
def delete_fhdhr_value(self, pluginitem, key, namespace="default"):
print("%s plugin is not allowed write access to fhdhr db namespaces." % self.name)
return
# Plugin
def set_plugin_value(self, pluginitem, key, value, namespace=None):
if not namespace:
namespace = self.namespace
elif namespace.lower() != self.namespace:
print("%s plugin is not allowed write access to %s db namespace." % (self.name, namespace))
return
return self._db.set_plugin_value(pluginitem, key, value, namespace=self.namespace)
def get_plugin_value(self, pluginitem, key, namespace=None):
if not namespace:
namespace = self.namespace
return self._db.get_plugin_value(pluginitem, key, namespace=namespace.lower())
def delete_plugin_value(self, pluginitem, key, namespace=None):
if not namespace:
namespace = self.namespace
elif namespace.lower() != self.namespace:
print("%s plugin is not allowed write access to %s db namespace." % (self.name, namespace))
return
return self._db.delete_plugin_value(pluginitem, key, namespace=self.namespace)
class Plugin_Config():
def __init__(self, config, name):
self._config = config
self.name = name
self.namespace = name.lower()
@property
def dict(self):
return self._config.dict.copy()
@property
def internal(self):
return self._config.internal.copy()
@property
def conf_default(self):
return self._config.conf_default.copy()
def write(self, key, value, namespace=None):
if not namespace:
namespace = self.namespace
elif str(namespace).lower() != self.namespace:
print("%s plugin is not allowed write access to fhdhr config namespaces." % self.name)
return
return self._config.write(key, value, self.namespace)
class Plugin_Utils():
def __init__(self, config, logger, db, plugin_name, plugin_manifest, modname):
self.config = Plugin_Config(config, plugin_manifest["name"])
self.db = Plugin_DB(db, plugin_manifest["name"])
self.logger = logger
self.namespace = plugin_manifest["name"].lower()
self.plugin_name = plugin_name
self.plugin_manifest = plugin_manifest
self.origin = None
class Plugin():
def __init__(self, config, logger, db, plugin_name, plugin_path, plugin_conf, plugin_manifest):
self.config = config
self.db = db
self.logger = logger
# Gather Info about Plugin
self.plugin_name = plugin_name
self.modname = os.path.basename(plugin_path)
self.path = plugin_path
self.module_type = imp.PKG_DIRECTORY
self.multi_plugin = (self.plugin_name != self.modname)
self.default_conf = plugin_conf
self.manifest = plugin_manifest
if self.multi_plugin:
self.plugin_dict_name = "%s_%s" % (plugin_name, self.modname)
else:
self.plugin_dict_name = plugin_name
self.plugin_utils = Plugin_Utils(config, logger, db, plugin_name, plugin_manifest, self.modname)
# Load the module
self._module = self._load()
def setup(self):
if self.type == "alt_epg":
self.config.register_valid_epg_method(self.name, self.plugin_dict_name)
elif self.type == "alt_stream":
self.config.register_valid_streaming_method(self.name, self.plugin_dict_name)
elif self.type == "web":
self.config.register_web_path(self.manifest["name"], self.path, self.plugin_dict_name)
if self.has_setup():
self._module.setup(self)
def has_setup(self):
return hasattr(self._module, 'setup')
def _load(self):
description = ('', '', self.module_type)
mod = imp.load_module(self.plugin_dict_name, None, self.path, description)
return mod
@property
def name(self):
return self.manifest["name"]
@property
def version(self):
return self.manifest["version"]
@property
def type(self):
return self.manifest["type"]
def __getattr__(self, name):
''' will only get called for undefined attributes '''
if name == "Plugin_OBJ":
return self._module.Plugin_OBJ
class PluginsHandler():
def __init__(self, settings):
self.config = settings
self.plugins = {}
self.found_plugins = []
self.found_plugins_conf = []
self.list_plugins()
def setup(self):
for plugin_name in list(self.plugins.keys()):
self.plugins[plugin_name].setup()
def load_plugin_configs(self):
for file_item_path in self.found_plugins_conf:
self.config.import_conf_json(file_item_path)
def list_plugins(self):
for directory in self.config.internal["paths"]["plugins_dir"]:
base = os.path.abspath(directory)
for filename in os.listdir(base):
abspath = os.path.join(base, filename)
if os.path.isdir(abspath):
plugin_conf = []
for subfilename in os.listdir(abspath):
subabspath = os.path.join(abspath, subfilename)
if subfilename.endswith("_conf.json"):
plugin_conf.append(subabspath)
self.found_plugins_conf.append(subabspath)
# Plugin/multi-plugin must have a basic manifest json
conffilepath = os.path.join(abspath, 'plugin.json')
if os.path.isfile(conffilepath):
plugin_manifest = json.load(open(conffilepath, 'r'))
for plugin_man_item in ["name", "version", "type"]:
if plugin_man_item not in list(plugin_manifest.keys()):
plugin_manifest[plugin_man_item] = None
self.config.register_version(os.path.basename(filename), plugin_manifest["version"], "plugin")
if plugin_manifest["type"] == "origin":
self.config.register_valid_origin_method(plugin_manifest["name"])
plugin_import_print_string = "Found %s type plugin: %s %s. " % (plugin_manifest["type"], plugin_manifest["name"], plugin_manifest["version"])
# Warn for multiple origins
if plugin_manifest["type"] == "origin" and len([plugin_name for plugin_name, plugin_path, plugin_conf, plugin_manifest in self.found_plugins if plugin_manifest["type"] == "origin"]):
plugin_import_print_string += " ImportWarning: Only one Origin Allowed."
if not any(plugin_manifest[plugin_item] for plugin_item in ["name", "version", "type"]):
plugin_import_print_string += " ImportWarning: Missing PLUGIN_* Value."
else:
# Single Plugin
if os.path.isfile(os.path.join(abspath, '__init__.py')):
plugin_manifest["tagged_mod"] = None
plugin_manifest["tagged_mod_type"] = None
self.found_plugins.append((os.path.basename(filename), abspath, plugin_conf, plugin_manifest))
else:
# Multi-Plugin
for subfilename in os.listdir(abspath):
subabspath = os.path.join(abspath, subfilename)
if os.path.isdir(subabspath):
subconffilepath = os.path.join(subabspath, 'plugin.json')
if os.path.isfile(subconffilepath):
subplugin_manifest = json.load(open(subconffilepath, 'r'))
for subplugin_man_item in ["name", "version", "type"]:
if subplugin_man_item not in list(subplugin_manifest.keys()):
subplugin_manifest[subplugin_man_item] = plugin_manifest[subplugin_man_item]
else:
subplugin_manifest = plugin_manifest
subplugin_manifest["tagged_mod"] = None
subplugin_manifest["tagged_mod_type"] = None
if plugin_manifest["type"] != subplugin_manifest["type"]:
subplugin_manifest["tagged_mod"] = plugin_manifest["name"]
subplugin_manifest["tagged_mod_type"] = plugin_manifest["type"]
if os.path.isfile(os.path.join(subabspath, '__init__.py')):
self.found_plugins.append((os.path.basename(filename), subabspath, plugin_conf, subplugin_manifest))
print(plugin_import_print_string)
self.load_plugin_configs()
def load_plugins(self, logger, db):
self.logger = logger
self.db = db
for plugin_name, plugin_path, plugin_conf, plugin_manifest in self.found_plugins:
plugin_item = Plugin(self.config, self.logger, self.db, plugin_name, plugin_path, plugin_conf, plugin_manifest)
self.plugins[plugin_item.plugin_dict_name] = plugin_item

View File

@ -6,6 +6,8 @@ import uuid
from .pages import fHDHR_Pages from .pages import fHDHR_Pages
from .files import fHDHR_Files from .files import fHDHR_Files
from .brython import fHDHR_Brython from .brython import fHDHR_Brython
from .hdhr import fHDHR_HDHR
from .rmg import fHDHR_RMG
from .api import fHDHR_API from .api import fHDHR_API
@ -34,16 +36,33 @@ class fHDHR_HTTP_Server():
self.route_list = {} self.route_list = {}
self.endpoints_obj = {} self.fhdhr.logger.info("Loading HTTP Pages Endpoints.")
self.endpoints_obj["pages"] = fHDHR_Pages(fhdhr) self.pages = fHDHR_Pages(fhdhr)
self.endpoints_obj["files"] = fHDHR_Files(fhdhr) self.add_endpoints(self.pages, "pages")
self.endpoints_obj["brython"] = fHDHR_Brython(fhdhr)
self.endpoints_obj["api"] = fHDHR_API(fhdhr)
self.selfadd_web_plugins() self.fhdhr.logger.info("Loading HTTP Files Endpoints.")
for endpoint_type in list(self.endpoints_obj.keys()): self.files = fHDHR_Files(fhdhr)
self.fhdhr.logger.info("Loading HTTP %s Endpoints." % endpoint_type) self.add_endpoints(self.files, "files")
self.add_endpoints(endpoint_type)
self.fhdhr.logger.info("Loading HTTP Brython Endpoints.")
self.brython = fHDHR_Brython(fhdhr)
self.add_endpoints(self.brython, "brython")
self.fhdhr.logger.info("Loading HTTP HDHR Endpoints.")
self.hdhr = fHDHR_HDHR(fhdhr)
self.add_endpoints(self.hdhr, "hdhr")
self.fhdhr.logger.info("Loading HTTP RMG Endpoints.")
self.rmg = fHDHR_RMG(fhdhr)
self.add_endpoints(self.rmg, "rmg")
self.fhdhr.logger.info("Loading HTTP API Endpoints.")
self.api = fHDHR_API(fhdhr)
self.add_endpoints(self.api, "api")
self.fhdhr.logger.info("Loading HTTP Origin Endpoints.")
self.origin_endpoints = self.fhdhr.originwrapper.origin.origin_web.fHDHR_Origin_Web(fhdhr)
self.add_endpoints(self.origin_endpoints, "origin_endpoints")
self.fhdhr.app.before_request(self.before_request) self.fhdhr.app.before_request(self.before_request)
self.fhdhr.app.after_request(self.after_request) self.fhdhr.app.after_request(self.after_request)
@ -51,16 +70,6 @@ class fHDHR_HTTP_Server():
self.fhdhr.threads["flask"] = threading.Thread(target=self.run) self.fhdhr.threads["flask"] = threading.Thread(target=self.run)
def selfadd_web_plugins(self):
for plugin_name in list(self.fhdhr.plugins.plugins.keys()):
if self.fhdhr.plugins.plugins[plugin_name].type == "web":
method = self.fhdhr.plugins.plugins[plugin_name].name.lower()
plugin_utils = self.fhdhr.plugins.plugins[plugin_name].plugin_utils
try:
self.endpoints_obj[method] = self.fhdhr.plugins.plugins[plugin_name].Plugin_OBJ(self.fhdhr, plugin_utils)
except Exception as e:
print(e)
def start(self): def start(self):
self.fhdhr.logger.info("Flask HTTP Thread Starting") self.fhdhr.logger.info("Flask HTTP Thread Starting")
self.fhdhr.threads["flask"].start() self.fhdhr.threads["flask"].start()
@ -78,8 +87,6 @@ class fHDHR_HTTP_Server():
session["instance_id"] = self.instance_id session["instance_id"] = self.instance_id
session["route_list"] = self.route_list session["route_list"] = self.route_list
session["user_agent"] = request.headers.get('User-Agent')
session["is_internal_api"] = self.detect_internal_api(request) session["is_internal_api"] = self.detect_internal_api(request)
if session["is_internal_api"]: if session["is_internal_api"]:
self.fhdhr.logger.debug("Client is using internal API call.") self.fhdhr.logger.debug("Client is using internal API call.")
@ -146,57 +153,49 @@ class fHDHR_HTTP_Server():
else: else:
return False return False
def add_endpoints(self, index_name): def add_endpoints(self, index_list, index_name):
item_list = [x for x in dir(self.endpoints_obj[index_name]) if self.isapath(x)] if index_name not in list(self.route_list.keys()):
endpoint_main = self.endpoints_obj[index_name] self.route_list[index_name] = {}
endpoint_main.fhdhr.version # dummy line
item_list = [x for x in dir(index_list) if self.isapath(x)]
for item in item_list: for item in item_list:
endpoints = eval("endpoint_main.%s.%s" % (item, "endpoints")) endpoints = eval("self.%s.%s.%s" % (index_name, item, "endpoints"))
if isinstance(endpoints, str): if isinstance(endpoints, str):
endpoints = [endpoints] endpoints = [endpoints]
handler = eval("endpoint_main.%s" % item) handler = eval("self.%s.%s" % (index_name, item))
endpoint_name = eval("endpoint_main.%s.%s" % (item, "endpoint_name")) endpoint_name = eval("self.%s.%s.%s" % (index_name, item, "endpoint_name"))
try: try:
endpoint_methods = eval("endpoint_main.%s.%s" % (item, "endpoint_methods")) endpoint_methods = eval("self.%s.%s.%s" % (index_name, item, "endpoint_methods"))
except AttributeError: except AttributeError:
endpoint_methods = ['GET'] endpoint_methods = ['GET']
try: try:
endpoint_access_level = eval("endpoint_main.%s.%s" % (item, "endpoint_access_level")) endpoint_access_level = eval("self.%s.%s.%s" % (index_name, item, "endpoint_access_level"))
except AttributeError: except AttributeError:
endpoint_access_level = 0 endpoint_access_level = 0
try: try:
pretty_name = eval("endpoint_main.%s.%s" % (item, "pretty_name")) pretty_name = eval("self.%s.%s.%s" % (index_name, item, "pretty_name"))
except AttributeError: except AttributeError:
pretty_name = endpoint_name pretty_name = endpoint_name
try: try:
endpoint_category = eval("endpoint_main.%s.%s" % (item, "endpoint_category")) endpoint_default_parameters = eval("self.%s.%s.%s" % (index_name, item, "endpoint_default_parameters"))
except AttributeError:
endpoint_category = index_name
try:
endpoint_default_parameters = eval("endpoint_main.%s.%s" % (item, "endpoint_default_parameters"))
except AttributeError: except AttributeError:
endpoint_default_parameters = {} endpoint_default_parameters = {}
self.fhdhr.logger.debug("Adding endpoint %s available at %s with %s methods." % (endpoint_name, ",".join(endpoints), ",".join(endpoint_methods))) self.fhdhr.logger.debug("Adding endpoint %s available at %s with %s methods." % (endpoint_name, ",".join(endpoints), ",".join(endpoint_methods)))
if endpoint_category not in list(self.route_list.keys()): if endpoint_name not in list(self.route_list[index_name].keys()):
self.route_list[endpoint_category] = {} self.route_list[index_name][endpoint_name] = {}
self.route_list[index_name][endpoint_name]["name"] = endpoint_name
if endpoint_name not in list(self.route_list[endpoint_category].keys()): self.route_list[index_name][endpoint_name]["endpoints"] = endpoints
self.route_list[endpoint_category][endpoint_name] = {} self.route_list[index_name][endpoint_name]["endpoint_methods"] = endpoint_methods
self.route_list[endpoint_category][endpoint_name]["name"] = endpoint_name self.route_list[index_name][endpoint_name]["endpoint_access_level"] = endpoint_access_level
self.route_list[endpoint_category][endpoint_name]["endpoints"] = endpoints self.route_list[index_name][endpoint_name]["endpoint_default_parameters"] = endpoint_default_parameters
self.route_list[endpoint_category][endpoint_name]["endpoint_methods"] = endpoint_methods self.route_list[index_name][endpoint_name]["pretty_name"] = pretty_name
self.route_list[endpoint_category][endpoint_name]["endpoint_access_level"] = endpoint_access_level
self.route_list[endpoint_category][endpoint_name]["endpoint_default_parameters"] = endpoint_default_parameters
self.route_list[endpoint_category][endpoint_name]["pretty_name"] = pretty_name
self.route_list[endpoint_category][endpoint_name]["endpoint_category"] = endpoint_category
for endpoint in endpoints: for endpoint in endpoints:
self.add_endpoint(endpoint=endpoint, self.add_endpoint(endpoint=endpoint,
@ -205,7 +204,7 @@ class fHDHR_HTTP_Server():
methods=endpoint_methods) methods=endpoint_methods)
def isapath(self, item): def isapath(self, item):
not_a_page_list = ["fhdhr", "plugin_utils"] not_a_page_list = ["fhdhr"]
if item in not_a_page_list: if item in not_a_page_list:
return False return False
elif item.startswith("__") and item.endswith("__"): elif item.startswith("__") and item.endswith("__"):

View File

@ -2,6 +2,7 @@
from .root_url import Root_URL from .root_url import Root_URL
from .startup_tasks import Startup_Tasks from .startup_tasks import Startup_Tasks
from .cluster import Cluster
from .settings import Settings from .settings import Settings
from .channels import Channels from .channels import Channels
from .xmltv import xmlTV from .xmltv import xmlTV
@ -10,7 +11,7 @@ from .w3u import W3U
from .epg import EPG from .epg import EPG
from .tuners import Tuners from .tuners import Tuners
from .debug import Debug_JSON from .debug import Debug_JSON
from .plugins import Plugins_JSON from .tools import API_Tools
from .route_list import Route_List from .route_list import Route_List
@ -25,6 +26,7 @@ class fHDHR_API():
self.root_url = Root_URL(fhdhr) self.root_url = Root_URL(fhdhr)
self.startup_tasks = Startup_Tasks(fhdhr) self.startup_tasks = Startup_Tasks(fhdhr)
self.cluster = Cluster(fhdhr)
self.settings = Settings(fhdhr) self.settings = Settings(fhdhr)
self.channels = Channels(fhdhr) self.channels = Channels(fhdhr)
self.xmltv = xmlTV(fhdhr) self.xmltv = xmlTV(fhdhr)
@ -33,7 +35,7 @@ class fHDHR_API():
self.epg = EPG(fhdhr) self.epg = EPG(fhdhr)
self.tuners = Tuners(fhdhr) self.tuners = Tuners(fhdhr)
self.debug = Debug_JSON(fhdhr) self.debug = Debug_JSON(fhdhr)
self.plugins = Plugins_JSON(fhdhr) self.tools = API_Tools(fhdhr)
self.route_list = Route_List(fhdhr) self.route_list = Route_List(fhdhr)

View File

@ -24,38 +24,22 @@ class Channels():
method = request.args.get('method', default=None, type=str) method = request.args.get('method', default=None, type=str)
redirect_url = request.args.get('redirect', default=None, type=str) redirect_url = request.args.get('redirect', default=None, type=str)
origin_methods = self.fhdhr.origins.valid_origins
origin = request.args.get('origin', default=None, type=str)
if origin not in origin_methods:
return "%s Invalid channels origin" % origin
if method == "get": if method == "get":
channels_info = {} channels_info = {}
if not origin: for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels()]:
origin_list = origin_methods channel_obj = self.fhdhr.device.channels.list[fhdhr_id]
else:
origin_list = [origin]
for origin_item in origin_list:
channels_info[origin_item] = {}
for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels(origin=origin_item)]:
channel_obj = self.fhdhr.device.channels.list[origin_item][fhdhr_id]
channel_dict = channel_obj.dict.copy() channel_dict = channel_obj.dict.copy()
channel_dict["m3u_url"] = channel_obj.api_m3u_url channel_dict["m3u_url"] = channel_obj.m3u_url
channel_dict["stream_url"] = channel_obj.api_stream_url channel_dict["stream_url"] = channel_obj.api_stream_url
channels_info[origin_item][channel_obj.number] = channel_dict channels_info[channel_obj.number] = channel_dict
# Sort the channels # Sort the channels
sorted_channel_list = channel_sort(list(channels_info[origin_item].keys())) sorted_channel_list = channel_sort(list(channels_info.keys()))
sorted_chan_guide = [] sorted_chan_guide = []
for channel in sorted_channel_list: for channel in sorted_channel_list:
sorted_chan_guide.append(channels_info[origin_item][channel]) sorted_chan_guide.append(channels_info[channel])
channels_info[origin_item] = sorted_chan_guide channels_info_json = json.dumps(sorted_chan_guide, indent=4)
channels_info_json = json.dumps(channels_info, indent=4)
return Response(status=200, return Response(status=200,
response=channels_info_json, response=channels_info_json,
@ -75,18 +59,18 @@ class Channels():
channel_method = channel[0] channel_method = channel[0]
channel_number = channel[1:] channel_number = channel[1:]
if str(channel_number) not in [str(x) for x in self.fhdhr.device.channels.get_channel_list("number", origin)]: if str(channel_number) not in [str(x) for x in self.fhdhr.device.channels.get_channel_list("number")]:
response = Response("Not Found", status=404) response = Response("Not Found", status=404)
response.headers["X-fHDHR-Error"] = "801 - Unknown Channel" response.headers["X-fHDHR-Error"] = "801 - Unknown Channel"
self.fhdhr.logger.error(response.headers["X-fHDHR-Error"]) self.fhdhr.logger.error(response.headers["X-fHDHR-Error"])
abort(response) abort(response)
if channel_method == "+": if channel_method == "+":
self.fhdhr.device.channels.set_channel_enablement("number", channel_number, channel_method, origin) self.fhdhr.device.channels.set_channel_enablement("number", channel_number, channel_method)
elif channel_method == "-": elif channel_method == "-":
self.fhdhr.device.channels.set_channel_enablement("number", channel_number, channel_method, origin) self.fhdhr.device.channels.set_channel_enablement("number", channel_number, channel_method)
elif channel_method == "x": elif channel_method == "x":
self.fhdhr.device.channels.set_channel_enablement("number", channel_number, "toggle", origin) self.fhdhr.device.channels.set_channel_enablement("number", channel_number, "toggle")
else: else:
self.fhdhr.logger.warning("Unknown favorite command %s" % request.args['favorite']) self.fhdhr.logger.warning("Unknown favorite command %s" % request.args['favorite'])
@ -95,14 +79,14 @@ class Channels():
elif method in ["enable", "disable"]: elif method in ["enable", "disable"]:
channel = request.args.get('channel', default=None, type=str) channel = request.args.get('channel', default=None, type=str)
if channel == "all": if channel == "all":
self.fhdhr.device.channels.set_channel_enablement_all(method, origin) self.fhdhr.device.channels.set_channel_enablement_all(method)
elif not channel or str(channel) not in [str(x) for x in self.fhdhr.device.channels.get_channel_list("number", origin)]: elif not channel or str(channel) not in [str(x) for x in self.fhdhr.device.channels.get_channel_list("number")]:
if redirect_url: if redirect_url:
return redirect("%s?retmessage=%s" % (redirect_url, urllib.parse.quote("%s Failed" % method))) return redirect("%s?retmessage=%s" % (redirect_url, urllib.parse.quote("%s Failed" % method)))
else: else:
return "%s Falied" % method return "%s Falied" % method
else: else:
self.fhdhr.device.channels.set_channel_enablement("number", channel, method, origin) self.fhdhr.device.channels.set_channel_enablement("number", channel, method)
elif method == "update": elif method == "update":
channel_id = request.form.get('id', None) channel_id = request.form.get('id', None)
@ -127,7 +111,7 @@ class Channels():
updatedict[key] = confvalue updatedict[key] = confvalue
elif key in ["favorite", "HD"]: elif key in ["favorite", "HD"]:
updatedict[key] = int(request.form.get(key)) updatedict[key] = int(request.form.get(key))
self.fhdhr.device.channels.set_channel_status("id", channel_id, updatedict, origin) self.fhdhr.device.channels.set_channel_status("id", channel_id, updatedict)
elif method == "modify": elif method == "modify":
channels_list = json.loads(request.form.get('channels', [])) channels_list = json.loads(request.form.get('channels', []))
@ -155,10 +139,10 @@ class Channels():
updatedict[key] = int(channel[key]) updatedict[key] = int(channel[key])
else: else:
channel_id = str(channel[key]) channel_id = str(channel[key])
self.fhdhr.device.channels.set_channel_status("id", channel_id, updatedict, origin) self.fhdhr.device.channels.set_channel_status("id", channel_id, updatedict)
elif method == "scan": elif method == "scan":
self.fhdhr.device.channels.get_channels(forceupdate=True, origin=origin) self.fhdhr.device.channels.get_channels(forceupdate=True)
else: else:
return "Invalid Method" return "Invalid Method"

View File

@ -3,7 +3,7 @@ import urllib.parse
import json import json
class Cluster_API(): class Cluster():
endpoints = ["/api/cluster"] endpoints = ["/api/cluster"]
endpoint_name = "api_cluster" endpoint_name = "api_cluster"
endpoint_methods = ["GET", "POST"] endpoint_methods = ["GET", "POST"]
@ -11,9 +11,8 @@ class Cluster_API():
"method": "get" "method": "get"
} }
def __init__(self, fhdhr, plugin_utils): def __init__(self, fhdhr):
self.fhdhr = fhdhr self.fhdhr = fhdhr
self.plugin_utils = plugin_utils
def __call__(self, *args): def __call__(self, *args):
return self.get(*args) return self.get(*args)
@ -25,18 +24,7 @@ class Cluster_API():
redirect_url = request.args.get('redirect', default=None, type=str) redirect_url = request.args.get('redirect', default=None, type=str)
if method == "get": if method == "get":
jsoncluster = self.fhdhr.device.interfaces[self.plugin_utils.namespace].cluster() jsoncluster = self.fhdhr.device.cluster.cluster()
cluster_json = json.dumps(jsoncluster, indent=4)
return Response(status=200,
response=cluster_json,
mimetype='application/json')
elif method == "ident":
jsoncluster = {
"name": self.fhdhr.config.dict["cluster"]["friendlyname"] or "%s %s" % (self.fhdhr.config.dict["fhdhr"]["friendlyname"], self.fhdhr.origins.valid_origins[0])
}
cluster_json = json.dumps(jsoncluster, indent=4) cluster_json = json.dumps(jsoncluster, indent=4)
return Response(status=200, return Response(status=200,
@ -47,17 +35,17 @@ class Cluster_API():
self.fhdhr.device.ssdp.m_search() self.fhdhr.device.ssdp.m_search()
elif method == 'add': elif method == 'add':
self.fhdhr.device.interfaces[self.plugin_utils.namespace].add(location) self.fhdhr.device.cluster.add(location)
elif method == 'del': elif method == 'del':
self.fhdhr.device.interfaces[self.plugin_utils.namespace].remove(location) self.fhdhr.device.cluster.remove(location)
elif method == 'sync': elif method == 'sync':
self.fhdhr.device.interfaces[self.plugin_utils.namespace].sync(location) self.fhdhr.device.cluster.sync(location)
elif method == 'leave': elif method == 'leave':
self.fhdhr.device.interfaces[self.plugin_utils.namespace].leave() self.fhdhr.device.cluster.leave()
elif method == 'disconnect': elif method == 'disconnect':
self.fhdhr.device.interfaces[self.plugin_utils.namespace].disconnect() self.fhdhr.device.cluster.disconnect()
elif method == 'alive': elif method == 'alive':
self.fhdhr.device.ssdp.do_alive(forcealive=True) self.fhdhr.device.ssdp.do_alive(forcealive=True)

View File

@ -19,16 +19,11 @@ class Debug_JSON():
debugjson = { debugjson = {
"base_url": base_url, "base_url": base_url,
"total channels": len(self.fhdhr.device.channels.list),
"tuner status": self.fhdhr.device.tuners.status(),
} }
cluster_json = json.dumps(debugjson, indent=4)
for origin in list(self.fhdhr.origins.origins_dict.keys()):
debugjson[origin] = {
"tuner status": self.fhdhr.device.tuners.status(origin),
"total channels": len(list(self.fhdhr.device.channels.list[origin].keys()))
}
debug_json = json.dumps(debugjson, indent=4)
return Response(status=200, return Response(status=200,
response=debug_json, response=cluster_json,
mimetype='application/json') mimetype='application/json')

View File

@ -23,18 +23,18 @@ class EPG():
method = request.args.get('method', default="get", type=str) method = request.args.get('method', default="get", type=str)
source = request.args.get('source', default=self.fhdhr.config.dict["epg"]["def_method"], type=str) source = request.args.get('source', default=self.fhdhr.config.dict["epg"]["def_method"], type=str)
if source not in list(self.fhdhr.config.dict["epg"]["valid_methods"].keys()): if source not in self.fhdhr.config.dict["epg"]["valid_methods"]:
return "%s Invalid epg method" % source return "%s Invalid xmltv method" % source
redirect_url = request.args.get('redirect', default=None, type=str) redirect_url = request.args.get('redirect', default=None, type=str)
if method == "get": if method == "get":
epgdict = self.fhdhr.device.epg.get_epg(source) epgdict = self.fhdhr.device.epg.get_epg(source)
if source in self.fhdhr.origins.valid_origins: if source in ["blocks", "origin", self.fhdhr.config.dict["main"]["dictpopname"]]:
epgdict = epgdict.copy() epgdict = epgdict.copy()
for c in list(epgdict.keys()): for c in list(epgdict.keys()):
chan_obj = self.fhdhr.device.channels.get_channel_obj("origin_id", epgdict[c]["id"], source) chan_obj = self.fhdhr.device.channels.get_channel_obj("origin_id", epgdict[c]["id"])
epgdict[chan_obj.number] = epgdict.pop(c) epgdict[chan_obj.number] = epgdict.pop(c)
epgdict[chan_obj.number]["name"] = chan_obj.dict["name"] epgdict[chan_obj.number]["name"] = chan_obj.dict["name"]
epgdict[chan_obj.number]["callsign"] = chan_obj.dict["callsign"] epgdict[chan_obj.number]["callsign"] = chan_obj.dict["callsign"]
@ -93,14 +93,14 @@ class EPG():
else: else:
chan_dict["listing_%s" % time_item] = str(datetime.datetime.fromtimestamp(sorted_chan_guide[channel]["listing"][0][time_item])) chan_dict["listing_%s" % time_item] = str(datetime.datetime.fromtimestamp(sorted_chan_guide[channel]["listing"][0][time_item]))
if source in self.fhdhr.origins.valid_origins: if source in ["blocks", "origin", self.fhdhr.config.dict["main"]["dictpopname"]]:
chan_obj = self.fhdhr.device.channels.get_channel_obj("origin_id", sorted_chan_guide[channel]["id"], source) chan_obj = self.fhdhr.device.channels.get_channel_obj("origin_id", sorted_chan_guide[channel]["id"])
chan_dict["name"] = chan_obj.dict["name"] chan_dict["name"] = chan_obj.dict["name"]
chan_dict["number"] = chan_obj.number chan_dict["number"] = chan_obj.number
chan_dict["chan_thumbnail"] = chan_obj.thumbnail chan_dict["chan_thumbnail"] = chan_obj.thumbnail
chan_dict["enabled"] = chan_obj.dict["enabled"] chan_dict["enabled"] = chan_obj.dict["enabled"]
chan_dict["m3u_url"] = chan_obj.api_m3u_url chan_dict["m3u_url"] = chan_obj.m3u_url
chan_dict["listing_thumbnail"] = chan_dict["listing_thumbnail"] or chan_obj.thumbnail chan_dict["listing_thumbnail"] = chan_dict["listing_thumbnail"] or chan_obj.thumbnail
else: else:

View File

@ -31,7 +31,7 @@ class Images():
elif method == "get": elif method == "get":
source = request.args.get('source', default=self.fhdhr.config.dict["epg"]["method"], type=str) source = request.args.get('source', default=self.fhdhr.config.dict["epg"]["method"], type=str)
if source in list(self.fhdhr.config.dict["epg"]["valid_methods"].keys()): if source in self.fhdhr.config.dict["epg"]["valid_methods"]:
image_type = request.args.get('type', default="content", type=str) image_type = request.args.get('type', default="content", type=str)
if image_type in ["content", "channel"]: if image_type in ["content", "channel"]:
image_id = request.args.get('id', default=None, type=str) image_id = request.args.get('id', default=None, type=str)

View File

@ -26,11 +26,6 @@ class M3U():
if method == "get": if method == "get":
origin_methods = self.fhdhr.origins.valid_origins
origin = request.args.get('origin', default=None, type=str)
if origin and origin not in origin_methods:
return "%s Invalid channels origin" % origin
FORMAT_DESCRIPTOR = "#EXTM3U" FORMAT_DESCRIPTOR = "#EXTM3U"
RECORD_MARKER = "#EXTINF" RECORD_MARKER = "#EXTINF"
@ -42,29 +37,14 @@ class M3U():
channel_items = [] channel_items = []
if origin:
if channel == "all": if channel == "all":
fileName = "channels.m3u" fileName = "channels.m3u"
for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels(origin)]: for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels()]:
channel_obj = self.fhdhr.device.channels.get_channel_obj("id", fhdhr_id, origin) channel_obj = self.fhdhr.device.channels.list[fhdhr_id]
if channel_obj.enabled: if channel_obj.enabled:
channel_items.append(channel_obj) channel_items.append(channel_obj)
elif str(channel) in [str(x) for x in self.fhdhr.device.channels.get_channel_list("number", origin)]: elif str(channel) in [str(x) for x in self.fhdhr.device.channels.get_channel_list("number")]:
channel_obj = self.fhdhr.device.channels.get_channel_obj("number", channel, origin) channel_obj = self.fhdhr.device.channels.get_channel_obj("number", channel)
fileName = "%s.m3u" % channel_obj.number
if channel_obj.enabled:
channel_items.append(channel_obj)
else:
return "Channel Disabled"
elif channel != "all" and str(channel) in [str(x) for x in self.fhdhr.device.channels.get_channel_list("id", origin)]:
channel_obj = self.fhdhr.device.channels.get_channel_obj("id", channel, origin)
fileName = "%s.m3u" % channel_obj.number
if channel_obj.enabled:
channel_items.append(channel_obj)
else:
return "Channel Disabled"
elif not origin and channel != "all" and str(channel) in [str(x) for x in self.fhdhr.device.channels.get_channel_list("id")]:
channel_obj = self.fhdhr.device.channels.get_channel_obj("id", channel)
fileName = "%s.m3u" % channel_obj.number fileName = "%s.m3u" % channel_obj.number
if channel_obj.enabled: if channel_obj.enabled:
channel_items.append(channel_obj) channel_items.append(channel_obj)

View File

@ -1,30 +0,0 @@
from flask import Response
import json
class Plugins_JSON():
endpoints = ["/api/plugins"]
endpoint_name = "api_plugins"
endpoint_methods = ["GET", "POST"]
def __init__(self, fhdhr):
self.fhdhr = fhdhr
def __call__(self, *args):
return self.get(*args)
def get(self, *args):
pluginsjson = {}
for plugin in list(self.fhdhr.plugins.plugins.keys()):
pluginsjson[plugin] = {
"name": plugin,
"manifest": self.fhdhr.plugins.plugins[plugin].manifest
}
plugins_json = json.dumps(pluginsjson, indent=4)
return Response(status=200,
response=plugins_json,
mimetype='application/json')

View File

@ -1,4 +1,4 @@
from flask import redirect from flask import redirect, request, session
class Root_URL(): class Root_URL():
@ -13,4 +13,20 @@ class Root_URL():
return self.get(*args) return self.get(*args)
def get(self, *args): def get(self, *args):
user_agent = request.headers.get('User-Agent')
# Client Devices Discovering Device Information
if not user_agent or session["is_plexmediaserver"]:
# Plex Remote Media Grabber redirect
if self.fhdhr.config.dict["rmg"]["enabled"] and session["is_plexmediaserver"]:
return redirect("/rmg")
# Client Device is looking for HDHR type device
else:
return redirect("/hdhr/device.xml")
# Anything Else is likely a Web Browser
else:
return redirect("/index") return redirect("/index")

View File

@ -30,8 +30,11 @@ class Settings():
web_settings_dict[config_section] = {} web_settings_dict[config_section] = {}
for config_item in list(self.fhdhr.config.conf_default[config_section].keys()): for config_item in list(self.fhdhr.config.conf_default[config_section].keys()):
real_config_section = config_section
if config_section == self.fhdhr.config.dict["main"]["dictpopname"]:
real_config_section = "origin"
web_settings_dict[config_section][config_item] = { web_settings_dict[config_section][config_item] = {
"value": self.fhdhr.config.dict[config_section][config_item], "value": self.fhdhr.config.dict[real_config_section][config_item],
} }
if self.fhdhr.config.conf_default[config_section][config_item]["config_web_hidden"]: if self.fhdhr.config.conf_default[config_section][config_item]["config_web_hidden"]:
web_settings_dict[config_section][config_item]["value"] = "***********" web_settings_dict[config_section][config_item]["value"] = "***********"
@ -53,7 +56,10 @@ class Settings():
else: else:
return "%s Falied" % method return "%s Falied" % method
self.fhdhr.config.write(config_name, config_value, config_section) if config_section == "origin":
config_section = self.fhdhr.config.dict["main"]["dictpopname"]
self.fhdhr.config.write(config_section, config_name, config_value)
elif method == "restart": elif method == "restart":
restart_thread = threading.Thread(target=self.restart_thread) restart_thread = threading.Thread(target=self.restart_thread)

View File

@ -16,8 +16,6 @@ class Startup_Tasks():
def get(self, *args): def get(self, *args):
self.fhdhr.logger.info("Running Startup Tasks.")
# Hit Channel Update API # Hit Channel Update API
haseverscanned = self.fhdhr.db.get_fhdhr_value("channels", "scanned_time") haseverscanned = self.fhdhr.db.get_fhdhr_value("channels", "scanned_time")
updatechannels = False updatechannels = False
@ -27,13 +25,10 @@ class Startup_Tasks():
updatechannels = True updatechannels = True
if updatechannels: if updatechannels:
for origin in list(self.fhdhr.origins.origins_dict.keys()): self.fhdhr.api.get(self.channel_update_url)
self.fhdhr.api.get("%s&origin=%s" % (self.channel_update_url, origin))
# Hit EPG Update API # Hit EPG Update API
for epg_method in self.fhdhr.device.epg.epg_methods: for epg_method in self.fhdhr.device.epg.epg_methods:
self.fhdhr.api.get("%s&source=%s" % (self.epg_update_url, epg_method)) self.fhdhr.api.get("%s&source=%s" % (self.epg_update_url, epg_method))
self.fhdhr.logger.info("Startup Tasks Complete.")
return "Success" return "Success"

View File

@ -3,17 +3,16 @@ import urllib.parse
import json import json
class DevTools_API(): class API_Tools():
endpoints = ["/api/devtools"] endpoints = ["/api/tools"]
endpoint_name = "api_devtools" endpoint_name = "api_tools"
endpoint_methods = ["GET", "POST"] endpoint_methods = ["GET", "POST"]
endpoint_default_parameters = { endpoint_default_parameters = {
"method": "get" "method": "get"
} }
def __init__(self, fhdhr, plugin_utils): def __init__(self, fhdhr):
self.fhdhr = fhdhr self.fhdhr = fhdhr
self.plugin_utils = plugin_utils
def __call__(self, *args): def __call__(self, *args):
return self.get(*args) return self.get(*args)
@ -37,11 +36,11 @@ class DevTools_API():
dirty_json_url = request.form.get('url', None) dirty_json_url = request.form.get('url', None)
try: try:
json_url_req = self.plugin_utils.web.session.get(dirty_json_url) json_url_req = self.fhdhr.web.session.get(dirty_json_url)
json_url_req.raise_for_status() json_url_req.raise_for_status()
json_resp = json_url_req.json() json_resp = json_url_req.json()
except self.plugin_utilsplugin_utils.web.exceptions.HTTPError as err: except self.fhdhr.web.exceptions.HTTPError as err:
self.plugin_utils.logger.error('Error while getting stations: %s' % err) self.fhdhr.logger.error('Error while getting stations: %s' % err)
json_resp = {"error": 'Error while getting stations: %s' % err} json_resp = {"error": 'Error while getting stations: %s' % err}
return_json = json.dumps(json_resp, indent=4) return_json = json.dumps(json_resp, indent=4)

View File

@ -31,48 +31,25 @@ class Tuners():
redirect_url = request.args.get('redirect', default=None, type=str) redirect_url = request.args.get('redirect', default=None, type=str)
origin_methods = self.fhdhr.origins.valid_origins if method in self.fhdhr.config.dict["streaming"]["valid_methods"]:
origin = request.args.get('origin', default=None, type=str)
if origin and origin not in origin_methods:
return "%s Invalid channels origin" % origin
if method in list(self.fhdhr.config.dict["streaming"]["valid_methods"].keys()):
channel_number = request.args.get('channel', None, type=str) channel_number = request.args.get('channel', None, type=str)
if not channel_number: if not channel_number:
return "Missing Channel" return "Missing Channel"
if origin: if str(channel_number) not in [str(x) for x in self.fhdhr.device.channels.get_channel_list("number")]:
if str(channel_number) in [str(x) for x in self.fhdhr.device.channels.get_channel_list("number", origin)]:
chan_obj = self.fhdhr.device.channels.get_channel_obj("number", channel_number, origin)
elif str(channel_number) in [str(x) for x in self.fhdhr.device.channels.get_channel_list("id", origin)]:
chan_obj = self.fhdhr.device.channels.get_channel_obj("id", channel_number, origin)
else:
response = Response("Not Found", status=404) response = Response("Not Found", status=404)
response.headers["X-fHDHR-Error"] = "801 - Unknown Channel" response.headers["X-fHDHR-Error"] = "801 - Unknown Channel"
self.fhdhr.logger.error(response.headers["X-fHDHR-Error"]) self.fhdhr.logger.error(response.headers["X-fHDHR-Error"])
abort(response) abort(response)
else: channel_dict = self.fhdhr.device.channels.get_channel_dict("number", channel_number)
if not channel_dict["enabled"]:
if str(channel_number) in [str(x) for x in self.fhdhr.device.channels.get_channel_list("id")]:
chan_obj = self.fhdhr.device.channels.get_channel_obj("id", channel_number)
else:
response = Response("Not Found", status=404)
response.headers["X-fHDHR-Error"] = "801 - Unknown Channel"
self.fhdhr.logger.error(response.headers["X-fHDHR-Error"])
abort(response)
if not chan_obj.dict["enabled"]:
response = Response("Service Unavailable", status=503) response = Response("Service Unavailable", status=503)
response.headers["X-fHDHR-Error"] = str("806 - Tune Failed") response.headers["X-fHDHR-Error"] = str("806 - Tune Failed")
self.fhdhr.logger.error(response.headers["X-fHDHR-Error"]) self.fhdhr.logger.error(response.headers["X-fHDHR-Error"])
abort(response) abort(response)
origin = chan_obj.origin
channel_number = chan_obj.number
duration = request.args.get('duration', default=0, type=int) duration = request.args.get('duration', default=0, type=int)
transcode_quality = request.args.get('transcode', default=None, type=str) transcode_quality = request.args.get('transcode', default=None, type=str)
@ -85,7 +62,6 @@ class Tuners():
stream_args = { stream_args = {
"channel": channel_number, "channel": channel_number,
"origin": origin,
"method": method, "method": method,
"duration": duration, "duration": duration,
"origin_quality": self.fhdhr.config.dict["streaming"]["origin_quality"], "origin_quality": self.fhdhr.config.dict["streaming"]["origin_quality"],
@ -97,9 +73,9 @@ class Tuners():
try: try:
if not tuner_number: if not tuner_number:
tunernum = self.fhdhr.device.tuners.first_available(origin, channel_number) tunernum = self.fhdhr.device.tuners.first_available(channel_number)
else: else:
tunernum = self.fhdhr.device.tuners.tuner_grab(tuner_number, origin, channel_number) tunernum = self.fhdhr.device.tuners.tuner_grab(tuner_number, channel_number)
except TunerError as e: except TunerError as e:
self.fhdhr.logger.info("A %s stream request for channel %s was rejected due to %s" self.fhdhr.logger.info("A %s stream request for channel %s was rejected due to %s"
% (stream_args["method"], str(stream_args["channel"]), str(e))) % (stream_args["method"], str(stream_args["channel"]), str(e)))
@ -108,20 +84,20 @@ class Tuners():
self.fhdhr.logger.error(response.headers["X-fHDHR-Error"]) self.fhdhr.logger.error(response.headers["X-fHDHR-Error"])
abort(response) abort(response)
tuner = self.fhdhr.device.tuners.tuners[origin][str(tunernum)] tuner = self.fhdhr.device.tuners.tuners[str(tunernum)]
try: try:
stream_args = self.fhdhr.device.tuners.get_stream_info(stream_args) stream_args = self.fhdhr.device.tuners.get_stream_info(stream_args)
except TunerError as e: except TunerError as e:
self.fhdhr.logger.info("A %s stream request for %s channel %s was rejected due to %s" self.fhdhr.logger.info("A %s stream request for channel %s was rejected due to %s"
% (origin, stream_args["method"], str(stream_args["channel"]), str(e))) % (stream_args["method"], str(stream_args["channel"]), str(e)))
response = Response("Service Unavailable", status=503) response = Response("Service Unavailable", status=503)
response.headers["X-fHDHR-Error"] = str(e) response.headers["X-fHDHR-Error"] = str(e)
self.fhdhr.logger.error(response.headers["X-fHDHR-Error"]) self.fhdhr.logger.error(response.headers["X-fHDHR-Error"])
tuner.close() tuner.close()
abort(response) abort(response)
self.fhdhr.logger.info("%s Tuner #%s to be used for stream." % (origin, tunernum)) self.fhdhr.logger.info("Tuner #%s to be used for stream." % tunernum)
tuner.set_status(stream_args) tuner.set_status(stream_args)
session["tuner_used"] = tunernum session["tuner_used"] = tunernum
@ -129,48 +105,29 @@ class Tuners():
elif method == "close": elif method == "close":
if not origin: if not tuner_number or str(tuner_number) not in list(self.fhdhr.device.tuners.tuners.keys()):
return "Missing Origin"
if not tuner_number or str(tuner_number) not in list(self.fhdhr.device.tuners.tuners[origin].keys()):
return "%s Invalid tuner" % str(tuner_number) return "%s Invalid tuner" % str(tuner_number)
session["tuner_used"] = tuner_number session["tuner_used"] = tuner_number
tuner = self.fhdhr.device.tuners.tuners[origin][str(tuner_number)] tuner = self.fhdhr.device.tuners.tuners[str(tuner_number)]
tuner.close() tuner.close()
elif method == "scan": elif method == "scan":
if not origin:
for origin in list(self.fhdhr.device.tuners.tuners.keys()):
if not tuner_number: if not tuner_number:
tunernum = self.fhdhr.device.tuners.first_available(origin, None) tunernum = self.fhdhr.device.tuners.first_available(None)
else: else:
tunernum = self.fhdhr.device.tuners.tuner_grab(tuner_number, origin, None) tunernum = self.fhdhr.device.tuners.tuner_grab(tuner_number, None)
tuner = self.fhdhr.device.tuners.tuners[origin][str(tunernum)] tuner = self.fhdhr.device.tuners.tuners[str(tunernum)]
tuner.channel_scan(origin=origin, grabbed=False) tuner.channel_scan(grabbed=True)
else:
if not tuner_number:
tunernum = self.fhdhr.device.tuners.first_available(origin, None)
else:
tunernum = self.fhdhr.device.tuners.tuner_grab(tuner_number, origin, None)
tuner = self.fhdhr.device.tuners.tuners[origin][str(tunernum)]
tuner.channel_scan(origin=origin, grabbed=True)
elif method == "status": elif method == "status":
if not origin:
if not tuner_number: if not tuner_number:
tuner_status = self.fhdhr.device.tuners.status() tuner_status = self.fhdhr.device.tuners.status()
else: elif str(tuner_number) in list(self.fhdhr.device.tuners.tuners.keys()):
tuner_status = ["Invalid Tuner %s" % tuner_number] tuner_status = self.fhdhr.device.tuners.tuners[str(tuner_number)].get_status()
else:
if not tuner_number:
tuner_status = self.fhdhr.device.tuners.status(origin)
elif str(tuner_number) in list(self.fhdhr.device.tuners.tuners[origin].keys()):
tuner_status = self.fhdhr.device.tuners.tuners[origin][str(tuner_number)].get_status()
else: else:
tuner_status = ["Invalid Tuner %s" % tuner_number] tuner_status = ["Invalid Tuner %s" % tuner_number]

View File

@ -26,11 +26,6 @@ class W3U():
if method == "get": if method == "get":
origin_methods = self.fhdhr.origins.valid_origins
origin = request.args.get('origin', default=None, type=str)
if origin and origin not in origin_methods:
return "%s Invalid channels origin" % origin
channel_info_m3u = { channel_info_m3u = {
"name": self.fhdhr.config.dict["fhdhr"]["friendlyname"], "name": self.fhdhr.config.dict["fhdhr"]["friendlyname"],
"image": '%s/favicon.ico' % base_url, "image": '%s/favicon.ico' % base_url,
@ -40,30 +35,15 @@ class W3U():
channel_items = [] channel_items = []
if origin:
if channel == "all": if channel == "all":
fileName = "channels.m3u" fileName = "channels.w3u"
for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels(origin)]: for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels()]:
channel_obj = self.fhdhr.device.channels.get_channel_obj("id", fhdhr_id, origin) channel_obj = self.fhdhr.device.channels.list[fhdhr_id]
if channel_obj.enabled: if channel_obj.enabled:
channel_items.append(channel_obj) channel_items.append(channel_obj)
elif str(channel) in [str(x) for x in self.fhdhr.device.channels.get_channel_list("number", origin)]: elif str(channel) in [str(x) for x in self.fhdhr.device.channels.get_channel_list("number")]:
channel_obj = self.fhdhr.device.channels.get_channel_obj("number", channel, origin) channel_obj = self.fhdhr.device.channels.get_channel_obj("number", channel)
fileName = "%s.m3u" % channel_obj.number fileName = "%s.w3u" % channel_obj.number
if channel_obj.enabled:
channel_items.append(channel_obj)
else:
return "Channel Disabled"
elif channel != "all" and str(channel) in [str(x) for x in self.fhdhr.device.channels.get_channel_list("id", origin)]:
channel_obj = self.fhdhr.device.channels.get_channel_obj("id", channel, origin)
fileName = "%s.m3u" % channel_obj.number
if channel_obj.enabled:
channel_items.append(channel_obj)
else:
return "Channel Disabled"
elif not origin and channel != "all" and str(channel) in [str(x) for x in self.fhdhr.device.channels.get_channel_list("id")]:
channel_obj = self.fhdhr.device.channels.get_channel_obj("id", channel)
fileName = "%s.m3u" % channel_obj.number
if channel_obj.enabled: if channel_obj.enabled:
channel_items.append(channel_obj) channel_items.append(channel_obj)
else: else:

View File

@ -38,7 +38,7 @@ class xmlTV():
method = request.args.get('method', default="get", type=str) method = request.args.get('method', default="get", type=str)
source = request.args.get('source', default=self.fhdhr.config.dict["epg"]["def_method"], type=str) source = request.args.get('source', default=self.fhdhr.config.dict["epg"]["def_method"], type=str)
if source not in list(self.fhdhr.config.dict["epg"]["valid_methods"].keys()): if source not in self.fhdhr.config.dict["epg"]["valid_methods"]:
return "%s Invalid xmltv method" % source return "%s Invalid xmltv method" % source
redirect_url = request.args.get('redirect', default=None, type=str) redirect_url = request.args.get('redirect', default=None, type=str)
@ -47,10 +47,10 @@ class xmlTV():
epgdict = self.fhdhr.device.epg.get_epg(source) epgdict = self.fhdhr.device.epg.get_epg(source)
if source in self.fhdhr.origins.valid_origins: if source in ["blocks", "origin", self.fhdhr.config.dict["main"]["dictpopname"]]:
epgdict = epgdict.copy() epgdict = epgdict.copy()
for c in list(epgdict.keys()): for c in list(epgdict.keys()):
chan_obj = self.fhdhr.device.channels.get_channel_obj("origin_id", epgdict[c]["id"], source) chan_obj = self.fhdhr.device.channels.get_channel_obj("origin_id", epgdict[c]["id"])
epgdict[chan_obj.number] = epgdict.pop(c) epgdict[chan_obj.number] = epgdict.pop(c)
epgdict[chan_obj.number]["name"] = chan_obj.dict["name"] epgdict[chan_obj.number]["name"] = chan_obj.dict["name"]
epgdict[chan_obj.number]["callsign"] = chan_obj.dict["callsign"] epgdict[chan_obj.number]["callsign"] = chan_obj.dict["callsign"]
@ -113,9 +113,9 @@ class xmlTV():
out = self.xmltv_headers() out = self.xmltv_headers()
if source in self.fhdhr.origins.valid_origins: if source in ["origin", "blocks", self.fhdhr.config.dict["main"]["dictpopname"]]:
for c in list(epgdict.keys()): for c in list(epgdict.keys()):
chan_obj = self.fhdhr.device.channels.get_channel_obj("origin_id", epgdict[c]["id"], source) chan_obj = self.fhdhr.device.channels.get_channel_obj("origin_id", epgdict[c]["id"])
epgdict[chan_obj.number] = epgdict.pop(c) epgdict[chan_obj.number] = epgdict.pop(c)
epgdict[chan_obj.number]["name"] = chan_obj.dict["name"] epgdict[chan_obj.number]["name"] = chan_obj.dict["name"]
epgdict[chan_obj.number]["callsign"] = chan_obj.dict["callsign"] epgdict[chan_obj.number]["callsign"] = chan_obj.dict["callsign"]

View File

@ -2,6 +2,7 @@
from .favicon_ico import Favicon_ICO from .favicon_ico import Favicon_ICO
from .style_css import Style_CSS from .style_css import Style_CSS
from .device_xml import Device_XML
class fHDHR_Files(): class fHDHR_Files():
@ -11,3 +12,4 @@ class fHDHR_Files():
self.favicon = Favicon_ICO(fhdhr) self.favicon = Favicon_ICO(fhdhr)
self.style = Style_CSS(fhdhr) self.style = Style_CSS(fhdhr)
self.device_xml = Device_XML(fhdhr)

View File

@ -0,0 +1,19 @@
from flask import redirect, session
class Device_XML():
endpoints = ["/device.xml"]
endpoint_name = "file_device_xml"
def __init__(self, fhdhr):
self.fhdhr = fhdhr
def __call__(self, *args):
return self.get(*args)
def get(self, *args):
if self.fhdhr.config.dict["rmg"]["enabled"] and session["is_plexmediaserver"]:
return redirect("/rmg/device.xml")
else:
return redirect("/hdhr/device.xml")

View File

@ -12,11 +12,10 @@ from .auto import Auto
from .tuner import Tuner from .tuner import Tuner
class Plugin_OBJ(): class fHDHR_HDHR():
def __init__(self, fhdhr, plugin_utils): def __init__(self, fhdhr):
self.fhdhr = fhdhr self.fhdhr = fhdhr
self.plugin_utils = plugin_utils
self.lineup_post = Lineup_Post(fhdhr) self.lineup_post = Lineup_Post(fhdhr)

View File

@ -9,37 +9,28 @@ class Auto():
def __init__(self, fhdhr): def __init__(self, fhdhr):
self.fhdhr = fhdhr self.fhdhr = fhdhr
@property
def source(self):
return self.fhdhr.config.dict["hdhr"]["source"] or self.fhdhr.origins.valid_origins[0]
def __call__(self, channel, *args): def __call__(self, channel, *args):
return self.get(channel, *args) return self.get(channel, *args)
def get(self, channel, *args): def get(self, channel, *args):
origin = self.source method = request.args.get('method', default=self.fhdhr.config.dict["streaming"]["method"], type=str)
redirect_url = "/api/tuners?method=%s" % (self.fhdhr.config.dict["streaming"]["method"]) redirect_url = "/api/tuners?method=%s" % (method)
if channel.startswith("v"): if channel.startswith("v"):
channel_number = channel.replace('v', '') channel_number = channel.replace('v', '')
elif channel.startswith("ch"): elif channel.startswith("ch"):
channel_freq = channel.replace('ch', '').split("-")[0] channel_freq = channel.replace('ch', '').split("-")[0]
subchannel = None subchannel = 0
if "-" in channel: if "-" in channel:
subchannel = channel.replace('ch', '').split("-")[1] subchannel = channel.replace('ch', '').split("-")[1]
if subchannel: self.fhdhr.logger.error("Not Implemented %s-%s" % (str(channel_freq), str(subchannel)))
self.fhdhr.logger.error("Not Implemented %s-%s" % (channel_freq, subchannel)) abort(501, "Not Implemented %s-%s" % (str(channel_freq), str(subchannel)))
abort(501, "Not Implemented %s-%s" % (channel_freq, subchannel))
else:
self.fhdhr.logger.error("Not Implemented %s" % (channel_freq, subchannel))
abort(501, "Not Implemented %s" % channel_freq)
else: else:
channel_number = channel channel_number = channel
redirect_url += "&channel=%s" % str(channel_number) redirect_url += "&channel=%s" % str(channel_number)
redirect_url += "&origin=%s" % str(origin)
duration = request.args.get('duration', default=0, type=int) duration = request.args.get('duration', default=0, type=int)
if duration: if duration:

View File

@ -6,16 +6,12 @@ from fHDHR.tools import sub_el
class HDHR_Device_XML(): class HDHR_Device_XML():
endpoints = ["/hdhr", "/hdhr/", "/hdhr/device.xml"] endpoints = ["/hdhr/device.xml"]
endpoint_name = "hdhr_device_xml" endpoint_name = "hdhr_device_xml"
def __init__(self, fhdhr): def __init__(self, fhdhr):
self.fhdhr = fhdhr self.fhdhr = fhdhr
@property
def source(self):
return self.fhdhr.config.dict["hdhr"]["source"] or self.fhdhr.origins.valid_origins[0]
def __call__(self, *args): def __call__(self, *args):
return self.get(*args) return self.get(*args)
@ -24,14 +20,10 @@ class HDHR_Device_XML():
base_url = request.url_root[:-1] base_url = request.url_root[:-1]
origin = self.source
origin_plugin_name = self.fhdhr.origins.origins_dict[origin].plugin_utils.plugin_name
origin_plugin_version = self.fhdhr.origins.origins_dict[origin].plugin_utils.plugin_manifest["version"]
out = xml.etree.ElementTree.Element('root') out = xml.etree.ElementTree.Element('root')
out.set('xmlns', "urn:schemas-upnp-org:device-1-0") out.set('xmlns', "urn:schemas-upnp-org:device-1-0")
sub_el(out, 'URLBase', "%s/hdhr" % base_url) sub_el(out, 'URLBase', "%s" % base_url)
specVersion_out = sub_el(out, 'specVersion') specVersion_out = sub_el(out, 'specVersion')
sub_el(specVersion_out, 'major', "1") sub_el(specVersion_out, 'major', "1")
@ -41,15 +33,15 @@ class HDHR_Device_XML():
sub_el(device_out, 'deviceType', "urn:schemas-upnp-org:device:MediaServer:1") sub_el(device_out, 'deviceType', "urn:schemas-upnp-org:device:MediaServer:1")
sub_el(device_out, 'friendlyName', "%s %s" % (self.fhdhr.config.dict["fhdhr"]["friendlyname"], origin)) sub_el(device_out, 'friendlyName', self.fhdhr.config.dict["fhdhr"]["friendlyname"])
sub_el(device_out, 'manufacturer', self.fhdhr.config.dict["hdhr"]["reporting_manufacturer"]) sub_el(device_out, 'manufacturer', self.fhdhr.config.dict["fhdhr"]["reporting_manufacturer"])
sub_el(device_out, 'manufacturerURL', "https://github.com/fHDHR/%s" % origin_plugin_name) sub_el(device_out, 'manufacturerURL', "https://github.com/fHDHR/%s" % self.fhdhr.config.dict["main"]["reponame"])
sub_el(device_out, 'modelName', self.fhdhr.config.dict["hdhr"]["reporting_model"]) sub_el(device_out, 'modelName', self.fhdhr.config.dict["fhdhr"]["reporting_model"])
sub_el(device_out, 'modelNumber', origin_plugin_version) sub_el(device_out, 'modelNumber', self.fhdhr.config.internal["versions"]["fHDHR"])
sub_el(device_out, 'serialNumber') sub_el(device_out, 'serialNumber')
sub_el(device_out, 'UDN', "uuid:%s%s" % (self.fhdhr.config.dict["main"]["uuid"], origin)) sub_el(device_out, 'UDN', "uuid:%s" % self.fhdhr.config.dict["main"]["uuid"])
fakefile = BytesIO() fakefile = BytesIO()
fakefile.write(b'<?xml version="1.0" encoding="UTF-8"?>\n') fakefile.write(b'<?xml version="1.0" encoding="UTF-8"?>\n')

View File

@ -9,10 +9,6 @@ class Discover_JSON():
def __init__(self, fhdhr): def __init__(self, fhdhr):
self.fhdhr = fhdhr self.fhdhr = fhdhr
@property
def source(self):
return self.fhdhr.config.dict["hdhr"]["source"] or self.fhdhr.origins.valid_origins[0]
def __call__(self, *args): def __call__(self, *args):
return self.get(*args) return self.get(*args)
@ -20,19 +16,17 @@ class Discover_JSON():
base_url = request.url_root[:-1] base_url = request.url_root[:-1]
origin = self.source
jsondiscover = { jsondiscover = {
"FriendlyName": "%s %s" % (self.fhdhr.config.dict["fhdhr"]["friendlyname"], origin), "FriendlyName": self.fhdhr.config.dict["fhdhr"]["friendlyname"],
"Manufacturer": self.fhdhr.config.dict["hdhr"]["reporting_manufacturer"], "Manufacturer": self.fhdhr.config.dict["fhdhr"]["reporting_manufacturer"],
"ModelNumber": self.fhdhr.config.dict["hdhr"]["reporting_model"], "ModelNumber": self.fhdhr.config.dict["fhdhr"]["reporting_model"],
"FirmwareName": self.fhdhr.config.dict["hdhr"]["reporting_firmware_name"], "FirmwareName": self.fhdhr.config.dict["fhdhr"]["reporting_firmware_name"],
"TunerCount": self.fhdhr.origins.origins_dict[origin].tuners, "TunerCount": self.fhdhr.config.dict["fhdhr"]["tuner_count"],
"FirmwareVersion": self.fhdhr.config.dict["hdhr"]["reporting_firmware_ver"], "FirmwareVersion": self.fhdhr.config.dict["fhdhr"]["reporting_firmware_ver"],
"DeviceID": "%s%s" % (self.fhdhr.config.dict["main"]["uuid"], origin), "DeviceID": self.fhdhr.config.dict["main"]["uuid"],
"DeviceAuth": self.fhdhr.config.dict["fhdhr"]["device_auth"], "DeviceAuth": self.fhdhr.config.dict["fhdhr"]["device_auth"],
"BaseURL": "%s/hdhr" % base_url, "BaseURL": "%s" % base_url,
"LineupURL": "%s/hdhr/lineup.json" % base_url "LineupURL": "%s/lineup.json" % base_url
} }
discover_json = json.dumps(jsondiscover, indent=4) discover_json = json.dumps(jsondiscover, indent=4)

View File

@ -0,0 +1,46 @@
from flask import Response, request
import json
from fHDHR.tools import channel_sort
class Lineup_JSON():
endpoints = ["/lineup.json", "/hdhr/lineup.json"]
endpoint_name = "hdhr_lineup_json"
def __init__(self, fhdhr):
self.fhdhr = fhdhr
def __call__(self, *args):
return self.get(*args)
def get(self, *args):
base_url = request.url_root[:-1]
show = request.args.get('show', default="all", type=str)
channelslist = {}
for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels()]:
channel_obj = self.fhdhr.device.channels.list[fhdhr_id]
if channel_obj.enabled or show == "found":
lineup_dict = channel_obj.lineup_dict
lineup_dict["URL"] = "%s%s" % (base_url, lineup_dict["URL"])
if show == "found" and channel_obj.enabled:
lineup_dict["Enabled"] = 1
elif show == "found" and not channel_obj.enabled:
lineup_dict["Enabled"] = 0
channelslist[channel_obj.number] = lineup_dict
# Sort the channels
sorted_channel_list = channel_sort(list(channelslist.keys()))
sorted_chan_guide = []
for channel in sorted_channel_list:
sorted_chan_guide.append(channelslist[channel])
lineup_json = json.dumps(sorted_chan_guide, indent=4)
return Response(status=200,
response=lineup_json,
mimetype='application/json')

View File

@ -11,28 +11,22 @@ class Lineup_Post():
def __init__(self, fhdhr): def __init__(self, fhdhr):
self.fhdhr = fhdhr self.fhdhr = fhdhr
@property
def source(self):
return self.fhdhr.config.dict["hdhr"]["source"] or self.fhdhr.origins.valid_origins[0]
def __call__(self, *args): def __call__(self, *args):
return self.get(*args) return self.get(*args)
def get(self, *args): def get(self, *args):
origin = self.source
if 'scan' in list(request.args.keys()): if 'scan' in list(request.args.keys()):
if request.args['scan'] == 'start': if request.args['scan'] == 'start':
try: try:
self.fhdhr.device.tuners.tuner_scan(origin) self.fhdhr.device.tuners.tuner_scan()
except TunerError as e: except TunerError as e:
self.fhdhr.logger.info(str(e)) self.fhdhr.logger.info(str(e))
return Response(status=200, mimetype='text/html') return Response(status=200, mimetype='text/html')
elif request.args['scan'] == 'abort': elif request.args['scan'] == 'abort':
self.fhdhr.device.tuners.stop_tuner_scan(origin) self.fhdhr.device.tuners.stop_tuner_scan()
return Response(status=200, mimetype='text/html') return Response(status=200, mimetype='text/html')
else: else:
@ -45,18 +39,18 @@ class Lineup_Post():
channel_method = request.args['favorite'][0] channel_method = request.args['favorite'][0]
channel_number = request.args['favorite'][1:] channel_number = request.args['favorite'][1:]
if str(channel_number) not in [str(x) for x in self.fhdhr.device.channels.get_channel_list("number", origin)]: if str(channel_number) not in [str(x) for x in self.fhdhr.device.channels.get_channel_list("number")]:
response = Response("Not Found", status=404) response = Response("Not Found", status=404)
response.headers["X-fHDHR-Error"] = "801 - Unknown Channel" response.headers["X-fHDHR-Error"] = "801 - Unknown Channel"
self.fhdhr.logger.error(response.headers["X-fHDHR-Error"]) self.fhdhr.logger.error(response.headers["X-fHDHR-Error"])
abort(response) abort(response)
if channel_method == "+": if channel_method == "+":
self.fhdhr.device.channels.set_channel_enablement("number", channel_number, channel_method, origin) self.fhdhr.device.channels.set_channel_enablement("number", channel_number, channel_method)
elif channel_method == "-": elif channel_method == "-":
self.fhdhr.device.channels.set_channel_enablement("number", channel_number, channel_method, origin) self.fhdhr.device.channels.set_channel_enablement("number", channel_number, channel_method)
elif channel_method == "x": elif channel_method == "x":
self.fhdhr.device.channels.set_channel_enablement("number", channel_number, "toggle", origin) self.fhdhr.device.channels.set_channel_enablement("number", channel_number, "toggle")
else: else:
self.fhdhr.logger.warning("Unknown favorite command %s" % request.args['favorite']) self.fhdhr.logger.warning("Unknown favorite command %s" % request.args['favorite'])

View File

@ -9,29 +9,21 @@ class Lineup_Status_JSON():
def __init__(self, fhdhr): def __init__(self, fhdhr):
self.fhdhr = fhdhr self.fhdhr = fhdhr
@property
def source(self):
return self.fhdhr.config.dict["hdhr"]["source"] or self.fhdhr.origins.valid_origins[0]
def __call__(self, *args): def __call__(self, *args):
return self.get(*args) return self.get(*args)
def get(self, *args): def get(self, *args):
origin = self.source tuner_status = self.fhdhr.device.tuners.status()
tuner_status = self.fhdhr.device.tuners.status(origin)
tuners_scanning = 0 tuners_scanning = 0
for tuner_number in list(tuner_status.keys()): for tuner_number in list(tuner_status.keys()):
if tuner_status[tuner_number]["status"] == "Scanning": if tuner_status[tuner_number]["status"] == "Scanning":
tuners_scanning += 1 tuners_scanning += 1
channel_count = len(list(self.fhdhr.device.channels.list[origin].keys()))
if tuners_scanning: if tuners_scanning:
jsonlineup = self.scan_in_progress(origin) jsonlineup = self.scan_in_progress()
elif not channel_count: elif not len(self.fhdhr.device.channels.list):
jsonlineup = self.scan_in_progress(origin) jsonlineup = self.scan_in_progress()
else: else:
jsonlineup = self.not_scanning() jsonlineup = self.not_scanning()
lineup_json = json.dumps(jsonlineup, indent=4) lineup_json = json.dumps(jsonlineup, indent=4)
@ -40,14 +32,11 @@ class Lineup_Status_JSON():
response=lineup_json, response=lineup_json,
mimetype='application/json') mimetype='application/json')
def scan_in_progress(self, origin): def scan_in_progress(self):
channel_count = len(list(self.fhdhr.device.channels.list[origin].keys()))
jsonlineup = { jsonlineup = {
"ScanInProgress": "true", "ScanInProgress": "true",
"Progress": 99, "Progress": 99,
"Found": channel_count "Found": len(self.fhdhr.device.channels.list)
} }
return jsonlineup return jsonlineup
@ -55,7 +44,7 @@ class Lineup_Status_JSON():
jsonlineup = { jsonlineup = {
"ScanInProgress": "false", "ScanInProgress": "false",
"ScanPossible": "true", "ScanPossible": "true",
"Source": self.fhdhr.config.dict["hdhr"]["reporting_tuner_type"], "Source": self.fhdhr.config.dict["fhdhr"]["reporting_tuner_type"],
"SourceList": [self.fhdhr.config.dict["hdhr"]["reporting_tuner_type"]], "SourceList": [self.fhdhr.config.dict["fhdhr"]["reporting_tuner_type"]],
} }
return jsonlineup return jsonlineup

View File

@ -12,10 +12,6 @@ class Lineup_XML():
def __init__(self, fhdhr): def __init__(self, fhdhr):
self.fhdhr = fhdhr self.fhdhr = fhdhr
@property
def source(self):
return self.fhdhr.config.dict["hdhr"]["source"] or self.fhdhr.origins.valid_origins[0]
def __call__(self, *args): def __call__(self, *args):
return self.get(*args) return self.get(*args)
@ -25,35 +21,24 @@ class Lineup_XML():
show = request.args.get('show', default="all", type=str) show = request.args.get('show', default="all", type=str)
origin = self.source
channelslist = {} channelslist = {}
sorted_chan_guide = [] for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels()]:
for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels(origin)]: channel_obj = self.fhdhr.device.channels.list[fhdhr_id]
channel_obj = self.fhdhr.device.channels.get_channel_obj("id", fhdhr_id, origin) if channel_obj.enabled or show == "found":
if channel_obj.enabled: lineup_dict = channel_obj.lineup_dict
channelslist[channel_obj.number] = channel_obj
# Sort the channels
sorted_channel_list = channel_sort(list(channelslist.keys()))
for channel in sorted_channel_list:
channel_obj = channelslist[channel]
lineup_dict = {
'GuideNumber': channel_obj.number,
'GuideName': channel_obj.dict['name'],
'Tags': ",".join(channel_obj.dict['tags']),
'URL': '/hdhr/auto/v%s' % channel_obj.number,
'HD': channel_obj.dict["HD"],
"Favorite": channel_obj.dict["favorite"],
}
lineup_dict["URL"] = "%s%s" % (base_url, lineup_dict["URL"]) lineup_dict["URL"] = "%s%s" % (base_url, lineup_dict["URL"])
if show == "found" and channel_obj.enabled: if show == "found" and channel_obj.enabled:
lineup_dict["Enabled"] = 1 lineup_dict["Enabled"] = 1
elif show == "found" and not channel_obj.enabled: elif show == "found" and not channel_obj.enabled:
lineup_dict["Enabled"] = 0 lineup_dict["Enabled"] = 0
sorted_chan_guide.append(lineup_dict) channelslist[channel_obj.number] = lineup_dict
# Sort the channels
sorted_channel_list = channel_sort(list(channelslist.keys()))
sorted_chan_guide = []
for channel in sorted_channel_list:
sorted_chan_guide.append(channelslist[channel])
out = xml.etree.ElementTree.Element('Lineup') out = xml.etree.ElementTree.Element('Lineup')
for lineup_dict in sorted_chan_guide: for lineup_dict in sorted_chan_guide:

View File

@ -9,39 +9,30 @@ class Tuner():
def __init__(self, fhdhr): def __init__(self, fhdhr):
self.fhdhr = fhdhr self.fhdhr = fhdhr
@property
def source(self):
return self.fhdhr.config.dict["hdhr"]["source"] or self.fhdhr.origins.valid_origins[0]
def __call__(self, tuner_number, channel, *args): def __call__(self, tuner_number, channel, *args):
return self.get(tuner_number, channel, *args) return self.get(tuner_number, channel, *args)
def get(self, tuner_number, channel, *args): def get(self, tuner_number, channel, *args):
origin = self.source method = request.args.get('method', default=self.fhdhr.config.dict["streaming"]["method"], type=str)
redirect_url = "/api/tuners?method=%s" % (self.fhdhr.config.dict["streaming"]["method"]) redirect_url = "/api/tuners?method=%s" % (method)
redirect_url += "&tuner=%s" % (tuner_number) redirect_url += "&tuner=%s" % str(tuner_number)
if channel.startswith("v"): if channel.startswith("v"):
channel_number = channel.replace('v', '') channel_number = channel.replace('v', '')
elif channel.startswith("ch"): elif channel.startswith("ch"):
channel_freq = channel.replace('ch', '').split("-")[0] channel_freq = channel.replace('ch', '').split("-")[0]
subchannel = None subchannel = 0
if "-" in channel: if "-" in channel:
subchannel = channel.replace('ch', '').split("-")[1] subchannel = channel.replace('ch', '').split("-")[1]
if subchannel: self.fhdhr.logger.error("Not Implemented %s-%s" % (str(channel_freq), str(subchannel)))
self.fhdhr.logger.error("Not Implemented %s-%s" % (channel_freq, subchannel)) abort(501, "Not Implemented %s-%s" % (str(channel_freq), str(subchannel)))
abort(501, "Not Implemented %s-%s" % (channel_freq, subchannel))
else:
self.fhdhr.logger.error("Not Implemented %s" % (channel_freq, subchannel))
abort(501, "Not Implemented %s" % channel_freq)
else: else:
channel_number = channel channel_number = channel
redirect_url += "&channel=%s" % str(channel_number) redirect_url += "&channel=%s" % str(channel_number)
redirect_url += "&origin=%s" % str(origin)
duration = request.args.get('duration', default=0, type=int) duration = request.args.get('duration', default=0, type=int)
if duration: if duration:

View File

@ -3,12 +3,14 @@
from .index_html import Index_HTML from .index_html import Index_HTML
from .channels_html import Channels_HTML from .channels_html import Channels_HTML
from .guide_html import Guide_HTML from .guide_html import Guide_HTML
from .cluster_html import Cluster_HTML
from .tuners_html import Tuners_HTML from .tuners_html import Tuners_HTML
from .xmltv_html import xmlTV_HTML from .xmltv_html import xmlTV_HTML
from .version_html import Version_HTML from .version_html import Version_HTML
from .diagnostics_html import Diagnostics_HTML from .diagnostics_html import Diagnostics_HTML
from .settings_html import Settings_HTML from .settings_html import Settings_HTML
from .channels_editor_html import Channels_Editor_HTML from .channels_editor_html import Channels_Editor_HTML
from .tools_html import Tools_HTML
class fHDHR_Pages(): class fHDHR_Pages():
@ -20,8 +22,10 @@ class fHDHR_Pages():
self.channels_html = Channels_HTML(fhdhr) self.channels_html = Channels_HTML(fhdhr)
self.channels_editor_html = Channels_Editor_HTML(fhdhr) self.channels_editor_html = Channels_Editor_HTML(fhdhr)
self.guide_html = Guide_HTML(fhdhr) self.guide_html = Guide_HTML(fhdhr)
self.cluster_html = Cluster_HTML(fhdhr)
self.tuners_html = Tuners_HTML(fhdhr) self.tuners_html = Tuners_HTML(fhdhr)
self.xmltv_html = xmlTV_HTML(fhdhr) self.xmltv_html = xmlTV_HTML(fhdhr)
self.version_html = Version_HTML(fhdhr) self.version_html = Version_HTML(fhdhr)
self.diagnostics_html = Diagnostics_HTML(fhdhr) self.diagnostics_html = Diagnostics_HTML(fhdhr)
self.settings_html = Settings_HTML(fhdhr) self.settings_html = Settings_HTML(fhdhr)
self.tools_html = Tools_HTML(fhdhr)

View File

@ -7,7 +7,6 @@ class Channels_Editor_HTML():
endpoints = ["/channels_editor", "/channels_editor.html"] endpoints = ["/channels_editor", "/channels_editor.html"]
endpoint_name = "page_channels_editor_html" endpoint_name = "page_channels_editor_html"
endpoint_access_level = 2 endpoint_access_level = 2
endpoint_category = "tool_pages"
pretty_name = "Channels Editor" pretty_name = "Channels Editor"
def __init__(self, fhdhr): def __init__(self, fhdhr):
@ -18,19 +17,14 @@ class Channels_Editor_HTML():
def get(self, *args): def get(self, *args):
origin = request.args.get('source', default=self.fhdhr.device.epg.def_method, type=str)
origin_methods = self.fhdhr.origins.valid_origins
if origin not in origin_methods:
origin = origin_methods[0]
channelslist = {} channelslist = {}
for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels(origin)]: for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels()]:
channel_obj = self.fhdhr.device.channels.get_channel_obj("id", fhdhr_id, origin) channel_obj = self.fhdhr.device.channels.list[fhdhr_id]
channel_dict = channel_obj.dict.copy() channel_dict = channel_obj.dict.copy()
channel_dict["number"] = channel_obj.number channel_dict["number"] = channel_obj.number
channel_dict["chan_thumbnail"] = channel_obj.thumbnail channel_dict["chan_thumbnail"] = channel_obj.thumbnail
channel_dict["m3u_url"] = channel_obj.api_m3u_url channel_dict["m3u_url"] = channel_obj.m3u_url
channelslist[channel_dict["number"]] = channel_dict channelslist[channel_dict["number"]] = channel_dict
@ -40,4 +34,4 @@ class Channels_Editor_HTML():
for channel in sorted_channel_list: for channel in sorted_channel_list:
sorted_chan_guide.append(channelslist[channel]) sorted_chan_guide.append(channelslist[channel])
return render_template('channels_editor.html', request=request, session=session, fhdhr=self.fhdhr, channelslist=sorted_chan_guide, origin=origin, origin_methods=origin_methods, list=list) return render_template('channels_editor.html', session=session, request=request, fhdhr=self.fhdhr, channelslist=sorted_chan_guide, list=list)

View File

@ -17,24 +17,19 @@ class Channels_HTML():
def get(self, *args): def get(self, *args):
origin = request.args.get('source', default=self.fhdhr.device.epg.def_method, type=str)
origin_methods = self.fhdhr.origins.valid_origins
if origin not in origin_methods:
origin = origin_methods[0]
channels_dict = { channels_dict = {
"Total Channels": len(self.fhdhr.device.channels.get_channels(origin)), "Total Channels": len(self.fhdhr.device.channels.get_channels()),
"Enabled": 0 "Enabled": 0
} }
channelslist = {} channelslist = {}
for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels(origin)]: for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels()]:
channel_obj = self.fhdhr.device.channels.get_channel_obj("id", fhdhr_id, origin) channel_obj = self.fhdhr.device.channels.list[fhdhr_id]
channel_dict = channel_obj.dict.copy() channel_dict = channel_obj.dict.copy()
channel_dict["number"] = channel_obj.number channel_dict["number"] = channel_obj.number
channel_dict["chan_thumbnail"] = channel_obj.thumbnail channel_dict["chan_thumbnail"] = channel_obj.thumbnail
channel_dict["m3u_url"] = channel_obj.api_m3u_url channel_dict["m3u_url"] = channel_obj.m3u_url
channelslist[channel_dict["number"]] = channel_dict channelslist[channel_dict["number"]] = channel_dict
if channel_dict["enabled"]: if channel_dict["enabled"]:
@ -46,4 +41,4 @@ class Channels_HTML():
for channel in sorted_channel_list: for channel in sorted_channel_list:
sorted_chan_guide.append(channelslist[channel]) sorted_chan_guide.append(channelslist[channel])
return render_template('channels.html', request=request, session=session, fhdhr=self.fhdhr, channelslist=sorted_chan_guide, channels_dict=channels_dict, origin=origin, origin_methods=origin_methods, list=list) return render_template('channels.html', session=session, request=request, fhdhr=self.fhdhr, channelslist=sorted_chan_guide, channels_dict=channels_dict, list=list)

View File

@ -0,0 +1,52 @@
from flask import request, render_template, session
import urllib.parse
class Cluster_HTML():
endpoints = ["/cluster", "/cluster.html"]
endpoint_name = "page_cluster_html"
endpoint_access_level = 1
pretty_name = "Cluster/SSDP"
def __init__(self, fhdhr):
self.fhdhr = fhdhr
self.location_dict = {
"name": self.fhdhr.config.dict["fhdhr"]["friendlyname"],
"location": self.fhdhr.api.base,
"joined": "N/A",
"url_query": self.fhdhr.api.base_quoted
}
def __call__(self, *args):
return self.get(*args)
def get(self, *args):
locations_list = []
if self.fhdhr.config.dict["fhdhr"]["discovery_address"]:
locations_list.append(self.location_dict)
fhdhr_list = self.fhdhr.device.cluster.get_list()
for location in list(fhdhr_list.keys()):
if location in list(self.fhdhr.device.cluster.cluster().keys()):
location_name = self.fhdhr.device.cluster.cluster()[location]["name"]
else:
try:
location_info_url = "%s/discover.json" % location
location_info_req = self.fhdhr.web.session.get(location_info_url)
location_info = location_info_req.json()
location_name = location_info["FriendlyName"]
except self.fhdhr.web.exceptions.ConnectionError:
self.fhdhr.logger.error("Unreachable: %s" % location)
location_dict = {
"name": location_name,
"location": location,
"joined": str(fhdhr_list[location]["Joined"]),
"url_query": urllib.parse.quote(location)
}
locations_list.append(location_dict)
return render_template('cluster.html', session=session, request=request, fhdhr=self.fhdhr, locations_list=locations_list)

View File

@ -5,7 +5,6 @@ class Diagnostics_HTML():
endpoints = ["/diagnostics", "/diagnostics.html"] endpoints = ["/diagnostics", "/diagnostics.html"]
endpoint_name = "page_diagnostics_html" endpoint_name = "page_diagnostics_html"
endpoint_access_level = 2 endpoint_access_level = 2
endpoint_category = "tool_pages"
pretty_name = "Diagnostics" pretty_name = "Diagnostics"
def __init__(self, fhdhr): def __init__(self, fhdhr):
@ -21,7 +20,7 @@ class Diagnostics_HTML():
button_dict = {} button_dict = {}
for route_group in list(session["route_list"].keys()): for route_group in list(session["route_list"].keys()):
if route_group not in ["pages", "brython", "files", "tool_pages"]: if route_group not in ["pages", "brython", "files"]:
button_dict[route_group] = [] button_dict[route_group] = []
for route_item in list(session["route_list"][route_group].keys()): for route_item in list(session["route_list"][route_group].keys()):
if not session["route_list"][route_group][route_item]["name"].startswith("page_"): if not session["route_list"][route_group][route_item]["name"].startswith("page_"):
@ -47,4 +46,4 @@ class Diagnostics_HTML():
curr_button_dict["button"] = False curr_button_dict["button"] = False
button_dict[route_group].append(curr_button_dict) button_dict[route_group].append(curr_button_dict)
return render_template('diagnostics.html', request=request, session=session, fhdhr=self.fhdhr, button_dict=button_dict, list=list) return render_template('diagnostics.html', session=session, request=request, fhdhr=self.fhdhr, button_dict=button_dict, list=list)

View File

@ -27,9 +27,6 @@ class Guide_HTML():
if source not in epg_methods: if source not in epg_methods:
source = self.fhdhr.device.epg.def_method source = self.fhdhr.device.epg.def_method
if not source:
return render_template('guide.html', request=request, session=session, fhdhr=self.fhdhr, chan_guide_list=chan_guide_list, epg_methods=epg_methods, source=source, list=list)
whatson = self.fhdhr.device.epg.whats_on_allchans(source) whatson = self.fhdhr.device.epg.whats_on_allchans(source)
# Sort the channels # Sort the channels
@ -63,14 +60,14 @@ class Guide_HTML():
else: else:
chan_dict["listing_%s" % time_item] = str(datetime.datetime.fromtimestamp(sorted_chan_guide[channel]["listing"][0][time_item])) chan_dict["listing_%s" % time_item] = str(datetime.datetime.fromtimestamp(sorted_chan_guide[channel]["listing"][0][time_item]))
if source in self.fhdhr.origins.valid_origins: if source in ["blocks", "origin", self.fhdhr.config.dict["main"]["dictpopname"]]:
chan_obj = self.fhdhr.device.channels.get_channel_obj("origin_id", sorted_chan_guide[channel]["id"], source) chan_obj = self.fhdhr.device.channels.get_channel_obj("origin_id", sorted_chan_guide[channel]["id"])
chan_dict["name"] = chan_obj.dict["name"] chan_dict["name"] = chan_obj.dict["name"]
chan_dict["number"] = chan_obj.number chan_dict["number"] = chan_obj.number
chan_dict["chan_thumbnail"] = chan_obj.thumbnail chan_dict["chan_thumbnail"] = chan_obj.thumbnail
chan_dict["enabled"] = chan_obj.dict["enabled"] chan_dict["enabled"] = chan_obj.dict["enabled"]
chan_dict["m3u_url"] = chan_obj.api_m3u_url chan_dict["m3u_url"] = chan_obj.m3u_url
chan_dict["listing_thumbnail"] = chan_dict["listing_thumbnail"] or chan_obj.thumbnail chan_dict["listing_thumbnail"] = chan_dict["listing_thumbnail"] or chan_obj.thumbnail
else: else:
@ -81,4 +78,4 @@ class Guide_HTML():
chan_guide_list.append(chan_dict) chan_guide_list.append(chan_dict)
return render_template('guide.html', request=request, session=session, fhdhr=self.fhdhr, chan_guide_list=chan_guide_list, epg_methods=epg_methods, source=source, list=list) return render_template('guide.html', session=session, request=request, fhdhr=self.fhdhr, chan_guide_list=chan_guide_list, epg_methods=epg_methods, source=source)

View File

@ -15,13 +15,15 @@ class Index_HTML():
def get(self, *args): def get(self, *args):
origin = self.fhdhr.origins.valid_origins[0] tuners_in_use = self.fhdhr.device.tuners.inuse_tuner_count()
max_tuners = self.fhdhr.device.tuners.max_tuners
fhdhr_status_dict = { fhdhr_status_dict = {
"Script Directory": str(self.fhdhr.config.internal["paths"]["script_dir"]), "Script Directory": str(self.fhdhr.config.internal["paths"]["script_dir"]),
"Config File": str(self.fhdhr.config.config_file), "Config File": str(self.fhdhr.config.config_file),
"Cache Path": str(self.fhdhr.config.internal["paths"]["cache_dir"]), "Cache Path": str(self.fhdhr.config.internal["paths"]["cache_dir"]),
"Total Channels": len(list(self.fhdhr.device.channels.list[origin].keys())), "Total Channels": len(self.fhdhr.device.channels.list),
"Tuner Usage": ("%s/%s" % (str(tuners_in_use), str(max_tuners))),
} }
return render_template('index.html', request=request, session=session, fhdhr=self.fhdhr, fhdhr_status_dict=fhdhr_status_dict, list=list) return render_template('index.html', session=session, request=request, fhdhr=self.fhdhr, fhdhr_status_dict=fhdhr_status_dict, list=list)

View File

@ -5,7 +5,6 @@ class Settings_HTML():
endpoints = ["/settings", "/settings.html"] endpoints = ["/settings", "/settings.html"]
endpoint_name = "page_settings_html" endpoint_name = "page_settings_html"
endpoint_access_level = 1 endpoint_access_level = 1
endpoint_category = "tool_pages"
pretty_name = "Settings" pretty_name = "Settings"
def __init__(self, fhdhr): def __init__(self, fhdhr):
@ -22,12 +21,15 @@ class Settings_HTML():
for config_item in list(self.fhdhr.config.conf_default[config_section].keys()): for config_item in list(self.fhdhr.config.conf_default[config_section].keys()):
if self.fhdhr.config.conf_default[config_section][config_item]["config_web"]: if self.fhdhr.config.conf_default[config_section][config_item]["config_web"]:
real_config_section = config_section
if config_section == self.fhdhr.config.dict["main"]["dictpopname"]:
real_config_section = "origin"
web_settings_dict[config_section][config_item] = { web_settings_dict[config_section][config_item] = {
"value": self.fhdhr.config.dict[config_section][config_item], "value": self.fhdhr.config.dict[real_config_section][config_item],
"value_default": self.fhdhr.config.conf_default[config_section][config_item]["value"], "value_default": self.fhdhr.config.conf_default[config_section][config_item]["value"],
"hide": self.fhdhr.config.conf_default[config_section][config_item]["config_web_hidden"] "hide": self.fhdhr.config.conf_default[config_section][config_item]["config_web_hidden"]
} }
if not len(web_settings_dict[config_section].keys()): if not len(web_settings_dict[config_section].keys()):
del web_settings_dict[config_section] del web_settings_dict[config_section]
return render_template('settings.html', request=request, session=session, fhdhr=self.fhdhr, web_settings_dict=web_settings_dict, list=list) return render_template('settings.html', session=session, request=request, fhdhr=self.fhdhr, web_settings_dict=web_settings_dict, list=list)

View File

@ -0,0 +1,18 @@
from flask import request, render_template, session
class Tools_HTML():
endpoints = ["/tools", "/tools.html"]
endpoint_name = "tools_html"
endpoint_access_level = 2
pretty_name = "Tools"
def __init__(self, fhdhr):
self.fhdhr = fhdhr
def __call__(self, *args):
return self.get(*args)
def get(self, *args):
return render_template('tools.html', session=session, request=request, fhdhr=self.fhdhr)

View File

@ -7,7 +7,6 @@ class Tuners_HTML():
endpoints = ["/tuners", "/tuners.html"] endpoints = ["/tuners", "/tuners.html"]
endpoint_name = "page_streams_html" endpoint_name = "page_streams_html"
endpoint_access_level = 0 endpoint_access_level = 0
endpoint_category = "tool_pages"
pretty_name = "Tuners" pretty_name = "Tuners"
def __init__(self, fhdhr): def __init__(self, fhdhr):
@ -18,36 +17,22 @@ class Tuners_HTML():
def get(self, *args): def get(self, *args):
tuner_status_dict = {} tuner_list = []
tuner_status = self.fhdhr.device.tuners.status() tuner_status = self.fhdhr.device.tuners.status()
for origin in list(tuner_status.keys()): tuner_scanning = 0
tuner_status_dict[origin] = {} for tuner in list(tuner_status.keys()):
tuner_status_dict[origin]["scan_count"] = 0
tuner_status_dict[origin]["status_list"] = []
for tuner in list(tuner_status[origin].keys()):
if tuner_status[origin][tuner]["status"] == "Scanning":
tuner_status_dict[origin]["scan_count"] += 1
tuner_dict = { tuner_dict = {
"number": str(tuner), "number": str(tuner),
"status": str(tuner_status[origin][tuner]["status"]), "status": str(tuner_status[tuner]["status"]),
"origin": "N/A",
"channel_number": "N/A",
"method": "N/A",
"running_time": "N/A",
"downloaded": "N/A",
} }
if tuner_status[tuner]["status"] == "Active":
tuner_dict["channel_number"] = tuner_status[tuner]["channel"]
tuner_dict["method"] = tuner_status[tuner]["method"]
tuner_dict["play_duration"] = str(tuner_status[tuner]["Play Time"])
tuner_dict["downloaded"] = humanized_filesize(tuner_status[tuner]["downloaded"])
elif tuner_status[tuner]["status"] == "Scanning":
tuner_scanning += 1
if tuner_status[origin][tuner]["status"] in ["Active", "Acquired", "Scanning"]: tuner_list.append(tuner_dict)
tuner_dict["origin"] = tuner_status[origin][tuner]["origin"]
tuner_dict["channel_number"] = tuner_status[origin][tuner]["channel"] or "N/A"
tuner_dict["running_time"] = str(tuner_status[origin][tuner]["running_time"])
if tuner_status[origin][tuner]["status"] in "Active": return render_template('tuners.html', session=session, request=request, fhdhr=self.fhdhr, tuner_list=tuner_list, tuner_scanning=tuner_scanning)
tuner_dict["method"] = tuner_status[origin][tuner]["method"]
tuner_dict["downloaded"] = humanized_filesize(tuner_status[origin][tuner]["downloaded"])
tuner_status_dict[origin]["status_list"].append(tuner_dict)
return render_template('tuners.html', request=request, session=session, fhdhr=self.fhdhr, tuner_status_dict=tuner_status_dict, list=list)

View File

@ -5,7 +5,6 @@ class Version_HTML():
endpoints = ["/version", "/version.html"] endpoints = ["/version", "/version.html"]
endpoint_name = "page_version_html" endpoint_name = "page_version_html"
endpoint_access_level = 1 endpoint_access_level = 1
endpoint_category = "tool_pages"
pretty_name = "Version" pretty_name = "Version"
def __init__(self, fhdhr): def __init__(self, fhdhr):
@ -15,19 +14,7 @@ class Version_HTML():
return self.get(*args) return self.get(*args)
def get(self, *args): def get(self, *args):
version_dict = {} version_dict = {}
for key in list(self.fhdhr.config.internal["versions"].keys()): for key in list(self.fhdhr.config.internal["versions"].keys()):
version_dict[key] = self.fhdhr.config.internal["versions"][key] version_dict[key] = self.fhdhr.config.internal["versions"][key]
return render_template('version.html', session=session, request=request, fhdhr=self.fhdhr, version_dict=version_dict, list=list)
# Sort the Version Info
sorted_version_list = sorted(version_dict, key=lambda i: (version_dict[i]['type'], version_dict[i]['name']))
sorted_version_dict = {
"fHDHR": version_dict["fHDHR"],
"fHDHR_web": version_dict["fHDHR_web"]
}
for version_item in sorted_version_list:
if version_item not in ["fHDHR", "fHDHR_web"]:
sorted_version_dict[version_item] = version_dict[version_item]
return render_template('version.html', request=request, session=session, fhdhr=self.fhdhr, version_dict=sorted_version_dict, list=list)

View File

@ -5,7 +5,6 @@ class xmlTV_HTML():
endpoints = ["/xmltv", "/xmltv.html"] endpoints = ["/xmltv", "/xmltv.html"]
endpoint_name = "page_xmltv_html" endpoint_name = "page_xmltv_html"
endpoint_access_level = 1 endpoint_access_level = 1
endpoint_category = "tool_pages"
pretty_name = "xmltv" pretty_name = "xmltv"
def __init__(self, fhdhr): def __init__(self, fhdhr):
@ -16,4 +15,4 @@ class xmlTV_HTML():
def get(self, *args): def get(self, *args):
return render_template('xmltv.html', request=request, session=session, fhdhr=self.fhdhr, list=list) return render_template('xmltv.html', session=session, request=request, fhdhr=self.fhdhr)

View File

@ -1,5 +1,4 @@
from .rmg_ident_xml import RMG_Ident_XML from .rmg_ident_xml import RMG_Ident_XML
from .device_xml import RMG_Device_XML from .device_xml import RMG_Device_XML
from .devices_discover import RMG_Devices_Discover from .devices_discover import RMG_Devices_Discover
@ -13,11 +12,10 @@ from .devices_devicekey_prefs import RMG_Devices_DeviceKey_Prefs
from .devices_devicekey_media import RMG_Devices_DeviceKey_Media from .devices_devicekey_media import RMG_Devices_DeviceKey_Media
class Plugin_OBJ(): class fHDHR_RMG():
def __init__(self, fhdhr, plugin_utils): def __init__(self, fhdhr):
self.fhdhr = fhdhr self.fhdhr = fhdhr
self.plugin_utils = plugin_utils
self.rmg_ident_xml = RMG_Ident_XML(fhdhr) self.rmg_ident_xml = RMG_Ident_XML(fhdhr)
self.device_xml = RMG_Device_XML(fhdhr) self.device_xml = RMG_Device_XML(fhdhr)

View File

@ -0,0 +1,58 @@
from flask import Response, request
from io import BytesIO
import xml.etree.ElementTree
from fHDHR.tools import sub_el
class RMG_Device_XML():
endpoints = ["/rmg/device.xml"]
endpoint_name = "rmg_device_xml"
def __init__(self, fhdhr):
self.fhdhr = fhdhr
def __call__(self, *args):
return self.get(*args)
def get(self, *args):
"""Device.xml referenced from SSDP"""
base_url = request.url_root[:-1]
out = xml.etree.ElementTree.Element('root')
out.set('xmlns', "urn:schemas-upnp-org:device-1-0")
specVersion_out = sub_el(out, 'specVersion')
sub_el(specVersion_out, 'major', "1")
sub_el(specVersion_out, 'minor', "0")
device_out = sub_el(out, 'device')
sub_el(device_out, 'deviceType', "urn:plex-tv:device:Media:1")
sub_el(device_out, 'friendlyName', self.fhdhr.config.dict["fhdhr"]["friendlyname"])
sub_el(device_out, 'manufacturer', self.fhdhr.config.dict["fhdhr"]["reporting_manufacturer"])
sub_el(device_out, 'manufacturerURL', "https://github.com/fHDHR/%s" % self.fhdhr.config.dict["main"]["reponame"])
sub_el(device_out, 'modelName', self.fhdhr.config.dict["fhdhr"]["reporting_model"])
sub_el(device_out, 'modelNumber', self.fhdhr.config.internal["versions"]["fHDHR"])
sub_el(device_out, 'modelDescription', self.fhdhr.config.dict["fhdhr"]["friendlyname"])
sub_el(device_out, 'modelURL', "https://github.com/fHDHR/%s" % self.fhdhr.config.dict["main"]["reponame"])
serviceList_out = sub_el(device_out, 'serviceList')
service_out = sub_el(serviceList_out, 'service')
sub_el(out, 'URLBase', "%s" % base_url)
sub_el(service_out, 'serviceType', "urn:plex-tv:service:MediaGrabber:1")
sub_el(service_out, 'serviceId', "urn:plex-tv:serviceId:MediaGrabber")
sub_el(device_out, 'UDN', "uuid:%s" % self.fhdhr.config.dict["main"]["uuid"])
fakefile = BytesIO()
fakefile.write(b'<?xml version="1.0" encoding="UTF-8"?>\n')
fakefile.write(xml.etree.ElementTree.tostring(out, encoding='UTF-8'))
device_xml = fakefile.getvalue()
return Response(status=200,
response=device_xml,
mimetype='application/xml')

View File

@ -6,7 +6,7 @@ from fHDHR.tools import sub_el
class RMG_Devices_DeviceKey(): class RMG_Devices_DeviceKey():
endpoints = ["/rmg/devices/<devicekey>"] endpoints = ["/devices/<devicekey>", "/rmg/devices/<devicekey>"]
endpoint_name = "rmg_devices_devicekey" endpoint_name = "rmg_devices_devicekey"
endpoint_methods = ["GET"] endpoint_methods = ["GET"]
@ -22,30 +22,22 @@ class RMG_Devices_DeviceKey():
base_url = request.url_root[:-1] base_url = request.url_root[:-1]
out = xml.etree.ElementTree.Element('MediaContainer') out = xml.etree.ElementTree.Element('MediaContainer')
if devicekey == self.fhdhr.config.dict["main"]["uuid"]:
if devicekey.startswith(self.fhdhr.config.dict["main"]["uuid"]):
origin = devicekey.split(self.fhdhr.config.dict["main"]["uuid"])[-1]
out.set('size', "1") out.set('size', "1")
if self.fhdhr.origins.origins_dict[origin].setup_success:
alive_status = "alive"
else:
alive_status = "dead"
device_out = sub_el(out, 'Device', device_out = sub_el(out, 'Device',
key="%s%s" % (self.fhdhr.config.dict["main"]["uuid"], origin), key=self.fhdhr.config.dict["main"]["uuid"],
make=self.fhdhr.config.dict["rmg"]["reporting_manufacturer"], make=self.fhdhr.config.dict["fhdhr"]["reporting_manufacturer"],
model=self.fhdhr.config.dict["rmg"]["reporting_model"], model=self.fhdhr.config.dict["fhdhr"]["reporting_model"],
modelNumber=self.fhdhr.config.internal["versions"]["fHDHR"], modelNumber=self.fhdhr.config.internal["versions"]["fHDHR"],
protocol="livetv", protocol="livetv",
status=alive_status, status="alive",
title="%s %s" % (self.fhdhr.config.dict["fhdhr"]["friendlyname"], origin), title=self.fhdhr.config.dict["fhdhr"]["friendlyname"],
tuners=str(self.fhdhr.origins.origins_dict[origin].tuners), tuners=str(self.fhdhr.config.dict["fhdhr"]["tuner_count"]),
uri="%s/rmg/%s%s" % (base_url, self.fhdhr.config.dict["main"]["uuid"], origin), uri=base_url,
uuid="device://tv.plex.grabbers.fHDHR/%s%s" % (self.fhdhr.config.dict["main"]["uuid"], origin), uuid="device://tv.plex.grabbers.fHDHR/%s" % self.fhdhr.config.dict["main"]["uuid"],
) )
tuner_status = self.fhdhr.device.tuners.status(origin) tuner_status = self.fhdhr.device.tuners.status()
for tuner_number in list(tuner_status.keys()): for tuner_number in list(tuner_status.keys()):
tuner_dict = tuner_status[tuner_number] tuner_dict = tuner_status[tuner_number]
@ -75,7 +67,7 @@ class RMG_Devices_DeviceKey():
index=tuner_number, index=tuner_number,
status="scanning", status="scanning",
progress="99", progress="99",
channelsFound=str(len(list(self.fhdhr.device.channels.list[origin].keys()))), channelsFound=str(len(self.fhdhr.device.channels.list)),
) )
# TODO networksScanned # TODO networksScanned

View File

@ -0,0 +1,47 @@
from flask import Response
from io import BytesIO
import xml.etree.ElementTree
from fHDHR.tools import sub_el
class RMG_Devices_DeviceKey_Channels():
endpoints = ["/devices/<devicekey>/channels", "/rmg/devices/<devicekey>/channels"]
endpoint_name = "rmg_devices_devicekey_channels"
endpoint_methods = ["GET"]
def __init__(self, fhdhr):
self.fhdhr = fhdhr
def __call__(self, devicekey, *args):
return self.get(devicekey, *args)
def get(self, devicekey, *args):
"""Returns the current channels."""
out = xml.etree.ElementTree.Element('MediaContainer')
if devicekey == self.fhdhr.config.dict["main"]["uuid"]:
out.set('size', str(len(self.fhdhr.device.channels.list)))
for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels()]:
channel_obj = self.fhdhr.device.channels.list[fhdhr_id]
if channel_obj.enabled:
sub_el(out, 'Channel',
drm="0",
channelIdentifier=channel_obj.rmg_stream_ident,
name=channel_obj.dict["name"],
origin=channel_obj.dict["callsign"],
number=str(channel_obj.number),
type="tv",
# TODO param
signalStrength="100",
signalQuality="100",
)
fakefile = BytesIO()
fakefile.write(b'<?xml version="1.0" encoding="UTF-8"?>\n')
fakefile.write(xml.etree.ElementTree.tostring(out, encoding='UTF-8'))
device_xml = fakefile.getvalue()
return Response(status=200,
response=device_xml,
mimetype='application/xml')

View File

@ -0,0 +1,31 @@
from flask import request, redirect
import urllib.parse
class RMG_Devices_DeviceKey_Media():
endpoints = ["/devices/<devicekey>/media/<channel>", "/rmg/devices/<devicekey>/media/<channel>"]
endpoint_name = "rmg_devices_devicekey_media"
endpoint_methods = ["GET"]
def __init__(self, fhdhr):
self.fhdhr = fhdhr
def __call__(self, devicekey, channel, *args):
return self.get(devicekey, channel, *args)
def get(self, devicekey, channel, *args):
param = request.args.get('method', default=None, type=str)
self.fhdhr.logger.debug("param:%s" % param)
method = self.fhdhr.config.dict["streaming"]["method"]
redirect_url = "/api/tuners?method=%s" % (method)
if str(channel).startswith('id://'):
channel = str(channel).replace('id://', '')
redirect_url += "&channel=%s" % str(channel)
redirect_url += "&accessed=%s" % urllib.parse.quote(request.url)
return redirect(redirect_url)

View File

@ -6,7 +6,7 @@ from fHDHR.tools import sub_el
class RMG_Devices_DeviceKey_Networks(): class RMG_Devices_DeviceKey_Networks():
endpoints = ["/rmg/devices/<devicekey>/networks"] endpoints = ["/devices/<devicekey>/networks", "/rmg/devices/<devicekey>/networks"]
endpoint_name = "rmg_devices_devicekey_networks" endpoint_name = "rmg_devices_devicekey_networks"
endpoint_methods = ["GET"] endpoint_methods = ["GET"]
@ -20,13 +20,12 @@ class RMG_Devices_DeviceKey_Networks():
"""In some cases, channel scanning is a two-step process, where the first stage consists of scanning for networks (this is called "fast scan").""" """In some cases, channel scanning is a two-step process, where the first stage consists of scanning for networks (this is called "fast scan")."""
out = xml.etree.ElementTree.Element('MediaContainer') out = xml.etree.ElementTree.Element('MediaContainer')
if devicekey.startswith(self.fhdhr.config.dict["main"]["uuid"]): if devicekey == self.fhdhr.config.dict["main"]["uuid"]:
origin = devicekey.split(self.fhdhr.config.dict["main"]["uuid"])[-1]
out.set('size', "1") out.set('size', "1")
sub_el(out, 'Network', sub_el(out, 'Network',
key="%s%s" % (self.fhdhr.config.dict["main"]["uuid"], origin), key="1",
title="%s %s" % (self.fhdhr.config.dict["fhdhr"]["friendlyname"], origin), title="fHDHR"
) )
fakefile = BytesIO() fakefile = BytesIO()

View File

@ -2,7 +2,7 @@ from flask import Response
class RMG_Devices_DeviceKey_Prefs(): class RMG_Devices_DeviceKey_Prefs():
endpoints = ["/rmg/devices/<devicekey>/prefs"] endpoints = ["/devices/<devicekey>/prefs", "/rmg/devices/<devicekey>/prefs"]
endpoint_name = "rmg_devices_devicekey_prefs" endpoint_name = "rmg_devices_devicekey_prefs"
endpoint_methods = ["GET", "PUT"] endpoint_methods = ["GET", "PUT"]
@ -15,7 +15,4 @@ class RMG_Devices_DeviceKey_Prefs():
def get(self, devicekey, *args): def get(self, devicekey, *args):
"""Prefs sent back from Plex in Key-Pair format""" """Prefs sent back from Plex in Key-Pair format"""
if devicekey.startswith(self.fhdhr.config.dict["main"]["uuid"]):
return Response(status=200)
return Response(status=200) return Response(status=200)

View File

@ -4,7 +4,7 @@ import xml.etree.ElementTree
class RMG_Devices_DeviceKey_Scan(): class RMG_Devices_DeviceKey_Scan():
endpoints = ["/rmg/devices/<devicekey>/scan"] endpoints = ["/devices/<devicekey>/scan", "/rmg/devices/<devicekey>/scan"]
endpoint_name = "rmg_devices_devicekey_scan" endpoint_name = "rmg_devices_devicekey_scan"
endpoint_methods = ["GET", "POST", "DELETE"] endpoint_methods = ["GET", "POST", "DELETE"]
@ -26,11 +26,9 @@ class RMG_Devices_DeviceKey_Scan():
self.fhdhr.logger.debug("Scan Requested network:%s, source:%s, provider:%s" % (network, source, provider)) self.fhdhr.logger.debug("Scan Requested network:%s, source:%s, provider:%s" % (network, source, provider))
out = xml.etree.ElementTree.Element('MediaContainer') out = xml.etree.ElementTree.Element('MediaContainer')
if devicekey == self.fhdhr.config.dict["main"]["uuid"]:
if devicekey.startswith(self.fhdhr.config.dict["main"]["uuid"]): tuner_status = self.fhdhr.device.tuners.status()
origin = devicekey.split(self.fhdhr.config.dict["main"]["uuid"])[-1]
tuner_status = self.fhdhr.device.tuners.status(origin)
tuner_scanning = 0 tuner_scanning = 0
for tuner in list(tuner_status.keys()): for tuner in list(tuner_status.keys()):
if tuner_status[tuner]["status"] == "Scanning": if tuner_status[tuner]["status"] == "Scanning":
@ -55,10 +53,9 @@ class RMG_Devices_DeviceKey_Scan():
elif request.method in ["DELETE"]: elif request.method in ["DELETE"]:
out = xml.etree.ElementTree.Element('MediaContainer') out = xml.etree.ElementTree.Element('MediaContainer')
if devicekey.startswith(self.fhdhr.config.dict["main"]["uuid"]): if devicekey == self.fhdhr.config.dict["main"]["uuid"]:
origin = devicekey.split(self.fhdhr.config.dict["main"]["uuid"])[-1]
self.fhdhr.device.tuners.stop_tuner_scan(origin) self.fhdhr.device.tuners.stop_tuner_scan()
out.set('status', "0") out.set('status', "0")
out.set('message', "Scan Aborted") out.set('message', "Scan Aborted")

View File

@ -6,7 +6,7 @@ from fHDHR.tools import sub_el
class RMG_Devices_DeviceKey_Scanners(): class RMG_Devices_DeviceKey_Scanners():
endpoints = ["/rmg/devices/<devicekey>/scanners"] endpoints = ["/devices/<devicekey>/scanners", "/rmg/devices/<devicekey>/scanners"]
endpoint_name = "rmg_devices_devicekey_scanners" endpoint_name = "rmg_devices_devicekey_scanners"
endpoint_methods = ["GET"] endpoint_methods = ["GET"]
@ -23,9 +23,7 @@ class RMG_Devices_DeviceKey_Scanners():
# 0 (atsc), 1 (cqam), 2 (dvb-s), 3 (iptv), 4 (virtual), 5 (dvb-t), 6 (dvb-c), 7 (isdbt) # 0 (atsc), 1 (cqam), 2 (dvb-s), 3 (iptv), 4 (virtual), 5 (dvb-t), 6 (dvb-c), 7 (isdbt)
out = xml.etree.ElementTree.Element('MediaContainer') out = xml.etree.ElementTree.Element('MediaContainer')
if devicekey.startswith(self.fhdhr.config.dict["main"]["uuid"]): if devicekey == self.fhdhr.config.dict["main"]["uuid"]:
origin = devicekey.split(self.fhdhr.config.dict["main"]["uuid"])[-1]
if method == "0": if method == "0":
out.set('size', "1") out.set('size', "1")
out.set('simultaneousScanners', "1") out.set('simultaneousScanners', "1")
@ -37,7 +35,7 @@ class RMG_Devices_DeviceKey_Scanners():
sub_el(scanner_out, 'Setting', sub_el(scanner_out, 'Setting',
id="provider", id="provider",
type="text", type="text",
enumValues=origin enumValues=self.fhdhr.config.dict["main"]["servicename"]
) )
fakefile = BytesIO() fakefile = BytesIO()

View File

@ -0,0 +1,49 @@
from flask import Response, request
from io import BytesIO
import xml.etree.ElementTree
from fHDHR.tools import sub_el
class RMG_Devices_Discover():
endpoints = ["/devices/discover", "/rmg/devices/discover"]
endpoint_name = "rmg_devices_discover"
endpoint_methods = ["GET", "POST"]
def __init__(self, fhdhr):
self.fhdhr = fhdhr
def __call__(self, *args):
return self.get(*args)
def get(self, *args):
"""This endpoint requests the grabber attempt to discover any devices it can, and it returns zero or more devices."""
base_url = request.url_root[:-1]
out = xml.etree.ElementTree.Element('MediaContainer')
out.set('size', "1")
sub_el(out, 'Device',
key=self.fhdhr.config.dict["main"]["uuid"],
make=self.fhdhr.config.dict["fhdhr"]["reporting_manufacturer"],
model=self.fhdhr.config.dict["fhdhr"]["reporting_model"],
modelNumber=self.fhdhr.config.internal["versions"]["fHDHR"],
protocol="livetv",
status="alive",
title=self.fhdhr.config.dict["fhdhr"]["friendlyname"],
tuners=str(self.fhdhr.config.dict["fhdhr"]["tuner_count"]),
uri=base_url,
uuid="device://tv.plex.grabbers.fHDHR/%s" % self.fhdhr.config.dict["main"]["uuid"],
thumb="favicon.ico",
interface='network'
# TODO add preferences
)
fakefile = BytesIO()
fakefile.write(b'<?xml version="1.0" encoding="UTF-8"?>\n')
fakefile.write(xml.etree.ElementTree.tostring(out, encoding='UTF-8'))
device_xml = fakefile.getvalue()
return Response(status=200,
response=device_xml,
mimetype='application/xml')

View File

@ -0,0 +1,54 @@
from flask import Response, request
from io import BytesIO
import xml.etree.ElementTree
from fHDHR.tools import sub_el
class RMG_Devices_Probe():
endpoints = ["/devices/probe", "/rmg/devices/probe"]
endpoint_name = "rmg_devices_probe"
endpoint_methods = ["GET", "POST"]
endpoint_default_parameters = {
"uri": "<base_url>"
}
def __init__(self, fhdhr):
self.fhdhr = fhdhr
def __call__(self, *args):
return self.get(*args)
def get(self, *args):
"""Probes a specific URI for a network device, and returns a device, if it exists at the given URI."""
base_url = request.url_root[:-1]
uri = request.args.get('uri', default=None, type=str)
out = xml.etree.ElementTree.Element('MediaContainer')
out.set('size', "1")
if uri == base_url:
sub_el(out, 'Device',
key=self.fhdhr.config.dict["main"]["uuid"],
make=self.fhdhr.config.dict["fhdhr"]["reporting_manufacturer"],
model=self.fhdhr.config.dict["fhdhr"]["reporting_model"],
modelNumber=self.fhdhr.config.internal["versions"]["fHDHR"],
protocol="livetv",
status="alive",
title=self.fhdhr.config.dict["fhdhr"]["friendlyname"],
tuners=str(self.fhdhr.config.dict["fhdhr"]["tuner_count"]),
uri=base_url,
uuid="device://tv.plex.grabbers.fHDHR/%s" % self.fhdhr.config.dict["main"]["uuid"],
thumb="favicon.ico",
interface='network'
)
fakefile = BytesIO()
fakefile.write(b'<?xml version="1.0" encoding="UTF-8"?>\n')
fakefile.write(xml.etree.ElementTree.tostring(out, encoding='UTF-8'))
device_xml = fakefile.getvalue()
return Response(status=200,
response=device_xml,
mimetype='application/xml')

View File

@ -6,7 +6,7 @@ from fHDHR.tools import sub_el
class RMG_Ident_XML(): class RMG_Ident_XML():
endpoints = ["/rmg/<devicekey>/"] endpoints = ["/rmg", "/rmg/"]
endpoint_name = "rmg_ident_xml" endpoint_name = "rmg_ident_xml"
def __init__(self, fhdhr): def __init__(self, fhdhr):
@ -15,19 +15,15 @@ class RMG_Ident_XML():
def __call__(self, *args): def __call__(self, *args):
return self.get(*args) return self.get(*args)
def get(self, devicekey, *args): def get(self, *args):
"""Device.xml referenced from SSDP"""
"""Provides general information about the media grabber""" """Provides general information about the media grabber"""
base_url = request.url_root[:-1] base_url = request.url_root[:-1]
out = xml.etree.ElementTree.Element('MediaContainer') out = xml.etree.ElementTree.Element('MediaContainer')
if devicekey.startswith(self.fhdhr.config.dict["main"]["uuid"]):
origin = devicekey.split(self.fhdhr.config.dict["main"]["uuid"])[-1]
sub_el(out, 'MediaGrabber', sub_el(out, 'MediaGrabber',
identifier="tv.plex.grabbers.fHDHR.%s" % origin, identifier="tv.plex.grabbers.fHDHR",
title="%s %s" % (self.fhdhr.config.dict["fhdhr"]["friendlyname"], origin), title=str(self.fhdhr.config.dict["fhdhr"]["friendlyname"]),
protocols="livetv", protocols="livetv",
icon="%s/favicon.ico" % base_url icon="%s/favicon.ico" % base_url
) )

View File

@ -28,6 +28,7 @@
<div> <div>
<button onclick="location.href='/index'" type="button">fHDHR</button> <button onclick="location.href='/index'" type="button">fHDHR</button>
<button onclick="location.href='/origin'" type="button">{{ fhdhr.config.dict["main"]["servicename"] }}</button>
{% for page_dict in session["route_list"]["pages"] %} {% for page_dict in session["route_list"]["pages"] %}
{% if session["route_list"]["pages"][page_dict]["name"] != "page_index_html" and fhdhr.config.dict["web_ui"]["access_level"] >= session["route_list"]["pages"][page_dict]["endpoint_access_level"] %} {% if session["route_list"]["pages"][page_dict]["name"] != "page_index_html" and fhdhr.config.dict["web_ui"]["access_level"] >= session["route_list"]["pages"][page_dict]["endpoint_access_level"] %}
@ -63,11 +64,17 @@
<hr align="center" width="100%"> <hr align="center" width="100%">
</div> </div>
{% for page_dict in session["route_list"]["tool_pages"] %} {% if fhdhr.config.dict["web_ui"]["cluster_bar"] %}
{% if session["route_list"]["tool_pages"][page_dict]["name"] != "page_index_html" and fhdhr.config.dict["web_ui"]["access_level"] >= session["route_list"]["tool_pages"][page_dict]["endpoint_access_level"] %} {% set locations = fhdhr.device.cluster.get_cluster_dicts_web() %}
<button onclick="location.href='{{ session["route_list"]["tool_pages"][page_dict]["endpoints"][0] }}'" type="button">{{ session["route_list"]["tool_pages"][page_dict]["pretty_name"] }}</button> {% if locations %}
{% endif %} <div>
{% for location in locations %}
<button onclick="location.href='{{ location["base_url"] }}'" type="button">{{ location["name"] }}</button>
{% endfor %} {% endfor %}
<hr align="center" width="100%">
</div>
{% endif %}
{% endif %}
{% block content %}{% endblock %} {% block content %}{% endblock %}
</body> </body>

View File

@ -4,14 +4,8 @@
<h4 style="text-align: center;">{{ fhdhr.config.dict["fhdhr"]["friendlyname"] }} Channels</h4> <h4 style="text-align: center;">{{ fhdhr.config.dict["fhdhr"]["friendlyname"] }} Channels</h4>
<p>
{% for origin in origin_methods %}
<button onclick="location.href='/channels?source={{ origin }}'" type="button">{{ origin }}</button>
{% endfor %}
</p>
<div style="text-align: center;"> <div style="text-align: center;">
<button onclick="location.href='/api/tuners?method=scan&origin={{ origin }}&redirect=/channels'" type="button">Force Channel Update</button> <button onclick="location.href='/api/tuners?method=scan&redirect=/channels'" type="button">Force Channel Update</button>
<p> Note: This may take some time.</p> <p> Note: This may take some time.</p>
</div> </div>
<br> <br>
@ -31,7 +25,7 @@
<br> <br>
<div style="text-align: center;"> <div style="text-align: center;">
<button onclick="location.href='/channels_editor?origin={{ origin }}'" type="button">Edit Channels</button> <button onclick="location.href='/channels_editor'" type="button">Edit Channels</button>
</div> </div>
<br> <br>

View File

@ -14,7 +14,7 @@
<table class="table-scroll"> <table class="table-scroll">
<thead> <thead>
<tr> <tr>
{% if source in fhdhr.origins.valid_origins %} {% if source in ["blocks", "origin", fhdhr.config.dict["main"]["dictpopname"]] %}
<th>Play</th> <th>Play</th>
{% endif %} {% endif %}
<th>Channel Name</th> <th>Channel Name</th>
@ -32,7 +32,7 @@
<tbody class="body-half-screen"> <tbody class="body-half-screen">
{% for chan_dict in chan_guide_list %} {% for chan_dict in chan_guide_list %}
<tr> <tr>
{% if source in fhdhr.origins.valid_origins %} {% if source in ["blocks", "origin", fhdhr.config.dict["main"]["dictpopname"]] %}
<td> <td>
{% if chan_dict["enabled"] %} {% if chan_dict["enabled"] %}
<a href="{{ chan_dict["m3u_url"] }}">Play</a> <a href="{{ chan_dict["m3u_url"] }}">Play</a>

View File

@ -14,8 +14,11 @@
{% for config_section in list(web_settings_dict.keys()) %} {% for config_section in list(web_settings_dict.keys()) %}
{% if config_section == "origin" %}
<h4 style="text-align: center;">{{ fhdhr.config.dict["main"]["dictpopname"] }}</h4>
{% else %}
<h4 style="text-align: center;">{{ config_section }}</h4> <h4 style="text-align: center;">{{ config_section }}</h4>
{% endif %}
<div class="container"> <div class="container">
<table class="table-settings center action-col text-edit-cols"> <table class="table-settings center action-col text-edit-cols">

View File

@ -2,9 +2,9 @@
{% block content %} {% block content %}
<h4 style="text-align: center;">{{ fhdhr.config.dict["fhdhr"]["friendlyname"] }} Dev Tools</h4> <h4 style="text-align: center;">{{ fhdhr.config.dict["fhdhr"]["friendlyname"] }} Tools</h4>
<form method="post" action="/api/devtools?method=prettyjson&redirect=/devtools"> <form method="post" action="/api/tools?method=prettyjson&redirect=/tools">
<a data-th="URL"><input type="text" name="url" value=""></a> <a data-th="URL"><input type="text" name="url" value=""></a>
<a data-th="Convert Json url to tabbbed"><input type="submit" value="Convert Json url to tabbbed"></a> <a data-th="Convert Json url to tabbbed"><input type="submit" value="Convert Json url to tabbbed"></a>
</form> </form>

View File

@ -4,17 +4,12 @@
<h4 style="text-align: center;">fHDHR Streams</h4> <h4 style="text-align: center;">fHDHR Streams</h4>
{% for origin in list(tuner_status_dict.keys()) %}
<h4 style="text-align: center;">{{ origin }}</h4>
<div class="container"> <div class="container">
<table class="table-medium center action-col"> <table class="table-medium center action-col">
<tbody> <tbody>
<tr> <tr>
<th>Tuner</th> <th>Tuner</th>
<th>Status</th> <th>Status</th>
<th>Origin</th>
<th>Channel</th> <th>Channel</th>
<th>Method</th> <th>Method</th>
<th>Time Active</th> <th>Time Active</th>
@ -22,22 +17,30 @@
<th>Actions</th> <th>Actions</th>
</tr> </tr>
{% for tuner_dict in tuner_status_dict[origin]["status_list"] %} {% for tuner_dict in tuner_list %}
<tr> <tr>
<td>{{ tuner_dict["number"] }}</td> <td>{{ tuner_dict["number"] }}</td>
<td>{{ tuner_dict["status"] }}</td> <td>{{ tuner_dict["status"] }}</td>
<td>{{ tuner_dict["origin"] }}</td> {% if tuner_dict["status"] in ["Active", "Acquired"] %}
<td>{{ tuner_dict["channel_number"] }}</td> <td>{{ tuner_dict["channel_number"] }}</td>
{% else %}
<td>N/A</td>
{% endif %}
{% if tuner_dict["status"] == "Active" %}
<td>{{ tuner_dict["method"] }}</td> <td>{{ tuner_dict["method"] }}</td>
<td>{{ tuner_dict["running_time"] }}</td> <td>{{ tuner_dict["play_duration"] }}</td>
<td>{{ tuner_dict["downloaded"] }}</td> <td>{{ tuner_dict["downloaded"] }}</td>
{% else %}
<td>N/A</td>
<td>N/A</td>
<td>N/A</td>
{% endif %}
<td> <td>
{% if tuner_dict["status"] != "Inactive" %} {% if tuner_dict["status"] != "Inactive" %}
<button onclick="location.href='/api/tuners?method=close&tuner={{ tuner_dict["number"] }}&origin={{ origin }}&redirect=/tuners'" type="button">Close</button> <button onclick="location.href='/api/tuners?method=close&tuner={{ tuner_dict["number"] }}&redirect=/tuners'" type="button">Close</button>
{% endif %} {% endif %}
{% if not tuner_status_dict[origin]["scan_count"] and tuner_dict["status"] == "Inactive" %} {% if not tuner_scanning and tuner_dict["status"] == "Inactive" %}
<button onclick="location.href='/api/tuners?method=scan&tuner={{ tuner_dict["number"] }}&origin={{ origin }}&redirect=/tuners'" type="button">Channel Scan</button> <button onclick="location.href='/api/tuners?method=scan&tuner={{ tuner_dict["number"] }}&redirect=/tuners'" type="button">Channel Scan</button>
{% endif %} {% endif %}
</td> </td>
</tr> </tr>
@ -46,6 +49,4 @@
</table> </table>
</div> </div>
{% endfor %}
{% endblock %} {% endblock %}

View File

@ -10,9 +10,8 @@
<tbody> <tbody>
{% for key in list(version_dict.keys()) %} {% for key in list(version_dict.keys()) %}
<tr> <tr>
<td>{{ version_dict[key]["type"] }}</td>
<td>{{ key }}</td> <td>{{ key }}</td>
<td>{{ version_dict[key]["version"] }}</td> <td>{{ version_dict[key] }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@ -14,12 +14,16 @@
<th>Actions</th> <th>Actions</th>
</tr> </tr>
{% for epg_method in list(fhdhr.config.dict["epg"]["valid_methods"].keys()) %} {% for epg_method in fhdhr.config.dict["epg"]["valid_methods"] %}
{% if epg_method not in [None, "None"] %} {% if epg_method not in [None, "None"] %}
{% set epg_method_name = epg_method %}
{% if epg_method == "origin" %}
{% set epg_method_name = fhdhr.config.dict["main"]["dictpopname"] %}
{% endif %}
<tr> <tr>
<td> {{ epg_method }}</td> <td> {{ epg_method_name }}</td>
<td><a href="/api/xmltv?method=get&source={{ epg_method }}">{{ epg_method }}</a></td> <td><a href="/api/xmltv?method=get&source={{ epg_method }}">{{ epg_method_name }}</a></td>
<td><a href="/api/epg?method=get&source={{ epg_method }}">{{ epg_method }}</a></td> <td><a href="/api/epg?method=get&source={{ epg_method }}">{{ epg_method_name }}</a></td>
<td> <td>
<button onclick="location.href='/api/xmltv?method=update&source={{ epg_method }}&redirect=/xmltv'" type="button">Update</button> <button onclick="location.href='/api/xmltv?method=update&source={{ epg_method }}&redirect=/xmltv'" type="button">Update</button>
<button onclick="location.href='/api/xmltv?method=clearcache&source={{ epg_method }}&redirect=/xmltv'" type="button">Clear Cache</button> <button onclick="location.href='/api/xmltv?method=clearcache&source={{ epg_method }}&redirect=/xmltv'" type="button">Clear Cache</button>

View File

@ -10,5 +10,11 @@
"config_file": true, "config_file": true,
"config_web": true "config_web": true
} }
,
"cluster_bar":{
"value": true,
"config_file": true,
"config_web": true
}
} }
} }

View File

@ -9,8 +9,9 @@ import pathlib
from fHDHR.cli import run from fHDHR.cli import run
import fHDHR_web import fHDHR_web
import plugins
SCRIPT_DIR = pathlib.Path(os.path.dirname(os.path.abspath(__file__))) SCRIPT_DIR = pathlib.Path(os.path.dirname(os.path.abspath(__file__)))
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(run.main(SCRIPT_DIR, fHDHR_web)) sys.exit(run.main(SCRIPT_DIR, fHDHR_web, plugins))

70
plugins/__init__.py Normal file
View File

@ -0,0 +1,70 @@
import os
import sys
import pathlib
plugins_top_dir = os.path.dirname(__file__)
print("Importing Plugins Prior to Script Run.")
plugin_dict = {}
for entry in os.scandir(plugins_top_dir):
if entry.is_dir() and not entry.is_file() and entry.name[0] != '_':
curr_dict = {}
plugin_use = True
# Import
imp_string = "from .%s import *" % entry.name
exec(imp_string)
for plugin_item in ["NAME", "VERSION", "TYPE"]:
plugin_item_eval_string = "%s.PLUGIN_%s" % (entry.name, plugin_item)
try:
curr_dict[plugin_item] = eval(plugin_item_eval_string)
except AttributeError:
curr_dict[plugin_item] = None
if curr_dict["TYPE"] == "origin":
curr_dict["PATH"] = pathlib.Path(os.path.dirname(os.path.abspath(__file__))).joinpath(entry.name).joinpath('origin')
elif curr_dict["TYPE"] in ["alt_epg", "alt_stream"]:
curr_dict["PATH"] = pathlib.Path(os.path.dirname(os.path.abspath(__file__))).joinpath(entry.name)
plugin_import_print_string = "Found %s type plugin: %s %s. " % (curr_dict["TYPE"], curr_dict["NAME"], curr_dict["VERSION"])
if not any(curr_dict[plugin_item] for plugin_item in ["NAME", "VERSION", "TYPE"]):
plugin_import_print_string += " ImportWarning: Missing PLUGIN_* Value."
plugin_use = False
elif curr_dict["TYPE"] not in ["origin", "alt_epg", "alt_stream"]:
plugin_use = False
plugin_import_print_string += " ImportWarning: Invalid PLUGIN_TYPE."
# Only allow a single origin
elif curr_dict["TYPE"] == "origin" and len([x for x in list(plugin_dict.keys()) if plugin_dict[x]["TYPE"] == "origin"]):
plugin_use = False
plugin_import_print_string += " ImportWarning: Only one Origin Allowed."
if plugin_use:
plugin_import_print_string += " Import Success"
# add to plugin_dict
print(plugin_import_print_string)
if plugin_use and entry.name not in plugin_dict:
plugin_dict[entry.name] = curr_dict
# Import Origin
if curr_dict["TYPE"] == "origin":
imp_string = "from .%s import origin" % entry.name
exec(imp_string)
imp_string = "from .%s import %s_Setup" % (entry.name, curr_dict["NAME"].upper())
try:
exec(imp_string)
except ImportError:
pass
elif curr_dict["TYPE"] == "alt_epg":
imp_string = "from .%s import *" % entry.name
exec(imp_string)
elif curr_dict["TYPE"] == "alt_stream":
imp_string = "from .%s import *" % entry.name
exec(imp_string)
if not len([x for x in list(plugin_dict.keys()) if plugin_dict[x]["TYPE"] == "origin"]):
print("No Origin Plugin found.")
sys.exit(1)

View File

@ -2,21 +2,30 @@ import datetime
from fHDHR.exceptions import EPGSetupError from fHDHR.exceptions import EPGSetupError
PLUGIN_NAME = "tvtv"
PLUGIN_VERSION = "v0.6.0-beta"
PLUGIN_TYPE = "alt_epg"
class Plugin_OBJ():
def __init__(self, channels, plugin_utils): class TVTV_Setup():
self.plugin_utils = plugin_utils def __init__(self, config):
pass
class tvtvEPG():
def __init__(self, fhdhr, channels):
self.fhdhr = fhdhr
self.channels = channels self.channels = channels
@property @property
def postalcode(self): def postalcode(self):
if self.plugin_utils.config.dict["tvtv"]["postalcode"]: if self.fhdhr.config.dict["tvtv"]["postalcode"]:
return self.plugin_utils.config.dict["tvtv"]["postalcode"] return self.fhdhr.config.dict["tvtv"]["postalcode"]
try: try:
postalcode_url = 'http://ipinfo.io/json' postalcode_url = 'http://ipinfo.io/json'
postalcode_req = self.plugin_utils.web.session.get(postalcode_url) postalcode_req = self.fhdhr.web.session.get(postalcode_url)
data = postalcode_req.json() data = postalcode_req.json()
postalcode = data["postal"] postalcode = data["postal"]
except Exception as e: except Exception as e:
@ -27,9 +36,9 @@ class Plugin_OBJ():
@property @property
def lineup_id(self): def lineup_id(self):
lineup_id_url = "https://www.tvtv.us/tvm/t/tv/v4/lineups?postalCode=%s" % self.postalcode lineup_id_url = "https://www.tvtv.us/tvm/t/tv/v4/lineups?postalCode=%s" % self.postalcode
if self.plugin_utils.config.dict["tvtv"]["lineuptype"]: if self.fhdhr.config.dict["tvtv"]["lineuptype"]:
lineup_id_url += "&lineupType=%s" % self.plugin_utils.config.dict["tvtv"]["lineuptype"] lineup_id_url += "&lineupType=%s" % self.fhdhr.config.dict["tvtv"]["lineuptype"]
lineup_id_req = self.plugin_utils.web.session.get(lineup_id_url) lineup_id_req = self.fhdhr.web.session.get(lineup_id_url)
data = lineup_id_req.json() data = lineup_id_req.json()
lineup_id = data[0]["lineupID"] lineup_id = data[0]["lineupID"]
return lineup_id return lineup_id
@ -113,43 +122,43 @@ class Plugin_OBJ():
stoptime = "%s%s" % (datesdict["stop"], "T00%3A00%3A00.000Z") stoptime = "%s%s" % (datesdict["stop"], "T00%3A00%3A00.000Z")
url = "https://www.tvtv.us/tvm/t/tv/v4/lineups/%s/listings/grid?start=%s&end=%s" % (self.lineup_id, starttime, stoptime) url = "https://www.tvtv.us/tvm/t/tv/v4/lineups/%s/listings/grid?start=%s&end=%s" % (self.lineup_id, starttime, stoptime)
self.get_cached_item(str(datesdict["start"]), url) self.get_cached_item(str(datesdict["start"]), url)
cache_list = self.plugin_utils.db.get_plugin_value("cache_list", "epg_cache", "tvtv") or [] cache_list = self.fhdhr.db.get_cacheitem_value("cache_list", "epg_cache", "tvtv") or []
return [self.plugin_utils.db.get_plugin_value(x, "epg_cache", "tvtv") for x in cache_list] return [self.fhdhr.db.get_cacheitem_value(x, "epg_cache", "tvtv") for x in cache_list]
def get_cached_item(self, cache_key, url): def get_cached_item(self, cache_key, url):
cacheitem = self.plugin_utils.db.get_plugin_value(cache_key, "epg_cache", "tvtv") cacheitem = self.fhdhr.db.get_cacheitem_value(cache_key, "epg_cache", "tvtv")
if cacheitem: if cacheitem:
self.plugin_utils.logger.info("FROM CACHE: %s" % cache_key) self.fhdhr.logger.info("FROM CACHE: %s" % cache_key)
return cacheitem return cacheitem
else: else:
self.plugin_utils.logger.info("Fetching: %s" % url) self.fhdhr.logger.info("Fetching: %s" % url)
try: try:
resp = self.plugin_utils.web.session.get(url) resp = self.fhdhr.web.session.get(url)
except self.plugin_utils.web.exceptions.HTTPError: except self.fhdhr.web.exceptions.HTTPError:
self.plugin_utils.logger.info('Got an error! Ignoring it.') self.fhdhr.logger.info('Got an error! Ignoring it.')
return return
result = resp.json() result = resp.json()
self.plugin_utils.db.set_plugin_value(cache_key, "epg_cache", result, "tvtv") self.fhdhr.db.set_cacheitem_value(cache_key, "epg_cache", result, "tvtv")
cache_list = self.plugin_utils.db.get_plugin_value("cache_list", "epg_cache", "tvtv") or [] cache_list = self.fhdhr.db.get_cacheitem_value("cache_list", "epg_cache", "tvtv") or []
cache_list.append(cache_key) cache_list.append(cache_key)
self.plugin_utils.db.set_plugin_value("cache_list", "epg_cache", cache_list, "tvtv") self.fhdhr.db.set_cacheitem_value("cache_list", "epg_cache", cache_list, "tvtv")
def remove_stale_cache(self, todaydate): def remove_stale_cache(self, todaydate):
cache_list = self.plugin_utils.db.get_plugin_value("cache_list", "epg_cache", "tvtv") or [] cache_list = self.fhdhr.db.get_cacheitem_value("cache_list", "epg_cache", "tvtv") or []
cache_to_kill = [] cache_to_kill = []
for cacheitem in cache_list: for cacheitem in cache_list:
cachedate = datetime.datetime.strptime(str(cacheitem), "%Y-%m-%d") cachedate = datetime.datetime.strptime(str(cacheitem), "%Y-%m-%d")
todaysdate = datetime.datetime.strptime(str(todaydate), "%Y-%m-%d") todaysdate = datetime.datetime.strptime(str(todaydate), "%Y-%m-%d")
if cachedate < todaysdate: if cachedate < todaysdate:
cache_to_kill.append(cacheitem) cache_to_kill.append(cacheitem)
self.plugin_utils.db.delete_plugin_value(cacheitem, "epg_cache", "tvtv") self.fhdhr.db.delete_cacheitem_value(cacheitem, "epg_cache", "tvtv")
self.plugin_utils.logger.info("Removing stale cache: %s" % cacheitem) self.fhdhr.logger.info("Removing stale cache: %s" % cacheitem)
self.plugin_utils.db.set_plugin_value("cache_list", "epg_cache", [x for x in cache_list if x not in cache_to_kill], "tvtv") self.fhdhr.db.set_cacheitem_value("cache_list", "epg_cache", [x for x in cache_list if x not in cache_to_kill], "tvtv")
def clear_cache(self): def clear_cache(self):
cache_list = self.plugin_utils.db.get_plugin_value("cache_list", "epg_cache", "tvtv") or [] cache_list = self.fhdhr.db.get_cacheitem_value("cache_list", "epg_cache", "tvtv") or []
for cacheitem in cache_list: for cacheitem in cache_list:
self.plugin_utils.db.delete_plugin_value(cacheitem, "epg_cache", "tvtv") self.fhdhr.db.delete_cacheitem_value(cacheitem, "epg_cache", "tvtv")
self.plugin_utils.logger.info("Removing cache: %s" % str(cacheitem)) self.fhdhr.logger.info("Removing cache: %s" % str(cacheitem))
self.plugin_utils.db.delete_plugin_value("cache_list", "epg_cache", "tvtv") self.fhdhr.db.delete_cacheitem_value("cache_list", "epg_cache", "tvtv")

View File

@ -1,5 +0,0 @@
{
"name":"tvtv",
"version":"v0.6.0-beta",
"type":"alt_epg"
}

View File

@ -4,21 +4,30 @@ import urllib.parse
from fHDHR.tools import xmldictmaker from fHDHR.tools import xmldictmaker
from fHDHR.exceptions import EPGSetupError from fHDHR.exceptions import EPGSetupError
PLUGIN_NAME = "zap2it"
PLUGIN_VERSION = "v0.6.0-beta"
PLUGIN_TYPE = "alt_epg"
class Plugin_OBJ():
def __init__(self, channels, plugin_utils): class ZAP2IT_Setup():
self.plugin_utils = plugin_utils def __init__(self, config):
pass
class zap2itEPG():
def __init__(self, fhdhr, channels):
self.fhdhr = fhdhr
self.channels = channels self.channels = channels
@property @property
def postalcode(self): def postalcode(self):
if self.plugin_utils.config.dict["zap2it"]["postalcode"]: if self.fhdhr.config.dict["zap2it"]["postalcode"]:
return self.plugin_utils.config.dict["zap2it"]["postalcode"] return self.fhdhr.config.dict["zap2it"]["postalcode"]
try: try:
postalcode_url = 'http://ipinfo.io/json' postalcode_url = 'http://ipinfo.io/json'
postalcode_req = self.plugin_utils.web.session.get(postalcode_url) postalcode_req = self.fhdhr.web.session.get(postalcode_url)
data = postalcode_req.json() data = postalcode_req.json()
postalcode = data["postal"] postalcode = data["postal"]
except Exception as e: except Exception as e:
@ -32,12 +41,12 @@ class Plugin_OBJ():
# Start time parameter is now rounded down to nearest `zap_timespan`, in s. # Start time parameter is now rounded down to nearest `zap_timespan`, in s.
zap_time = datetime.datetime.utcnow().timestamp() zap_time = datetime.datetime.utcnow().timestamp()
self.remove_stale_cache(zap_time) self.remove_stale_cache(zap_time)
zap_time_window = int(self.plugin_utils.config.dict["zap2it"]["timespan"]) * 3600 zap_time_window = int(self.fhdhr.config.dict["zap2it"]["timespan"]) * 3600
zap_time = int(zap_time - (zap_time % zap_time_window)) zap_time = int(zap_time - (zap_time % zap_time_window))
# Fetch data in `zap_timespan` chunks. # Fetch data in `zap_timespan` chunks.
i_times = [] i_times = []
for i in range(int(7 * 24 / int(self.plugin_utils.config.dict["zap2it"]["timespan"]))): for i in range(int(7 * 24 / int(self.fhdhr.config.dict["zap2it"]["timespan"]))):
i_times.append(zap_time + (i * zap_time_window)) i_times.append(zap_time + (i * zap_time_window))
cached_items = self.get_cached(i_times) cached_items = self.get_cached(i_times)
@ -111,18 +120,18 @@ class Plugin_OBJ():
for i_time in i_times: for i_time in i_times:
parameters = { parameters = {
'aid': self.plugin_utils.config.dict["zap2it"]['affiliate_id'], 'aid': self.fhdhr.config.dict["zap2it"]['affiliate_id'],
'country': self.plugin_utils.config.dict["zap2it"]['country'], 'country': self.fhdhr.config.dict["zap2it"]['country'],
'device': self.plugin_utils.config.dict["zap2it"]['device'], 'device': self.fhdhr.config.dict["zap2it"]['device'],
'headendId': self.plugin_utils.config.dict["zap2it"]['headendid'], 'headendId': self.fhdhr.config.dict["zap2it"]['headendid'],
'isoverride': "true", 'isoverride': "true",
'languagecode': self.plugin_utils.config.dict["zap2it"]['languagecode'], 'languagecode': self.fhdhr.config.dict["zap2it"]['languagecode'],
'pref': 'm,p', 'pref': 'm,p',
'timespan': self.plugin_utils.config.dict["zap2it"]['timespan'], 'timespan': self.fhdhr.config.dict["zap2it"]['timespan'],
'timezone': self.plugin_utils.config.dict["zap2it"]['timezone'], 'timezone': self.fhdhr.config.dict["zap2it"]['timezone'],
'userId': self.plugin_utils.config.dict["zap2it"]['userid'], 'userId': self.fhdhr.config.dict["zap2it"]['userid'],
'postalCode': str(self.postalcode), 'postalCode': str(self.postalcode),
'lineupId': '%s-%s-DEFAULT' % (self.plugin_utils.config.dict["zap2it"]['country'], self.plugin_utils.config.dict["zap2it"]['device']), 'lineupId': '%s-%s-DEFAULT' % (self.fhdhr.config.dict["zap2it"]['country'], self.fhdhr.config.dict["zap2it"]['device']),
'time': i_time, 'time': i_time,
'Activity_ID': 1, 'Activity_ID': 1,
'FromPage': "TV%20Guide", 'FromPage': "TV%20Guide",
@ -131,43 +140,43 @@ class Plugin_OBJ():
url = 'https://tvlistings.zap2it.com/api/grid?' url = 'https://tvlistings.zap2it.com/api/grid?'
url += urllib.parse.urlencode(parameters) url += urllib.parse.urlencode(parameters)
self.get_cached_item(str(i_time), url) self.get_cached_item(str(i_time), url)
cache_list = self.plugin_utils.db.get_plugin_value("cache_list", "epg_cache", "zap2it") or [] cache_list = self.fhdhr.db.get_cacheitem_value("cache_list", "epg_cache", "zap2it") or []
return [self.plugin_utils.db.get_plugin_value(x, "epg_cache", "zap2it") for x in cache_list] return [self.fhdhr.db.get_cacheitem_value(x, "epg_cache", "zap2it") for x in cache_list]
def get_cached_item(self, cache_key, url): def get_cached_item(self, cache_key, url):
cacheitem = self.plugin_utils.db.get_plugin_value(cache_key, "epg_cache", "zap2it") cacheitem = self.fhdhr.db.get_cacheitem_value(cache_key, "epg_cache", "zap2it")
if cacheitem: if cacheitem:
self.plugin_utils.logger.info("FROM CACHE: %s" % cache_key) self.fhdhr.logger.info("FROM CACHE: %s" % cache_key)
return cacheitem return cacheitem
else: else:
self.plugin_utils.logger.info("Fetching: %s" % url) self.fhdhr.logger.info("Fetching: %s" % url)
try: try:
resp = self.plugin_utils.web.session.get(url) resp = self.fhdhr.web.session.get(url)
except self.plugin_utils.web.exceptions.HTTPError: except self.fhdhr.web.exceptions.HTTPError:
self.plugin_utils.logger.info('Got an error! Ignoring it.') self.fhdhr.logger.info('Got an error! Ignoring it.')
return return
result = resp.json() result = resp.json()
self.plugin_utils.db.set_plugin_value(cache_key, "epg_cache", result, "zap2it") self.fhdhr.db.set_cacheitem_value(cache_key, "epg_cache", result, "zap2it")
cache_list = self.plugin_utils.db.get_plugin_value("cache_list", "epg_cache", "zap2it") or [] cache_list = self.fhdhr.db.get_cacheitem_value("cache_list", "epg_cache", "zap2it") or []
cache_list.append(cache_key) cache_list.append(cache_key)
self.plugin_utils.db.set_plugin_value("cache_list", "epg_cache", cache_list, "zap2it") self.fhdhr.db.set_cacheitem_value("cache_list", "epg_cache", cache_list, "zap2it")
def remove_stale_cache(self, zap_time): def remove_stale_cache(self, zap_time):
cache_list = self.plugin_utils.db.get_plugin_value("cache_list", "epg_cache", "zap2it") or [] cache_list = self.fhdhr.db.get_cacheitem_value("cache_list", "epg_cache", "zap2it") or []
cache_to_kill = [] cache_to_kill = []
for cacheitem in cache_list: for cacheitem in cache_list:
cachedate = int(cacheitem) cachedate = int(cacheitem)
if cachedate < zap_time: if cachedate < zap_time:
cache_to_kill.append(cacheitem) cache_to_kill.append(cacheitem)
self.plugin_utils.db.delete_plugin_value(cacheitem, "epg_cache", "zap2it") self.fhdhr.db.delete_cacheitem_value(cacheitem, "epg_cache", "zap2it")
self.plugin_utils.logger.info("Removing stale cache: %s" % cacheitem) self.fhdhr.logger.info("Removing stale cache: %s" % cacheitem)
self.plugin_utils.db.set_plugin_value("cache_list", "epg_cache", [x for x in cache_list if x not in cache_to_kill], "zap2it") self.fhdhr.db.set_cacheitem_value("cache_list", "epg_cache", [x for x in cache_list if x not in cache_to_kill], "zap2it")
def clear_cache(self): def clear_cache(self):
cache_list = self.plugin_utils.db.get_plugin_value("cache_list", "epg_cache", "zap2it") or [] cache_list = self.fhdhr.db.get_cacheitem_value("cache_list", "epg_cache", "zap2it") or []
for cacheitem in cache_list: for cacheitem in cache_list:
self.plugin_utils.db.delete_plugin_value(cacheitem, "epg_cache", "zap2it") self.fhdhr.db.delete_cacheitem_value(cacheitem, "epg_cache", "zap2it")
self.plugin_utils.logger.info("Removing cache: %s" % cacheitem) self.fhdhr.logger.info("Removing cache: %s" % cacheitem)
self.plugin_utils.db.delete_plugin_value("cache_list", "epg_cache", "zap2it") self.fhdhr.db.delete_cacheitem_value("cache_list", "epg_cache", "zap2it")

View File

@ -1,5 +0,0 @@
{
"name":"zap2it",
"version":"v0.6.0-beta",
"type":"alt_epg"
}

View File

@ -1,14 +0,0 @@
{
"cluster":{
"enabled":{
"value": true,
"config_file": true,
"config_web": true
},
"friendlyname":{
"value": "none",
"config_file": true,
"config_web": true
}
}
}

View File

@ -1,157 +0,0 @@
from collections import OrderedDict
class Plugin_OBJ():
def __init__(self, fhdhr, plugin_utils):
self.fhdhr = fhdhr
self.plugin_utils = plugin_utils
self.friendlyname = self.fhdhr.config.dict["cluster"]["friendlyname"] or "%s %s" % (self.fhdhr.config.dict["fhdhr"]["friendlyname"], self.fhdhr.origins.valid_origins[0])
if self.plugin_utils.config.dict["fhdhr"]["discovery_address"]:
self.startup_sync()
def cluster(self):
return self.plugin_utils.db.get_plugin_value("cluster", "dict") or self.default_cluster()
def get_cluster_dicts_web(self):
fhdhr_list = self.cluster()
locations = []
for location in list(fhdhr_list.keys()):
item_dict = {
"base_url": fhdhr_list[location]["base_url"],
"name": fhdhr_list[location]["name"]
}
if item_dict["base_url"] != self.plugin_utils.api.base:
locations.append(item_dict)
if len(locations):
locations = sorted(locations, key=lambda i: i['name'])
return locations
else:
return None
def get_list(self):
cluster = self.plugin_utils.db.get_plugin_value("cluster", "dict") or self.default_cluster()
return_dict = {}
for location in list(cluster.keys()):
if location != self.plugin_utils.api.base:
return_dict[location] = {
"Joined": True
}
detected_list = self.plugin_utils.ssdp.ssdp_handling[self.plugin_utils.namespace].detect_method.get()
for location in detected_list:
if location not in list(cluster.keys()):
return_dict[location] = {
"Joined": False
}
return_dict = OrderedDict(sorted(return_dict.items()))
return return_dict
def default_cluster(self):
defdict = {}
defdict[self.plugin_utils.api.base] = {
"base_url": self.plugin_utils.api.base,
"name": self.friendlyname
}
return defdict
def startup_sync(self):
self.plugin_utils.logger.info("Syncronizing with Cluster.")
cluster = self.plugin_utils.db.get_plugin_value("cluster", "dict") or self.default_cluster()
if not len(list(cluster.keys())):
self.plugin_utils.logger.info("No Cluster Found.")
else:
self.plugin_utils.logger.info("Found %s clustered services." % str(len(list(cluster.keys()))))
for location in list(cluster.keys()):
if location != self.plugin_utils.api.base:
self.plugin_utils.logger.debug("Checking Cluster Syncronization information from %s." % location)
sync_url = "%s/api/cluster?method=get" % location
try:
sync_open = self.plugin_utils.web.session.get(sync_url)
retrieved_cluster = sync_open.json()
if self.plugin_utils.api.base not in list(retrieved_cluster.keys()):
return self.leave()
except self.plugin_utils.web.exceptions.ConnectionError:
self.plugin_utils.logger.error("Unreachable: %s" % location)
def leave(self):
self.plugin_utils.logger.info("Leaving cluster.")
self.plugin_utils.db.set_plugin_value("cluster", "dict", self.default_cluster())
def disconnect(self):
cluster = self.plugin_utils.db.get_plugin_value("cluster", "dict") or self.default_cluster()
for location in list(cluster.keys()):
if location != self.plugin_utils.api.base:
self.plugin_utils.logger.info("Informing %s that I am departing the Cluster." % location)
sync_url = "%s/api/cluster?method=del&location=%s" % (location, self.plugin_utils.api.base)
try:
self.plugin_utils.web.session.get(sync_url)
except self.plugin_utils.web.exceptions.ConnectionError:
self.plugin_utils.logger.error("Unreachable: %s" % location)
self.leave()
def sync(self, location):
sync_url = "%s/api/cluster?method=get" % location
try:
sync_open = self.plugin_utils.web.session.get(sync_url)
self.plugin_utils.db.set_plugin_value("cluster", "dict", sync_open.json())
except self.plugin_utils.web.exceptions.ConnectionError:
self.plugin_utils.logger.error("Unreachable: %s" % location)
def push_sync(self):
cluster = self.plugin_utils.db.get_plugin_value("cluster", "dict") or self.default_cluster()
for location in list(cluster.keys()):
if location != self.plugin_utils.api.base:
sync_url = "%s/api/cluster?method=sync&location=%s" % (location, self.plugin_utils.api.base_quoted)
try:
self.plugin_utils.web.session.get(sync_url)
except self.plugin_utils.web.exceptions.ConnectionError:
self.plugin_utils.logger.error("Unreachable: %s" % location)
def add(self, location):
cluster = self.plugin_utils.db.get_fhdhr_value("cluster", "dict") or self.default_cluster()
if location not in list(cluster.keys()):
self.plugin_utils.logger.info("Adding %s to cluster." % location)
cluster[location] = {"base_url": location}
location_info_url = "%s/api/cluster?method=ident" % location
try:
location_info_req = self.plugin_utils.web.session.get(location_info_url)
except self.plugin_utils.web.exceptions.ConnectionError:
self.plugin_utils.logger.error("Unreachable: %s" % location)
del cluster[location]
self.plugin_utils.db.set_plugin_value("cluster", "dict", cluster)
return
location_info = location_info_req.json()
cluster[location]["name"] = location_info["name"]
cluster_info_url = "%s/api/cluster?method=get" % location
try:
cluster_info_req = self.plugin_utils.web.session.get(cluster_info_url)
except self.plugin_utils.web.exceptions.ConnectionError:
self.plugin_utils.logger.error("Unreachable: %s" % location)
del cluster[location]
self.plugin_utils.db.set_plugin_value("cluster", "dict", cluster)
return
cluster_info = cluster_info_req.json()
for cluster_key in list(cluster_info.keys()):
if cluster_key not in list(cluster.keys()):
cluster[cluster_key] = cluster_info[cluster_key]
self.plugin_utils.db.set_plugin_value("cluster", "dict", cluster)
self.push_sync()
def remove(self, location):
cluster = self.plugin_utils.db.get_plugin_value("cluster", "dict") or self.default_cluster()
if location in list(cluster.keys()):
self.plugin_utils.logger.info("Removing %s from cluster." % location)
del cluster[location]
sync_url = "%s/api/cluster?method=leave" % location
try:
self.plugin_utils.web.session.get(sync_url)
except self.plugin_utils.web.exceptions.ConnectionError:
self.plugin_utils.logger.error("Unreachable: %s" % location)
self.push_sync()
self.plugin_utils.db.set_plugin_value("cluster", "dict", cluster)

View File

@ -1,3 +0,0 @@
{
"type":"interface"
}

View File

@ -1,5 +0,0 @@
{
"name":"Cluster",
"version":"v0.6.0-beta",
"type":"interface"
}

View File

@ -1,70 +0,0 @@
class fHDHR_Detect():
def __init__(self, fhdhr):
self.fhdhr = fhdhr
self.fhdhr.db.delete_fhdhr_value("ssdp_detect", "list")
def set(self, location):
detect_list = self.fhdhr.db.get_fhdhr_value("ssdp_detect", "list") or []
if location not in detect_list:
detect_list.append(location)
self.fhdhr.db.set_fhdhr_value("ssdp_detect", "list", detect_list)
def get(self):
return self.fhdhr.db.get_fhdhr_value("ssdp_detect", "list") or []
class Plugin_OBJ():
def __init__(self, fhdhr, plugin_utils, broadcast_ip, max_age):
self.fhdhr = fhdhr
self.detect_method = fHDHR_Detect(fhdhr)
self.broadcast_ip = broadcast_ip
self.device_xml_path = '/cluster/device.xml'
self.schema = "upnp:rootdevice"
self.max_age = max_age
@property
def enabled(self):
return self.fhdhr.config.dict["cluster"]["enabled"]
@property
def notify(self):
data = ''
data_command = "NOTIFY * HTTP/1.1"
data_dict = {
"HOST": "%s:%d" % ("239.255.255.250", 1900),
"NTS": "ssdp:alive",
"USN": 'uuid:%s::%s' % (self.fhdhr.config.dict["main"]["uuid"], self.schema),
"LOCATION": "%s%s" % (self.fhdhr.api.base, self.device_xml_path),
"EXT": '',
"SERVER": 'fHDHR/%s UPnP/1.0' % self.fhdhr.version,
"Cache-Control:max-age=": self.max_age,
"NT": self.schema,
}
data += "%s\r\n" % data_command
for data_key in list(data_dict.keys()):
data += "%s:%s\r\n" % (data_key, data_dict[data_key])
data += "\r\n"
return data
def on_recv(self, headers, cmd, ssdp_handling):
if cmd[0] == 'NOTIFY' and cmd[1] == '*':
try:
if headers["server"].startswith("fHDHR"):
if headers["location"].endswith("/device.xml"):
savelocation = headers["location"].split("/device.xml")[0]
if savelocation.endswith("/cluster"):
savelocation = savelocation.replace("/cluster", '')
if savelocation != self.fhdhr.api.base:
self.detect_method.set(savelocation)
except KeyError:
return

Some files were not shown because too many files have changed in this diff Show More