Skip to content

Commit

Permalink
[Version 2.2] add IPTV support
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew Blackburn committed Aug 4, 2017
1 parent bf636e7 commit dbb7514
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 64 deletions.
14 changes: 9 additions & 5 deletions PlexDVRAPI/src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,38 @@
from Components.Language import language
from Tools.Directories import resolveFilename, SCOPE_PLUGINS, SCOPE_LANGUAGE

tunerTypes = ('DVB-C', 'DVB-T', 'DVB-S', 'multi')
tunerTypes = ('DVB-C', 'DVB-T', 'DVB-S', 'iptv', 'multi')

tunertypes = {
'DVB-C' : 'Cable',
'DVB-T' : 'Antenna',
'DVB-S' : 'Cable',
'multi' : 'Cable'
'multi' : 'Cable',
'iptv' : 'Cable'
}

tunerports = {
'DVB-C' : '6081',
'DVB-T' : '6082',
'DVB-S' : '6083',
'multi' : '6084'
'multi' : '6084',
'iptv' : '6085'
}

tunerfolders = {
'DVB-C' : 'cable',
'DVB-T' : 'antenna',
'DVB-S' : 'satellite',
'multi' : 'multi'
'multi' : 'multi',
'iptv' : 'iptv'
}

porttypes = {
6081 : 'DVB-C',
6082 : 'DVB-T',
6083 : 'DVB-S',
6084 : 'multi'
6084 : 'multi',
6085 : 'iptv'
}

def _ifinfo(sock, addr, ifname):
Expand Down
2 changes: 1 addition & 1 deletion PlexDVRAPI/src/about.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from Components.Pixmap import Pixmap
from Components.Sources.StaticText import StaticText

PLUGIN_VERSION = '2.1'
PLUGIN_VERSION = '2.2'

