Skip to content

Commit

Permalink
Implement program caching for more metadata in listings (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelarnauts committed Mar 22, 2020
1 parent 0ca9cf5 commit 7e86106
Show file tree
Hide file tree
Showing 21 changed files with 482 additions and 132 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ version = $(shell xmllint --xpath 'string(/addon/@version)' addon.xml)
git_branch = $(shell git rev-parse --abbrev-ref HEAD)
git_hash = $(shell git rev-parse --short HEAD)
zip_name = $(name)-$(version)-$(git_branch)-$(git_hash).zip
include_files = addon_entry.py addon.xml CHANGELOG.md LICENSE README.md resources/
include_files = addon_entry.py addon.xml CHANGELOG.md LICENSE README.md service_entry.py resources/
include_paths = $(patsubst %,$(name)/%,$(include_files))
exclude_files = \*.new \*.orig \*.pyc \*.pyo

Expand Down
1 change: 1 addition & 0 deletions addon.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<extension point="xbmc.python.pluginsource" library="addon_entry.py">
<provides>video</provides>
</extension>
<extension point="xbmc.service" library="service_entry.py"/>
<extension point="xbmc.addon.metadata">
<summary lang="en_GB">Watch content from VIER, VIJF and ZES.</summary>
<platform>all</platform>
Expand Down
33 changes: 33 additions & 0 deletions resources/language/resource.language.en_gb/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,18 @@ msgctxt "#30713"
msgid "The requested video was not found in the guide."
msgstr ""

msgctxt "#30714"
msgid "Local metadata is cleared."
msgstr ""

msgctxt "#30715"
msgid "Updating metadata"
msgstr ""

msgctxt "#30716"
msgid "Updating metadata ({index}/{total})..."
msgstr ""

msgctxt "#30717"
msgid "This program is not available in the catalogue."
msgstr ""
Expand All @@ -141,3 +153,24 @@ msgstr ""
msgctxt "#30805"
msgid "Password"
msgstr ""

msgctxt "#30820"
msgid "Interface"
msgstr ""

msgctxt "#30827"
msgid "Metadata"
msgstr ""

msgctxt "#30829"
msgid "Periodically refresh metadata in the background"
msgstr ""

msgctxt "#30831"
msgid "Update local metadata now"
msgstr ""

msgctxt "#30833"
msgid "Clear local metadata"
msgstr ""

33 changes: 33 additions & 0 deletions resources/language/resource.language.nl_nl/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,18 @@ msgctxt "#30713"
msgid "The requested video was not found in the guide."
msgstr "De gevraagde video werd niet gevonden in de tv-gids."

msgctxt "#30714"
msgid "Local metadata is cleared."
msgstr "De locale metadata is verwijderd."

msgctxt "#30715"
msgid "Updating metadata"
msgstr "Vernieuwen metadata"

msgctxt "#30716"
msgid "Updating metadata ({index}/{total})..."
msgstr "Vernieuwen metadata ({index}/{total})..."

msgctxt "#30717"
msgid "This program is not available in the catalogue."
msgstr "Dit programma is niet beschikbaar in de catalogus."
Expand All @@ -142,3 +154,24 @@ msgstr "E-mailadres"
msgctxt "#30805"
msgid "Password"
msgstr "Wachtwoord"

msgctxt "#30820"
msgid "Interface"
msgstr "Interface"

msgctxt "#30827"
msgid "Metadata"
msgstr "Metadata"

msgctxt "#30829"
msgid "Periodically refresh metadata in the background"
msgstr "Vernieuw de metdata automatisch in de achtergrond"

msgctxt "#30831"
msgid "Update local metadata now"
msgstr "De locale metadata nu vernieuwen"

msgctxt "#30833"
msgid "Clear local metadata"
msgstr "De locale metadata verwijderen"

27 changes: 22 additions & 5 deletions resources/lib/addon.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@

from __future__ import absolute_import, division, unicode_literals

import logging

from routing import Plugin

from resources.lib import kodilogging

kodilogging.config()
routing = Plugin()
_LOGGER = logging.getLogger('addon')


@routing.route('/')
Expand Down Expand Up @@ -67,11 +70,11 @@ def show_catalog_program(channel, program):
Catalog().show_program(channel, program)


@routing.route('/program/program/<channel>/<program>/<season>')
@routing.route('/catalog/program/<channel>/<program>/<season>')
def show_catalog_program_season(channel, program, season):
""" Show a program from the catalog """
from resources.lib.modules.catalog import Catalog
Catalog().show_program_season(channel, program, int(season))
Catalog().show_program_season(channel, program, season)


@routing.route('/search')
Expand All @@ -82,11 +85,11 @@ def show_search(query=None):
Search().show_search(query)


@routing.route('/play/catalog/<channel>/<uuid>')
def play(channel, uuid):
@routing.route('/play/catalog/<uuid>')
def play(uuid):
""" Play the requested item """
from resources.lib.modules.player import Player
Player().play(channel, uuid)
Player().play(uuid)


@routing.route('/play/page/<channel>/<page>')
Expand All @@ -101,6 +104,20 @@ def play_from_page(channel, page):
Player().play_from_page(channel, unquote(page))


@routing.route('/metadata/update')
def metadata_update():
""" Update the metadata for the listings (called from settings) """
from resources.lib.modules.metadata import Metadata
Metadata().update()


@routing.route('/metadata/clean')
def metadata_clean():
""" Clear metadata (called from settings) """
from resources.lib.modules.metadata import Metadata
Metadata().clean()


def run(params):
""" Run the routing plugin """
routing.run(params)
34 changes: 25 additions & 9 deletions resources/lib/kodiutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@
SORT_METHODS = dict(
unsorted=xbmcplugin.SORT_METHOD_UNSORTED,
label=xbmcplugin.SORT_METHOD_LABEL_IGNORE_FOLDERS,
title=xbmcplugin.SORT_METHOD_TITLE,
episode=xbmcplugin.SORT_METHOD_EPISODE,
duration=xbmcplugin.SORT_METHOD_DURATION,
year=xbmcplugin.SORT_METHOD_VIDEO_YEAR,
date=xbmcplugin.SORT_METHOD_DATE,
)
DEFAULT_SORT_METHODS = [
'unsorted', 'label'
'unsorted', 'title'
]

_LOGGER = logging.getLogger('kodiutils')
Expand Down Expand Up @@ -269,7 +270,7 @@ def set_locale():
setlocale(LC_ALL, locale_lang)
except (Error, ValueError) as exc:
if locale_lang != 'en_GB':
_LOGGER.debug("Your system does not support locale '{locale}': {error}", locale=locale_lang, error=exc)
_LOGGER.debug("Your system does not support locale '%s': %s", locale_lang, exc)
set_locale.cached = False
return False
set_locale.cached = True
Expand Down Expand Up @@ -423,14 +424,14 @@ def listdir(path):
def mkdir(path):
"""Create a directory (using xbmcvfs)"""
from xbmcvfs import mkdir as vfsmkdir
_LOGGER.debug("Create directory '{path}'.", path=path)
_LOGGER.debug("Create directory '%s'.", path)
return vfsmkdir(path)


def mkdirs(path):
"""Create directory including parents (using xbmcvfs)"""
from xbmcvfs import mkdirs as vfsmkdirs
_LOGGER.debug("Recursively create directory '{path}'.", path=path)
_LOGGER.debug("Recursively create directory '%s'.", path)
return vfsmkdirs(path)


Expand Down Expand Up @@ -458,14 +459,14 @@ def stat_file(path):
def delete(path):
"""Remove a file (using xbmcvfs)"""
from xbmcvfs import delete as vfsdelete
_LOGGER.debug("Delete file '{path}'.", path=path)
_LOGGER.debug("Delete file '%s'.", path)
return vfsdelete(path)


def container_refresh(url=None):
"""Refresh the current container or (re)load a container by URL"""
if url:
_LOGGER.debug('Execute: Container.Refresh({url})', url=url)
_LOGGER.debug('Execute: Container.Refresh(%s)', url)
xbmc.executebuiltin('Container.Refresh({url})'.format(url=url))
else:
_LOGGER.debug('Execute: Container.Refresh')
Expand All @@ -475,7 +476,7 @@ def container_refresh(url=None):
def container_update(url):
"""Update the current container while respecting the path history."""
if url:
_LOGGER.debug('Execute: Container.Update({url})', url=url)
_LOGGER.debug('Execute: Container.Update(%s)', url)
xbmc.executebuiltin('Container.Update({url})'.format(url=url))
else:
# URL is a mandatory argument for Container.Update, use Container.Refresh instead
Expand Down Expand Up @@ -529,7 +530,7 @@ def get_cache(key, ttl=None):

with open_file(fullpath, 'r') as fdesc:
try:
_LOGGER.info('Fetching {file} from cache', file=filename)
_LOGGER.debug('Fetching %s from cache', filename)
import json
value = json.load(fdesc)
return value
Expand All @@ -547,6 +548,21 @@ def set_cache(key, data):
mkdirs(path)

with open_file(fullpath, 'w') as fdesc:
_LOGGER.info('Storing to cache as {file}', file=filename)
_LOGGER.debug('Storing to cache as %s', filename)
import json
json.dump(data, fdesc)


def invalidate_cache(ttl=None):
""" Clear the cache """
path = get_cache_path()
if not exists(path):
return
_, files = listdir(path)
import time
now = time.mktime(time.localtime())
for filename in files:
fullpath = path + filename
if ttl and now - stat_file(fullpath).st_mtime() < ttl:
continue
delete(fullpath)
30 changes: 15 additions & 15 deletions resources/lib/modules/catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from resources.lib.kodiutils import TitleItem
from resources.lib.modules.menu import Menu
from resources.lib.viervijfzes import CHANNELS
from resources.lib.viervijfzes.auth import AuthApi
from resources.lib.viervijfzes.content import ContentApi, UnavailableException

_LOGGER = logging.getLogger('catalog')
Expand All @@ -19,7 +20,8 @@ class Catalog:

def __init__(self):
""" Initialise object """
self._api = ContentApi()
auth = AuthApi(kodiutils.get_setting('username'), kodiutils.get_setting('password'))
self._api = ContentApi(auth)
self._menu = Menu()

def show_catalog(self):
Expand All @@ -34,9 +36,9 @@ def show_catalog(self):

listing = [self._menu.generate_titleitem(item) for item in items]

# Sort items by label, but don't put folders at the top.
# Sort items by title
# Used for A-Z listing or when movies and episodes are mixed.
kodiutils.show_listing(listing, 30003, content='tvshows', sort='label')
kodiutils.show_listing(listing, 30003, content='tvshows', sort='title')

def show_catalog_channel(self, channel):
""" Show the programs of a specific channel
Expand All @@ -52,9 +54,9 @@ def show_catalog_channel(self, channel):
for item in items:
listing.append(self._menu.generate_titleitem(item))

# Sort items by label, but don't put folders at the top.
# Sort items by title
# Used for A-Z listing or when movies and episodes are mixed.
kodiutils.show_listing(listing, 30003, content='tvshows', sort='label')
kodiutils.show_listing(listing, 30003, content='tvshows', sort='title')

def show_program(self, channel, program_id):
""" Show a program from the catalog
Expand All @@ -75,7 +77,7 @@ def show_program(self, channel, program_id):

# Go directly to the season when we have only one season
if len(program.seasons) == 1:
self.show_program_season(channel, program_id, program.seasons.values()[0].number)
self.show_program_season(channel, program_id, program.seasons.values()[0].uuid)
return

studio = CHANNELS.get(program.channel, {}).get('studio_icon')
Expand All @@ -87,9 +89,8 @@ def show_program(self, channel, program_id):
listing.append(
TitleItem(
title='* %s' % kodiutils.localize(30204), # * All seasons
path=kodiutils.url_for('show_catalog_program_season', channel=channel, program=program_id, season=-1),
path=kodiutils.url_for('show_catalog_program_season', channel=channel, program=program_id, season='-1'),
art_dict={
'thumb': program.cover,
'fanart': program.background,
},
info_dict={
Expand All @@ -107,9 +108,8 @@ def show_program(self, channel, program_id):
listing.append(
TitleItem(
title=s.title, # kodiutils.localize(30205, season=s.number), # Season {season}
path=kodiutils.url_for('show_catalog_program_season', channel=channel, program=program_id, season=s.number),
path=kodiutils.url_for('show_catalog_program_season', channel=channel, program=program_id, season=s.uuid),
art_dict={
'thumb': s.cover,
'fanart': program.background,
},
info_dict={
Expand All @@ -123,13 +123,13 @@ def show_program(self, channel, program_id):
)

# Sort by label. Some programs return seasons unordered.
kodiutils.show_listing(listing, 30003, content='tvshows', sort=['label'])
kodiutils.show_listing(listing, 30003, content='tvshows')

def show_program_season(self, channel, program_id, season):
def show_program_season(self, channel, program_id, season_uuid):
""" Show the episodes of a program from the catalog
:type channel: str
:type program_id: str
:type season: int
:type season_uuid: str
"""
try:
program = self._api.get_program(channel, program_id)
Expand All @@ -138,12 +138,12 @@ def show_program_season(self, channel, program_id, season):
kodiutils.end_of_directory()
return

if season == -1:
if season_uuid == "-1":
# Show all episodes
episodes = program.episodes
else:
# Show the episodes of the season that was selected
episodes = [e for e in program.episodes if e.season == season]
episodes = [e for e in program.episodes if e.season_uuid == season_uuid]

listing = [self._menu.generate_titleitem(episode) for episode in episodes]

Expand Down
Loading

0 comments on commit 7e86106

Please sign in to comment.