diff --git a/data/internal_config/serviceconf.json b/data/internal_config/ZZ_serviceconf.json similarity index 100% rename from data/internal_config/serviceconf.json rename to data/internal_config/ZZ_serviceconf.json diff --git a/data/internal_config/database.json b/data/internal_config/database.json new file mode 100644 index 0000000..2f56146 --- /dev/null +++ b/data/internal_config/database.json @@ -0,0 +1,39 @@ +{ + "database":{ + "type":{ + "value": "sqlite", + "config_file": true, + "config_web": false + }, + "driver":{ + "value": "none", + "config_file": true, + "config_web": false + }, + "user":{ + "value": "none", + "config_file": true, + "config_web": false + }, + "pass":{ + "value": "none", + "config_file": true, + "config_web": false + }, + "host":{ + "value": "none", + "config_file": true, + "config_web": false + }, + "port":{ + "value": "none", + "config_file": true, + "config_web": false + }, + "name":{ + "value": "none", + "config_file": true, + "config_web": false + } + } +} diff --git a/data/internal_config/epg.json b/data/internal_config/epg.json new file mode 100644 index 0000000..4f6f657 --- /dev/null +++ b/data/internal_config/epg.json @@ -0,0 +1,9 @@ +{ + "epg":{ + "images":{ + "value": "pass", + "config_file": true, + "config_web": true + } + } +} diff --git a/data/internal_config/fhdhr.json b/data/internal_config/fhdhr.json index c621b07..333247f 100644 --- a/data/internal_config/fhdhr.json +++ b/data/internal_config/fhdhr.json @@ -1,21 +1,4 @@ { - "main":{ - "uuid":{ - "value": "none", - "config_file": true, - "config_web": false - }, - "cache_dir":{ - "value": "none", - "config_file": true, - "config_web": true - }, - "thread_method":{ - "value": "multiprocessing", - "config_file": true, - "config_web": true - } - }, "fhdhr":{ "address":{ "value": "0.0.0.0", @@ -74,87 +57,5 @@ "config_file": true, "config_web": true } - }, - "epg":{ - "images":{ - "value": "pass", - "config_file": true, - "config_web": true - } - }, - "ffmpeg":{ - "path":{ - "value": "ffmpeg", - "config_file": true, - "config_web": true - }, - "bytes_per_read":{ - "value": 1152000, - "config_file": true, - "config_web": true - } - }, - "vlc":{ - "path":{ - "value": "cvlc", - "config_file": true, - "config_web": true - }, - "bytes_per_read":{ - "value": 1152000, - "config_file": true, - "config_web": true - } - }, - "direct_stream":{ - "chunksize":{ - "value": 1048576, - "config_file": true, - "config_web": true - } - }, - "logging":{ - "level":{ - "value": "WARNING", - "config_file": true, - "config_web": true - } - }, - "database":{ - "type":{ - "value": "sqlite", - "config_file": true, - "config_web": false - }, - "driver":{ - "value": "none", - "config_file": true, - "config_web": false - }, - "user":{ - "value": "none", - "config_file": true, - "config_web": false - }, - "pass":{ - "value": "none", - "config_file": true, - "config_web": false - }, - "host":{ - "value": "none", - "config_file": true, - "config_web": false - }, - "port":{ - "value": "none", - "config_file": true, - "config_web": false - }, - "name":{ - "value": "none", - "config_file": true, - "config_web": false - } } } diff --git a/data/internal_config/logging.json b/data/internal_config/logging.json new file mode 100644 index 0000000..87b53ec --- /dev/null +++ b/data/internal_config/logging.json @@ -0,0 +1,9 @@ +{ + "logging":{ + "level":{ + "value": "WARNING", + "config_file": true, + "config_web": true + } + } +} diff --git a/data/internal_config/main.json b/data/internal_config/main.json new file mode 100644 index 0000000..bbc6776 --- /dev/null +++ b/data/internal_config/main.json @@ -0,0 +1,19 @@ +{ + "main":{ + "uuid":{ + "value": "none", + "config_file": true, + "config_web": false + }, + "cache_dir":{ + "value": "none", + "config_file": true, + "config_web": true + }, + "thread_method":{ + "value": "multiprocessing", + "config_file": true, + "config_web": true + } + } +} diff --git a/data/internal_config/rmg.json b/data/internal_config/rmg.json new file mode 100644 index 0000000..bf088aa --- /dev/null +++ b/data/internal_config/rmg.json @@ -0,0 +1,9 @@ +{ + "rmg":{ + "enabled":{ + "value": true, + "config_file": true, + "config_web": false + } + } +} diff --git a/data/internal_config/streaming.json b/data/internal_config/streaming.json new file mode 100644 index 0000000..9f78279 --- /dev/null +++ b/data/internal_config/streaming.json @@ -0,0 +1,33 @@ +{ + "ffmpeg":{ + "path":{ + "value": "ffmpeg", + "config_file": true, + "config_web": true + }, + "bytes_per_read":{ + "value": 1152000, + "config_file": true, + "config_web": true + } + }, + "vlc":{ + "path":{ + "value": "cvlc", + "config_file": true, + "config_web": true + }, + "bytes_per_read":{ + "value": 1152000, + "config_file": true, + "config_web": true + } + }, + "direct_stream":{ + "chunksize":{ + "value": 1048576, + "config_file": true, + "config_web": true + } + } +} diff --git a/data/www/templates/base.html b/data/www/templates/base.html index b49e7fd..3465dcc 100644 --- a/data/www/templates/base.html +++ b/data/www/templates/base.html @@ -22,7 +22,7 @@ - + diff --git a/data/www/templates/channels.html b/data/www/templates/channels.html index 1a0f806..c2081b4 100644 --- a/data/www/templates/channels.html +++ b/data/www/templates/channels.html @@ -5,7 +5,7 @@
Note: This may take some time.
+Note: This may take some time.
| Item | +HDHR | +RMG | +Non-Specific | +||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| {{ button_item["label"] }} | + {% if button_item["hdhr"] %} ++ {% else %} + | + {% endif %} + {% if button_item["rmg"] %} + | + {% else %} + | + {% endif %} + {% if button_item["other"] %} + | + {% else %} + | + {% endif %} + | |||||
| {{ tuner_dict["number"] }} | {{ tuner_dict["status"] }} | - {% if tuner_dict["status"] == "Active" %} + {% if tuner_dict["status"] in ["Active", "Acquired"] %}{{ tuner_dict["channel_number"] }} | + {% else %} +N/A | + {% endif %} + {% if tuner_dict["status"] == "Active" %}{{ tuner_dict["method"] }} | {{ tuner_dict["play_duration"] }} | {{ tuner_dict["downloaded"] }} | @@ -28,12 +32,14 @@N/A | N/A | N/A | -N/A | {% endif %}
- {% if tuner_dict["status"] in ["Active", "Acquired"] %}
-
+ {% if tuner_dict["status"] != "Inactive" %}
+
+ {% endif %}
+ {% if not tuner_scanning and tuner_dict["status"] == "Inactive" %}
+
{% endif %}
|
diff --git a/fHDHR/device/__init__.py b/fHDHR/device/__init__.py
index e3f7484..fc442e7 100644
--- a/fHDHR/device/__init__.py
+++ b/fHDHR/device/__init__.py
@@ -2,7 +2,6 @@ from .channels import Channels
from .epg import EPG
from .tuners import Tuners
from .images import imageHandler
-from .station_scan import Station_Scan
from .ssdp import SSDPServer
from .cluster import fHDHR_Cluster
@@ -19,8 +18,6 @@ class fHDHR_Device():
self.images = imageHandler(fhdhr, self.epg)
- self.station_scan = Station_Scan(fhdhr, self.channels)
-
self.ssdp = SSDPServer(fhdhr)
self.cluster = fHDHR_Cluster(fhdhr, self.ssdp)
diff --git a/fHDHR/device/cluster.py b/fHDHR/device/cluster.py
index 1bf15a5..3a6e629 100644
--- a/fHDHR/device/cluster.py
+++ b/fHDHR/device/cluster.py
@@ -124,7 +124,7 @@ class fHDHR_Cluster():
self.fhdhr.logger.info("Adding %s to cluster." % location)
cluster[location] = {"base_url": location}
- location_info_url = location + "/discover.json"
+ 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:
diff --git a/fHDHR/device/ssdp.py b/fHDHR/device/ssdp/__init__.py
similarity index 71%
rename from fHDHR/device/ssdp.py
rename to fHDHR/device/ssdp/__init__.py
index 5fdae10..1c0ea75 100644
--- a/fHDHR/device/ssdp.py
+++ b/fHDHR/device/ssdp/__init__.py
@@ -2,21 +2,9 @@
import socket
import struct
-
-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 []
+from .ssdp_detect import fHDHR_Detect
+from .rmg_ssdp import RMG_SSDP
+from .hdhr_ssdp import HDHR_SSDP
class SSDPServer():
@@ -33,18 +21,14 @@ class SSDPServer():
self.port = 1900
self.iface = None
self.address = None
- self.server = 'fHDHR/%s UPnP/1.0' % fhdhr.version
allowed_protos = ("ipv4", "ipv6")
if self.proto not in allowed_protos:
raise ValueError("Invalid proto - expected one of {}".format(allowed_protos))
- self.nt = 'urn:schemas-upnp-org:device:MediaServer:1'
- self.usn = 'uuid:' + fhdhr.config.dict["main"]["uuid"] + '::' + self.nt
self.location = ('http://' + fhdhr.config.dict["fhdhr"]["discovery_address"] + ':' +
str(fhdhr.config.dict["fhdhr"]["port"]) + '/device.xml')
- self.al = self.location
- self.max_age = 1800
+
self._iface = None
if self.proto == "ipv4":
@@ -95,9 +79,11 @@ class SSDPServer():
self.sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, 1)
self.sock.bind((self.bind_address, self.port))
- self.notify_payload = self.create_notify_payload()
self.msearch_payload = self.create_msearch_payload()
+ self.rmg_ssdp = RMG_SSDP(fhdhr, self._broadcast_ip)
+ self.hdhr_ssdp = HDHR_SSDP(fhdhr, self._broadcast_ip)
+
self.m_search()
def on_recv(self, data, address):
@@ -123,21 +109,33 @@ class SSDPServer():
# SSDP discovery
self.fhdhr.logger.debug("Received qualifying M-SEARCH from {}".format(address))
self.fhdhr.logger.debug("M-SEARCH data: {}".format(headers))
- notify = self.notify_payload
- self.fhdhr.logger.debug("Created NOTIFY: {}".format(notify))
- try:
- self.sock.sendto(notify, address)
- except OSError as e:
- # Most commonly: We received a multicast from an IP not in our subnet
- self.fhdhr.logger.debug("Unable to send NOTIFY to {}: {}".format(address, e))
- pass
+
+ notify_list = []
+
+ 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 notify in notify_list:
+
+ self.fhdhr.logger.debug("Created NOTIFY: {}".format(notify))
+ try:
+ self.sock.sendto(notify, address)
+ except OSError as e:
+ # Most commonly: We received a multicast from an IP not in our subnet
+ self.fhdhr.logger.debug("Unable to send NOTIFY to {}: {}".format(address, e))
+ pass
elif cmd[0] == 'NOTIFY' and cmd[1] == '*':
# SSDP presence
self.fhdhr.logger.debug("NOTIFY data: {}".format(headers))
try:
if headers["server"].startswith("fHDHR"):
if headers["location"] != self.location:
- self.detect_method.set(headers["location"].split("/device.xml")[0])
+ savelocation = headers["location"].split("/device.xml")[0]
+ self.detect_method.set(savelocation)
except KeyError:
return
else:
@@ -147,31 +145,6 @@ class SSDPServer():
data = self.msearch_payload
self.sock.sendto(data, self._address)
- def create_notify_payload(self):
- if self.max_age is not None and not isinstance(self.max_age, int):
- raise ValueError("max_age must by of type: int")
- data = (
- "NOTIFY * HTTP/1.1\r\n"
- "HOST:{}\r\n"
- "NT:{}\r\n"
- "NTS:ssdp:alive\r\n"
- "USN:{}\r\n"
- "SERVER:{}\r\n"
- ).format(
- self._broadcast_ip,
- self.nt,
- self.usn,
- self.server
- )
- if self.location is not None:
- data += "LOCATION:{}\r\n".format(self.location)
- if self.al is not None:
- data += "AL:{}\r\n".format(self.al)
- if self.max_age is not None:
- data += "Cache-Control:max-age={}\r\n".format(self.max_age)
- data += "\r\n"
- return data.encode("utf-8")
-
def create_msearch_payload(self):
data = (
"M-SEARCH * HTTP/1.1\r\n"
diff --git a/fHDHR/device/ssdp/hdhr_ssdp.py b/fHDHR/device/ssdp/hdhr_ssdp.py
new file mode 100644
index 0000000..7f8cb11
--- /dev/null
+++ b/fHDHR/device/ssdp/hdhr_ssdp.py
@@ -0,0 +1,44 @@
+
+
+class HDHR_SSDP():
+
+ def __init__(self, fhdhr, _broadcast_ip):
+ self.fhdhr = fhdhr
+
+ self.ssdp_content = None
+
+ self._broadcast_ip = _broadcast_ip
+ self.nt = 'urn:schemas-upnp-org:device:MediaServer:1'
+ self.usn = 'uuid:' + fhdhr.config.dict["main"]["uuid"] + '::' + self.nt
+ self.server = 'fHDHR/%s UPnP/1.0' % fhdhr.version
+ self.location = ('http://' + fhdhr.config.dict["fhdhr"]["discovery_address"] + ':' +
+ str(fhdhr.config.dict["fhdhr"]["port"]) + '/device.xml')
+ self.al = self.location
+ self.max_age = 1800
+
+ def get(self):
+ if self.ssdp_content:
+ return self.ssdp_content.encode("utf-8")
+
+ data = (
+ "NOTIFY * HTTP/1.1\r\n"
+ "HOST:{}\r\n"
+ "NT:{}\r\n"
+ "NTS:ssdp:alive\r\n"
+ "USN:{}\r\n"
+ "SERVER:{}\r\n"
+ ).format(
+ self._broadcast_ip,
+ self.nt,
+ self.usn,
+ self.server
+ )
+ if self.location is not None:
+ data += "LOCATION:{}\r\n".format(self.location)
+ if self.al is not None:
+ data += "AL:{}\r\n".format(self.al)
+ if self.max_age is not None:
+ data += "Cache-Control:max-age={}\r\n".format(self.max_age)
+ data += "\r\n"
+ self.ssdp_content = data
+ return data.encode("utf-8")
diff --git a/fHDHR/device/ssdp/rmg_ssdp.py b/fHDHR/device/ssdp/rmg_ssdp.py
new file mode 100644
index 0000000..31d6466
--- /dev/null
+++ b/fHDHR/device/ssdp/rmg_ssdp.py
@@ -0,0 +1,44 @@
+
+
+class RMG_SSDP():
+
+ def __init__(self, fhdhr, _broadcast_ip):
+ self.fhdhr = fhdhr
+
+ self.ssdp_content = None
+
+ self._broadcast_ip = _broadcast_ip
+ self.nt = 'urn:schemas-upnp-org:device-1-0'
+ self.usn = 'uuid:' + fhdhr.config.dict["main"]["uuid"] + '::' + self.nt
+ self.server = 'fHDHR/%s UPnP/1.0' % fhdhr.version
+ self.location = ('http://' + fhdhr.config.dict["fhdhr"]["discovery_address"] + ':' +
+ str(fhdhr.config.dict["fhdhr"]["port"]) + '/device.xml')
+ self.al = self.location
+ self.max_age = 1800
+
+ def get(self):
+ if self.ssdp_content:
+ return self.ssdp_content.encode("utf-8")
+
+ data = (
+ "NOTIFY * HTTP/1.1\r\n"
+ "HOST:{}\r\n"
+ "NT:{}\r\n"
+ "NTS:ssdp:alive\r\n"
+ "USN:{}\r\n"
+ "SERVER:{}\r\n"
+ ).format(
+ self._broadcast_ip,
+ self.nt,
+ self.usn,
+ self.server
+ )
+ if self.location is not None:
+ data += "LOCATION:{}\r\n".format(self.location)
+ if self.al is not None:
+ data += "AL:{}\r\n".format(self.al)
+ if self.max_age is not None:
+ data += "Cache-Control:max-age={}\r\n".format(self.max_age)
+ data += "\r\n"
+ self.ssdp_content = data
+ return data.encode("utf-8")
diff --git a/fHDHR/device/ssdp/ssdp_detect.py b/fHDHR/device/ssdp/ssdp_detect.py
new file mode 100644
index 0000000..e9800b5
--- /dev/null
+++ b/fHDHR/device/ssdp/ssdp_detect.py
@@ -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 []
diff --git a/fHDHR/device/station_scan.py b/fHDHR/device/station_scan.py
deleted file mode 100644
index 69c7347..0000000
--- a/fHDHR/device/station_scan.py
+++ /dev/null
@@ -1,43 +0,0 @@
-import multiprocessing
-import threading
-
-
-class Station_Scan():
-
- def __init__(self, fhdhr, channels):
- self.fhdhr = fhdhr
-
- self.channels = channels
-
- self.fhdhr.db.delete_fhdhr_value("station_scan", "scanning")
-
- def scan(self, waitfordone=False):
- self.fhdhr.logger.info("Channel Scan Requested by Client.")
-
- scan_status = self.fhdhr.db.get_fhdhr_value("station_scan", "scanning")
- if scan_status:
- self.fhdhr.logger.info("Channel Scan Already In Progress!")
- else:
- self.fhdhr.db.set_fhdhr_value("station_scan", "scanning", 1)
-
- if waitfordone:
- self.runscan()
- else:
- if self.fhdhr.config.dict["main"]["thread_method"] in ["multiprocessing"]:
- chanscan = multiprocessing.Process(target=self.runscan)
- elif self.fhdhr.config.dict["main"]["thread_method"] in ["threading"]:
- chanscan = threading.Thread(target=self.runscan)
- if self.fhdhr.config.dict["main"]["thread_method"] in ["multiprocessing", "threading"]:
- chanscan.start()
-
- def runscan(self):
- self.channels.get_channels(forceupdate=True)
- self.fhdhr.logger.info("Requested Channel Scan Complete.")
- self.fhdhr.db.delete_fhdhr_value("station_scan", "scanning")
-
- def scanning(self):
- scan_status = self.fhdhr.db.get_fhdhr_value("station_scan", "scanning")
- if not scan_status:
- return False
- else:
- return True
diff --git a/fHDHR/device/tuners/__init__.py b/fHDHR/device/tuners/__init__.py
index 65610cb..7ddbb8d 100644
--- a/fHDHR/device/tuners/__init__.py
+++ b/fHDHR/device/tuners/__init__.py
@@ -20,31 +20,51 @@ class Tuners():
for i in range(0, self.max_tuners):
self.tuners[str(i)] = Tuner(fhdhr, i, epg)
- def tuner_grab(self, tuner_number):
+ 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
+
+ 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 stop_tuner_scan(self):
+ tunernum = self.get_scanning_tuner()
+ if tunernum:
+ self.tuners[str(tunernum)].close()
+
+ def tuner_scan(self):
+ """Temporarily use a tuner for a scan"""
+ if not self.available_tuner_count():
+ raise TunerError("805 - All Tuners In Use")
+
+ tunernumber = self.get_available_tuner()
+ self.tuners[str(tunernumber)].channel_scan()
+
+ if not tunernumber:
+ raise TunerError("805 - All Tuners In Use")
+
+ def tuner_grab(self, tuner_number, channel_number):
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[str(tuner_number)].grab()
+ self.tuners[str(tuner_number)].grab(channel_number)
return tuner_number
- def first_available(self):
+ def first_available(self, channel_number):
if not self.available_tuner_count():
raise TunerError("805 - All Tuners In Use")
- for tunernum in list(self.tuners.keys()):
- try:
- self.tuners[str(tunernum)].grab()
- except TunerError:
- continue
- else:
- return tunernum
+ tunernumber = self.get_available_tuner()
- raise TunerError("805 - All Tuners In Use")
+ if not tunernumber:
+ raise TunerError("805 - All Tuners In Use")
+ else:
+ self.tuners[str(tunernumber)].grab(channel_number)
+ return tunernumber
def tuner_close(self, tunernum):
self.tuners[str(tunernum)].close()
@@ -58,16 +78,14 @@ class Tuners():
def available_tuner_count(self):
available_tuners = 0
for tunernum in list(self.tuners.keys()):
- tuner_status = self.tuners[str(tunernum)].get_status()
- if tuner_status["status"] == "Inactive":
+ if not self.tuners[str(tunernum)].tuner_lock.locked():
available_tuners += 1
return available_tuners
def inuse_tuner_count(self):
inuse_tuners = 0
for tunernum in list(self.tuners.keys()):
- tuner_status = self.tuners[str(tunernum)].get_status()
- if tuner_status["status"] == "Active":
+ if self.tuners[str(tunernum)].tuner_lock.locked():
inuse_tuners += 1
return inuse_tuners
diff --git a/fHDHR/device/tuners/stream/direct_m3u8_stream.py b/fHDHR/device/tuners/stream/direct_m3u8_stream.py
index aace1f1..8140e22 100644
--- a/fHDHR/device/tuners/stream/direct_m3u8_stream.py
+++ b/fHDHR/device/tuners/stream/direct_m3u8_stream.py
@@ -86,9 +86,6 @@ class Direct_M3U8_Stream():
yield chunk
self.tuner.add_downloaded_size(chunk_size)
- if playlist.target_duration:
- time.sleep(int(playlist.target_duration))
-
self.fhdhr.logger.info("Connection Closed: Tuner Lock Removed")
except GeneratorExit:
diff --git a/fHDHR/device/tuners/tuner.py b/fHDHR/device/tuners/tuner.py
index a431012..09c7421 100644
--- a/fHDHR/device/tuners/tuner.py
+++ b/fHDHR/device/tuners/tuner.py
@@ -1,3 +1,4 @@
+import multiprocessing
import threading
import datetime
@@ -17,17 +18,51 @@ class Tuner():
self.tuner_lock = threading.Lock()
self.set_off_status()
+ if fhdhr.config.dict["fhdhr"]["address"] == "0.0.0.0":
+ self.location = ('http://127.0.0.1:%s' % str(fhdhr.config.dict["fhdhr"]["port"]))
+ else:
+ self.location = ('http://%s:%s' % (fhdhr.config.dict["fhdhr"]["address"], str(fhdhr.config.dict["fhdhr"]["port"])))
+
+ self.chanscan_url = "%s/api/channels?method=scan" % (self.location)
+ self.close_url = "%s/api/tuners?method=close&tuner=%s" % (self.location, str(self.number))
+
+ def channel_scan(self):
+ if self.tuner_lock.locked():
+ self.fhdhr.logger.error("Tuner #%s is not available." % str(self.number))
+ raise TunerError("804 - Tuner In Use")
+
+ if self.status["status"] == "Scanning":
+ self.fhdhr.logger.info("Channel Scan Already In Progress!")
+ else:
+
+ self.tuner_lock.acquire()
+ self.status["status"] = "Scanning"
+ self.fhdhr.logger.info("Tuner #%s Performing Channel Scan." % str(self.number))
+
+ if self.fhdhr.config.dict["main"]["thread_method"] in ["multiprocessing"]:
+ chanscan = multiprocessing.Process(target=self.runscan)
+ elif self.fhdhr.config.dict["main"]["thread_method"] in ["threading"]:
+ chanscan = threading.Thread(target=self.runscan)
+ if self.fhdhr.config.dict["main"]["thread_method"] in ["multiprocessing", "threading"]:
+ chanscan.start()
+
+ def runscan(self):
+ self.fhdhr.web.session.get(self.chanscan_url)
+ self.fhdhr.logger.info("Requested Channel Scan Complete.")
+ self.fhdhr.web.session.get(self.close_url)
+
def add_downloaded_size(self, bytes_count):
if "downloaded" in list(self.status.keys()):
self.status["downloaded"] += bytes_count
- def grab(self):
+ def grab(self, channel_number):
if self.tuner_lock.locked():
self.fhdhr.logger.error("Tuner #" + str(self.number) + " is not available.")
raise TunerError("804 - Tuner In Use")
self.tuner_lock.acquire()
self.status["status"] = "Acquired"
- self.fhdhr.logger.info("Tuner #" + str(self.number) + " Acquired.")
+ self.status["channel"] = channel_number
+ self.fhdhr.logger.info("Tuner #%s Acquired." % str(self.number))
def close(self):
self.set_off_status()
diff --git a/fHDHR/http/__init__.py b/fHDHR/http/__init__.py
index b96fc26..1fd3ec7 100644
--- a/fHDHR/http/__init__.py
+++ b/fHDHR/http/__init__.py
@@ -3,8 +3,9 @@ from flask import Flask, request
from .pages import fHDHR_Pages
from .files import fHDHR_Files
+from .hdhr import fHDHR_HDHR
+from .rmg import fHDHR_RMG
from .api import fHDHR_API
-from .watch import fHDHR_WATCH
class fHDHR_HTTP_Server():
@@ -27,14 +28,18 @@ class fHDHR_HTTP_Server():
self.files = fHDHR_Files(fhdhr)
self.add_endpoints(self.files, "files")
+ 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 Stream Endpoints.")
- self.watch = fHDHR_WATCH(fhdhr)
- self.add_endpoints(self.watch, "watch")
-
self.app.before_request(self.before_request)
self.app.after_request(self.after_request)
diff --git a/fHDHR/http/api/__init__.py b/fHDHR/http/api/__init__.py
index 0e8e65f..359f9a6 100644
--- a/fHDHR/http/api/__init__.py
+++ b/fHDHR/http/api/__init__.py
@@ -1,12 +1,13 @@
+from .root_url import Root_URL
+
from .cluster import Cluster
from .settings import Settings
from .channels import Channels
-from .lineup_post import Lineup_Post
from .xmltv import xmlTV
from .m3u import M3U
from .epg import EPG
-from .watch import Watch
+from .tuners import Tuners
from .debug import Debug_JSON
from .images import Images
@@ -17,14 +18,15 @@ class fHDHR_API():
def __init__(self, fhdhr):
self.fhdhr = fhdhr
+ self.root_url = Root_URL(fhdhr)
+
self.cluster = Cluster(fhdhr)
self.settings = Settings(fhdhr)
self.channels = Channels(fhdhr)
self.xmltv = xmlTV(fhdhr)
self.m3u = M3U(fhdhr)
self.epg = EPG(fhdhr)
- self.watch = Watch(fhdhr)
+ self.tuners = Tuners(fhdhr)
self.debug = Debug_JSON(fhdhr)
- self.lineup_post = Lineup_Post(fhdhr)
self.images = Images(fhdhr)
diff --git a/fHDHR/http/api/channels.py b/fHDHR/http/api/channels.py
index b242829..855bd1f 100644
--- a/fHDHR/http/api/channels.py
+++ b/fHDHR/http/api/channels.py
@@ -94,7 +94,7 @@ class Channels():
self.fhdhr.device.channels.set_channel_status("id", channel_id, updatedict)
elif method == "scan":
- self.fhdhr.device.station_scan.scan(waitfordone=True)
+ self.fhdhr.device.channels.get_channels(forceupdate=True)
else:
return "Invalid Method"
diff --git a/fHDHR/http/api/root_url.py b/fHDHR/http/api/root_url.py
new file mode 100644
index 0000000..68a8d75
--- /dev/null
+++ b/fHDHR/http/api/root_url.py
@@ -0,0 +1,32 @@
+from flask import redirect, request
+
+
+class Root_URL():
+ endpoints = ["/"]
+ endpoint_name = "page_root_html"
+ endpoint_methods = ["GET", "POST"]
+
+ def __init__(self, fhdhr):
+ self.fhdhr = fhdhr
+
+ def __call__(self, *args):
+ return self.get(*args)
+
+ def get(self, *args):
+
+ user_agent = request.headers.get('User-Agent')
+
+ # Client Devices Discovering Device Information
+ if not user_agent or str(user_agent).lower().startswith("plexmediaserver"):
+
+ # Plex Remote Media Grabber redirect
+ if self.fhdhr.config.dict["rmg"]["enabled"] and str(user_agent).lower().startswith("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")
diff --git a/fHDHR/http/api/watch.py b/fHDHR/http/api/tuners.py
similarity index 92%
rename from fHDHR/http/api/watch.py
rename to fHDHR/http/api/tuners.py
index fdab038..b2e2fbd 100644
--- a/fHDHR/http/api/watch.py
+++ b/fHDHR/http/api/tuners.py
@@ -5,10 +5,9 @@ import uuid
from fHDHR.exceptions import TunerError
-class Watch():
- """Methods to create xmltv.xml"""
- endpoints = ["/api/watch"]
- endpoint_name = "api_watch"
+class Tuners():
+ endpoints = ["/api/tuners"]
+ endpoint_name = "api_tuners"
endpoint_methods = ["GET", "POST"]
def __init__(self, fhdhr):
@@ -70,9 +69,9 @@ class Watch():
try:
if not tuner_number:
- tunernum = self.fhdhr.device.tuners.first_available()
+ tunernum = self.fhdhr.device.tuners.first_available(channel_number)
else:
- tunernum = self.fhdhr.device.tuners.tuner_grab(tuner_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)))
@@ -109,6 +108,14 @@ class Watch():
tuner = self.fhdhr.device.tuners.tuners[str(tuner_number)]
tuner.close()
+ elif method == "scan":
+
+ if not tuner_number:
+ self.fhdhr.device.tuners.tuner_scan()
+ else:
+ tuner = self.fhdhr.device.tuners.tuners[str(tuner_number)]
+ tuner.channel_scan()
+
else:
return "%s Invalid Method" % method
diff --git a/fHDHR/http/files/__init__.py b/fHDHR/http/files/__init__.py
index 1821936..905566b 100644
--- a/fHDHR/http/files/__init__.py
+++ b/fHDHR/http/files/__init__.py
@@ -2,13 +2,7 @@
from .favicon_ico import Favicon_ICO
from .style_css import Style_CSS
-
from .device_xml import Device_XML
-from .lineup_xml import Lineup_XML
-
-from .discover_json import Discover_JSON
-from .lineup_json import Lineup_JSON
-from .lineup_status_json import Lineup_Status_JSON
class fHDHR_Files():
@@ -18,10 +12,4 @@ class fHDHR_Files():
self.favicon = Favicon_ICO(fhdhr)
self.style = Style_CSS(fhdhr)
-
self.device_xml = Device_XML(fhdhr)
- self.lineup_xml = Lineup_XML(fhdhr)
-
- self.discover_json = Discover_JSON(fhdhr)
- self.lineup_json = Lineup_JSON(fhdhr)
- self.lineup_status_json = Lineup_Status_JSON(fhdhr)
diff --git a/fHDHR/http/files/device_xml.py b/fHDHR/http/files/device_xml.py
index b1e590c..3bca8cf 100644
--- a/fHDHR/http/files/device_xml.py
+++ b/fHDHR/http/files/device_xml.py
@@ -1,8 +1,4 @@
-from flask import Response, request
-from io import BytesIO
-import xml.etree.ElementTree
-
-from fHDHR.tools import sub_el
+from flask import request, redirect
class Device_XML():
@@ -17,31 +13,9 @@ class Device_XML():
def get(self, *args):
- base_url = request.url_root[:-1]
-
- out = xml.etree.ElementTree.Element('root')
- out.set('xmlns', "urn:schemas-upnp-org:device-1-0")
-
- sub_el(out, 'URLBase', base_url)
-
- 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:schemas-upnp-org:device:MediaServer: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, 'modelName', self.fhdhr.config.dict["fhdhr"]["reporting_model"])
- sub_el(device_out, 'modelNumber', self.fhdhr.config.dict["fhdhr"]["reporting_model"])
- sub_el(device_out, 'serialNumber')
- sub_el(device_out, 'UDN', "uuid:" + self.fhdhr.config.dict["main"]["uuid"])
-
- fakefile = BytesIO()
- fakefile.write(b'\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')
+ user_agent = request.headers.get('User-Agent')
+ if (self.fhdhr.config.dict["rmg"]["enabled"] and
+ str(user_agent).lower().startswith("plexmediaserver")):
+ return redirect("/rmg/device.xml")
+ else:
+ return redirect("/hdhr/device.xml")
diff --git a/fHDHR/http/hdhr/__init__.py b/fHDHR/http/hdhr/__init__.py
new file mode 100644
index 0000000..a545826
--- /dev/null
+++ b/fHDHR/http/hdhr/__init__.py
@@ -0,0 +1,31 @@
+
+
+from .lineup_xml import Lineup_XML
+from .discover_json import Discover_JSON
+from .lineup_json import Lineup_JSON
+from .lineup_status_json import Lineup_Status_JSON
+
+from .lineup_post import Lineup_Post
+from .device_xml import HDHR_Device_XML
+
+from .auto import Auto
+from .tuner import Tuner
+
+
+class fHDHR_HDHR():
+
+ def __init__(self, fhdhr):
+ self.fhdhr = fhdhr
+
+ self.lineup_post = Lineup_Post(fhdhr)
+
+ self.device_xml = HDHR_Device_XML(fhdhr)
+
+ self.auto = Auto(fhdhr)
+ self.tuner = Tuner(fhdhr)
+
+ self.lineup_xml = Lineup_XML(fhdhr)
+
+ self.discover_json = Discover_JSON(fhdhr)
+ self.lineup_json = Lineup_JSON(fhdhr)
+ self.lineup_status_json = Lineup_Status_JSON(fhdhr)
diff --git a/fHDHR/http/watch/auto.py b/fHDHR/http/hdhr/auto.py
similarity index 90%
rename from fHDHR/http/watch/auto.py
rename to fHDHR/http/hdhr/auto.py
index 789cd5b..63e4664 100644
--- a/fHDHR/http/watch/auto.py
+++ b/fHDHR/http/hdhr/auto.py
@@ -3,8 +3,8 @@ import urllib.parse
class Auto():
- endpoints = ['/auto/