class PlexDVRAPI_About(Screen):
skin="""
Expand Down
2 changes: 1 addition & 1 deletion PlexDVRAPI/src/getDeviceInfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def discoverJSON(self, dvb_type):
return discover

def tunercount(dvbtype):
return len(nimmanager.getNimListOfType(dvbtype)) if dvbtype != "multi" else len(nimmanager.nimList())
return len(nimmanager.getNimListOfType(dvbtype)) if dvbtype not in ('multi','iptv') else len(nimmanager.nimList())

def discoverdata(dvbtype):
device_info = getDeviceInfo()
Expand Down
43 changes: 28 additions & 15 deletions PlexDVRAPI/src/getLineup.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import re
import json
from urllib import unquote
from os import path, mkdir
from sys import modules

from enigma import eServiceReference
from Components.config import config

from . import tunerfolders

class getLineup:
def __init__(self, duplicates = False, single_bouquet = 'all'):
self.duplicates = duplicates
Expand All @@ -22,7 +21,7 @@ def __init__(self, duplicates = False, single_bouquet = 'all'):
self.channelNames = {} # key SID:TSID:ONID:NAMESPACE in hex
self.bouquets_filenames = []
self.bouquets_flags = {}
self.bouquets_names = [] # contains tuple pairs, e.g. [(filename1, bouquet_name1), (filename1, bouquet_name1)]
self.bouquets_names = [] # contains tuple pairs, e.g. [(filename1, bouquet_name1), (filename2, bouquet_name2)]
self.channel_numbers_names_and_refs = []
self.video_allowed_types = [1, 4, 5, 17, 22, 24, 25, 27, 135]
self.read_services()
Expand Down Expand Up @@ -96,15 +95,18 @@ def read_tv_bouquets(self):
content = bouquet.read()
bouquet.close()

for row in content.split("\n"):
content_split = content.split("\n")
content_len = len(content_split)
for idx in range(content_len):
row = content_split[idx]
channel_name = ''
if name == '' and row.startswith("#NAME "):
if not (self.bouquets_flags[filename] & self.isInvisible): # not invisible bouquet
name = row.strip()[6:]
self.bouquets_names.append((filename, name))
elif row.startswith("#SERVICE "):
if "http" in row:
channel_number += 1
continue
if content_len > (idx + 1) and content_split[idx + 1].startswith("#DESCRIPTION "): # check if channel name exists in bouquets file
channel_name = content_split[idx + 1].strip()[13:]
service_ref = row[9:].strip()
service_ref_split = service_ref.split(":")
if len(service_ref_split) < 10:
Expand All @@ -126,28 +128,36 @@ def read_tv_bouquets(self):
continue
if (self.bouquets_flags[filename] & self.isInvisible): # invisible bouquet
continue
if int(service_ref_split[0], 16) != 1: # not a regular service. Might be IPTV.
if int(service_ref_split[0]) != 1: # not a regular service. Might be IPTV.
continue
if service_flags != 0: # not a normal service that can be fed directly into the "play"-handler.
continue
if int(service_ref_split[2], 16) not in self.video_allowed_types:
continue
if service_ref in self.refs_added and not self.duplicates:
continue
self.refs_added.append(service_ref)
if "http" not in row: # not http stream
self.refs_added.append(service_ref)
sid = int(service_ref_split[3], 16)
tsid = int(service_ref_split[4], 16)
onid = int(service_ref_split[5], 16)
namespace = int(service_ref_split[6], 16)
key = "%x:%x:%x:%x" % (sid, tsid, onid, namespace)
if key not in self.channelNames:
if key not in self.channelNames and ("http" not in row or ("http" in row and channel_name == "")):
continue
if channel_name == "":
channel_name = self.channelNames[key]
if len(service_ref_split) > 10 and "http" in service_ref_split[10]: # http stream
http_link = unquote(service_ref_split[10].strip())
self.channel_numbers_names_and_refs.append((str(channel_number), channel_name, http_link, "iptv"))
continue
self.channel_numbers_names_and_refs.append((str(channel_number), self.channelNames[key], service_ref, self.tunerType(namespace)))
service_ref_clean = ':'.join(service_ref_split[:10]) + ":"
self.channel_numbers_names_and_refs.append((str(channel_number), channel_name, service_ref_clean, self.tunerType(namespace)))

def tunerType(self, namespace):
if (namespace / (16**4)) == 0xFFFF:
if (namespace >> 16) == 0xFFFF:
return "DVB-C"
if (namespace / (16**4)) == 0xEEEE:
if (namespace >> 16) == 0xEEEE:
return "DVB-T"
return "DVB-S"

Expand Down Expand Up @@ -185,11 +195,14 @@ def createJSON(self, ip="0.0.0.0", port=8001, dvb_type="DVB-S"):
self.lineup = []

for c_n_r in output:
if dvb_type == 'multi' or c_n_r[3] == dvb_type:
if dvb_type in ('multi') or c_n_r[3] == dvb_type:
self.data_tmp = {}
self.data_tmp['GuideNumber']='%s' % c_n_r[0]
self.data_tmp['GuideName']='%s' % c_n_r[1]
self.data_tmp['URL']='http://%s:%d/%s' % (ip, port, c_n_r[2])
if "http" in c_n_r[2]:
self.data_tmp['URL'] = c_n_r[2]
else:
self.data_tmp['URL']='http://%s:%d/%s' % (ip, port, c_n_r[2])
self.lineup.append(self.data_tmp)
return self.lineup

Expand Down
2 changes: 1 addition & 1 deletion PlexDVRAPI/src/getLineupStatus.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from os import path, mkdir
from sys import modules

from . import tunerTypes, tunertypes, tunerfolders
from . import tunerTypes, tunertypes

lineup_status = {}

Expand Down
97 changes: 56 additions & 41 deletions PlexDVRAPI/src/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from Components.ActionMap import ActionMap
from Components.Button import Button
from Components.config import config, configfile, ConfigSubsection, ConfigSelection, getConfigListEntry
from Components.config import config, configfile, ConfigSubsection, ConfigSelection, getConfigListEntry, ConfigSelectionNumber
from Components.ConfigList import ConfigListScreen
from Components.Label import Label
from Components.Sources.StaticText import StaticText
Expand All @@ -26,6 +26,7 @@

config.plexdvrapi = ConfigSubsection()
config.plexdvrapi.bouquets_list = ConfigSelection(default = "all", choices = [('all', _('All'))] + getBouquetsList())
config.plexdvrapi.iptv_tunercount = ConfigSelectionNumber(min = 1, max = 10, stepwidth = 1, default = 2, wraparound = True)

BaseURL = {}
FriendlyName = {}
Expand All @@ -34,13 +35,20 @@
NoOfChannels = {}
choicelist = []

def TunerInfoDebug():
for type in tunerTypes:
def TunerInfoDebug(type=None):
if type:
print '[Plex DVR API] %s' % str(BaseURL[type]).replace('\n','')
print '[Plex DVR API] %s' % str(FriendlyName[type]).replace('\n','')
print '[Plex DVR API] %s' % str(Source[type]).replace('\n','')
print '[Plex DVR API] %s' % str(TunerCount[type]).replace('\n','')
print '[Plex DVR API] %s' % str(NoOfChannels[type]).replace('\n\n','')
else:
for type in tunerTypes:
print '[Plex DVR API] %s' % str(BaseURL[type]).replace('\n','')
print '[Plex DVR API] %s' % str(FriendlyName[type]).replace('\n','')
print '[Plex DVR API] %s' % str(Source[type]).replace('\n','')
print '[Plex DVR API] %s' % str(TunerCount[type]).replace('\n','')
print '[Plex DVR API] %s' % str(NoOfChannels[type]).replace('\n\n','')

def TunerInfo():
for type in tunerTypes:
Expand All @@ -56,13 +64,14 @@ def TunerInfo():
rmdir('/www/%s' % tunerfolders[type])

discover = getdeviceinfo.discoverdata(type)
noofchannels = getlineup.noofchannels(type)
BaseURL[type] = 'BaseURL: %s\n' % str(discover["BaseURL"])
FriendlyName[type] = 'FriendlyName: %s\n' % str(discover["FriendlyName"])
TunerCount[type] = 'TunerCount: %s\n' % str(getdeviceinfo.tunercount(type))
Source[type] = 'Source: %s\n' % str(tunerfolders[type]).title()
NoOfChannels[type] = 'Channels: %s\n\n' % str(getlineup.noofchannels(type))
TunerCount[type] = 'TunerCount: %s\n' % str(getdeviceinfo.tunercount(type)) if type != 'iptv' else 'TunerCount: %s\n' % str(config.plexdvrapi.iptv_tunercount.value)
Source[type] = 'Source: %s\n' % str(tunerfolders[type]).title() if type != 'iptv' else str(tunerfolders[type]).upper()
NoOfChannels[type] = 'Channels: %s\n\n' % str(noofchannels)

if getdeviceinfo.tunercount(type) > 0 and getlineup.noofchannels(type) > 0:
if getdeviceinfo.tunercount(type) > 0 and noofchannels > 0:
choicelist.append((type, str(tunerfolders[type]).title()))

TunerInfo()
Expand All @@ -79,19 +88,19 @@ def TunerInfo():

class PlexDVRAPI_Setup(ConfigListScreen, Screen):
skin="""
<screen position="center,center" size="600,325">
<widget name="config" position="10,10" size="580,50" scrollbarMode="showOnDemand" />
<widget name="TunerInfoLabel" position="10,70" size="580,185" font="Regular;22"/>
<widget name="HintLabel" position="10,200" size="580,75" font="Regular;22" valign="bottom"/>
<ePixmap pixmap="skin_default/buttons/red.png" position="0,285" size="140,40" alphatest="on"/>
<ePixmap pixmap="skin_default/buttons/green.png" position="150,285" size="140,40" alphatest="on"/>
<ePixmap pixmap="skin_default/buttons/yellow.png" position="300,285" size="140,40" alphatest="on"/>
<ePixmap pixmap="skin_default/buttons/blue.png" position="450,285" size="140,40" alphatest="on"/>
<widget name="key_red" position="0,285" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1"/>
<widget name="key_green" position="150,285" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1"/>
<widget name="key_yellow" position="300,285" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" transparent="1"/>
<widget name="key_blue" position="450,285" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" transparent="1"/>
</screen>"""
<screen position="center,center" size="600,350">
<widget name="config" position="10,10" size="580,75" scrollbarMode="showOnDemand" />
<widget name="TunerInfoLabel" position="10,95" size="580,185" font="Regular;22"/>
<widget name="HintLabel" position="10,225" size="580,75" font="Regular;22" valign="bottom"/>
<ePixmap pixmap="skin_default/buttons/red.png" position="0,305" size="140,40" alphatest="on"/>
<ePixmap pixmap="skin_default/buttons/green.png" position="150,310" size="140,40" alphatest="on"/>
<ePixmap pixmap="skin_default/buttons/yellow.png" position="300,310" size="140,40" alphatest="on"/>
<ePixmap pixmap="skin_default/buttons/blue.png" position="450,310" size="140,40" alphatest="on"/>
<widget name="key_red" position="0,310" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1"/>
<widget name="key_green" position="150,310" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1"/>
<widget name="key_yellow" position="300,310" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" transparent="1"/>
<widget name="key_blue" position="450,310" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" transparent="1"/>
</screen>"""

