1
0
mirror of https://github.com/fHDHR/fHDHR_NextPVR.git synced 2025-12-06 16:26:57 -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
},
"method":{
"value": "none",
"value": "blocks",
"config_file": true,
"config_web": true
},

View File

@ -15,6 +15,26 @@
"config_file": 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":{
"value": "fHDHR",
"config_file": true,
@ -34,6 +54,16 @@
"value": "fHDHR",
"config_file": 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

@ -14,7 +14,12 @@
"value": "fHDHR",
"config_file": false,
"config_web": false
},
},
"dictpopname":{
"value": "fHDHR",
"config_file": false,
"config_web": false
},
"reponame":{
"value": "fHDHR",
"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
from .originwrapper import OriginServiceWrapper
from .device import fHDHR_Device
from .api import fHDHR_API_URLs
import fHDHR.tools
fHDHR_VERSION = "v0.6.0-beta"
class fHDHR_INT_OBJ():
def __init__(self, settings, logger, db, plugins):
def __init__(self, settings, logger, db):
self.version = fHDHR_VERSION
self.config = settings
self.logger = logger
self.db = db
self.plugins = plugins
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)
for plugin_name in list(self.plugins.plugins.keys()):
self.plugins.plugins[plugin_name].plugin_utils.api = self.api
self.threads = {}
@ -30,11 +27,11 @@ class fHDHR_INT_OBJ():
class fHDHR_OBJ():
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):
''' 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.config
import fHDHR.logger
import fHDHR.plugins
import fHDHR.origins
from fHDHR.db import fHDHRdb
ERR_CODE = 1
@ -27,10 +25,10 @@ def build_args_parser():
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):
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):
@ -66,41 +64,23 @@ def run(settings, logger, db, script_dir, fHDHR_web, plugins):
return ERR_CODE
def start(args, script_dir, fHDHR_web):
def start(args, script_dir, fHDHR_web, plugins):
"""Get Configuration for fHDHR and start"""
try:
settings = get_configuration(args, script_dir, fHDHR_web)
settings = get_configuration(args, script_dir, plugins, fHDHR_web)
except fHDHR.exceptions.ConfigurationError as e:
print(e)
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)
# Setup Database
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)
def main(script_dir, fHDHR_web):
def main(script_dir, fHDHR_web, plugins):
"""fHDHR run script entry point"""
print("Loading fHDHR %s" % fHDHR_VERSION)
@ -109,7 +89,7 @@ def main(script_dir, fHDHR_web):
try:
args = build_args_parser()
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"]:
return returned_code
except KeyboardInterrupt:

View File

@ -13,28 +13,29 @@ from fHDHR.tools import isint, isfloat, is_arithmetic, is_docker
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.internal = {}
self.conf_default = {}
self.dict = {}
self.internal["versions"] = {}
self.config_file = filename
self.core_setup(script_dir)
self.plugins_setup()
self.user_config()
self.config_verification()
def core_setup(self, script_dir):
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')
www_dir = pathlib.Path(fHDHR_web_dir).joinpath('www_dir')
self.internal["paths"] = {
"script_dir": script_dir,
"data_dir": data_dir,
"plugins_dir": [internal_plugins_dir],
"cache_dir": pathlib.Path(data_dir).joinpath('cache'),
"internal_config": pathlib.Path(data_dir).joinpath('internal_config'),
"fHDHR_web_dir": fHDHR_web_dir,
@ -53,61 +54,68 @@ class Config():
if str(file_item_path).endswith("_conf.json"):
self.read_json_config(file_item_path)
self.dict["epg"]["valid_methods"] = {None: {}}
self.dict["origins"] = {}
self.dict["origins"]["valid_methods"] = {}
self.dict["streaming"]["valid_methods"] = {"direct": {}}
self.dict["plugin_web_paths"] = {}
self.dict["epg"]["valid_methods"] = ["origin", "blocks", None]
self.dict["streaming"]["valid_methods"] = ["direct"]
self.load_versions()
def register_web_path(self, name, path, plugin_dict_name):
self.dict["plugin_web_paths"][name.lower()] = {
"name": name,
"namespace": name.lower(),
"path": path,
"plugin": plugin_dict_name
}
def plugins_setup(self):
def register_valid_origin_method(self, method_item):
self.dict["origins"]["valid_methods"][method_item.lower()] = {
"name": method_item,
"namespace": method_item.lower(),
}
# Load Origin Paths
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]
self.internal["paths"]["origin"] = origin_dir
self.internal["paths"]["origin_web"] = pathlib.Path(origin_dir).joinpath('origin_web')
def register_valid_streaming_method(self, method_item, plugin_dict_name):
self.dict["streaming"]["valid_methods"][method_item.lower()] = {
"name": method_item,
"namespace": method_item.lower(),
"plugin": plugin_dict_name
}
# Load Plugin Conf
for dir_type in ["alt_epg", "origin", "alt_stream"]:
if dir_type == "origin":
dir_tops = [self.internal["paths"]["origin"]]
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:
for file_item in os.listdir(top_dir):
file_item_path = pathlib.Path(top_dir).joinpath(file_item)
if file_item_path.is_dir():
for sub_file_item in os.listdir(file_item_path):
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)
else:
if str(file_item_path).endswith("_conf.json"):
self.read_json_config(file_item_path)
def register_valid_epg_method(self, method_item, plugin_dict_name):
self.dict["epg"]["valid_methods"][method_item.lower()] = {
"name": method_item,
"namespace": method_item.lower(),
"plugin": plugin_dict_name
}
# Rename the Origin conf section
self.dict["origin"] = self.dict.pop(self.dict["main"]["dictpopname"])
def register_version(self, item_name, item_version, item_type):
self.internal["versions"][item_name] = {
"name": item_name,
"version": item_version,
"type": item_type
}
# 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"]
def import_conf_json(self, file_item_path):
self.read_json_config(file_item_path)
# 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):
self.register_version("fHDHR", fHDHR_VERSION, "fHDHR")
self.register_version("fHDHR_web", self.fHDHR_web.fHDHR_web_VERSION, "fHDHR")
self.internal["versions"] = {}
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()
self.register_version("Operating System", opersystem, "env")
self.internal["versions"]["Operating System"] = opersystem
if opersystem in ["Linux", "Darwin"]:
# Linux/Mac
if os.getuid() == 0 or os.geteuid() == 0:
@ -120,19 +128,23 @@ class Config():
print("Uncommon Operating System, use at your own risk.")
isdocker = is_docker()
self.register_version("Docker", isdocker, "env")
self.internal["versions"]["Docker"] = isdocker
def user_config(self):
print("Loading Configuration File: %s" % self.config_file)
self.read_ini_config(self.config_file)
def config_verification_plugins(self):
def config_verification(self):
required_missing = {}
# create dict and combine items
for config_section in list(self.conf_default.keys()):
for config_item in list(self.conf_default[config_section].keys()):
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()):
required_missing[config_section] = []
required_missing[config_section].append(config_item)
@ -144,25 +156,19 @@ class Config():
self.dict["epg"]["method"] = [self.dict["epg"]["method"]]
epg_methods = []
for epg_method in self.dict["epg"]["method"]:
if epg_method in list(self.dict["epg"]["valid_methods"].keys()):
epg_methods.append(epg_method)
elif epg_method in list(self.dict["origins"]["valid_methods"].keys()):
if epg_method == self.dict["main"]["dictpopname"] or epg_method == "origin":
epg_methods.append("origin")
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)
else:
raise fHDHR.exceptions.ConfigurationError("Invalid EPG Method. Exiting...")
if self.dict["epg"]["method"]:
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):
self.dict["epg"]["def_method"] = self.dict["epg"]["method"][0]
if not self.dict["main"]["uuid"]:
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 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')
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":
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":
@ -254,9 +263,11 @@ class Config():
import_val = False
if import_val:
if each_section == self.dict["main"]["dictpopname"]:
each_section = "origin"
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:
value = None
@ -273,6 +284,8 @@ class Config():
elif isinstance(value, list):
",".join(value)
if section == self.dict["main"]["dictpopname"]:
section = "origin"
self.dict[section][key] = value
config_handler = configparser.ConfigParser()

View File

@ -32,10 +32,28 @@ MYSQL_TABLE_ARGS = {'mysql_engine': 'InnoDB',
'mysql_collate': 'utf8mb4_unicode_ci'}
class PluginValues(BASE):
__tablename__ = 'plugin_values'
class ChannelValues(BASE):
__tablename__ = 'channel_values'
__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)
key = Column(String(255), primary_key=True)
value = Column(Text())
@ -130,6 +148,198 @@ class fHDHRdb(object):
def get_uri(self):
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
def set_fhdhr_value(self, item, key, value, namespace='default'):
@ -148,8 +358,8 @@ class fHDHRdb(object):
session.commit()
# DNE - Insert
else:
new_pluginitemvalue = fHDHRValues(item=item, namespace=namespace, key=key, value=value)
session.add(new_pluginitemvalue)
new_cacheitemvalue = fHDHRValues(item=item, namespace=namespace, key=key, value=value)
session.add(new_cacheitemvalue)
session.commit()
except SQLAlchemyError:
session.rollback()
@ -193,67 +403,3 @@ class fHDHRdb(object):
raise
finally:
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 .images import imageHandler
from .ssdp import SSDPServer
from .cluster import fHDHR_Cluster
class fHDHR_Device():
def __init__(self, fhdhr, origins):
self.fhdhr = fhdhr
def __init__(self, fhdhr, originwrapper, plugins):
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.ssdp = SSDPServer(fhdhr)
self.interfaces = {}
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)
self.cluster = fHDHR_Cluster(fhdhr, self.ssdp)

View File

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

View File

@ -3,23 +3,22 @@ import time
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.origin = origin
self.id_system = id_system
if not channel_id:
if origin_id:
channel_id = id_system.get(origin_id, origin)
channel_id = id_system.get(origin_id)
else:
channel_id = id_system.assign(origin)
channel_id = id_system.assign()
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.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 number(self):
@ -97,9 +96,9 @@ class Channel():
self.dict["tags"] = self.dict["origin_tags"]
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"]:
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"])
if not self.dict["number"]:
self.dict["number"] = self.dict["origin_number"].split(".")[0]
@ -129,7 +128,7 @@ class Channel():
if "created" not in list(self.dict.keys()):
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
def default_dict(self):
@ -145,37 +144,64 @@ class Channel():
}
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 []
if self.dict["id"] in channel_ids:
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):
for key in list(updatedict.keys()):
if key == "number":
updatedict[key] = str(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
def generic_image_url(self):
return "/api/images?method=generate&type=channel&message=%s" % self.number
@property
def api_stream_url(self):
return '/api/tuners?method=%s&channel=%s&origin=%s' % (self.fhdhr.config.dict["streaming"]["method"], self.dict["id"], self.origin)
def hdhr_stream_url(self):
return '/auto/%s' % self.hdhr_stream_ident
@property
def api_m3u_url(self):
return '/api/m3u?method=get&channel=%s&origin=%s' % (self.dict["id"], self.origin)
def hdhr_stream_ident(self):
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):
if enablement == "+":
self.dict["favorite"] = 1
elif enablement == "-":
elif enablement == "+":
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):
if enablement == "disable":
@ -187,7 +213,7 @@ class Channel():
self.dict["enabled"] = False
else:
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):
''' 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():
def __init__(self, fhdhr, channels, origins):
def __init__(self, fhdhr, channels, originwrapper, plugins):
self.fhdhr = fhdhr
self.origins = origins
self.origin = originwrapper
self.channels = channels
self.plugins = plugins
self.epgdict = {}
self.epg_methods = self.fhdhr.config.dict["epg"]["method"] or []
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.epg_methods = self.fhdhr.config.dict["epg"]["method"]
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.epg_handling = {}
self.blocks = blocksEPG(self.fhdhr, self.channels)
self.epg_handling = {
"origin": self.origin,
"blocks": self.blocks,
}
self.epg_method_selfadd()
self.def_method = self.fhdhr.config.dict["epg"]["def_method"]
@ -40,14 +44,16 @@ class EPG():
def clear_epg_cache(self, method=None):
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
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'):
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):
nowtime = time.time()
epgdict = self.get_epg(method)
if channel_number not in list(epgdict.keys()):
epgdict[channel_number] = {
"callsign": "",
"name": "",
"number": str(channel_number),
"id": "",
"thumbnail": "",
"listing": []
}
for listing in epgdict[channel_number]["listing"]:
try:
listings = epgdict[channel_number]["listing"]
except KeyError:
listings = []
for listing in listings:
for time_item in ["time_start", "time_end"]:
time_value = listing[time_item]
if str(time_value).endswith("+00:00"):
@ -90,19 +90,16 @@ class EPG():
def whats_on_allchans(self, method=None):
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
if (method == self.fhdhr.config.dict["main"]["dictpopname"] or
method not in self.fhdhr.config.dict["epg"]["valid_methods"]):
method = "origin"
channel_guide_dict = {}
epgdict = self.get_epg(method)
epgdict = epgdict.copy()
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"])
channel_number = chan_obj.number
epgdict[channel_number] = epgdict.pop(c)
@ -122,13 +119,10 @@ class EPG():
def get_epg(self, method=None):
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
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()):
return self.epgdict[method]
@ -159,29 +153,28 @@ class EPG():
return next(item for item in event_list if item["id"] == event_id) or None
def epg_method_selfadd(self):
for plugin_name in list(self.fhdhr.plugins.plugins.keys()):
if self.fhdhr.plugins.plugins[plugin_name].type == "alt_epg":
method = self.fhdhr.plugins.plugins[plugin_name].name.lower()
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())
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"]
for method in new_epgtype_list:
self.epg_handling[method] = eval("self.plugins.%sEPG(self.fhdhr, self.channels)" % method)
def update(self, method=None):
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
if (not method or
method not in self.fhdhr.config.dict["epg"]["valid_methods"]):
method = self.def_method
self.fhdhr.logger.info("Updating %s EPG cache." % method)
programguide = self.epg_handling[method].update_epg()
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()
# sort the channel listings by time stamp
for cnum in list(programguide.keys()):
@ -197,7 +190,7 @@ class EPG():
clean_prog_guide[cnum] = programguide[cnum].copy()
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"])
else:
chan_obj = None
@ -258,10 +251,10 @@ class EPG():
programguide = clean_prog_guide.copy()
# 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
for fhdhr_id in [x["id"] for x in self.channels.get_channels(method)]:
chan_obj = self.channels.get_channel_obj("id", fhdhr_id, method)
for fhdhr_id in [x["id"] for x in self.channels.get_channels()]:
chan_obj = self.channels.list[fhdhr_id]
if str(chan_obj.number) not in list(programguide.keys()):
programguide[str(chan_obj.number)] = chan_obj.epgdict
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.fhdhr.db.set_fhdhr_value("epg_dict", method, programguide)
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):
self.fhdhr.logger.info("EPG Update Thread Starting")

