Skip to content

Commit

Permalink
v.0.4.8k
Browse files Browse the repository at this point in the history
  • Loading branch information
Lunatixz committed Apr 8, 2024
1 parent dacbaf8 commit 948956e
Show file tree
Hide file tree
Showing 19 changed files with 171 additions and 242 deletions.
2 changes: 1 addition & 1 deletion 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.4.8j" name="PseudoTV Live" provider-name="Lunatixz">
<addon id="plugin.video.pseudotv.live" version="0.4.8k" 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
2 changes: 1 addition & 1 deletion addons.xml.md5
Original file line number Diff line number Diff line change
@@ -1 +1 @@
5b1e7d92f4bf27fa15b381429b4041bf
176a670e00bc0f5e8654c223c915a4bf
2 changes: 1 addition & 1 deletion 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.4.8j" name="PseudoTV Live" provider-name="Lunatixz">
<addon id="plugin.video.pseudotv.live" version="0.4.8k" 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
Original file line number Diff line number Diff line change
Expand Up @@ -1233,6 +1233,11 @@ msgctxt "#32165"
msgid "No Changes Applied!
msgstr ""

msgctxt "#32166"
msgid "Playback Unavailable! Guide data is outdated.\n%s will refresh in under % minutes"
msgstr ""

msgc

# Help - strings 33000 thru 33999 reserved for common strings used in add-ons

Expand Down Expand Up @@ -1317,7 +1322,7 @@ msgid "Clear all Pre-defined selections and remove their channels."
msgstr ""

msgctxt "#33041"
msgid "Global rules apply to all channels, Advanced channel manager rules apply per channel. *see readme for details.\nGlobals are the default setting for every channel regardless of its Advanced Rules."
msgid "Global rules apply to all channels, Advanced channel manager rules apply per channel. *see readme for details.\nGlobals are the default setting for every channel."
msgstr ""

msgctxt "#33042"
Expand Down Expand Up @@ -1465,7 +1470,7 @@ msgid "Create dynamic path"
msgstr ""

msgctxt "#33121"
msgid "Group TV shows in blocks of x. [0 Disabled, Two Shows Default] (Pagination size influences grouping).\nChanges will propagate slowly over time and are not instantaneous."
msgid "Group TV shows in blocks of x. [0 Disabled, 2 Default] (Pagination size influences grouping).\nChanges will propagate slowly over time and are not instantaneous."
msgstr ""

msgctxt "#33129"
Expand Down
5 changes: 2 additions & 3 deletions plugin.video.pseudotv.live/resources/lib/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,7 @@ def _validFileList(fileArray):
self.runActions(RULES_ACTION_CHANNEL_BUILD_START, citem, inherited=self)
for file in citem['path']:
if self.service._interrupt() or self.service._suspend(): break
else:
fileArray.append(self.buildFileList(citem, self.runActions(RULES_ACTION_CHANNEL_BUILD_PATH, citem, file, inherited=self), 'video', self.limit, self.sort, self.limits))
else: fileArray.append(self.buildFileList(citem, self.runActions(RULES_ACTION_CHANNEL_BUILD_PATH, citem, file, inherited=self), 'video', self.limit, self.sort, self.limits))