def __init__(self, session, menu_path=""):
instance = None
Expand Down Expand Up @@ -120,19 +129,14 @@ def __init__(self, session, menu_path=""):
title = _("Plex DVR API for Enigma2")
self.menu_path = ""
Screen.setTitle(self, title)
TunerInfo()
TunerInfoDebug()

self.savedval = config.plexdvrapi.type.value
self.changed = False
self.savedtypeval = config.plexdvrapi.type.value
config.plexdvrapi.bouquets_list.setChoices([('all', _('All'))] + getBouquetsList())

self.onChangedEntry = [ ]
self.list = []
self.list.append(getConfigListEntry(_('Tuner type to use.'), config.plexdvrapi.type))
self.list.append(getConfigListEntry(_('Bouquet to use.'), config.plexdvrapi.bouquets_list))
ConfigListScreen.__init__(self, self.list, session = session, on_change = self.populate)
self["config"].list = self.list
self["config"].l.setList(self.list)
ConfigListScreen.__init__(self, self.list, session = session, on_change = self.onChange)

self["TunerInfoLabel"] = Label()
self["HintLabel"] = Label()
Expand Down Expand Up @@ -167,22 +171,32 @@ def __init__(self, session, menu_path=""):
self["key_yellow"].hide()
self["key_blue"] = Button(_("About"))

