Skip to content

Commit

Permalink
v.0.5.1o
Browse files Browse the repository at this point in the history
  • Loading branch information
Lunatixz committed Aug 2, 2024
1 parent ada6143 commit e6daa05
Show file tree
Hide file tree
Showing 13 changed files with 89 additions and 70 deletions.
4 changes: 2 additions & 2 deletions addons.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addons>
<addon id="plugin.video.pseudotv.live" version="0.5.1n" name="PseudoTV Live" provider-name="Lunatixz">
<addon id="plugin.video.pseudotv.live" version="0.5.1o" name="PseudoTV Live" provider-name="Lunatixz">
<requires>
<import addon="xbmc.python" version="3.0.1"/>
<import addon="pvr.iptvsimple" version="21.8.0"/>
Expand Down Expand Up @@ -36,7 +36,7 @@
</item>
<item library="resources/lib/context_record.py" args="del">
<label>30117</label>
<visible>[String.Contains(ListItem.Plot,"item=") + ListItem.Path] + [Window.IsVisible(tvrecordings)]</visible>
<visible>[String.Contains(ListItem.PVRInstanceName, PseudoTV Live) + Window.IsVisible(tvrecordings)]</visible>
</item>
<item library="resources/lib/context_play.py" args="playlist">
<label>30076</label>
Expand Down
2 changes: 1 addition & 1 deletion addons.xml.md5
Original file line number Diff line number Diff line change
@@ -1 +1 @@
b43c120cefe2a9d3bca620be775e31ff
c685e20ea9f0579988b0588d94dbd75d
4 changes: 2 additions & 2 deletions plugin.video.pseudotv.live/addon.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<addon id="plugin.video.pseudotv.live" version="0.5.1n" name="PseudoTV Live" provider-name="Lunatixz">
<addon id="plugin.video.pseudotv.live" version="0.5.1o" name="PseudoTV Live" provider-name="Lunatixz">
<requires>
<import addon="xbmc.python" version="3.0.1"/>
<import addon="pvr.iptvsimple" version="21.8.0"/>
Expand Down Expand Up @@ -35,7 +35,7 @@
</item>
<item library="resources/lib/context_record.py" args="del">
<label>30117</label>
<visible>[String.Contains(ListItem.Plot,"item=") + ListItem.Path] + [Window.IsVisible(tvrecordings)]</visible>
<visible>[String.Contains(ListItem.PVRInstanceName, PseudoTV Live) + Window.IsVisible(tvrecordings)]</visible>
</item>
<item library="resources/lib/context_play.py" args="playlist">
<label>30076</label>
Expand Down
10 changes: 4 additions & 6 deletions plugin.video.pseudotv.live/changelog.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
v.0.5.2
-Notice The following steps are required!.
- Previous Channels and Backups may encouter issues, it is recommend, but not required users start clean.
- Open PseudoTV Live settings, under Miscellaneous; Click "Utility Menu" and select "Delete M3U/XMLTV".
- Open Kodi settings, under PVR & Live TV; Click "clear data" and select "All".

-Fixed Playlist Playback not progressing.
-Fixed Recordings Persistence issues.
-Added Automatic Recordings Cleanup to settings.
-Improved Channel Manager logo utility.
-Imporved Channel Manager path utility.
-Improved Channel Manager path utility.
-Improved Channel Content parsing.
-Added Advanced Channel Rules UI to Channel Manager.
-Added Dynamic Smartplaylist builder to path selections.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -613,8 +613,9 @@ msgctxt "#30148"
msgid "Select Interval in minutes. [-1 = Indefinitely, 0 = Randomly]"
msgstr ""



msgctxt "#30149"
msgid "Enable Automatic PVR Cleaning"
msgstr ""

# Skin - strings 31000 thru 31999 reserved for skins

Expand Down Expand Up @@ -1595,6 +1596,10 @@ msgctxt "#33140"
msgid "Create Channel & Genre subfolders in template resource pack."
msgstr ""

msgctxt "#33148"
msgid "Remove PseudoTV Live recordings when media path no longer exists."
msgstr ""