View File

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

View File

@ -4,20 +4,22 @@ import struct
import time
import threading
from .ssdp_detect import fHDHR_Detect
from .rmg_ssdp import RMG_SSDP
from .hdhr_ssdp import HDHR_SSDP
class SSDPServer():
def __init__(self, fhdhr):
self.fhdhr = fhdhr
self.ssdp_handling = {}
self.methods = [x for x in list(self.fhdhr.plugins.plugins.keys()) if self.fhdhr.plugins.plugins[x].type == "ssdp"]
self.detect_method = fHDHR_Detect(fhdhr)
self.fhdhr.threads["ssdp"] = threading.Thread(target=self.run)
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.config.dict["ssdp"]["enabled"]):
self.setup_ssdp()
self.sock.bind((self.bind_address, 1900))
@ -27,18 +29,12 @@ class SSDPServer():
self.max_age = int(fhdhr.config.dict["ssdp"]["max_age"])
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.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):
self.fhdhr.logger.info("SSDP Server Starting")
self.fhdhr.threads["ssdp"].start()
@ -66,22 +62,21 @@ class SSDPServer():
if send_alive:
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()
def do_notify(self, address):
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'):
notify_data = self.ssdp_handling[ssdp_handler].notify
if isinstance(notify_data, list):
notify_list.extend(notify_data)
else:
notify_list.append(notify_data)
hdhr_notify = self.hdhr_ssdp.get()
notify_list.append(hdhr_notify)
if self.fhdhr.config.dict["rmg"]["enabled"]:
rmg_notify = self.rmg_ssdp.get()
notify_list.append(rmg_notify)
for notifydata in notify_list:
notifydata = notifydata.encode("utf-8")
self.fhdhr.logger.debug("Created {}".format(notifydata))
try:
@ -108,10 +103,6 @@ class SSDPServer():
headers = [x.split(':', 1) for x in lines]
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] == '*':
# SSDP discovery
self.fhdhr.logger.debug("Received qualifying M-SEARCH from {}".format(address))
@ -119,14 +110,26 @@ class SSDPServer():
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))
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:
self.fhdhr.logger.debug('Unknown SSDP command %s %s' % (cmd[0], cmd[1]))
def m_search(self):
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):
@ -173,7 +176,7 @@ class SSDPServer():
if self.proto == "ipv4":
self.af_type = socket.AF_INET
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"
elif self.proto == "ipv6":
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.ssdp_content = None
self.broadcast_ip = broadcast_ip
self.device_xml_path = '/hdhr/device.xml'
self.cable_schema = "urn:schemas-opencable-com:service:Security: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
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
else:
self.schema = self.ota_schema
self.max_age = max_age
@property
def enabled(self):
return self.fhdhr.config.dict["hdhr"]["enabled"]
@property
def notify(self):
def get(self):
if self.ssdp_content:
return self.ssdp_content.encode("utf-8")
data = ''
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 += "\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():
def __init__(self, fhdhr, epg, channels):
def __init__(self, fhdhr, epg, channels, plugins):
self.fhdhr = fhdhr
self.channels = channels
self.plugins = plugins
self.epg = epg
self.max_tuners = int(self.fhdhr.config.dict["fhdhr"]["tuner_count"])
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):
self.tuners[origin][str(i)] = Tuner(fhdhr, i, epg, origin)
def get_available_tuner(self):
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):
for plugin_name in list(self.fhdhr.plugins.plugins.keys()):
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)
def stop_tuner_scan(self):
tunernum = self.get_scanning_tuner()
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"""
if not self.available_tuner_count():
raise TunerError("805 - All Tuners In Use")
if origin == "all":
origins = list(self.tuners.keys())
else:
origins = [origin]
tunernumber = self.get_available_tuner()
self.tuners[str(tunernumber)].channel_scan()
for origin in origins:
if not tunernumber:
raise TunerError("805 - All Tuners In Use")
if not self.available_tuner_count(origin):
raise TunerError("805 - All Tuners In Use")
def tuner_grab(self, tuner_number, channel_number):
tunernumber = self.get_available_tuner(origin)
self.tuners[str(tunernumber)].channel_scan(origin)
if not tunernumber:
raise TunerError("805 - All Tuners In Use")
def tuner_grab(self, tuner_number, origin, channel_number):
if str(tuner_number) not in list(self.tuners[origin].keys()):
self.fhdhr.logger.error("Tuner %s does not exist for %s." % (tuner_number, origin))
if str(tuner_number) not in list(self.tuners.keys()):
self.fhdhr.logger.error("Tuner %s does not exist." % str(tuner_number))
raise TunerError("806 - Tune Failed")
# 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
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")
tunernumber = self.get_available_tuner(origin)
tunernumber = self.get_available_tuner()
if not tunernumber:
raise TunerError("805 - All Tuners In Use")
else:
self.tuners[origin][str(tunernumber)].grab(origin, channel_number)
self.tuners[str(tunernumber)].grab(channel_number)
return tunernumber
def tuner_close(self, tunernum, origin):
self.tuners[origin][str(tunernum)].close()
def tuner_close(self, tunernum):
self.tuners[str(tunernum)].close()
def status(self, origin=None):
def status(self):
all_status = {}
if origin:
for tunernum in list(self.tuners[origin].keys()):
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()
for tunernum in list(self.tuners.keys()):
all_status[tunernum] = self.tuners[str(tunernum)].get_status()
return all_status
def available_tuner_count(self, origin):
def available_tuner_count(self):
available_tuners = 0
for tunernum in list(self.tuners[origin].keys()):
if not self.tuners[origin][str(tunernum)].tuner_lock.locked():
for tunernum in list(self.tuners.keys()):
if not self.tuners[str(tunernum)].tuner_lock.locked():
available_tuners += 1
return available_tuners
def inuse_tuner_count(self, origin):
def inuse_tuner_count(self):
inuse_tuners = 0
for tunernum in list(self.tuners[origin].keys()):
if self.tuners[origin][str(tunernum)].tuner_lock.locked():
for tunernum in list(self.tuners.keys()):
if self.tuners[str(tunernum)].tuner_lock.locked():
inuse_tuners += 1
return inuse_tuners
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:
raise TunerError("806 - Tune Failed")
@ -167,14 +143,10 @@ class Tuners():
while True:
self.fhdhr.logger.info("Opening m3u8 for reading %s" % m3u8_url)
try:
if stream_args["stream_info"]["headers"]:
videoUrlM3u = m3u8.load(m3u8_url, headers=stream_args["stream_info"]["headers"])
else:
videoUrlM3u = m3u8.load(m3u8_url)
except Exception as e:
self.fhdhr.logger.info("m3u8 load error: %s" % e)
return m3u8_url
if stream_args["stream_info"]["headers"]:
videoUrlM3u = m3u8.load(m3u8_url, headers=stream_args["stream_info"]["headers"])
else:
videoUrlM3u = m3u8.load(m3u8_url)
if 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():
def __init__(self, fhdhr, stream_args, tuner):
def __init__(self, fhdhr, stream_args, tuner, plugins):
self.fhdhr = fhdhr
self.stream_args = stream_args
self.plugins = plugins
if stream_args["method"] == "direct":
if self.stream_args["true_content_type"].startswith(tuple(["application/", "text/"])):
self.method = Direct_M3U8_Stream(fhdhr, stream_args, tuner)
else:
self.method = Direct_Stream(fhdhr, stream_args, tuner)
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):
return self.method.get()

View File

@ -34,15 +34,10 @@ class Direct_M3U8_Stream():
while self.tuner.tuner_lock.locked():
try:
if self.stream_args["stream_info"]["headers"]:
playlist = m3u8.load(self.stream_args["stream_info"]["url"], headers=self.stream_args["stream_info"]["headers"])
else:
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
if self.stream_args["stream_info"]["headers"]:
playlist = m3u8.load(self.stream_args["stream_info"]["url"], headers=self.stream_args["stream_info"]["headers"])
else:
playlist = m3u8.load(self.stream_args["stream_info"]["url"])
segments = playlist.segments

View File

@ -8,22 +8,22 @@ from .stream import Stream
class Tuner():
def __init__(self, fhdhr, inum, epg, origin):
def __init__(self, fhdhr, inum, epg, plugins):
self.fhdhr = fhdhr
self.plugins = plugins
self.number = inum
self.origin = origin
self.epg = epg
self.tuner_lock = threading.Lock()
self.set_off_status()
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:
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")
if self.status["status"] == "Scanning":
@ -33,16 +33,14 @@ class Tuner():
if not grabbed:
self.tuner_lock.acquire()
self.status["status"] = "Scanning"
self.status["origin"] = origin
self.status["time_start"] = datetime.datetime.utcnow()
self.fhdhr.logger.info("Tuner #%s Performing Channel Scan for %s origin." % (self.number, origin))
self.fhdhr.logger.info("Tuner #%s Performing Channel Scan." % str(self.number))
chanscan = threading.Thread(target=self.runscan, args=(origin,))
chanscan = threading.Thread(target=self.runscan)
chanscan.start()
def runscan(self, origin):
self.fhdhr.api.get("%s&origin=%s" % (self.chanscan_url, origin))
self.fhdhr.logger.info("Requested Channel Scan for %s origin Complete." % origin)
def runscan(self):
self.fhdhr.api.get(self.chanscan_url)
self.fhdhr.logger.info("Requested Channel Scan Complete.")
self.close()
self.fhdhr.api.get(self.close_url)
@ -50,15 +48,13 @@ class Tuner():
if "downloaded" in list(self.status.keys()):
self.status["downloaded"] += bytes_count
def grab(self, origin, channel_number):
def grab(self, channel_number):
if self.tuner_lock.locked():
self.fhdhr.logger.error("Tuner #%s is not available." % self.number)
raise TunerError("804 - Tuner In Use")
self.tuner_lock.acquire()
self.status["status"] = "Acquired"
self.status["origin"] = origin
self.status["channel"] = channel_number
self.status["time_start"] = datetime.datetime.utcnow()
self.fhdhr.logger.info("Tuner #%s Acquired." % str(self.number))
def close(self):
@ -69,22 +65,19 @@ class Tuner():
def get_status(self):
current_status = self.status.copy()
current_status["epg"] = {}
if current_status["status"] in ["Acquired", "Active", "Scanning"]:
current_status["running_time"] = str(
if current_status["status"] == "Active":
current_status["Play Time"] = str(
humanized_time(
int((datetime.datetime.utcnow() - current_status["time_start"]).total_seconds())))
current_status["time_start"] = str(current_status["time_start"])
if current_status["status"] in ["Active"]:
if current_status["origin"] in self.epg.epg_methods:
current_status["epg"] = self.epg.whats_on_now(current_status["channel"], method=current_status["origin"])
current_status["epg"] = self.epg.whats_on_now(current_status["channel"])
return current_status
def set_off_status(self):
self.status = {"status": "Inactive"}
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()
def set_status(self, stream_args):
@ -95,7 +88,6 @@ class Tuner():
"clients_id": [],
"method": stream_args["method"],
"accessed": [stream_args["accessed"]],
"origin": stream_args["origin"],
"channel": stream_args["channel"],
"proxied_url": stream_args["stream_info"]["url"],
"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 .files import fHDHR_Files
from .brython import fHDHR_Brython
from .hdhr import fHDHR_HDHR
from .rmg import fHDHR_RMG
from .api import fHDHR_API
@ -34,16 +36,33 @@ class fHDHR_HTTP_Server():
self.route_list = {}
self.endpoints_obj = {}
self.endpoints_obj["pages"] = fHDHR_Pages(fhdhr)
self.endpoints_obj["files"] = fHDHR_Files(fhdhr)
self.endpoints_obj["brython"] = fHDHR_Brython(fhdhr)
self.endpoints_obj["api"] = fHDHR_API(fhdhr)
self.fhdhr.logger.info("Loading HTTP Pages Endpoints.")
self.pages = fHDHR_Pages(fhdhr)
self.add_endpoints(self.pages, "pages")
self.selfadd_web_plugins()
for endpoint_type in list(self.endpoints_obj.keys()):
self.fhdhr.logger.info("Loading HTTP %s Endpoints." % endpoint_type)
self.add_endpoints(endpoint_type)
self.fhdhr.logger.info("Loading HTTP Files Endpoints.")
self.files = fHDHR_Files(fhdhr)
self.add_endpoints(self.files, "files")
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.after_request(self.after_request)
@ -51,16 +70,6 @@ class fHDHR_HTTP_Server():
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):
self.fhdhr.logger.info("Flask HTTP Thread Starting")
self.fhdhr.threads["flask"].start()
@ -78,8 +87,6 @@ class fHDHR_HTTP_Server():
session["instance_id"] = self.instance_id
session["route_list"] = self.route_list
session["user_agent"] = request.headers.get('User-Agent')
session["is_internal_api"] = self.detect_internal_api(request)
if session["is_internal_api"]:
self.fhdhr.logger.debug("Client is using internal API call.")
@ -146,57 +153,49 @@ class fHDHR_HTTP_Server():
else:
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)]
endpoint_main = self.endpoints_obj[index_name]
endpoint_main.fhdhr.version # dummy line
if index_name not in list(self.route_list.keys()):
self.route_list[index_name] = {}
item_list = [x for x in dir(index_list) if self.isapath(x)]
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):
endpoints = [endpoints]
handler = eval("endpoint_main.%s" % item)
endpoint_name = eval("endpoint_main.%s.%s" % (item, "endpoint_name"))
handler = eval("self.%s.%s" % (index_name, item))
endpoint_name = eval("self.%s.%s.%s" % (index_name, item, "endpoint_name"))
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:
endpoint_methods = ['GET']
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:
endpoint_access_level = 0
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:
pretty_name = endpoint_name
try:
endpoint_category = eval("endpoint_main.%s.%s" % (item, "endpoint_category"))
except AttributeError:
endpoint_category = index_name
try:
endpoint_default_parameters = eval("endpoint_main.%s.%s" % (item, "endpoint_default_parameters"))
endpoint_default_parameters = eval("self.%s.%s.%s" % (index_name, item, "endpoint_default_parameters"))
except AttributeError:
endpoint_default_parameters = {}
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()):
self.route_list[endpoint_category] = {}
if endpoint_name not in list(self.route_list[endpoint_category].keys()):
self.route_list[endpoint_category][endpoint_name] = {}
self.route_list[endpoint_category][endpoint_name]["name"] = endpoint_name
self.route_list[endpoint_category][endpoint_name]["endpoints"] = endpoints
self.route_list[endpoint_category][endpoint_name]["endpoint_methods"] = endpoint_methods
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
if endpoint_name not in list(self.route_list[index_name].keys()):
self.route_list[index_name][endpoint_name] = {}
self.route_list[index_name][endpoint_name]["name"] = endpoint_name
self.route_list[index_name][endpoint_name]["endpoints"] = endpoints
self.route_list[index_name][endpoint_name]["endpoint_methods"] = endpoint_methods
self.route_list[index_name][endpoint_name]["endpoint_access_level"] = endpoint_access_level
self.route_list[index_name][endpoint_name]["endpoint_default_parameters"] = endpoint_default_parameters
self.route_list[index_name][endpoint_name]["pretty_name"] = pretty_name
for endpoint in endpoints:
self.add_endpoint(endpoint=endpoint,
@ -205,7 +204,7 @@ class fHDHR_HTTP_Server():
methods=endpoint_methods)
def isapath(self, item):
not_a_page_list = ["fhdhr", "plugin_utils"]
not_a_page_list = ["fhdhr"]
if item in not_a_page_list:
return False
elif item.startswith("__") and item.endswith("__"):

View File

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

View File

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

View File

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

View File

@ -19,16 +19,11 @@ class Debug_JSON():
debugjson = {
"base_url": base_url,
"total channels": len(self.fhdhr.device.channels.list),
"tuner status": self.fhdhr.device.tuners.status(),
}
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)
cluster_json = json.dumps(debugjson, indent=4)
return Response(status=200,
response=debug_json,
response=cluster_json,
mimetype='application/json')

View File

@ -23,18 +23,18 @@ class EPG():
method = request.args.get('method', default="get", 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()):
return "%s Invalid epg method" % source
if source not in self.fhdhr.config.dict["epg"]["valid_methods"]:
return "%s Invalid xmltv method" % source
redirect_url = request.args.get('redirect', default=None, type=str)
if method == "get":
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()
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]["name"] = chan_obj.dict["name"]
epgdict[chan_obj.number]["callsign"] = chan_obj.dict["callsign"]
@ -93,14 +93,14 @@ class EPG():
else:
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:
chan_obj = self.fhdhr.device.channels.get_channel_obj("origin_id", sorted_chan_guide[channel]["id"], source)
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"])
chan_dict["name"] = chan_obj.dict["name"]
chan_dict["number"] = chan_obj.number
chan_dict["chan_thumbnail"] = chan_obj.thumbnail
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
else:

View File

@ -31,7 +31,7 @@ class Images():
elif method == "get":
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)
if image_type in ["content", "channel"]:
image_id = request.args.get('id', default=None, type=str)

View File

@ -26,11 +26,6 @@ class M3U():
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"
RECORD_MARKER = "#EXTINF"
@ -42,29 +37,14 @@ class M3U():
channel_items = []
if origin:
if channel == "all":
fileName = "channels.m3u"
for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels(origin)]:
channel_obj = self.fhdhr.device.channels.get_channel_obj("id", fhdhr_id, origin)
if channel_obj.enabled:
channel_items.append(channel_obj)
elif str(channel) in [str(x) for x in self.fhdhr.device.channels.get_channel_list("number", origin)]:
channel_obj = self.fhdhr.device.channels.get_channel_obj("number", channel, origin)
fileName = "%s.m3u" % channel_obj.number
if channel == "all":
fileName = "channels.m3u"
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:
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)
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)
fileName = "%s.m3u" % channel_obj.number
if channel_obj.enabled:
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():
@ -13,4 +13,20 @@ class Root_URL():
return self.get(*args)
def get(self, *args):
return redirect("/index")
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")

View File

@ -30,8 +30,11 @@ class Settings():
web_settings_dict[config_section] = {}
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] = {
"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"]:
web_settings_dict[config_section][config_item]["value"] = "***********"
@ -53,7 +56,10 @@ class Settings():
else:
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":
restart_thread = threading.Thread(target=self.restart_thread)

View File

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

View File

@ -3,17 +3,16 @@ import urllib.parse
import json
class DevTools_API():
endpoints = ["/api/devtools"]
endpoint_name = "api_devtools"
class API_Tools():
endpoints = ["/api/tools"]
endpoint_name = "api_tools"
endpoint_methods = ["GET", "POST"]
endpoint_default_parameters = {
"method": "get"
}
def __init__(self, fhdhr, plugin_utils):
def __init__(self, fhdhr):
self.fhdhr = fhdhr
self.plugin_utils = plugin_utils
def __call__(self, *args):
return self.get(*args)
@ -37,11 +36,11 @@ class DevTools_API():
dirty_json_url = request.form.get('url', None)
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_resp = json_url_req.json()
except self.plugin_utilsplugin_utils.web.exceptions.HTTPError as err:
self.plugin_utils.logger.error('Error while getting stations: %s' % err)
except self.fhdhr.web.exceptions.HTTPError as err:
self.fhdhr.logger.error('Error while getting stations: %s' % err)
json_resp = {"error": 'Error while getting stations: %s' % err}
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)
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
if method in list(self.fhdhr.config.dict["streaming"]["valid_methods"].keys()):
if method in self.fhdhr.config.dict["streaming"]["valid_methods"]:
channel_number = request.args.get('channel', None, type=str)
if not channel_number:
return "Missing Channel"
if 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.headers["X-fHDHR-Error"] = "801 - Unknown Channel"
self.fhdhr.logger.error(response.headers["X-fHDHR-Error"])
abort(response)
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.headers["X-fHDHR-Error"] = "801 - Unknown Channel"
self.fhdhr.logger.error(response.headers["X-fHDHR-Error"])
abort(response)
else:
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"]:
channel_dict = self.fhdhr.device.channels.get_channel_dict("number", channel_number)
if not channel_dict["enabled"]:
response = Response("Service Unavailable", status=503)
response.headers["X-fHDHR-Error"] = str("806 - Tune Failed")
self.fhdhr.logger.error(response.headers["X-fHDHR-Error"])
abort(response)
origin = chan_obj.origin
channel_number = chan_obj.number
duration = request.args.get('duration', default=0, type=int)
transcode_quality = request.args.get('transcode', default=None, type=str)
@ -85,7 +62,6 @@ class Tuners():
stream_args = {
"channel": channel_number,
"origin": origin,
"method": method,
"duration": duration,
"origin_quality": self.fhdhr.config.dict["streaming"]["origin_quality"],
@ -97,9 +73,9 @@ class Tuners():
try:
if not tuner_number:
tunernum = self.fhdhr.device.tuners.first_available(origin, channel_number)
tunernum = self.fhdhr.device.tuners.first_available(channel_number)
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:
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)))
@ -108,20 +84,20 @@ class Tuners():
self.fhdhr.logger.error(response.headers["X-fHDHR-Error"])
abort(response)
tuner = self.fhdhr.device.tuners.tuners[origin][str(tunernum)]
tuner = self.fhdhr.device.tuners.tuners[str(tunernum)]
try:
stream_args = self.fhdhr.device.tuners.get_stream_info(stream_args)
except TunerError as e:
self.fhdhr.logger.info("A %s stream request for %s channel %s was rejected due to %s"
% (origin, stream_args["method"], str(stream_args["channel"]), str(e)))
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)))
response = Response("Service Unavailable", status=503)
response.headers["X-fHDHR-Error"] = str(e)
self.fhdhr.logger.error(response.headers["X-fHDHR-Error"])
tuner.close()
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)
session["tuner_used"] = tunernum
@ -129,50 +105,31 @@ class Tuners():
elif method == "close":
if not origin:
return "Missing Origin"
if not tuner_number or str(tuner_number) not in list(self.fhdhr.device.tuners.tuners[origin].keys()):
if not tuner_number or str(tuner_number) not in list(self.fhdhr.device.tuners.tuners.keys()):
return "%s Invalid tuner" % str(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()
elif method == "scan":
if not origin:
for origin in list(self.fhdhr.device.tuners.tuners.keys()):
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=False)
if not tuner_number:
tunernum = self.fhdhr.device.tuners.first_available(None)
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)
tunernum = self.fhdhr.device.tuners.tuner_grab(tuner_number, None)
tuner = self.fhdhr.device.tuners.tuners[str(tunernum)]
tuner.channel_scan(grabbed=True)
elif method == "status":
if not origin:
if not tuner_number:
tuner_status = self.fhdhr.device.tuners.status()
else:
tuner_status = ["Invalid Tuner %s" % tuner_number]
if not tuner_number:
tuner_status = self.fhdhr.device.tuners.status()
elif str(tuner_number) in list(self.fhdhr.device.tuners.tuners.keys()):
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:
tuner_status = ["Invalid Tuner %s" % tuner_number]
tuner_status = ["Invalid Tuner %s" % tuner_number]
tuner_status_json = json.dumps(tuner_status, indent=4)

View File

@ -26,11 +26,6 @@ class W3U():
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 = {
"name": self.fhdhr.config.dict["fhdhr"]["friendlyname"],
"image": '%s/favicon.ico' % base_url,
@ -40,30 +35,15 @@ class W3U():
channel_items = []
if origin:
if channel == "all":
fileName = "channels.m3u"
for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels(origin)]:
channel_obj = self.fhdhr.device.channels.get_channel_obj("id", fhdhr_id, origin)
if channel_obj.enabled:
channel_items.append(channel_obj)
elif str(channel) in [str(x) for x in self.fhdhr.device.channels.get_channel_list("number", origin)]:
channel_obj = self.fhdhr.device.channels.get_channel_obj("number", channel, origin)
fileName = "%s.m3u" % channel_obj.number
if channel == "all":
fileName = "channels.w3u"
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:
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
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)
fileName = "%s.w3u" % channel_obj.number
if channel_obj.enabled:
channel_items.append(channel_obj)
else:

View File

@ -38,7 +38,7 @@ class xmlTV():
method = request.args.get('method', default="get", 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
redirect_url = request.args.get('redirect', default=None, type=str)
@ -47,10 +47,10 @@ class xmlTV():
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()
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]["name"] = chan_obj.dict["name"]
epgdict[chan_obj.number]["callsign"] = chan_obj.dict["callsign"]
@ -113,9 +113,9 @@ class xmlTV():
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()):
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]["name"] = chan_obj.dict["name"]
epgdict[chan_obj.number]["callsign"] = chan_obj.dict["callsign"]

View File

@ -2,6 +2,7 @@
from .favicon_ico import Favicon_ICO
from .style_css import Style_CSS
from .device_xml import Device_XML
class fHDHR_Files():
@ -11,3 +12,4 @@ class fHDHR_Files():
self.favicon = Favicon_ICO(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
class Plugin_OBJ():
class fHDHR_HDHR():
def __init__(self, fhdhr, plugin_utils):
def __init__(self, fhdhr):
self.fhdhr = fhdhr
self.plugin_utils = plugin_utils
self.lineup_post = Lineup_Post(fhdhr)

View File

@ -9,37 +9,28 @@ class Auto():
def __init__(self, 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):
return self.get(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"):
channel_number = channel.replace('v', '')
elif channel.startswith("ch"):
channel_freq = channel.replace('ch', '').split("-")[0]
subchannel = None
subchannel = 0
if "-" in channel:
subchannel = channel.replace('ch', '').split("-")[1]
if subchannel:
self.fhdhr.logger.error("Not Implemented %s-%s" % (channel_freq, 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)
self.fhdhr.logger.error("Not Implemented %s-%s" % (str(channel_freq), str(subchannel)))
abort(501, "Not Implemented %s-%s" % (str(channel_freq), str(subchannel)))
else:
channel_number = channel
redirect_url += "&channel=%s" % str(channel_number)
redirect_url += "&origin=%s" % str(origin)
duration = request.args.get('duration', default=0, type=int)
if duration:

View File

@ -6,16 +6,12 @@ from fHDHR.tools import sub_el
class HDHR_Device_XML():
endpoints = ["/hdhr", "/hdhr/", "/hdhr/device.xml"]
endpoints = ["/hdhr/device.xml"]
endpoint_name = "hdhr_device_xml"
def __init__(self, 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):
return self.get(*args)
@ -24,14 +20,10 @@ class HDHR_Device_XML():
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.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')
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, 'friendlyName', "%s %s" % (self.fhdhr.config.dict["fhdhr"]["friendlyname"], origin))
sub_el(device_out, 'manufacturer', self.fhdhr.config.dict["hdhr"]["reporting_manufacturer"])
sub_el(device_out, 'manufacturerURL', "https://github.com/fHDHR/%s" % origin_plugin_name)
sub_el(device_out, 'modelName', self.fhdhr.config.dict["hdhr"]["reporting_model"])
sub_el(device_out, 'modelNumber', origin_plugin_version)
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, '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.write(b'<?xml version="1.0" encoding="UTF-8"?>\n')

View File

@ -9,10 +9,6 @@ class Discover_JSON():
def __init__(self, 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):
return self.get(*args)
@ -20,19 +16,17 @@ class Discover_JSON():
base_url = request.url_root[:-1]
origin = self.source
jsondiscover = {
"FriendlyName": "%s %s" % (self.fhdhr.config.dict["fhdhr"]["friendlyname"], origin),
"Manufacturer": self.fhdhr.config.dict["hdhr"]["reporting_manufacturer"],
"ModelNumber": self.fhdhr.config.dict["hdhr"]["reporting_model"],
"FirmwareName": self.fhdhr.config.dict["hdhr"]["reporting_firmware_name"],
"TunerCount": self.fhdhr.origins.origins_dict[origin].tuners,
"FirmwareVersion": self.fhdhr.config.dict["hdhr"]["reporting_firmware_ver"],
"DeviceID": "%s%s" % (self.fhdhr.config.dict["main"]["uuid"], origin),
"FriendlyName": self.fhdhr.config.dict["fhdhr"]["friendlyname"],
"Manufacturer": self.fhdhr.config.dict["fhdhr"]["reporting_manufacturer"],
"ModelNumber": self.fhdhr.config.dict["fhdhr"]["reporting_model"],
"FirmwareName": self.fhdhr.config.dict["fhdhr"]["reporting_firmware_name"],
"TunerCount": self.fhdhr.config.dict["fhdhr"]["tuner_count"],
"FirmwareVersion": self.fhdhr.config.dict["fhdhr"]["reporting_firmware_ver"],
"DeviceID": self.fhdhr.config.dict["main"]["uuid"],
"DeviceAuth": self.fhdhr.config.dict["fhdhr"]["device_auth"],
"BaseURL": "%s/hdhr" % base_url,
"LineupURL": "%s/hdhr/lineup.json" % base_url
"BaseURL": "%s" % base_url,
"LineupURL": "%s/lineup.json" % base_url
}
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):
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):
return self.get(*args)
def get(self, *args):
origin = self.source
if 'scan' in list(request.args.keys()):
if request.args['scan'] == 'start':
try:
self.fhdhr.device.tuners.tuner_scan(origin)
self.fhdhr.device.tuners.tuner_scan()
except TunerError as e:
self.fhdhr.logger.info(str(e))
return Response(status=200, mimetype='text/html')
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')
else:
@ -45,18 +39,18 @@ class Lineup_Post():
channel_method = request.args['favorite'][0]
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.headers["X-fHDHR-Error"] = "801 - Unknown Channel"
self.fhdhr.logger.error(response.headers["X-fHDHR-Error"])
abort(response)
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 == "-":
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":
self.fhdhr.device.channels.set_channel_enablement("number", channel_number, "toggle", origin)
self.fhdhr.device.channels.set_channel_enablement("number", channel_number, "toggle")
else:
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):
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):
return self.get(*args)
def get(self, *args):
origin = self.source
tuner_status = self.fhdhr.device.tuners.status(origin)
tuner_status = self.fhdhr.device.tuners.status()
tuners_scanning = 0
for tuner_number in list(tuner_status.keys()):
if tuner_status[tuner_number]["status"] == "Scanning":
tuners_scanning += 1
channel_count = len(list(self.fhdhr.device.channels.list[origin].keys()))
if tuners_scanning:
jsonlineup = self.scan_in_progress(origin)
elif not channel_count:
jsonlineup = self.scan_in_progress(origin)
jsonlineup = self.scan_in_progress()
elif not len(self.fhdhr.device.channels.list):
jsonlineup = self.scan_in_progress()
else:
jsonlineup = self.not_scanning()
lineup_json = json.dumps(jsonlineup, indent=4)
@ -40,14 +32,11 @@ class Lineup_Status_JSON():
response=lineup_json,
mimetype='application/json')
def scan_in_progress(self, origin):
channel_count = len(list(self.fhdhr.device.channels.list[origin].keys()))
def scan_in_progress(self):
jsonlineup = {
"ScanInProgress": "true",
"Progress": 99,
"Found": channel_count
"Found": len(self.fhdhr.device.channels.list)
}
return jsonlineup
@ -55,7 +44,7 @@ class Lineup_Status_JSON():
jsonlineup = {
"ScanInProgress": "false",
"ScanPossible": "true",
"Source": self.fhdhr.config.dict["hdhr"]["reporting_tuner_type"],
"SourceList": [self.fhdhr.config.dict["hdhr"]["reporting_tuner_type"]],
"Source": self.fhdhr.config.dict["fhdhr"]["reporting_tuner_type"],
"SourceList": [self.fhdhr.config.dict["fhdhr"]["reporting_tuner_type"]],
}
return jsonlineup

View File

@ -12,10 +12,6 @@ class Lineup_XML():
def __init__(self, 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):
return self.get(*args)
@ -25,35 +21,24 @@ class Lineup_XML():
show = request.args.get('show', default="all", type=str)
origin = self.source
channelslist = {}
sorted_chan_guide = []
for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels(origin)]:
channel_obj = self.fhdhr.device.channels.get_channel_obj("id", fhdhr_id, origin)
if channel_obj.enabled:
channelslist[channel_obj.number] = channel_obj
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:
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"])
if show == "found" and channel_obj.enabled:
lineup_dict["Enabled"] = 1
elif show == "found" and not channel_obj.enabled:
lineup_dict["Enabled"] = 0
sorted_chan_guide.append(lineup_dict)
sorted_chan_guide.append(channelslist[channel])
out = xml.etree.ElementTree.Element('Lineup')
for lineup_dict in sorted_chan_guide:

View File

@ -9,39 +9,30 @@ class Tuner():
def __init__(self, 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):
return self.get(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"):
channel_number = channel.replace('v', '')
elif channel.startswith("ch"):
channel_freq = channel.replace('ch', '').split("-")[0]
subchannel = None
subchannel = 0
if "-" in channel:
subchannel = channel.replace('ch', '').split("-")[1]
if subchannel:
self.fhdhr.logger.error("Not Implemented %s-%s" % (channel_freq, 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)
self.fhdhr.logger.error("Not Implemented %s-%s" % (str(channel_freq), str(subchannel)))
abort(501, "Not Implemented %s-%s" % (str(channel_freq), str(subchannel)))
else:
channel_number = channel
redirect_url += "&channel=%s" % str(channel_number)
redirect_url += "&origin=%s" % str(origin)
duration = request.args.get('duration', default=0, type=int)
if duration:

View File

@ -3,12 +3,14 @@
from .index_html import Index_HTML
from .channels_html import Channels_HTML
from .guide_html import Guide_HTML
from .cluster_html import Cluster_HTML
from .tuners_html import Tuners_HTML
from .xmltv_html import xmlTV_HTML
from .version_html import Version_HTML
from .diagnostics_html import Diagnostics_HTML
from .settings_html import Settings_HTML
from .channels_editor_html import Channels_Editor_HTML
from .tools_html import Tools_HTML
class fHDHR_Pages():
@ -20,8 +22,10 @@ class fHDHR_Pages():
self.channels_html = Channels_HTML(fhdhr)
self.channels_editor_html = Channels_Editor_HTML(fhdhr)
self.guide_html = Guide_HTML(fhdhr)
self.cluster_html = Cluster_HTML(fhdhr)
self.tuners_html = Tuners_HTML(fhdhr)
self.xmltv_html = xmlTV_HTML(fhdhr)
self.version_html = Version_HTML(fhdhr)
self.diagnostics_html = Diagnostics_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"]
endpoint_name = "page_channels_editor_html"
endpoint_access_level = 2
endpoint_category = "tool_pages"
pretty_name = "Channels Editor"
def __init__(self, fhdhr):
@ -18,19 +17,14 @@ class Channels_Editor_HTML():
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 = {}
for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels(origin)]:
channel_obj = self.fhdhr.device.channels.get_channel_obj("id", fhdhr_id, origin)
for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels()]:
channel_obj = self.fhdhr.device.channels.list[fhdhr_id]
channel_dict = channel_obj.dict.copy()
channel_dict["number"] = channel_obj.number
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
@ -40,4 +34,4 @@ class Channels_Editor_HTML():
for channel in sorted_channel_list:
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):
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 = {
"Total Channels": len(self.fhdhr.device.channels.get_channels(origin)),
"Total Channels": len(self.fhdhr.device.channels.get_channels()),
"Enabled": 0
}
channelslist = {}
for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels(origin)]:
channel_obj = self.fhdhr.device.channels.get_channel_obj("id", fhdhr_id, origin)
for fhdhr_id in [x["id"] for x in self.fhdhr.device.channels.get_channels()]:
channel_obj = self.fhdhr.device.channels.list[fhdhr_id]
channel_dict = channel_obj.dict.copy()
channel_dict["number"] = channel_obj.number
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
if channel_dict["enabled"]:
@ -46,4 +41,4 @@ class Channels_HTML():
for channel in sorted_channel_list:
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"]
endpoint_name = "page_diagnostics_html"
endpoint_access_level = 2
endpoint_category = "tool_pages"
pretty_name = "Diagnostics"
def __init__(self, fhdhr):
@ -21,7 +20,7 @@ class Diagnostics_HTML():
button_dict = {}
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] = []
for route_item in list(session["route_list"][route_group].keys()):
if not session["route_list"][route_group][route_item]["name"].startswith("page_"):
@ -47,4 +46,4 @@ class Diagnostics_HTML():
curr_button_dict["button"] = False
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:
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)
# Sort the channels
@ -63,14 +60,14 @@ class Guide_HTML():
else:
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:
chan_obj = self.fhdhr.device.channels.get_channel_obj("origin_id", sorted_chan_guide[channel]["id"], source)
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"])
chan_dict["name"] = chan_obj.dict["name"]
chan_dict["number"] = chan_obj.number
chan_dict["chan_thumbnail"] = chan_obj.thumbnail
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
else:
@ -81,4 +78,4 @@ class Guide_HTML():
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):
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 = {
"Script Directory": str(self.fhdhr.config.internal["paths"]["script_dir"]),
"Config File": str(self.fhdhr.config.config_file),
"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"]
endpoint_name = "page_settings_html"
endpoint_access_level = 1
endpoint_category = "tool_pages"
pretty_name = "Settings"
def __init__(self, fhdhr):
@ -22,12 +21,15 @@ class Settings_HTML():
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"]:
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] = {
"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"],
"hide": self.fhdhr.config.conf_default[config_section][config_item]["config_web_hidden"]
}
if not len(web_settings_dict[config_section].keys()):
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"]
endpoint_name = "page_streams_html"
endpoint_access_level = 0
endpoint_category = "tool_pages"
pretty_name = "Tuners"
def __init__(self, fhdhr):
@ -18,36 +17,22 @@ class Tuners_HTML():
def get(self, *args):
tuner_status_dict = {}
tuner_list = []
tuner_status = self.fhdhr.device.tuners.status()
for origin in list(tuner_status.keys()):
tuner_status_dict[origin] = {}
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_scanning = 0
for tuner in list(tuner_status.keys()):
tuner_dict = {
"number": str(tuner),
"status": str(tuner_status[tuner]["status"]),
}
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
tuner_dict = {
"number": str(tuner),
"status": str(tuner_status[origin][tuner]["status"]),
"origin": "N/A",
"channel_number": "N/A",
"method": "N/A",
"running_time": "N/A",
"downloaded": "N/A",
}
tuner_list.append(tuner_dict)
if tuner_status[origin][tuner]["status"] in ["Active", "Acquired", "Scanning"]:
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":
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)
return render_template('tuners.html', session=session, request=request, fhdhr=self.fhdhr, tuner_list=tuner_list, tuner_scanning=tuner_scanning)

View File

@ -5,7 +5,6 @@ class Version_HTML():
endpoints = ["/version", "/version.html"]
endpoint_name = "page_version_html"
endpoint_access_level = 1
endpoint_category = "tool_pages"
pretty_name = "Version"
def __init__(self, fhdhr):
@ -15,19 +14,7 @@ class Version_HTML():
return self.get(*args)
def get(self, *args):
version_dict = {}
for key in list(self.fhdhr.config.internal["versions"].keys()):
version_dict[key] = self.fhdhr.config.internal["versions"][key]
# 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)
return render_template('version.html', session=session, request=request, fhdhr=self.fhdhr, version_dict=version_dict, list=list)

View File

@ -5,7 +5,6 @@ class xmlTV_HTML():
endpoints = ["/xmltv", "/xmltv.html"]
endpoint_name = "page_xmltv_html"
endpoint_access_level = 1
endpoint_category = "tool_pages"
pretty_name = "xmltv"
def __init__(self, fhdhr):
@ -16,4 +15,4 @@ class xmlTV_HTML():
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 .device_xml import RMG_Device_XML
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
class Plugin_OBJ():
class fHDHR_RMG():
def __init__(self, fhdhr, plugin_utils):
def __init__(self, fhdhr):
self.fhdhr = fhdhr
self.plugin_utils = plugin_utils
self.rmg_ident_xml = RMG_Ident_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():
endpoints = ["/rmg/devices/<devicekey>"]
endpoints = ["/devices/<devicekey>", "/rmg/devices/<devicekey>"]
endpoint_name = "rmg_devices_devicekey"
endpoint_methods = ["GET"]
@ -22,30 +22,22 @@ class RMG_Devices_DeviceKey():
base_url = request.url_root[:-1]
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]
if devicekey == self.fhdhr.config.dict["main"]["uuid"]:
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',
key="%s%s" % (self.fhdhr.config.dict["main"]["uuid"], origin),
make=self.fhdhr.config.dict["rmg"]["reporting_manufacturer"],
model=self.fhdhr.config.dict["rmg"]["reporting_model"],
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_status,
title="%s %s" % (self.fhdhr.config.dict["fhdhr"]["friendlyname"], origin),
tuners=str(self.fhdhr.origins.origins_dict[origin].tuners),
uri="%s/rmg/%s%s" % (base_url, self.fhdhr.config.dict["main"]["uuid"], origin),
uuid="device://tv.plex.grabbers.fHDHR/%s%s" % (self.fhdhr.config.dict["main"]["uuid"], origin),
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"],
)
tuner_status = self.fhdhr.device.tuners.status(origin)
tuner_status = self.fhdhr.device.tuners.status()
for tuner_number in list(tuner_status.keys()):
tuner_dict = tuner_status[tuner_number]
@ -75,7 +67,7 @@ class RMG_Devices_DeviceKey():
index=tuner_number,
status="scanning",
progress="99",
channelsFound=str(len(list(self.fhdhr.device.channels.list[origin].keys()))),
channelsFound=str(len(self.fhdhr.device.channels.list)),
)
# 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():
endpoints = ["/rmg/devices/<devicekey>/networks"]
endpoints = ["/devices/<devicekey>/networks", "/rmg/devices/<devicekey>/networks"]
endpoint_name = "rmg_devices_devicekey_networks"
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")."""
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]
if devicekey == self.fhdhr.config.dict["main"]["uuid"]:
out.set('size', "1")
sub_el(out, 'Network',
key="%s%s" % (self.fhdhr.config.dict["main"]["uuid"], origin),
title="%s %s" % (self.fhdhr.config.dict["fhdhr"]["friendlyname"], origin),
key="1",
title="fHDHR"
)
fakefile = BytesIO()

