Skip to content

Commit 8404966

Browse files
committed
More assorted fixes
This PR includes: - Fix requirements.txt for xmlschema on Python 2.7 - Add tagline and plotoutline to TV channels - Add label2Mask to sort methods - Add label2 episodes to folders (does not work in Kodi yet) - Add episode and season to folders (see video pane)
1 parent 159a16a commit 8404966

File tree

17 files changed

+95
-103
lines changed

17 files changed

+95
-103
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ codecov:
7676
curl --data-binary @.github/codecov.yml https://codecov.io/validate
7777

7878
clean:
79+
@echo -e "$(white)=$(blue) Cleaning up$(reset)"
7980
find . -name '*.py[cod]' -type f -delete
8081
find . -name '__pycache__' -type d -delete
81-
rm -rf .pytest_cache/ .tox/
82-
rm -f *.log test/userdata/tokens/*.tkn
82+
rm -rf .pytest_cache/ .tox/ *.log test/userdata/tokens/*.tkn

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ pylint
99
git+git://github.com/dagwieers/kodi-plugin-routing.git@setup#egg=routing
1010
setuptools >= 41
1111
tox-travis
12+
xmlschema==1.0.18; python_version == '2.7'

resources/language/resource.language.en_gb/strings.po

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -712,18 +712,6 @@ msgctxt "#30874"
712712
msgid "Open Up Next service add-on settings."
713713
msgstr ""
714714

715-
msgctxt "#30875"
716-
msgid "Install Twitter add-on"
717-
msgstr ""
718-
719-
msgctxt "#30877"
720-
msgid "Enable Twitter integration"
721-
msgstr ""
722-
723-
msgctxt "#30879"
724-
msgid "Twitter settings…"
725-
msgstr ""
726-
727715
msgctxt "#30881"
728716
msgid "Install PySocks library"
729717
msgstr ""

resources/language/resource.language.nl_nl/strings.po

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -712,18 +712,6 @@ msgctxt "#30874"
712712
msgid "Open Up Next service add-on settings."
713713
msgstr "Open de Up Next service add-on instellingen."
714714

715-
msgctxt "#30875"
716-
msgid "Install Twitter add-on"
717-
msgstr "Installeer de Twitter add-on"
718-
719-
msgctxt "#30877"
720-
msgid "Enable Twitter integration"
721-
msgstr "Activeer Twitter integratie"
722-
723-
msgctxt "#30879"
724-
msgid "Twitter settings…"
725-
msgstr "Twitter instellingen…"
726-
727715
msgctxt "#30881"
728716
msgid "Install PySocks library"
729717
msgstr "Installeer de PySocks library"

resources/lib/apihelper.py

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,14 @@ def __map_seasons(self, program, seasons, episodes):
160160
content = 'seasons'
161161

162162
episode = random.choice(episodes)
163-
info_labels = self._metadata.get_info_labels(episode, season=True)
164163
program_type = episode.get('programType')
164+
info_labels = self._metadata.get_info_labels(episode, season=True)
165+
info_labels.update(
166+
episode=len(episodes), # Total number of episodes in '* All seasons'
167+
season=len(seasons), # Total number of seasons in '* All seasons'
168+
tagline=localize(30133), # All seasons
169+
title=localize(30133), # All seasons
170+
)
165171

166172
# Reverse sort seasons if program_type is 'reeksaflopend' or 'daily'
167173
if program_type in ('daily', 'reeksaflopend'):
@@ -173,26 +179,34 @@ def __map_seasons(self, program, seasons, episodes):
173179
label=localize(30133), # All seasons
174180
path=url_for('programs', program=program, season='allseasons'),
175181
art_dict=self._metadata.get_art(episode, season='allseasons'),
176-
info_dict=info_labels,
182+
info_dict=info_labels.copy(),
177183
))
178184

179185
# NOTE: Sort the episodes ourselves, because Kodi does not allow to set to 'ascending'
180186
seasons = sorted(seasons, key=lambda k: k['key'], reverse=not ascending)
181187

182188
for season in seasons:
183189
season_key = season.get('key', '')
190+
episodelist = [e for e in episodes if e.get('seasonName') == season_key]
184191
# If more than 300 episodes exist, we may end up with an empty season (Winteruur)
185192
try:
186-
episode = random.choice([e for e in episodes if e.get('seasonName') == season_key])
193+
episode = random.choice(episodelist)
187194
except IndexError:
188195
episode = episodes[0]
189196

190197
label = '%s %s' % (localize(30131), season_key) # Season X
198+
info_labels.update(
199+
episode=len(episodelist), # Number of episodes in this folder
200+
season=1, # Number of seasons in this folder
201+
tagline=label,
202+
title=label,
203+
)
204+
191205
season_items.append(TitleItem(
192206
label=label,
193207
path=url_for('programs', program=program, season=season_key),
194208
art_dict=self._metadata.get_art(episode, season=True),
195-
info_dict=info_labels,
209+
info_dict=info_labels.copy(),
196210
prop_dict=self._metadata.get_properties(episode),
197211
))
198212
return season_items, sort, ascending, content
@@ -640,7 +654,7 @@ def list_channels(self, channels=None, live=True):
640654
label += ' [COLOR yellow]| %s[/COLOR]' % playing_now
641655
# A single Live channel means it is the entry for channel's TV Show listing, so make it stand out
642656
if channels and len(channels) == 1:
643-
label = '[B]%s[/B]' % label
657+
label = '[COLOR yellow][B]%s[/B][/COLOR]' % label
644658
is_playable = True
645659
if channel.get('name') in ['een', 'canvas', 'ketnet']:
646660
if get_setting_bool('showfanart', default=True):
@@ -649,7 +663,16 @@ def list_channels(self, channels=None, live=True):
649663
else:
650664
plot = localize(30142, **channel) # Watch live
651665
# NOTE: Playcount and resumetime are required to not have live streams as "Watched" and resumed
652-
info_dict = dict(title=label, plot=plot, studio=channel.get('studio'), mediatype='video', playcount=0, duration=0)
666+
info_dict = dict(
667+
title=label,
668+
plot=plot,
669+
plotoutline=playing_now,
670+
tagline=playing_now,
671+
studio=channel.get('studio'),
672+
mediatype='video',
673+
playcount=0,
674+
duration=0,
675+
)
653676
prop_dict = dict(resumetime=0)
654677
stream_dict = dict(duration=0)
655678
context_menu.append((
@@ -699,10 +722,10 @@ def list_youtube(channels=None):
699722
label = localize(30143, **youtube) # Channel on YouTube
700723
# A single Live channel means it is the entry for channel's TV Show listing, so make it stand out
701724
if channels and len(channels) == 1:
702-
label = '[B]%s[/B]' % label
725+
label = '[COLOR yellow][B]%s[/B][/COLOR]' % label
703726
plot = localize(30144, **youtube) # Watch on YouTube
704727
# NOTE: Playcount is required to not have live streams as "Watched"
705-
info_dict = dict(title=label, plot=plot, studio=channel.get('studio'), mediatype='video', playcount=0)
728+
info_dict = dict(title=label, plot=plot, studio=channel.get('studio'), playcount=0)
706729

707730
context_menu = [(
708731
localize(30413), # Refresh menu

resources/lib/kodiutils.py

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,17 @@
1515

1616
SORT_METHODS = dict(
1717
# date=xbmcplugin.SORT_METHOD_DATE,
18-
dateadded=xbmcplugin.SORT_METHOD_DATEADDED,
19-
duration=xbmcplugin.SORT_METHOD_DURATION,
20-
episode=xbmcplugin.SORT_METHOD_EPISODE,
21-
# genre=xbmcplugin.SORT_METHOD_GENRE,
22-
# label=xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE,
23-
label=xbmcplugin.SORT_METHOD_LABEL,
24-
title=xbmcplugin.SORT_METHOD_TITLE,
25-
# none=xbmcplugin.SORT_METHOD_UNSORTED,
18+
dateadded=dict(method=xbmcplugin.SORT_METHOD_DATEADDED, label2='%a'),
19+
duration=dict(method=xbmcplugin.SORT_METHOD_DURATION, label2='%D'),
20+
episode=dict(method=xbmcplugin.SORT_METHOD_EPISODE, label2='%D'),
21+
# genre=dict(method=xbmcplugin.SORT_METHOD_GENRE, label2='%D'),
22+
# label=dict(method=xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE, label2='%D'),
23+
label=dict(method=xbmcplugin.SORT_METHOD_LABEL, label2='%D'),
24+
title=dict(method=xbmcplugin.SORT_METHOD_TITLE, label2='%D'),
25+
# none=dict(method=xbmcplugin.SORT_METHOD_UNSORTED, label2='%D'),
2626
# FIXME: We would like to be able to sort by unprefixed title (ignore date/episode prefix)
27-
# title=xbmcplugin.SORT_METHOD_TITLE_IGNORE_THE,
28-
unsorted=xbmcplugin.SORT_METHOD_UNSORTED,
27+
# title=dict(method=xbmcplugin.SORT_METHOD_TITLE_IGNORE_THE, label2='%D'),
28+
unsorted=dict(method=xbmcplugin.SORT_METHOD_UNSORTED, label2='%D'),
2929
)
3030

3131
WEEKDAY_LONG = {
@@ -86,6 +86,16 @@ def __missing__(self, key):
8686
return '{' + key + '}'
8787

8888

89+
def translate_path(path):
90+
"""Translate special xbmc paths"""
91+
return to_unicode(xbmc.translatePath(path))
92+
93+
94+
def get_addon_info(key):
95+
"""Return addon information"""
96+
return to_unicode(ADDON.getAddonInfo(key))
97+
98+
8999
def addon_icon():
90100
"""Cache and return add-on icon"""
91101
return get_addon_info('icon')
@@ -108,14 +118,19 @@ def addon_name():
108118

109119
def addon_path():
110120
"""Cache and return add-on path"""
111-
return get_addon_info('path')
121+
return translate_path(get_addon_info('path'))
112122

113123

114124
def addon_profile():
115125
"""Cache and return add-on profile"""
116126
return to_unicode(xbmc.translatePath(ADDON.getAddonInfo('profile')))
117127

118128

129+
def addon_version():
130+
"""Cache and return add-on version"""
131+
return get_addon_info('version')
132+
133+
119134
def url_for(name, *args, **kwargs):
120135
"""Wrapper for routing.url_for() to lookup by name"""
121136
import addon
@@ -164,12 +179,12 @@ def show_listing(list_items, category=None, sort='unsorted', ascending=True, con
164179
sort = 'unsorted'
165180

166181
# Add all sort methods to GUI (start with preferred)
167-
xbmcplugin.addSortMethod(handle=plugin.handle, sortMethod=SORT_METHODS[sort])
182+
xbmcplugin.addSortMethod(handle=plugin.handle, sortMethod=SORT_METHODS[sort]['method'], label2Mask=SORT_METHODS[sort]['label2'])
168183
for key in sorted(SORT_METHODS):
169184
if key != sort:
170-
xbmcplugin.addSortMethod(handle=plugin.handle, sortMethod=SORT_METHODS[key])
185+
xbmcplugin.addSortMethod(handle=plugin.handle, sortMethod=SORT_METHODS[key]['method'], label2Mask=SORT_METHODS[key]['label2'])
171186

172-
# FIXME: This does not appear to be working, we have to order it ourselves
187+
# FIXME: This does not appear to be working, we have to order it ourselves and use 'unsorted' method
173188
# xbmcplugin.setProperty(handle=plugin.handle, key='sort.ascending', value='true' if ascending else 'false')
174189
# if ascending:
175190
# xbmcplugin.setProperty(handle=plugin.handle, key='sort.order', value=str(SORT_METHODS[sort]))
@@ -185,7 +200,7 @@ def show_listing(list_items, category=None, sort='unsorted', ascending=True, con
185200
# - item is a playable file (playable, path)
186201
# - item is non-actionable item (not playable, no path)
187202
is_folder = bool(not title_item.is_playable and title_item.path)
188-
is_playable = bool(title_item.is_playable and title_item.path)
203+
is_playable = bool(title_item.is_playable and not title_item.path.endswith('/noop'))
189204

190205
list_item = ListItem(label=title_item.label)
191206

@@ -220,6 +235,10 @@ def show_listing(list_items, category=None, sort='unsorted', ascending=True, con
220235
# type is one of: video, music, pictures, game
221236
list_item.setInfo(type='video', infoLabels=title_item.info_dict)
222237

238+
# Add number of episodes to folders
239+
if is_folder and title_item.info_dict.get('episode'):
240+
list_item.setLabel2(str(title_item.info_dict.get('episode')))
241+
223242
if title_item.stream_dict:
224243
# type is one of: video, audio, subtitle
225244
list_item.addStreamInfo('video', title_item.stream_dict)
@@ -300,6 +319,14 @@ def get_search_string():
300319
return search_string
301320

302321

322+
def multiselect(heading='', options=None, autoclose=0, preselect=None, use_details=False):
323+
"""Show a Kodi multi-select dialog"""
324+
from xbmcgui import Dialog
325+
if not heading:
326+
heading = addon_name()
327+
return Dialog().multiselect(heading=heading, options=options, autoclose=autoclose, preselect=preselect, useDetails=use_details)
328+
329+
303330
def ok_dialog(heading='', message=''):
304331
"""Show Kodi's OK dialog"""
305332
from xbmcgui import Dialog
@@ -318,14 +345,6 @@ def notification(heading='', message='', icon='info', time=4000):
318345
Dialog().notification(heading=heading, message=message, icon=icon, time=time)
319346

320347

321-
def multiselect(heading='', options=None, autoclose=0, preselect=None, use_details=False):
322-
"""Show a Kodi multi-select dialog"""
323-
from xbmcgui import Dialog
324-
if not heading:
325-
heading = addon_name()
326-
return Dialog().multiselect(heading=heading, options=options, autoclose=autoclose, preselect=preselect, useDetails=use_details)
327-
328-
329348
def set_locale():
330349
"""Load the proper locale for date strings, only once"""
331350
if hasattr(set_locale, 'cached'):
@@ -356,7 +375,7 @@ def localize(string_id, **kwargs):
356375
def localize_time(time):
357376
"""Return localized time"""
358377
time_format = xbmc.getRegion('time').replace(':%S', '') # Strip off seconds
359-
return time.strftime(time_format).lstrip('0') # Remove leading zero on all platforms
378+
return time.strftime(time_format)
360379

361380

362381
def localize_date(date, strftime):
@@ -674,11 +693,6 @@ def get_cache_path():
674693
return getattr(get_cache_path, 'cached')
675694

676695

677-
def get_addon_info(key):
678-
"""Return addon information"""
679-
return to_unicode(ADDON.getAddonInfo(key))
680-
681-
682696
def listdir(path):
683697
"""Return all files in a directory (using xbmcvfs)"""
684698
from xbmcvfs import listdir as vfslistdir

resources/lib/metadata.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ def get_episode(self, api_data):
414414

415415
# VRT NU Suggest API
416416
if api_data.get('type') == 'program':
417-
return int()
417+
return api_data.get('episode_count', int()) # The number of episodes
418418

419419
# VRT NU Schedule API (some are missing vrt.whatson-id)
420420
if api_data.get('vrt.whatson-id') or api_data.get('startTime'):
@@ -604,7 +604,6 @@ def get_info_labels(self, api_data, season=False, date=None, channel=None):
604604
if api_data.get('type') == 'episode':
605605
info_labels = dict(
606606
title=self.get_title(api_data),
607-
# sorttitle=self.get_title(api_data), # NOTE: Does not appear to work
608607
tvshowtitle=self.get_tvshowtitle(api_data),
609608
# date=self.get_date(api_data), # NOTE: Not sure when or how this is used
610609
aired=self.get_aired(api_data),
@@ -628,6 +627,7 @@ def get_info_labels(self, api_data, season=False, date=None, channel=None):
628627
info_labels = dict(
629628
tvshowtitle=self.get_tvshowtitle(api_data),
630629
plot=self.get_plot(api_data),
630+
episode=self.get_episode(api_data),
631631
mediatype=self.get_mediatype(api_data, season=season),
632632
studio=self.get_studio(api_data),
633633
tag=self.get_tag(api_data),
@@ -638,7 +638,6 @@ def get_info_labels(self, api_data, season=False, date=None, channel=None):
638638
if api_data.get('vrt.whatson-id') or api_data.get('startTime'):
639639
info_labels = dict(
640640
title=self.get_title(api_data),
641-
# sorttitle=self.get_title(api_data), # NOTE: Does not appear to work
642641
tvshowtitle=self.get_tvshowtitle(api_data),
643642
aired=self.get_aired(api_data),
644643
plot=self.get_plot(api_data, date=date),

resources/lib/resumepoints.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ def update(self, asset_id, title, url, watch_later=None, position=None, total=No
7575
# Update
7676
if (self.still_watching(position, total) or watch_later is True
7777
or (path and path.startswith('plugin://plugin.video.vrt.nu/play/upnext'))):
78-
# Normally, VRT NU resumepoints are deleted when an episode is (un)watched and Kodi GUI automatically sets the (un)watched status when Kodi Player exits.
79-
# This mechanism doesn't work with "Up Next" episodes because these episodes are not initiated from a ListItem in Kodi GUI.
80-
# For "Up Next" episodes, we should never delete the VRT NU resumepoints to make sure the watched status can be forced in Kodi GUI using the playcount infolabel.
78+
# Normally, VRT NU resumepoints are deleted when an episode is (un)watched and Kodi GUI automatically sets the (un)watched status when Player exits
79+
# This mechanism doesn't work with "Up Next" episodes because these episodes are not initiated from a ListItem in Kodi GUI
80+
# For "Up Next" episodes, we should never delete the VRT NU resumepoints to make sure the watched status can be forced using the playcount infolabel
8181

8282
log(3, "[Resumepoints] Update resumepoint '{asset_id}' {position}/{total}", asset_id=asset_id, position=position, total=total)
8383

resources/lib/tvguide.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ def get_channel_items(self, date=None, channel=None):
136136
path = url_for('tvguide', date=date, channel=chan.get('name'))
137137
plot = '[B]%s[/B]\n%s' % (datelong, localize(30302, **chan))
138138
else:
139-
label = '[B]%s[/B]' % localize(30303, **chan)
139+
label = '[COLOR yellow][B]%s[/B][/COLOR]' % localize(30303, **chan)
140140
path = url_for('tvguide_channel', channel=chan.get('name'))
141141
plot = '%s\n\n%s' % (localize(30302, **chan), self.live_description(chan.get('name')))
142142

resources/settings.xml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,6 @@
5252
<setting label="30869" help="30870" type="action" action="InstallAddon(service.upnext)" option="close" visible="!System.HasAddon(service.upnext)"/> <!-- Install Up Next add-on -->
5353
<setting label="30871" help="30872" type="bool" id="useupnext" default="true" visible="System.HasAddon(service.upnext)" />
5454
<setting label="30873" help="30874" type="action" action="Addon.OpenSettings(service.upnext)" enable="eq(-1,true)" option="close" visible="System.HasAddon(service.upnext)" subsetting="true"/> <!-- Up Next settings -->
55-
<!-- Twitter -->
56-
<!-- setting label="30875" help="30876" type="action" action="InstallAddon(service.twitter)" option="close" visible="!System.HasAddon(service.twitter)"/ -->
57-
<setting label="30877" help="30878" type="bool" id="usetwitter" default="true" visible="System.HasAddon(service.twitter)"/>
58-
<setting label="30879" help="30880" type="action" option="close" action="Addon.OpenSettings(service.twitter)" enable="eq(-1,true)" visible="System.HasAddon(service.twitter)" subsetting="true"/>
5955
<!-- PySocks -->
6056
<setting label="30881" help="30882" type="action" action="InstallAddon(script.module.pysocks)" option="close" visible="!System.HasAddon(script.module.pysocks)"/>
6157
</category>

0 commit comments

Comments
 (0)