msgctxt "#33149"
msgid "Override Low-Power background pausing and performance throttling."
msgstr ""
Expand Down
16 changes: 10 additions & 6 deletions plugin.video.pseudotv.live/resources/lib/context_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,28 @@ def __init__(self, sysARG: dict={}, listitem: xbmcgui.ListItem=xbmcgui.ListItem(

def add(self):
self.fitem['label'] = (self.fitem.get('label') or self.listitem.getLabel())
m3u = M3U()
m3u = M3U()
xmltv = XMLTVS()
ritem = m3u.getRecordItem(self.fitem)
if DIALOG.yesnoDialog('Would you like to add:\n[B]%s[/B]\nto recordings?'%(ritem['label'])):
with busy_dialog(), suspendActivity():
if (m3u.addRecording(ritem) & XMLTVS().addRecording(ritem,self.fitem)):
DIALOG.notificationWait('%s\n%s'%(ritem['label'],LANGUAGE(30116)),wait=2)
if (m3u.addRecording(ritem), xmltv.addRecording(ritem,self.fitem)):
DIALOG.notificationWait('%s\n%s'%(ritem['label'],LANGUAGE(30116)))
del m3u
del xmltv


def remove(self):
self.fitem['label'] = (self.fitem.get('label') or self.listitem.getLabel())
m3u = M3U()
m3u = M3U()
xmltv = XMLTVS()
ritem = m3u.getRecordItem(self.fitem)
if DIALOG.yesnoDialog('Would you like to remove:\n[B]%s[/B]\nfrom recordings?'%(ritem['label'])):
with busy_dialog(), suspendActivity():
if (m3u.delRecording(ritem) & XMLTVS().delRecording(ritem)):
DIALOG.notificationWait('%s\n%s'%(ritem['label'],LANGUAGE(30118)),wait=2)
if (m3u.delRecording(ritem), xmltv.delRecording(ritem)):
DIALOG.notificationWait('%s\n%s'%(ritem['label'],LANGUAGE(30118)))
del m3u
del xmltv


if __name__ == '__main__':
Expand Down
10 changes: 4 additions & 6 deletions plugin.video.pseudotv.live/resources/lib/fileaccess.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ def delete(filename):
@staticmethod
def exists(filename):
if filename.startswith('stack://'):
try: filename = (filename.split('stack://')[1].split(' , '))[0]
except: pass
try: filename = (filename.split('stack://')[1].split(' , '))[0]
except Exception as e: log('FileAccess: exists failed! %s'%(e), xbmc.LOGERROR)
try:
return xbmcvfs.exists(filename)
except UnicodeDecodeError:
Expand All @@ -91,10 +91,8 @@ def openSMB(filename, mode, encoding=DEFAULT_ENCODING):
fle = 0
if os.name.lower() == 'nt':
newname = '\\\\' + filename[6:]
try:
fle = codecs.open(newname, mode, encoding)
except:
fle = 0
try: fle = codecs.open(newname, mode, encoding)
except: fle = 0
return fle


Expand Down
15 changes: 9 additions & 6 deletions plugin.video.pseudotv.live/resources/lib/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,7 @@ def stripRegion(s):
try:
match = re.compile('(.*) \((.*)\)', re.IGNORECASE).search(s)
if match.group(1): return match.group(1)
except: pass
return s
except: return s

def chanceBool(percent=25):
return random.randrange(100) <= percent
Expand All @@ -125,7 +124,8 @@ def decodeString(base64_bytes):
try:
message_bytes = zlib.decompress(base64.b64decode(base64_bytes.encode(DEFAULT_ENCODING)))
return message_bytes.decode(DEFAULT_ENCODING)
except:
except Exception as e:
log('Globals: decodeString failed! %s'%(e), xbmc.LOGERROR)
return ''

def decodePlot(text: str = '') -> dict:
Expand All @@ -144,8 +144,11 @@ def unescapeString(text, table=HTML_ESCAPE):

def getJSON(file):
fle = (FileAccess.open(file, 'r') or '')
try: data = loadJSON(fle.read())
except: data = {}
try:
data = loadJSON(fle.read())
except Exception as e:
log('Globals: getJSON failed! %s'%(e), xbmc.LOGERROR)
data = {}
fle.close()
return data

Expand Down Expand Up @@ -589,7 +592,7 @@ def getIDbyPath(path):
try:
if path.startswith('special://'): return re.compile('special://home/addons/(.*?)/resources', re.IGNORECASE).search(path).group(1)
elif path.startswith('plugin://'): return re.compile('plugin://(.*?)/', re.IGNORECASE).search(path).group(1)
except: pass
except Exception as e: log('Globals: getIDbyPath failed! %s'%(e), xbmc.LOGERROR)
return path

def mergeDictLST(dict1,dict2):
Expand Down
42 changes: 21 additions & 21 deletions plugin.video.pseudotv.live/resources/lib/m3u.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,6 @@ def log(self, msg, level=xbmc.LOGDEBUG):
return log('%s: %s'%(self.__class__.__name__,msg),level)


def _verify(self, stations=[], recordings=[], chkPath=True):
if stations: #remove abandoned m3u entries; Stations that are not found in the channel list
stations = [station for station in stations for channel in Channels().getChannels() if channel.get('id') == station.get('id',str(random.random()))]
self.log('_verify, stations = %s'%(len(stations)))
return stations
elif recordings:#remove recordings that no longer exists on disk
if chkPath: recordings = [recording for recording in recordings if hasFile(decodeString(dict(urllib.parse.parse_qsl(recording.get('url','').replace('.pvr',''))).get("id",'')))]
else: recordings = [recording for recording in recordings if recording.get('media',False)]
self.log('_verify, recordings = %s'%(len(recordings)))
return recordings
return []


def _load(self, file=M3UFLEPATH):
self.log('_load, file = %s'%file)
if file.startswith('http'):
Expand Down Expand Up @@ -216,10 +203,10 @@ def _save(self, file=M3UFLEPATH):
mins = [opts.pop(opts.index(key)) for key in list(M3U_MIN.keys()) if key in opts] #min required m3u entries.
line = '#EXTINF:-1 tvg-chno="%s" tvg-id="%s" tvg-name="%s" tvg-logo="%s" group-title="%s" radio="%s" catchup="%s" %s,%s\n'
self.M3UDATA['stations'] = self.sortStations(self.M3UDATA.get('stations',[]))
self.M3UDATA['recordings'] = self.sortStations(self.M3UDATA.get('recordings',[]))
self.M3UDATA['recordings'] = self.sortStations(self.M3UDATA.get('recordings',[]), key='name')
self.log('_save, saving %s stations and %s recordings to %s'%(len(self.M3UDATA['stations']),len(self.M3UDATA['recordings']),file))

for station in self.M3UDATA['recordings'] + self.M3UDATA['stations']:
for station in (self.M3UDATA['recordings'] + self.M3UDATA['stations']):
optional = ''
xplaylist = ''
kodiprops = {}
Expand Down Expand Up @@ -257,16 +244,29 @@ def _reload(self):
return True


def _verify(self, stations=[], recordings=[], chkPath=SETTINGS.getSettingBool('Clean_Recordings')):
if stations: #remove abandoned m3u entries; Stations that are not found in the channel list
stations = [station for station in stations for channel in Channels().getChannels() if channel.get('id') == station.get('id',str(random.random()))]
self.log('_verify, stations = %s'%(len(stations)))
return stations
elif recordings:#remove recordings that no longer exists on disk
if chkPath: recordings = [recording for recording in recordings if hasFile(decodeString(dict(urllib.parse.parse_qsl(recording.get('url',''))).get('vid').replace('.pvr','')))]
else: recordings = [recording for recording in recordings if recording.get('media',False)]
self.log('_verify, recordings = %s'%(len(recordings)))
return recordings
return []


def cleanSelf(self, items, key='id', slug='@%s'%(slugify(ADDON_NAME))): # remove imports (Non PseudoTV Live)
if not slug: return items
stations = self._verify(stations=[station for station in items if station.get(key,'').endswith(slug) and not station.get('media',False)])
recordings = self._verify(recordings=[recording for recording in items if recording.get(key,'').endswith(slug) and recording.get('media',False)])
stations = self.sortStations(self._verify(stations=[station for station in items if station.get(key,'').endswith(slug) and not station.get('media',False)]))
recordings = self.sortStations(self._verify(recordings=[recording for recording in items if recording.get(key,'').endswith(slug) and recording.get('media',False)]), key='name')
self.log('cleanSelf, slug = %s, key = %s: returning: stations = %s, recordings = %s'%(slug,key,len(stations),len(recordings)))
return self.sortStations(stations), recordings
return stations, recordings


def sortStations(self, stations):
return sorted(stations, key=lambda k: k['number'])
def sortStations(self, stations, key='number'):
return sorted(stations, key=lambda k: k.get(key))


def getMitem(self):
Expand All @@ -285,7 +285,7 @@ def getStations(self):


def getRecordings(self):
recordings = self.sortStations(self.M3UDATA.get('recordings',[]))
recordings = self.sortStations(self.M3UDATA.get('recordings',[]), key='name')
self.log('getRecordings, recordings = %s'%(len(recordings)))
return recordings

Expand Down
25 changes: 13 additions & 12 deletions plugin.video.pseudotv.live/resources/lib/xmltvs.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,13 @@ def _save(self, file: str=XMLTVFLEPATH, reset: bool=True) -> bool:
generator_info_url = self.cleanString(data['generator-info-url']),
generator_info_name = self.cleanString(data['generator-info-name']))

programmes = self.sortProgrammes(self.XMLTVDATA['programmes'])
for channel in self.sortChannels(self.cleanChannels(self.XMLTVDATA['recordings'] + self.XMLTVDATA['channels'], programmes)):
self.XMLTVDATA['programmes'] = self.sortProgrammes(self.XMLTVDATA['programmes'])
self.XMLTVDATA['channels'] = self.cleanChannels(self.sortChannels(self.XMLTVDATA['channels']) , self.XMLTVDATA['programmes'])
self.XMLTVDATA['recordings'] = self.cleanChannels(self.sortChannels(self.XMLTVDATA['recordings']), self.XMLTVDATA['programmes'])

for channel in (self.XMLTVDATA['recordings'] + self.XMLTVDATA['channels']):
writer.addChannel(channel)
for program in programmes:
for program in self.XMLTVDATA['programmes']:
writer.addProgramme(program)

try:
Expand Down Expand Up @@ -149,7 +152,7 @@ def loadStopTimes(self, channels: list=[], programmes: list=[], fallback=None):

def getRecordings(self) -> list:
self.log('getRecordings')
return self.sortChannels(self.XMLTVDATA.get('recordings',[]))
return self.sortChannels(self.XMLTVDATA.get('recordings',[]))


def getChannels(self) -> list:
Expand Down Expand Up @@ -219,14 +222,12 @@ def filterHoliday(program):


def sortChannels(self, channels: list) -> list:
try: channels.sort(key=lambda x:x.get('display-name'))
except: pass
return channels
return sorted(channels, key=lambda k:k.get('display-name'))


def sortProgrammes(self, programmes: list) -> list:
programmes.sort(key=lambda x:x['start'])
programmes.sort(key=lambda x:x['channel'])
programmes.sort(key=lambda k:k.get('start'))
programmes.sort(key=lambda k:k.get('channel'))
self.log('sortProgrammes, programmes = %s'%(len(programmes)))
return programmes

Expand Down Expand Up @@ -264,7 +265,7 @@ def addRecording(self, ritem: dict, fitem: dict):

fitem['start'] = getUTCstamp()
fitem['stop'] = fitem['start'] + fitem['duration']
if self.addProgram(ritem['id'],self.getProgramItem(ritem,fitem)):
if self.addProgram(ritem['id'],self.getProgramItem(ritem,fitem),encodeDESC=False):
return self._save()


Expand All @@ -282,11 +283,11 @@ def addChannel(self, citem: dict) -> bool:
return True


def addProgram(self, id: str, item: dict) -> bool:
def addProgram(self, id: str, item: dict, encodeDESC: bool=True) -> bool:
pitem = {'channel' : id,
'category' : [(self.cleanString(genre.replace('Unknown','Undefined')),LANG) for genre in item['categories']],
'title' : [(self.cleanString(item['title']), LANG)],
'desc' : [(encodePlot(self.cleanString(item['desc']),item['fitem']), LANG)],
'desc' : [(encodePlot(self.cleanString(item['desc']),item['fitem']), LANG) if encodeDESC else (self.cleanString(item['desc']), LANG)],
'stop' : (datetime.datetime.fromtimestamp(float(item['stop'])).strftime(DTFORMAT)),
'start' : (datetime.datetime.fromtimestamp(float(item['start'])).strftime(DTFORMAT)),
'icon' : [{'src': item['thumb']}],
Expand Down
12 changes: 6 additions & 6 deletions plugin.video.pseudotv.live/resources/lib/xsp.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,16 @@ def parseXSP(self, path: str, media: str='video', sort: dict={}, filter: dict={}
xml.close()

try: media = 'music' if dom.getElementsByTagName('smartplaylist')[0].attributes['type'].value.lower() in MUSIC_TYPES else 'video'
except Exception as e: self.log("parseXSP, parsing failed! %s"%(e), xbmc.LOGDEBUG)
except Exception as e: self.log("parseXSP, parsing media failed! %s"%(e), xbmc.LOGDEBUG)

try: limit = dom.getElementsByTagName('limit')[0].childNodes[0].nodeValue
except Exception as e: self.log("parseXSP, parsing failed! %s"%(e), xbmc.LOGDEBUG)
except Exception as e: self.log("parseXSP, parsing limit failed! %s"%(e), xbmc.LOGDEBUG)

try: sort.update({"method":dom.getElementsByTagName('order')[0].childNodes[0].nodeValue.lower()}) #todo pop rules to filter var.
except Exception as e: self.log("parseXSP, parsing failed! %s"%(e), xbmc.LOGDEBUG)
except Exception as e: self.log("parseXSP, parsing method failed! %s"%(e), xbmc.LOGDEBUG)

try: sort.update({"order":dom.getElementsByTagName('order')[0].getAttribute('direction').lower()})#todo pop rules to filter var.
except Exception as e: self.log("parseXSP, parsing failed! %s"%(e), xbmc.LOGDEBUG)
except Exception as e: self.log("parseXSP, parsing order failed! %s"%(e), xbmc.LOGDEBUG)

try:
type = dom.getElementsByTagName('smartplaylist')[0].attributes['type'].value
Expand All @@ -101,9 +101,9 @@ def parseXSP(self, path: str, media: str='video', sort: dict={}, filter: dict={}
elif rule.getAttribute('field').lower() in ['playlist','virtualfolder'] and rule.getAttribute('operator').lower() in ['is','contains']:
paths.extend(self.findXSP(rule.getElementsByTagName("value")[0].childNodes[0].data))
except Exception as e:
self.log("parseXSP, parsing failed! %s"%(e), xbmc.LOGDEBUG)
self.log("parseXSP, parsing paths failed! %s"%(e), xbmc.LOGDEBUG)
type = ''

self.log("parseXSP, type = %s, paths = %s, media = %s, sort = %s, filter = %s, limit = %s"%(type, paths, media, sort, filter, limit))
except Exception as e: self.log("parseXSP, failed! %s"%(e), xbmc.LOGERROR)
return paths, media, sort, filter, limit
Expand Down
10 changes: 10 additions & 0 deletions plugin.video.pseudotv.live/resources/settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,16 @@
</constraints>
<control type="spinner" format="string"/>
</setting>
<setting id="Clean_Recordings" type="boolean" label="30149" help="33148">
<level>2</level>
<default>true</default>
<control type="toggle"/>
<dependencies>
<dependency type="visible">
<condition operator="is" setting="Client_Mode">0</condition>
</dependency>
</dependencies>
</setting>
<setting id="Enable_Grouping" type="boolean" label="30063" help="33063">
<level>1</level>
<default>true</default>
Expand Down
Binary file not shown.

0 comments on commit e6daa05

Please sign in to comment.