self.runActions(RULES_ACTION_CHANNEL_BUILD_STOP, citem, inherited=self)
fileArray = self.runActions(RULES_ACTION_CHANNEL_BUILD_FILELIST_PRE, citem, fileArray, inherited=self) #Primary rule for handling adv. interleaving, must return single list to avoid default interleave() below. Add avd. rule to setDictLST duplicates.
Expand Down Expand Up @@ -419,7 +418,7 @@ def buildList(self, citem, path, media='video', page=SETTINGS.getSettingInt('Pag
if len(dirList) > 0: dirList = randomShuffle(dirList)
if len(fileList) > 0: fileList = randomShuffle(fileList)

self.setTrailers(trailerslist)
if len(trailerslist) > 0: self.setTrailers(trailerslist)
self.log("buildList, id: %s returning (%s) files, (%s) dirs."%(citem['id'],len(fileList),len(dirList)))
return fileList, dirList

Expand Down
1 change: 0 additions & 1 deletion plugin.video.pseudotv.live/resources/lib/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@
RULES_ACTION_CHANNEL_BUILD_STOP = 9
RULES_ACTION_CHANNEL_STOP = 10
##player
RULES_ACTION_PLAYBACK = 11
RULES_ACTION_PLAYER_START = 12
RULES_ACTION_PLAYER_CHANGE = 13
RULES_ACTION_PLAYER_STOP = 14
Expand Down
6 changes: 4 additions & 2 deletions plugin.video.pseudotv.live/resources/lib/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,16 @@ def run(sysARG):
vid = decodeString(params.get("vid",'') or None)
radio = (params.get("radio",'') or 'False').lower() == "true"
log("Default: run, params = %s"%(params))

if mode == 'guide':
hasAddon(PVR_CLIENT_ID,install=True,enable=True)
BUILTIN.executebuiltin("Dialog.Close(all)")
BUILTIN.executebuiltin("ReplaceWindow(TVGuide,pvr://channels/tv/%s)"%(quoteString(ADDON_NAME)))
elif mode == 'settings':
hasAddon(PVR_CLIENT_ID,install=True,enable=True)
BUILTIN.executebuiltin('Addon.OpenSettings(%s)'%ADDON_ID)
SETTINGS.openSettings()
elif chid and not vid:
return DIALOG.notificationDialog(LANGUAGE(32166)%(PVR_CLIENT_NAME,IPTV_SIMPLE_SETTINGS().get('m3uRefreshIntervalMins')))
elif mode == 'vod':
threadit(Plugin(sysARG).playVOD)(title,vid)
elif mode == 'live':
Expand Down
94 changes: 34 additions & 60 deletions plugin.video.pseudotv.live/resources/lib/fillers.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,20 @@ def __init__(self, builder):
self.jsonRPC = builder.jsonRPC
self.runActions = builder.runActions
self.resources = Resources(self.jsonRPC,self.cache)

self.bctTypes = {"ratings" :{"max":1 ,"auto":builder.incRatings == 1,"enabled":bool(builder.incRatings),"sources":builder.srcRatings,"items":{}},
"bumpers" :{"max":1 ,"auto":builder.incBumpers == 1,"enabled":bool(builder.incBumpers),"sources":builder.srcBumpers,"items":{}},
"adverts" :{"max":builder.incAdverts,"auto":builder.incAdverts == 1,"enabled":bool(builder.incAdverts),"sources":builder.srcAdverts,"items":{}},
"trailers" :{"max":builder.incTrailer,"auto":builder.incTrailer == 1,"enabled":bool(builder.incTrailer),"sources":builder.srcTrailer,"items":{}}}
self.fillSources()
print('bctTypes',self.bctTypes)


def log(self, msg, level=xbmc.LOGDEBUG):
return log('%s: %s'%(self.__class__.__name__,msg),level)


def fillSources(self):
if self.bctTypes['trailers']['enabled'] and SETTINGS.getSettingInt('Include_Trailers') < 2:
if self.bctTypes['trailers'].get('enabled',False) and SETTINGS.getSettingInt('Include_Trailers') < 2:
self.bctTypes['trailers']['items'] = mergeDictLST(self.bctTypes['trailers']['items'],self.builder.getTrailers())

for ftype, values in self.bctTypes.items():
Expand Down Expand Up @@ -81,31 +81,15 @@ def _parseResource(path):
if not hasAddon(path, install=True): return {}
return self.resources.walkResource(path,exts=VIDEO_EXTS)

def _rating(data):
def _sortbyfile(data):
tmpDCT = {}
for path, files in data.items():
for file in files:
dur = self.jsonRPC.getDuration(os.path.join(path,file), accurate=True)
if dur > 0: tmpDCT.setdefault(file.split('.')[0].lower(),[]).append({'file':os.path.join(path,file),'duration':dur,'label':file.split('.')[0]})
return tmpDCT

def _bumper(data):
tmpDCT = {}
for path, files in data.items():
for file in files:
dur = self.jsonRPC.getDuration(os.path.join(path,file), accurate=True)
if dur > 0: tmpDCT.setdefault(os.path.basename(path).lower(),[]).append({'file':os.path.join(path,file),'duration':dur,'label':os.path.basename(path)})
return tmpDCT

def _advert(data):
tmpDCT = {}
for path, files in data.items():
for file in files:
dur = self.jsonRPC.getDuration(os.path.join(path,file), accurate=True)
if dur > 0: tmpDCT.setdefault(os.path.basename(path).lower(),[]).append({'file':os.path.join(path,file),'duration':dur,'label':os.path.basename(path)})
return tmpDCT

def _trailer(data):

def _sortbyfolder(data):
tmpDCT = {}
for path, files in data.items():
for file in files:
Expand All @@ -114,11 +98,11 @@ def _trailer(data):
return tmpDCT

if path.startswith('plugin://'): return _parseVFS(path)
if ftype == 'ratings': return _rating(_parseResource(path))
elif ftype == 'bumpers': return _bumper(_parseResource(path))
elif ftype == 'adverts': return _advert(_parseResource(path))
elif ftype == 'trailers': return _trailer(_parseResource(path))
return {}
if ftype == 'ratings': return _sortbyfile(_parseResource(path))
elif ftype == 'bumpers': return _sortbyfolder(_parseResource(path))
elif ftype == 'adverts': return _sortbyfolder(_parseResource(path))
elif ftype == 'trailers': return _sortbyfolder(_parseResource(path))
else: return {}


def convertMPAA(self, ompaa):
Expand All @@ -132,38 +116,22 @@ def convertMPAA(self, ompaa):
return mpaa, tmpLST


def getRating(self, keys=[]):
def getSingle(self, type, keys=['resources']):
try:
tmpLST = []
for key in keys: tmpLST.extend(self.bctTypes['ratings'].get('items',{}).get(key.lower(),[]))
for key in keys: tmpLST.extend(self.bctTypes.get(type,{}).get('items',{}).get(key.lower(),[]))
return random.choice(tmpLST)
except: return {}


def getBumper(self, keys=['resources']):
try:
tmpLST = []
for key in keys: tmpLST.extend(self.bctTypes['bumpers'].get('items',{}).get(key.lower(),[]))
return random.choice(tmpLST)
except: return {}


def getAdverts(self, keys=['resources'], count=1):
def getMulti(self, type, keys=['resources'], count=1):
try:
tmpLST = []
for key in keys: tmpLST.extend(self.bctTypes['adverts'].get('items',{}).get(key.lower(),[]))
for key in keys: tmpLST.extend(self.bctTypes.get(type,{}).get('items',{}).get(key.lower(),[]))
return setDictLST(random.choices(tmpLST,k=count))
except: return []


def getTrailers(self, keys=['resources'], count=1):
try:
tmpLST = []
for key in keys: tmpLST.extend(self.bctTypes['trailers'].get('items',{}).get(key.lower(),[]))
return setDictLST(random.choices(tmpLST,k=count))
except: return []


def injectBCTs(self, citem, fileList):
nfileList = []
for idx, fileItem in enumerate(fileList):
Expand All @@ -176,17 +144,26 @@ def injectBCTs(self, citem, fileList):
chtype = citem.get('type','')
chname = citem.get('name','')
fitem = fileItem.copy()
# nitem = fileList[idx + 1].copy()
ftype = fileItem.get('type','')
fgenre = (fileItem.get('genre') or citem.get('group') or '')
if isinstance(fgenre,list) and len(fgenre) > 0: fgenre = fgenre[0]


addRatings = self.bctTypes['ratings'].get('enabled',False)
addBumpers = self.bctTypes['bumpers'].get('enabled',False)
addAdverts = self.bctTypes['adverts'].get('enabled',False)
addTrailers = self.bctTypes['trailers'].get('enabled',False)

cntAdverts = PAGE_LIMIT if self.bctTypes['adverts']['auto'] else self.bctTypes['adverts']['max']
cntTrailers = PAGE_LIMIT if self.bctTypes['trailers']['auto'] else self.bctTypes['trailers']['max']

#pre roll - bumpers
if self.bctTypes['bumpers']['enabled']:
if addBumpers:
# #todo movie bumpers for audio/video codecs? imax bumpers?
if ftype.startswith(tuple(TV_TYPES)):
if chtype in ['Playlists','TV Networks','TV Genres','Mixed Genres','Custom']:
bkeys = ['resources',chname, fgenre] if chanceBool(SETTINGS.getSettingInt('Random_Bumper_Chance')) else [chname, fgenre]
bitem = self.getBumper(bkeys)
bitem = self.getSingle('bumper',bkeys)
if bitem.get('file') and bitem.get('duration',0) > 0:
runtime += bitem.get('duration')
self.log('injectBCTs, adding bumper %s - %s'%(bitem.get('file'),bitem.get('duration')))
Expand All @@ -195,10 +172,10 @@ def injectBCTs(self, citem, fileList):
nfileList.append(self.builder.buildCells(citem,bitem.get('duration'),entries=1,info=item)[0])

#pre roll - ratings
if self.bctTypes['ratings']['enabled']:
if addRatings:
if ftype.startswith(tuple(MOVIE_TYPES)):
mpaa, rkeys = self.convertMPAA(fileItem.get('mpaa','NR'))
ritem = self.getRating(rkeys)
ritem = self.getSingle('rating',rkeys)
if ritem.get('file') and ritem.get('duration',0) > 0:
runtime += ritem.get('duration')
self.log('injectBCTs, adding rating %s - %s'%(ritem.get('file'),ritem.get('duration')))
Expand All @@ -212,18 +189,17 @@ def injectBCTs(self, citem, fileList):

# post roll - commercials
pfileList = []
pfillRuntime = roundRuntimeUP(runtime)
pfillRuntime = ((runtime *60) // 900) * 60 #time between nears half hour for auto fill.
pchance = (chanceBool(SETTINGS.getSettingInt('Random_Advert_Chance')) | chanceBool(SETTINGS.getSettingInt('Random_Trailers_Chance')))

self.log('injectBCTs, post roll current runtime %s, available runtime %s'%(runtime, pfillRuntime))
if self.bctTypes['adverts']['enabled']:
acnt = PAGE_LIMIT if self.bctTypes['adverts']['auto'] else self.bctTypes['adverts']['max']
afillRuntime = (pfillRuntime // 2) if self.bctTypes['trailers']['enabled'] else pfillRuntime #if trailers enabled only fill half the required space, leaving room for trailers.
if addAdverts:
afillRuntime = (pfillRuntime // 2) if addTrailers else pfillRuntime #if trailers enabled only fill half the required space, leaving room for trailers.
pfillRuntime -= afillRuntime
self.log('injectBCTs, advert fill runtime %s'%(afillRuntime))
if chtype in ['Playlists','TV Networks','TV Genres','Mixed Genres','Custom']:
akeys = ['resources',chname, fgenre] if pchance else [chname, fgenre]
for aitem in self.getAdverts(akeys, acnt):
for aitem in self.getMulti('adverts',akeys, cntAdverts):
if aitem.get('file') and aitem.get('duration',0) > 0:
if afillRuntime <= 0: break
afillRuntime -= aitem.get('duration')
Expand All @@ -233,13 +209,11 @@ def injectBCTs(self, citem, fileList):
pfileList.append(self.builder.buildCells(citem,aitem.get('duration'),entries=1,info=item)[0])

# post roll - trailers
if self.bctTypes['trailers']['enabled']:
self.log('injectBCTs, trailers fill runtime %s'%(pfillRuntime))
tcnt = PAGE_LIMIT if self.bctTypes['trailers']['auto'] else self.bctTypes['trailers']['max']
if addTrailers:
self.log('injectBCTs, trailers fill runtime %s'%(pfillRuntime))
if chtype in ['Playlists','TV Networks','TV Genres','Movie Genres','Movie Studios','Mixed Genres','Custom']:
tkeys = ['resources',chname, fgenre] if pchance else [chname, fgenre]
for titem in self.getTrailers(tkeys, tcnt):
for titem in self.getMulti('trailers',tkeys, cntTrailers):
if titem.get('file') and titem.get('duration',0) > 0:
if pfillRuntime <= 0: break
pfillRuntime -= titem.get('duration')
Expand Down
8 changes: 1 addition & 7 deletions plugin.video.pseudotv.live/resources/lib/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,13 +281,7 @@ def openAddonSettings(ctl=(0,1),id=ADDON_ID):
def pagination(list, end):
for start in range(0, len(list), end):
yield seq[start:start+end]

def roundRuntimeUP(runtime):
runtime = datetime.datetime.fromtimestamp(runtime)
next_half_hour = runtime.replace(minute=0, second=0) + datetime.timedelta(minutes=30)
if next_half_hour < runtime: next_half_hour += datetime.timedelta(days=1)
return (next_half_hour - runtime).total_seconds()


def roundTimeDown(dt, offset=30): # round the given time down to the nearest
n = datetime.datetime.fromtimestamp(dt)
delta = datetime.timedelta(minutes=offset)
Expand Down
6 changes: 3 additions & 3 deletions plugin.video.pseudotv.live/resources/lib/jsonrpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,11 +299,11 @@ def parseYoutubeRuntime(self, id):

def getDuration(self, path, item={}, accurate=bool(SETTINGS.getSettingInt('Duration_Type'))):
self.log("getDuration, accurate = %s, path = %s" % (accurate, path))
runtime = (item.get('runtime') or item.get('duration') or (item.get('streamdetails', {}).get('video',[{}])[0].get('duration',0)))
if not runtime and path.startswith('plugin://plugin.video.youtube/play/?video_id='):
runtime = (item.get('runtime') or item.get('duration') or item.get('streamdetails', {}).get('video',[{}])[0].get('duration') or 0)
if not runtime and path.startswith(('plugin://plugin.video.youtube','plugin://plugin.video.tubed','plugin://plugin.video.invidious')):
try: runtime = self.parseYoutubeRuntime(path.split('?video_id=')[1])
except: runtime = 0

if (runtime == 0 or accurate):
if not path.startswith(tuple(VFS_TYPES)):# no additional parsing needed item[runtime] has only meta available.
duration = 0
Expand Down
8 changes: 6 additions & 2 deletions plugin.video.pseudotv.live/resources/lib/kodi.py
Original file line number Diff line number Diff line change
Expand Up @@ -1060,7 +1060,9 @@ def progressBGDialog(self, percent=0, control=None, message='', header=ADDON_NAM
control.create(header, message)
elif control:
if int(percent) == 100 or control.isFinished():
if hasattr(control, 'close'): control.close()
if hasattr(control, 'close'):
control.close()
return None
elif hasattr(control, 'update'): control.update(int(percent), header, message)
if wait: MONITOR.waitForAbort(wait/1000)
return control
Expand All @@ -1072,7 +1074,9 @@ def progressDialog(self, percent=0, control=None, message='', header=ADDON_NAME)
control.create(header, message)
elif control:
if int(percent) == 100 or control.iscanceled():
if hasattr(control, 'close'): control.close()
if hasattr(control, 'close'):
control.close()
return None
elif hasattr(control, 'update'): control.update(int(percent), message)
return control

Expand Down
Loading

0 comments on commit 948956e

Please sign in to comment.