View File

@ -2,7 +2,7 @@ from flask import Response
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_methods = ["GET", "PUT"]
@ -15,7 +15,4 @@ class RMG_Devices_DeviceKey_Prefs():
def get(self, devicekey, *args):
"""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)

View File

@ -4,7 +4,7 @@ import xml.etree.ElementTree
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_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))
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]
tuner_status = self.fhdhr.device.tuners.status(origin)
tuner_status = self.fhdhr.device.tuners.status()
tuner_scanning = 0
for tuner in list(tuner_status.keys()):
if tuner_status[tuner]["status"] == "Scanning":
@ -55,10 +53,9 @@ class RMG_Devices_DeviceKey_Scan():
elif request.method in ["DELETE"]:
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]
if devicekey == self.fhdhr.config.dict["main"]["uuid"]:
self.fhdhr.device.tuners.stop_tuner_scan(origin)
self.fhdhr.device.tuners.stop_tuner_scan()
out.set('status', "0")
out.set('message', "Scan Aborted")

View File

@ -6,7 +6,7 @@ from fHDHR.tools import sub_el
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_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)
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]
if devicekey == self.fhdhr.config.dict["main"]["uuid"]:
if method == "0":
out.set('size', "1")
out.set('simultaneousScanners', "1")
@ -37,7 +35,7 @@ class RMG_Devices_DeviceKey_Scanners():
sub_el(scanner_out, 'Setting',
id="provider",
type="text",
enumValues=origin
enumValues=self.fhdhr.config.dict["main"]["servicename"]
)
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():
endpoints = ["/rmg/<devicekey>/"]
endpoints = ["/rmg", "/rmg/"]
endpoint_name = "rmg_ident_xml"
def __init__(self, fhdhr):
@ -15,22 +15,18 @@ class RMG_Ident_XML():
def __call__(self, *args):
return self.get(*args)
def get(self, devicekey, *args):
"""Device.xml referenced from SSDP"""
def get(self, *args):
"""Provides general information about the media grabber"""
base_url = request.url_root[:-1]
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',
identifier="tv.plex.grabbers.fHDHR.%s" % origin,
title="%s %s" % (self.fhdhr.config.dict["fhdhr"]["friendlyname"], origin),
protocols="livetv",
icon="%s/favicon.ico" % base_url
)
sub_el(out, 'MediaGrabber',
identifier="tv.plex.grabbers.fHDHR",
title=str(self.fhdhr.config.dict["fhdhr"]["friendlyname"]),
protocols="livetv",
icon="%s/favicon.ico" % base_url
)
fakefile = BytesIO()
fakefile.write(b'<?xml version="1.0" encoding="UTF-8"?>\n')

View File

@ -28,6 +28,7 @@
<div>
<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"] %}
{% 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%">
</div>
{% for page_dict in session["route_list"]["tool_pages"] %}
{% 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"] %}
<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 fhdhr.config.dict["web_ui"]["cluster_bar"] %}
{% set locations = fhdhr.device.cluster.get_cluster_dicts_web() %}
{% if locations %}
<div>
{% for location in locations %}
<button onclick="location.href='{{ location["base_url"] }}'" type="button">{{ location["name"] }}</button>
{% endfor %}
<hr align="center" width="100%">
</div>
{% endif %}
{% endfor %}
{% endif %}
{% block content %}{% endblock %}
</body>

View File

@ -4,14 +4,8 @@
<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;">
<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>
</div>
<br>
@ -31,7 +25,7 @@
<br>
<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>
<br>

View File

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

View File

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

View File

@ -2,9 +2,9 @@
{% 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="Convert Json url to tabbbed"><input type="submit" value="Convert Json url to tabbbed"></a>
</form>

View File

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

View File

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

View File

@ -14,12 +14,16 @@
<th>Actions</th>
</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"] %}
{% set epg_method_name = epg_method %}
{% if epg_method == "origin" %}
{% set epg_method_name = fhdhr.config.dict["main"]["dictpopname"] %}
{% endif %}
<tr>
<td> {{ epg_method }}</td>
<td><a href="/api/xmltv?method=get&source={{ epg_method }}">{{ epg_method }}</a></td>
<td><a href="/api/epg?method=get&source={{ epg_method }}">{{ epg_method }}</a></td>
<td> {{ epg_method_name }}</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_name }}</a></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=clearcache&source={{ epg_method }}&redirect=/xmltv'" type="button">Clear Cache</button>

View File

@ -10,5 +10,11 @@
"config_file": 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
import fHDHR_web
import plugins
SCRIPT_DIR = pathlib.Path(os.path.dirname(os.path.abspath(__file__)))
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
PLUGIN_NAME = "tvtv"
PLUGIN_VERSION = "v0.6.0-beta"
PLUGIN_TYPE = "alt_epg"
class Plugin_OBJ():
def __init__(self, channels, plugin_utils):
self.plugin_utils = plugin_utils
class TVTV_Setup():
def __init__(self, config):
pass
class tvtvEPG():
def __init__(self, fhdhr, channels):
self.fhdhr = fhdhr
self.channels = channels
@property
def postalcode(self):
if self.plugin_utils.config.dict["tvtv"]["postalcode"]:
return self.plugin_utils.config.dict["tvtv"]["postalcode"]
if self.fhdhr.config.dict["tvtv"]["postalcode"]:
return self.fhdhr.config.dict["tvtv"]["postalcode"]
try:
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()
postalcode = data["postal"]
except Exception as e:
@ -27,9 +36,9 @@ class Plugin_OBJ():
@property
def lineup_id(self):
lineup_id_url = "https://www.tvtv.us/tvm/t/tv/v4/lineups?postalCode=%s" % self.postalcode
if self.plugin_utils.config.dict["tvtv"]["lineuptype"]:
lineup_id_url += "&lineupType=%s" % self.plugin_utils.config.dict["tvtv"]["lineuptype"]
lineup_id_req = self.plugin_utils.web.session.get(lineup_id_url)
if self.fhdhr.config.dict["tvtv"]["lineuptype"]:
lineup_id_url += "&lineupType=%s" % self.fhdhr.config.dict["tvtv"]["lineuptype"]
lineup_id_req = self.fhdhr.web.session.get(lineup_id_url)
data = lineup_id_req.json()
lineup_id = data[0]["lineupID"]
return lineup_id
@ -113,43 +122,43 @@ class Plugin_OBJ():
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)
self.get_cached_item(str(datesdict["start"]), url)
cache_list = self.plugin_utils.db.get_plugin_value("cache_list", "epg_cache", "tvtv") or []
return [self.plugin_utils.db.get_plugin_value(x, "epg_cache", "tvtv") for x in cache_list]
cache_list = self.fhdhr.db.get_cacheitem_value("cache_list", "epg_cache", "tvtv") or []
return [self.fhdhr.db.get_cacheitem_value(x, "epg_cache", "tvtv") for x in cache_list]
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:
self.plugin_utils.logger.info("FROM CACHE: %s" % cache_key)
self.fhdhr.logger.info("FROM CACHE: %s" % cache_key)
return cacheitem
else:
self.plugin_utils.logger.info("Fetching: %s" % url)
self.fhdhr.logger.info("Fetching: %s" % url)
try:
resp = self.plugin_utils.web.session.get(url)
except self.plugin_utils.web.exceptions.HTTPError:
self.plugin_utils.logger.info('Got an error! Ignoring it.')
resp = self.fhdhr.web.session.get(url)
except self.fhdhr.web.exceptions.HTTPError:
self.fhdhr.logger.info('Got an error! Ignoring it.')
return
result = resp.json()
self.plugin_utils.db.set_plugin_value(cache_key, "epg_cache", result, "tvtv")
cache_list = self.plugin_utils.db.get_plugin_value("cache_list", "epg_cache", "tvtv") or []
self.fhdhr.db.set_cacheitem_value(cache_key, "epg_cache", result, "tvtv")
cache_list = self.fhdhr.db.get_cacheitem_value("cache_list", "epg_cache", "tvtv") or []
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):
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 = []
for cacheitem in cache_list:
cachedate = datetime.datetime.strptime(str(cacheitem), "%Y-%m-%d")
todaysdate = datetime.datetime.strptime(str(todaydate), "%Y-%m-%d")
if cachedate < todaysdate:
cache_to_kill.append(cacheitem)
self.plugin_utils.db.delete_plugin_value(cacheitem, "epg_cache", "tvtv")
self.plugin_utils.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.delete_cacheitem_value(cacheitem, "epg_cache", "tvtv")
self.fhdhr.logger.info("Removing stale cache: %s" % cacheitem)
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):
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:
self.plugin_utils.db.delete_plugin_value(cacheitem, "epg_cache", "tvtv")
self.plugin_utils.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(cacheitem, "epg_cache", "tvtv")
self.fhdhr.logger.info("Removing cache: %s" % str(cacheitem))
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.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):
self.plugin_utils = plugin_utils
class ZAP2IT_Setup():
def __init__(self, config):
pass
class zap2itEPG():
def __init__(self, fhdhr, channels):
self.fhdhr = fhdhr
self.channels = channels
@property
def postalcode(self):
if self.plugin_utils.config.dict["zap2it"]["postalcode"]:
return self.plugin_utils.config.dict["zap2it"]["postalcode"]
if self.fhdhr.config.dict["zap2it"]["postalcode"]:
return self.fhdhr.config.dict["zap2it"]["postalcode"]
try:
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()
postalcode = data["postal"]
except Exception as e:
@ -32,12 +41,12 @@ class Plugin_OBJ():
# Start time parameter is now rounded down to nearest `zap_timespan`, in s.
zap_time = datetime.datetime.utcnow().timestamp()
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))
# Fetch data in `zap_timespan` chunks.
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))
cached_items = self.get_cached(i_times)
@ -111,18 +120,18 @@ class Plugin_OBJ():
for i_time in i_times:
parameters = {
'aid': self.plugin_utils.config.dict["zap2it"]['affiliate_id'],
'country': self.plugin_utils.config.dict["zap2it"]['country'],
'device': self.plugin_utils.config.dict["zap2it"]['device'],
'headendId': self.plugin_utils.config.dict["zap2it"]['headendid'],
'aid': self.fhdhr.config.dict["zap2it"]['affiliate_id'],
'country': self.fhdhr.config.dict["zap2it"]['country'],
'device': self.fhdhr.config.dict["zap2it"]['device'],
'headendId': self.fhdhr.config.dict["zap2it"]['headendid'],
'isoverride': "true",
'languagecode': self.plugin_utils.config.dict["zap2it"]['languagecode'],
'languagecode': self.fhdhr.config.dict["zap2it"]['languagecode'],
'pref': 'm,p',
'timespan': self.plugin_utils.config.dict["zap2it"]['timespan'],
'timezone': self.plugin_utils.config.dict["zap2it"]['timezone'],
'userId': self.plugin_utils.config.dict["zap2it"]['userid'],
'timespan': self.fhdhr.config.dict["zap2it"]['timespan'],
'timezone': self.fhdhr.config.dict["zap2it"]['timezone'],
'userId': self.fhdhr.config.dict["zap2it"]['userid'],
'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,
'Activity_ID': 1,
'FromPage': "TV%20Guide",
@ -131,43 +140,43 @@ class Plugin_OBJ():
url = 'https://tvlistings.zap2it.com/api/grid?'
url += urllib.parse.urlencode(parameters)
self.get_cached_item(str(i_time), url)
cache_list = self.plugin_utils.db.get_plugin_value("cache_list", "epg_cache", "zap2it") or []
return [self.plugin_utils.db.get_plugin_value(x, "epg_cache", "zap2it") for x in cache_list]
cache_list = self.fhdhr.db.get_cacheitem_value("cache_list", "epg_cache", "zap2it") or []
return [self.fhdhr.db.get_cacheitem_value(x, "epg_cache", "zap2it") for x in cache_list]
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:
self.plugin_utils.logger.info("FROM CACHE: %s" % cache_key)
self.fhdhr.logger.info("FROM CACHE: %s" % cache_key)
return cacheitem
else:
self.plugin_utils.logger.info("Fetching: %s" % url)
self.fhdhr.logger.info("Fetching: %s" % url)
try:
resp = self.plugin_utils.web.session.get(url)
except self.plugin_utils.web.exceptions.HTTPError:
self.plugin_utils.logger.info('Got an error! Ignoring it.')
resp = self.fhdhr.web.session.get(url)
except self.fhdhr.web.exceptions.HTTPError:
self.fhdhr.logger.info('Got an error! Ignoring it.')
return
result = resp.json()
self.plugin_utils.db.set_plugin_value(cache_key, "epg_cache", result, "zap2it")
cache_list = self.plugin_utils.db.get_plugin_value("cache_list", "epg_cache", "zap2it") or []
self.fhdhr.db.set_cacheitem_value(cache_key, "epg_cache", result, "zap2it")
cache_list = self.fhdhr.db.get_cacheitem_value("cache_list", "epg_cache", "zap2it") or []
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):
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 = []
for cacheitem in cache_list:
cachedate = int(cacheitem)
if cachedate < zap_time:
cache_to_kill.append(cacheitem)
self.plugin_utils.db.delete_plugin_value(cacheitem, "epg_cache", "zap2it")
self.plugin_utils.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.delete_cacheitem_value(cacheitem, "epg_cache", "zap2it")
self.fhdhr.logger.info("Removing stale cache: %s" % cacheitem)
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):
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:
self.plugin_utils.db.delete_plugin_value(cacheitem, "epg_cache", "zap2it")
self.plugin_utils.logger.info("Removing cache: %s" % cacheitem)
self.plugin_utils.db.delete_plugin_value("cache_list", "epg_cache", "zap2it")
self.fhdhr.db.delete_cacheitem_value(cacheitem, "epg_cache", "zap2it")
self.fhdhr.logger.info("Removing cache: %s" % cacheitem)
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