test
This commit is contained in:
parent
8c5cd7371f
commit
89a04f919b
@ -1,17 +1,17 @@
|
|||||||
{
|
{
|
||||||
"main":{
|
"main":{
|
||||||
"servicename":{
|
"servicename":{
|
||||||
"value": "PlutoTV",
|
"value": "NewsOn",
|
||||||
"config_file": false,
|
"config_file": false,
|
||||||
"config_web": false
|
"config_web": false
|
||||||
},
|
},
|
||||||
"dictpopname":{
|
"dictpopname":{
|
||||||
"value": "plutotv",
|
"value": "newson",
|
||||||
"config_file": false,
|
"config_file": false,
|
||||||
"config_web": false
|
"config_web": false
|
||||||
},
|
},
|
||||||
"reponame":{
|
"reponame":{
|
||||||
"value": "fHDHR_PlutoTV",
|
"value": "fHDHR_NewsOn",
|
||||||
"config_file": false,
|
"config_file": false,
|
||||||
"config_web": false
|
"config_web": false
|
||||||
},
|
},
|
||||||
@ -28,7 +28,7 @@
|
|||||||
},
|
},
|
||||||
"fhdhr":{
|
"fhdhr":{
|
||||||
"friendlyname":{
|
"friendlyname":{
|
||||||
"value": "fHDHR-PlutoTV",
|
"value": "fHDHR-NewsOn",
|
||||||
"config_file": true,
|
"config_file": true,
|
||||||
"config_web": true
|
"config_web": true
|
||||||
},
|
},
|
||||||
@ -43,7 +43,7 @@
|
|||||||
"config_web": true
|
"config_web": true
|
||||||
},
|
},
|
||||||
"reporting_firmware_name":{
|
"reporting_firmware_name":{
|
||||||
"value": "fHDHR_PlutoTV",
|
"value": "fHDHR_NewsOn",
|
||||||
"config_file": true,
|
"config_file": true,
|
||||||
"config_web": true
|
"config_web": true
|
||||||
}
|
}
|
||||||
@ -60,22 +60,5 @@
|
|||||||
"config_web": true
|
"config_web": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"plutotv":{
|
"newson":{}
|
||||||
"username":{
|
|
||||||
"value": "none",
|
|
||||||
"config_file": true,
|
|
||||||
"config_web": true
|
|
||||||
},
|
|
||||||
"password":{
|
|
||||||
"value": "none",
|
|
||||||
"config_file": true,
|
|
||||||
"config_web": true,
|
|
||||||
"config_web_hidden": true
|
|
||||||
},
|
|
||||||
"force_best":{
|
|
||||||
"value": false,
|
|
||||||
"config_file": true,
|
|
||||||
"config_web": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,3 @@
|
|||||||
import urllib.parse
|
|
||||||
import m3u8
|
|
||||||
import time
|
|
||||||
|
|
||||||
|
|
||||||
class OriginChannels():
|
class OriginChannels():
|
||||||
@ -13,78 +10,9 @@ class OriginChannels():
|
|||||||
|
|
||||||
def get_channels(self):
|
def get_channels(self):
|
||||||
|
|
||||||
url = self.base_api_url + "/v2/channels.json"
|
|
||||||
urlopn = self.fhdhr.web.session.get(url)
|
|
||||||
pluto_chan_list = urlopn.json()
|
|
||||||
|
|
||||||
channel_list = []
|
channel_list = []
|
||||||
for channel_dict in pluto_chan_list:
|
|
||||||
|
|
||||||
if (channel_dict["isStitched"]
|
|
||||||
and channel_dict["visibility"] in ["everyone"]
|
|
||||||
and not channel_dict['onDemand']
|
|
||||||
and channel_dict["name"] != "Announcement"):
|
|
||||||
|
|
||||||
clean_station_item = {
|
|
||||||
"name": channel_dict["name"],
|
|
||||||
"callsign": channel_dict["name"],
|
|
||||||
"number": str(channel_dict["number"]),
|
|
||||||
"id": str(channel_dict["_id"]),
|
|
||||||
}
|
|
||||||
channel_list.append(clean_station_item)
|
|
||||||
return channel_list
|
return channel_list
|
||||||
|
|
||||||
def get_channel_stream(self, chandict):
|
def get_channel_stream(self, chandict):
|
||||||
url = self.base_api_url + "/v2/channels.json"
|
streamurl = None
|
||||||
urlopn = self.fhdhr.web.session.get(url)
|
|
||||||
pluto_chan_list = urlopn.json()
|
|
||||||
pluto_chandict = self.get_channel_dict_pluto(pluto_chan_list, "_id", chandict["origin_id"])
|
|
||||||
|
|
||||||
streamurl = pluto_chandict["stitched"]["urls"][0]["url"]
|
|
||||||
streamurl = self.channel_stream_url_cleanup(streamurl)
|
|
||||||
if self.fhdhr.config.dict["origin"]["force_best"]:
|
|
||||||
streamurl = self.m3u8_beststream(streamurl)
|
|
||||||
return streamurl
|
return streamurl
|
||||||
|
|
||||||
def get_channel_dict_pluto(self, chanlist, keyfind, valfind):
|
|
||||||
return next(item for item in chanlist if item[keyfind] == valfind)
|
|
||||||
|
|
||||||
def channel_stream_url_cleanup(self, streamurl):
|
|
||||||
|
|
||||||
streamurl = streamurl.replace("\\u0026", "&")
|
|
||||||
streamurl_base = streamurl.split("?")[0]
|
|
||||||
streamurl_params = streamurl.split("?")[1].split("&")
|
|
||||||
|
|
||||||
paramdict = {}
|
|
||||||
|
|
||||||
for param in streamurl_params:
|
|
||||||
paramkey = param.split("=")[0]
|
|
||||||
paramval = param.split("=")[1]
|
|
||||||
paramdict[paramkey] = paramval
|
|
||||||
|
|
||||||
paramdict["deviceMake"] = "Chrome"
|
|
||||||
paramdict["deviceType"] = "web"
|
|
||||||
paramdict["deviceModel"] = "Chrome"
|
|
||||||
paramdict["sid"] = self.fhdhr.config.dict["main"]["uuid"] + str(time.time())
|
|
||||||
paramdict["userId"] = self.origin.userid or ''
|
|
||||||
|
|
||||||
paramdict["serverSideAds"] = "true"
|
|
||||||
|
|
||||||
return streamurl_base + "?" + urllib.parse.urlencode(paramdict)
|
|
||||||
|
|
||||||
def m3u8_beststream(self, m3u8_url):
|
|
||||||
bestStream = None
|
|
||||||
videoUrlM3u = m3u8.load(m3u8_url)
|
|
||||||
if not videoUrlM3u.is_variant:
|
|
||||||
return m3u8_url
|
|
||||||
|
|
||||||
for videoStream in videoUrlM3u.playlists:
|
|
||||||
if not bestStream:
|
|
||||||
bestStream = videoStream
|
|
||||||
elif videoStream.stream_info.bandwidth > bestStream.stream_info.bandwidth:
|
|
||||||
bestStream = videoStream
|
|
||||||
|
|
||||||
if not bestStream:
|
|
||||||
return bestStream.absolute_uri
|
|
||||||
else:
|
|
||||||
return m3u8_url
|
|
||||||
|
|||||||
@ -1,6 +1,3 @@
|
|||||||
import datetime
|
|
||||||
|
|
||||||
import fHDHR.tools
|
|
||||||
|
|
||||||
|
|
||||||
class OriginEPG():
|
class OriginEPG():
|
||||||
@ -8,160 +5,7 @@ class OriginEPG():
|
|||||||
def __init__(self, fhdhr):
|
def __init__(self, fhdhr):
|
||||||
self.fhdhr = fhdhr
|
self.fhdhr = fhdhr
|
||||||
|
|
||||||
self.base_api_url = 'https://api.pluto.tv'
|
|
||||||
|
|
||||||
def xmltimestamp_pluto(self, inputtime):
|
|
||||||
xmltime = inputtime.replace('Z', '+00:00')
|
|
||||||
xmltime = datetime.datetime.fromisoformat(xmltime)
|
|
||||||
xmltime = xmltime.strftime('%Y%m%d%H%M%S %z')
|
|
||||||
return xmltime
|
|
||||||
|
|
||||||
def duration_pluto_minutes(self, induration):
|
|
||||||
return ((int(induration))/1000/60)
|
|
||||||
|
|
||||||
def pluto_calculate_duration(self, start_time, end_time):
|
|
||||||
start_time = start_time.replace('Z', '+00:00')
|
|
||||||
start_time = datetime.datetime.fromisoformat(start_time)
|
|
||||||
|
|
||||||
end_time = end_time.replace('Z', '+00:00')
|
|
||||||
end_time = datetime.datetime.fromisoformat(end_time)
|
|
||||||
|
|
||||||
duration = (end_time - start_time).total_seconds() / 60
|
|
||||||
return duration
|
|
||||||
|
|
||||||
def update_epg(self, fhdhr_channels):
|
def update_epg(self, fhdhr_channels):
|
||||||
programguide = {}
|
programguide = {}
|
||||||
|
|
||||||
todaydate = datetime.datetime.utcnow().date()
|
|
||||||
self.remove_stale_cache(todaydate)
|
|
||||||
|
|
||||||
time_list = []
|
|
||||||
xtimestart = datetime.datetime.today().replace(hour=0, minute=0, second=0, microsecond=0)
|
|
||||||
xtime = xtimestart
|
|
||||||
xtimeend = datetime.datetime.utcnow() + datetime.timedelta(hours=6)
|
|
||||||
while xtime <= xtimeend:
|
|
||||||
guide_time = {"start": str(xtime.strftime('%Y-%m-%dT%H:00:00'))}
|
|
||||||
if (xtime + datetime.timedelta(hours=6)) <= xtimeend:
|
|
||||||
guide_time["end"] = str((xtime + datetime.timedelta(hours=6)).strftime('%Y-%m-%dT%H:00:00'))
|
|
||||||
else:
|
|
||||||
guide_time["end"] = str(xtimeend.strftime('%Y-%m-%dT%H:00:00'))
|
|
||||||
xtime = xtime + datetime.timedelta(hours=6)
|
|
||||||
time_list.append(guide_time)
|
|
||||||
|
|
||||||
cached_items = self.get_cached(time_list)
|
|
||||||
|
|
||||||
for result in cached_items:
|
|
||||||
|
|
||||||
for c in result:
|
|
||||||
|
|
||||||
if (c["isStitched"]
|
|
||||||
and c["visibility"] in ["everyone"]
|
|
||||||
and not c['onDemand']
|
|
||||||
and c["name"] != "Announcement"):
|
|
||||||
|
|
||||||
cdict = fHDHR.tools.xmldictmaker(c, ["name", "number", "_id", "timelines", "colorLogoPNG"], list_items=["timelines"])
|
|
||||||
|
|
||||||
chandict = fhdhr_channels.get_channel_dict("origin_id", cdict["_id"])
|
|
||||||
|
|
||||||
if str(chandict['number']) not in list(programguide.keys()):
|
|
||||||
|
|
||||||
programguide[str(chandict['number'])] = {
|
|
||||||
"callsign": chandict["callsign"],
|
|
||||||
"name": chandict["name"] or chandict["callsign"],
|
|
||||||
"number": chandict["number"],
|
|
||||||
"id": str(chandict["origin_id"]),
|
|
||||||
"thumbnail": None,
|
|
||||||
"listing": [],
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
thumbnail = cdict["colorLogoPNG"]["path"].split("?")[0]
|
|
||||||
except TypeError:
|
|
||||||
thumbnail = None
|
|
||||||
programguide[str(chandict['number'])]["thumbnail"] = thumbnail
|
|
||||||
|
|
||||||
for program_item in cdict["timelines"]:
|
|
||||||
|
|
||||||
progdict = fHDHR.tools.xmldictmaker(program_item, ['_id', 'start', 'stop', 'title', 'episode'])
|
|
||||||
episodedict = fHDHR.tools.xmldictmaker(program_item['episode'], ['duration', 'poster', '_id', 'rating', 'description', 'genre', 'subGenre', 'name'])
|
|
||||||
|
|
||||||
if not episodedict["duration"]:
|
|
||||||
episodedict["duration"] = self.pluto_calculate_duration(progdict["start"], progdict["stop"])
|
|
||||||
else:
|
|
||||||
episodedict["duration"] = self.duration_pluto_minutes(episodedict["duration"])
|
|
||||||
|
|
||||||
clean_prog_dict = {
|
|
||||||
"time_start": self.xmltimestamp_pluto(progdict["start"]),
|
|
||||||
"time_end": self.xmltimestamp_pluto(progdict["stop"]),
|
|
||||||
"duration_minutes": episodedict["duration"],
|
|
||||||
"thumbnail": None,
|
|
||||||
"title": progdict['title'] or "Unavailable",
|
|
||||||
"sub-title": episodedict['name'] or "Unavailable",
|
|
||||||
"description": episodedict['description'] or "Unavailable",
|
|
||||||
"rating": episodedict['rating'] or "N/A",
|
|
||||||
"episodetitle": None,
|
|
||||||
"releaseyear": None,
|
|
||||||
"genres": [],
|
|
||||||
"seasonnumber": None,
|
|
||||||
"episodenumber": None,
|
|
||||||
"isnew": False,
|
|
||||||
"id": episodedict['_id'] or self.xmltimestamp_pluto(progdict["start"]),
|
|
||||||
}
|
|
||||||
try:
|
|
||||||
thumbnail = episodedict["poster"]["path"].split("?")[0]
|
|
||||||
except TypeError:
|
|
||||||
thumbnail = None
|
|
||||||
clean_prog_dict["thumbnail"] = thumbnail
|
|
||||||
|
|
||||||
clean_prog_dict["genres"].extend(episodedict["genre"].split(" \\u0026 "))
|
|
||||||
clean_prog_dict["genres"].append(episodedict["subGenre"])
|
|
||||||
|
|
||||||
programguide[str(chandict["number"])]["listing"].append(clean_prog_dict)
|
|
||||||
|
|
||||||
return programguide
|
return programguide
|
||||||
|
|
||||||
def get_cached(self, time_list):
|
|
||||||
for times in time_list:
|
|
||||||
url = self.base_api_url + '/v2/channels?start=%s.000Z&stop=%s.000Z' % (times["start"], times["end"])
|
|
||||||
self.get_cached_item(times["start"], url)
|
|
||||||
cache_list = self.fhdhr.db.get_cacheitem_value("cache_list", "offline_cache", "origin") or []
|
|
||||||
return [self.fhdhr.db.get_cacheitem_value(x, "offline_cache", "origin") for x in cache_list]
|
|
||||||
|
|
||||||
def get_cached_item(self, cache_key, url):
|
|
||||||
cache_key = datetime.datetime.strptime(cache_key, '%Y-%m-%dT%H:%M:%S').timestamp()
|
|
||||||
cacheitem = self.fhdhr.db.get_cacheitem_value(str(cache_key), "offline_cache", "origin")
|
|
||||||
if cacheitem:
|
|
||||||
self.fhdhr.logger.info('FROM CACHE: ' + str(cache_key))
|
|
||||||
return cacheitem
|
|
||||||
else:
|
|
||||||
self.fhdhr.logger.info('Fetching: ' + url)
|
|
||||||
try:
|
|
||||||
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.fhdhr.db.set_cacheitem_value(str(cache_key), "offline_cache", result, "origin")
|
|
||||||
cache_list = self.fhdhr.db.get_cacheitem_value("cache_list", "offline_cache", "origin") or []
|
|
||||||
cache_list.append(str(cache_key))
|
|
||||||
self.fhdhr.db.set_cacheitem_value("cache_list", "offline_cache", cache_list, "origin")
|
|
||||||
|
|
||||||
def remove_stale_cache(self, todaydate):
|
|
||||||
cache_clear_time = todaydate.strftime('%Y-%m-%dT%H:00:00')
|
|
||||||
cache_clear_time = datetime.datetime.strptime(cache_clear_time, '%Y-%m-%dT%H:%M:%S').timestamp()
|
|
||||||
cache_list = self.fhdhr.db.get_cacheitem_value("cache_list", "offline_cache", "origin") or []
|
|
||||||
cache_to_kill = []
|
|
||||||
for cacheitem in cache_list:
|
|
||||||
if float(cacheitem) < cache_clear_time:
|
|
||||||
cache_to_kill.append(cacheitem)
|
|
||||||
self.fhdhr.db.delete_cacheitem_value(str(cacheitem), "offline_cache", "origin")
|
|
||||||
self.fhdhr.logger.info('Removing stale cache: ' + str(cacheitem))
|
|
||||||
self.fhdhr.db.set_cacheitem_value("cache_list", "offline_cache", [x for x in cache_list if x not in cache_to_kill], "origin")
|
|
||||||
|
|
||||||
def clear_cache(self):
|
|
||||||
cache_list = self.fhdhr.db.get_cacheitem_value("cache_list", "offline_cache", "origin") or []
|
|
||||||
for cacheitem in cache_list:
|
|
||||||
self.fhdhr.db.delete_cacheitem_value(cacheitem, "offline_cache", "origin")
|
|
||||||
self.fhdhr.logger.info('Removing cache: ' + str(cacheitem))
|
|
||||||
self.fhdhr.db.delete_cacheitem_value("cache_list", "offline_cache", "origin")
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
class OriginService():
|
class OriginService():
|
||||||
@ -6,43 +5,8 @@ class OriginService():
|
|||||||
def __init__(self, fhdhr):
|
def __init__(self, fhdhr):
|
||||||
self.fhdhr = fhdhr
|
self.fhdhr = fhdhr
|
||||||
|
|
||||||
self.base_api_url = 'https://api.pluto.tv'
|
|
||||||
self.login_url = self.base_api_url + '/v1/auth/local'
|
|
||||||
|
|
||||||
self.token = None
|
|
||||||
self.userid = None
|
|
||||||
|
|
||||||
self.login()
|
|
||||||
|
|
||||||
def login(self):
|
|
||||||
self.fhdhr.logger.info("Logging into PlutoTV")
|
|
||||||
if (not self.fhdhr.config.dict["origin"]["username"] or not self.fhdhr.config.dict["origin"]["password"]):
|
|
||||||
self.fhdhr.logger.warning("No Username/Password set, will operate in Guest Mode.")
|
|
||||||
return True
|
|
||||||
|
|
||||||
form_data = {
|
|
||||||
'optIn': 'true',
|
|
||||||
'password': self.fhdhr.config.dict["origin"]["password"],
|
|
||||||
'synced': 'false',
|
|
||||||
'email': self.fhdhr.config.dict["origin"]["username"]
|
|
||||||
}
|
|
||||||
try:
|
|
||||||
loginreq = self.fhdhr.web.session.post(self.login_url, data=form_data)
|
|
||||||
loginresp = json.loads(loginreq.content)
|
|
||||||
if "accessToken" not in list(loginresp.keys()):
|
|
||||||
self.fhdhr.logger.warning("Login Failed, will use Guest Mode.")
|
|
||||||
return True
|
|
||||||
self.fhdhr.logger.info("Login Success!")
|
|
||||||
except Exception as e:
|
|
||||||
self.fhdhr.logger.warning("Login Failed, will use Guest Mode. " + str(e))
|
|
||||||
return True
|
|
||||||
self.userid = loginresp["_id"]
|
|
||||||
self.token = loginresp["accessToken"]
|
|
||||||
return True
|
|
||||||
|
|
||||||
def get_status_dict(self):
|
def get_status_dict(self):
|
||||||
ret_status_dict = {
|
ret_status_dict = {
|
||||||
"Login": "Success" if self.userid else "Guest Mode",
|
"Login": "Success",
|
||||||
"Username": self.fhdhr.config.dict["origin"]["username"],
|
|
||||||
}
|
}
|
||||||
return ret_status_dict
|
return ret_status_dict
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user