assert PlexDVRAPI_Setup.instance is None, "class InfoBar is a singleton class and just one instance of this class is allowed!"
assert PlexDVRAPI_Setup.instance is None, "class is a singleton class and just one instance of this class is allowed!"
PlexDVRAPI_Setup.instance = self

self.onLayoutFinish.append(self.populate)
self.onClose.append(self.__onClose)

if not self.populate in self["config"].onSelectionChanged:
self["config"].onSelectionChanged.append(self.populate)

def __onClose(self):
PlexDVRAPI_Setup.instance = None

def about(self):
self.session.open(PlexDVRAPI_About, self.menu_path)

def onChange(self):
self.populate()

def createmenu(self):
self.list = []
self.list.append(getConfigListEntry(_('Tuner type to use.'), config.plexdvrapi.type))
self.list.append(getConfigListEntry(_('Bouquet to use.'), config.plexdvrapi.bouquets_list))
if config.plexdvrapi.type.value == 'iptv':
self.list.append(getConfigListEntry(_('Number of concurent streams.'), config.plexdvrapi.iptv_tunercount))
self["config"].list = self.list
self["config"].l.setList(self.list)

def populate(self, answer=None):
self.createmenu()
setup_exists = False
self["actions"].setEnabled(False)
self["key_red"].hide()
Expand All @@ -198,7 +212,10 @@ def populate(self, answer=None):
elif self["config"].getCurrent() is not None:
type = config.plexdvrapi.type.value
currentconfig = self["config"].getCurrent()[0]

TunerInfo()
TunerInfoDebug(type)

self.label = (BaseURL[type]+FriendlyName[type]+Source[type]+TunerCount[type]+NoOfChannels[type])

for types in tunerTypes:
Expand All @@ -221,9 +238,12 @@ def populate(self, answer=None):
if currentconfig == _('Tuner type to use.'):
self["HintLabel"].setText(_('Press OK to continue setting up this tuner or press LEFT / RIGHT to select a different tuner type.'))
self.hinttext = _('Press LEFT / RIGHT to select a different tuner type.')
else:
elif currentconfig == _('Bouquet to use.'):
self["HintLabel"].setText(_('Press OK to continue setting up this tuner or select a different tuner type.'))
self.hinttext = _('Press LEFT / RIGHT to select a different bouquet.')
else:
self["HintLabel"].setText(_('Press OK to continue setting up this tuner or select a different tuner type.'))
self.hinttext = _('Press LEFT / RIGHT to set number of concurent streams.')
self.hinttext = self.hinttext + '\n'+_('Press GREEN button to save your configuration.')
self["okaction"].setEnabled(True)
self["key_green"].setText(_("Save"))
Expand Down Expand Up @@ -266,7 +286,7 @@ def ok(self):


def keySave(self):
if self.savedval != config.plexdvrapi.type.value and path.exists('/etc/enigma2/%s.device' % self.savedval):
if self.savedtypeval != config.plexdvrapi.type.value and path.exists('/etc/enigma2/%s.device' % self.savedtypeval):
self.session.openWithCallback(self.saveconfirm, MessageBox,text = _("It seems you have already setup on another tuner, As Plex Server can only support one tuner type, to use this additional tuner type you will need to setup a 2nd Plex Server, do you want to continue creating the files?"), type = MessageBox.TYPE_YESNO)
else:
self.saveconfirm(True)
Expand All @@ -278,13 +298,13 @@ def saveconfirm(self, answer):
if not path.exists('/etc/enigma2/%s.discover' % type):
newsetup = True
print '[Plex DVR API] Creating files for %s' % type
if not path.exists('/etc/enigma2/%s.device' % self.savedval):
if not path.exists('/etc/enigma2/%s.device' % self.savedtypeval):
getdeviceinfo.write_device_xml(dvbtype=type)
config.plexdvrapi.type.save()
config.plexdvrapi.bouquets_list.save()
configfile.save()
getdeviceinfo.write_discover(dvbtype=type)
if self.savedval != config.plexdvrapi.type.value and path.exists('/etc/enigma2/%s.device' % self.savedval) or newsetup:
if self.savedtypeval != config.plexdvrapi.type.value and path.exists('/etc/enigma2/%s.device' % self.savedtypeval) or newsetup:
self.session.openWithCallback(self.rebootconfirm, MessageBox,text = _("Files created, Please restart enigma2 and then you should be able to add this STB to Plex DVR.\nDo you want to do this now ?"), type = MessageBox.TYPE_YESNO)
else:
self.close()
Expand All @@ -307,11 +327,6 @@ def keyCancel(self):
else:
self.close()

def updateTunerInfo(value):
PlexDVRAPI_Setup.instance.populate()
if not config.plexdvrapi.type.notifiers:
config.plexdvrapi.type.addNotifier(updateTunerInfo, initial_call = False)

def startssdp(dvbtype):
discover = getdeviceinfo.discoverdata(dvbtype)
device_uuid = discover['DeviceUUID']
Expand Down

0 comments on commit dbb7514

Please sign in to comment.