Conversation
|
Big fat disclaimer, I am not a python coder, I know it very little. I will work more on it, but this was mostly vibe-coded. (do not shame me to much ;) I do want some early review happening however to see if the general approach is alright, and get some testing feedback from those who dare. I've been using an earlier version (where it would just stream live tv) for 2 weeks now and that part is great. (I haven't updated that on our TV yet as that was enough for the WAF for now :p) all other testing I did locally on my laptop and through the included (almost 100) tests of the test suite. I haven't looked at the test code almost at all however. |
|
Those are a lot of changes! Perhaps too much for a PR for NL Ziet. But anyways, good stuff. I do have some remarks, which I will post in the PR. One major this is that we should not include Python libs in other add-ons. So please see if you can use https://kodi.tv/addons/omega/script.module.qrcode/ for the QR Code stuff. Another thing is that I advice you to use https://pypi.org/project/sakee/. That is my Kodi emulator that you can use to run an add-on without Kodi and get some simple ASCII results. |
Most changes are scafolding changes needed for NLZIET, we can do those in seperate MR's to get into the framework, but then they'd have no user yet. Either way is fine. I've tried to split my commits into 'somewhat smallish' commits from a feature point of view, to avoid having 'one huge fully featured with everything' commit however.
So far as I could see, this is not an official addon, so we cannot depend on it, and will have to ask users to manually install it. There's other ways, with pure python implementations to do QR codes of course. But as I stated in the commit, the QR-code IS script.module.qrcode. So whatever approach you suggest we can do.
I've just ran whatever unittest.yaml did, which is installing the requirements.txt (which I would assume uses sakee?) and running the (full) pytest suite. |
|
P.s. if not already implied, try to review it on a per commit basis ;)
|
basrieter
left a comment
There was a problem hiding this comment.
Ok, looks like a lot of nice work! I will need to play around with it a bit, especially the device login.
For now I went through some of the (none-channel related) changes and left comments.
One thing (besides the QR stuff) is that you are making a lot of changes outside of adding NL Ziet. I understand these changes (some are nice, like the profile dependent search history). However, they clutter the PR and make it difficult to review and test.
So, even thought I understand your urge to update some 20 year old code, it would be better to separate this from the NL Ziet channel.
There was a problem hiding this comment.
I wonder if this will work, as we use Weblate to translate stuff. Retrospect is set up to use the en-gb as the main language and Weblate will them try to fill the rest. But we will see what happens.
There was a problem hiding this comment.
yeah, as a native dutch/german speaker I figured i'll add those 3 languages as well. I saw we used weblate for a lot of the other stuff; but I also got missing/empty strings when testing it on my system, so that was a little annoying :)
There was a problem hiding this comment.
So in general these tests are great. However....(there is always a but).....
I don't use tests for code testing at all (bare minimum) like you are doing here. These tests are really great stuff, but the tests in Retrospect are used to actually call API end-points and see if they still provide the API messages that work for Retrospect. So I abuse the unit tests to do integration testing. That is way the UriHandler is not mocked at all: I actually need it to do the http(s)-calls.
There was a problem hiding this comment.
yeah, I wasn't sure how to deal with that. I test both actually, but NLZIET requires a payed subscription. From early testing, after having an account, it may work without actually paying for it, just leaving the account 'on', so then we can still do some of the testing.
I didn't want to hammer the API, which is why I added both, real api tests for authentication, and mocking of everything else. This way, I could add a lot of different tests. There is one real API test that also validates the real API against the mocks.
Also, maybe more importantly, to be able to work on this one needs NLZIET credentials to do testing, which even account holders, may not want to do (locally), so being able to mock, allows for some basic tests to be performed. also mocking the API's at least gives us control what they will return, the real API will often return different content for certain endpoints of course. So I figured best of both worlds?
If the problem is the conftest thing, I can also make it part of only the NLZIET mocks. I just didn't know what the problem was and how to deal with it :)
There was a problem hiding this comment.
I understand, as long as the mocking is not interfering with other tests. So you cannot mock the UriHandler or Logger for on a global scope.
There was a problem hiding this comment.
So basically what you do here, and in the other test, I usually combine. Because writing such extensive tests, just takes up too much time. My philosophy is, that if the API changes and breaks 2 things can happen:
- Parsing results on 0 results, or
- The converting from API to results fails
Both are covered in my simple tests like
def test_tv4_tv_shows(self):
url = "https://www.goplay.be/programmas/"
items = self._test_folder_url(url, 20)Which basically uses the TV4 channel (in this case) and just runs a parse off the given URL.
There was a problem hiding this comment.
I understand your philosophy, but I avoided it for the two previously mentioned reasons. a) other contributors may not have an account they want to use/sacrifice because of FUD on getting banned; being able to run tests locally without an internet connection :)
Having said that, I think it's trivial to do the same that we did with the authentication tests, run against the REAL API when username/password are set, against the MOCKS if not. I think doing mocks doesn't add a lot, I'll update this sunday as well.
tests/conftest.py
Outdated
There was a problem hiding this comment.
So this basically disables the UriHandler for all tests and it breaks all tests. So this won't work for Retrospect as we need actual https responses.
There was a problem hiding this comment.
yeah, fair enough; as mentioned above. i'll fix it sunday :)
There was a problem hiding this comment.
So, this file get's auto generated from the plugin.video.retrospect/resources/data/settings_template.xml. So the only thing you need to do is to configure your settings in the chn_nlziet.json, clear Python caches and run Retrospect once. It will generate the correct settings.xml.
There was a problem hiding this comment.
ahh, good to know, I wasn't sure how this worked, or rather, was confused by the committed settings.xml. Why are there settings committed? Should I commit them at all?
There was a problem hiding this comment.
Please don't update the urihandler.py. It is one of the core modules and should contain all stuff you need. If you really need something, please make the impact as minimal as possible.
There was a problem hiding this comment.
Please use the RETROSPECT_STDOUT_LOGGING to disable logging, not via these changes.
There was a problem hiding this comment.
these are old debug commits; as I said, I still need to review most of the code changes :p
resources/lib/deviceauthdialog.py
Outdated
| return None | ||
|
|
||
|
|
||
| class DeviceAuthDialog(xbmcgui.WindowDialog): |
There was a problem hiding this comment.
I don't like mixing the xbmcgui.WindowDialog in Kodi plugin add-ons. It just looks ugly and never matches the actual skin users use. So I dialogs should be the most simple ones, where the skinning is part of the main skin, not Retrospect.
Can it not work with xbmc.executebuiltin('ShowPicture("https://example.com/image.jpg")')?
There was a problem hiding this comment.
I have no idea :D i was presented with three options, either the programmatically, like now, the standard dialog which did not allow for the needed modification of adding a QR code, or using a 'XML based dialog' which meant adding a skin, which I didn't want to do. I was under the impression that the programmatical gui would just use the skin, I was wrong, so I'll try this more, but will the showpicture work ontop of a dialog box? idk.
There was a problem hiding this comment.
So after digging deeper into this XML based dialog, the changes make a lot of sense to use that. I refrained from using it as we didn't have XML dialogs yet, but I also see it's quite trivial and works with skins, not against it. I'll fix that asap.
Every change presented to the framework, was done to support NLZIET (with device flow ;p) the profile dependent search history is truly just a 'nice to have' ;) So what do you recommend? just open single MR's for the trivial stuff, do you want to cherry pick some of them into master when you think they are good? Anything is fine by me; but the gross of the changes are needed for this new channel to work reliably. |
|
Great work! I was looking for this for so long! 😀 So I'm happy to test. Iptv manager is not working yet right? Would be nice to use it to have some catchup options and watch stuff back from the epg. Is that something you're working on? Also, which branch should I test? The review version? Or the other one. |
|
With the latest version I'm getting this error; "De afhankelijkheid op script.module.qrcode versie 4.7.2 kan niet worden voldaan." Any idea? |
|
@oliv3r Please ping me if you need help or if I need to review stuff. I could also add you to the Retrospect Slack if that makes live easier? |
shitty translation of 'missing script.module.qrcode' addon :) should work fine without it btw. I marked the dependency as optional, so i think during addon installation/update it will ask you 'do you want ot install script.module.qrcode' and if you say no, the code will still work just fine. |
Yes please! I'll figure that would. I've changed my approach slightly. I will keep pushing my (functional) WIP stuff onto this branch, it will be messy, at times. but the idea is, that 'proper things' will get their own merge requests, and we should see the number of patches shrink. this MR will then just have the 'final feature' so when we merge this, then nlziet will be 'done' for now from my point of view :) |
I missed your comment; but I keep pushing my 'work in progress' to this branch. Right now, I have working: livetv; Video on Demand, multiple profile support; iptv manager integration with 'catch-up' and watch-ahead, HOWEVER! Kodi does NOT support watch ahead sadly. so for that to work, we need to fix kodi. |
|
Great work! I am fixing Vier (Play.tv) at the moment and reviewing your smaller PR's. |
|
@oliv3r if you rebase on |
Container.Update triggers a separate plugin instance that races with endOfDirectory. Slow API responses cause Kodi to render the stale search-history page instead of the actual results. Run the search directly in the keyboard branch so results are rendered in the same plugin instance. Known issue: empty needle is passed through, so refreshing search results triggers the keyboard pop-up instead of re-running the query. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
Empty folder results previously showed an Error notification regardless of the configured empty_list_behavior setting, something not considered to be an error. Now only the 'error' behavior triggers an Error notification; all other modes use Info. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
Without SORT_METHOD_UNSORTED as default, Kodi sorts alphabetically. This reorders pinned items (search, explore pages) meant to stay at fixed positions and shuffles live channel listings out of EPG order. Make SORT_METHOD_UNSORTED the default sort method when any pinned or live items are present, matching the existing search behavior. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
The DRM warning dialog is unnecessary when inputstreamhelper confirms that InputStream Adaptive and the Widevine CDM are installed. Fall through to the existing warning if the helper is unavailable. This effectively makes the show_drm_paid_warning setting a no-op when Widevine is present. A future refactor could replace the setting entirely with a pop-up only when Widevine is not detected. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
There are currently no tests for the PATCH http method where the others do have them. Add it so we can refactor this in the future more easily. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
The if/elif chain dispatched on body type first, with the HTTP method buried inside POST branches, making it harder to extend with new methods. Restructure to resolve the effective method up front, then dispatch with flat independent if blocks in natural HTTP order (GET, POST, PATCH). When no explicit method is passed but body arguments are present, the request is silently promoted from GET to POST. This preserves existing behavior relied on by existing callers. Ideally callers should specify the method explicitly and the handler should reject ambiguous requests. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
The HTTP request handler supported GET, POST, and PATCH but not DELETE. Add it to be more compliant with the HTTP specification, but also so that others can make use of it in the future. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
Add up to three playable shortcuts before the season folders: Continue Watching (resume-aware), Most Recent Episode, and First Episode. When Continue Watching points to the same episode as First Episode (i.e. the series has not been started), the Continue item is omitted. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
Implements create_iptv_streams() and create_iptv_epg() for IPTV Manager support. Provides all NLZIET live channels and 6 days of EPG data (3 past + 3 future). Replay-enabled programs include direct playback links in the EPG grid. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
Populate tv_show_title on episode MediaItems so service.upnext can show
the correct series title in its 'Up Next' popup.
The series title is extracted from the /v8/series/{id} detail response
in the existing extract_series_data() preprocessor and stored in each
season FolderItem's metaData under 'nlziet:series_title'.
A new extract_series_title() preprocessor on the /v9/series/ parser
reads that key from self.parentItem.metaData and caches it on the
channel instance. create_episode_item() then uses it to set
item.tv_show_title, which is forwarded to service.upnext via the
existing VideoAction.__get_up_next_data() framework method.
No new framework code is required; the trigger logic in videoaction.py
already fires automatically for any non-live episode when service.upnext
is installed.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
For each algorithm test in TestEpisodeBoundaryDetection there is now a
corresponding integration test in TestNLZietChannelLive (auto-inherited
by TestNLZietChannelMocked so it runs without credentials).
Tests cover the three key real-world orderings from HAR recordings:
- Flikken Maastricht S18: descending, clean broadcastAt (A1 / A13)
- De Luizenmoeder S1: descending, non-monotonic re-broadcast dates (A1 / A10)
- Fawlty Towers S1: ascending, clean broadcastAt (A1 / A6)
Changes:
- nlziet_mocks.py: Added three HAR-derived episode fixtures (FM S18,
Luizenmoeder S1, Fawlty S1) and MOCK_EPISODES_BY_SEASON lookup dict;
extended dispatch() to handle /v9/series/{id}/episodes endpoint.
- test_chn_nlziet.py: Added 6 boundary tests to TestNLZietChannelLive
(_boundary_eps helper + first/recent assertion for each fixture).
Co-authored-by: Claude Sonnet 4.6
Signed-off-by: Oliver Bramley <oliver@bramley.dev>
Signed-off-by: Your Name <you@example.com>
Kodi generates settings.xml from the template only when a channel is first installed (no __pycache__). Since chn_nlziet.json was added after the initial channel discovery, the settings were never included. Commit the generated settings.xml directly so the 7 NLZIET channel settings are available immediately without requiring a fresh install: - nlziet_device_setup (device flow setup action) - nlziet_username / nlziet_password / nlziet_password_set - nlziet_log_off (logout action) - nlziet_select_profile - nlziet_live_start_offset Visibility indices for all other channels are shifted accordingly. Co-authored-by: Claude Sonnet 4.6 Signed-off-by: Oliver Bramley <oliver@bramley.dev> Signed-off-by: Your Name <you@example.com>
Replace __fetch_live_genres() (20 API calls on every EPG refresh) with a
lazy, queue-based enrichment system that populates descriptions and genres
incrementally — 10 items per 30-second cycle — without blocking the initial
EPG load.
## Architecture
### epg_enrichment.py (new)
Module-level helpers shared by the channel and the background service:
- load/save_detail_cache(): JSON cache in LocalSettings with 3-day TTL pruning
- load/save_enrich_queue(): ordered work list in LocalSettings
- build_enrich_queue(): builds priority-sorted queue from all programmes,
excluding already-cached items; ordering: now → future (soonest first) →
past (most-recent first, idle backfill only)
- fetch_and_cache(batch, headers): calls /v9/item/detail/ for ≤10 items,
extracts description + genres[0].name, updates cache
### api.py
Added EPG_DETAIL_CACHE_KEY, EPG_ENRICH_QUEUE_KEY, EPG_ENRICH_BATCH_SIZE=10,
EPG_CACHE_TTL_DAYS=3.
### chn_nlziet.py — create_iptv_epg()
- Applies description+genre from cache to all EPG items on every refresh
- After building the full EPG, rebuilds the enrich queue (uncached items only)
- Removed __fetch_live_genres() — no longer needed
### retroservice.py
Replaced the one-shot script with a real xbmc.Monitor loop (waitForAbort(30)).
Each 30-second tick calls _drain_epg_enrich_queue():
1. Pop 10 items from the queue
2. Fetch item/detail (reuses NLZIETOAuth2Handler for auth headers)
3. Update cache; save remaining queue
4. If any new data: set service.iptv.manager last_refreshed=0 → triggers
IPTV Manager to regenerate epg.xml with fresh descriptions
## Cold-cache behaviour
- Cycle 1 (~30s): ch0-ch9 get descriptions in the 'now' column
- Cycle 2 (~60s): ch10-ch19 'now' described — full 'now' column done
- Cycles 3-N: next/future slots enriched, soonest first
- Full ~60-item view window populated in ~3 min; warm cache = 0 API calls
Co-authored-by: Claude Sonnet 4.6
Signed-off-by: Your Name <you@example.com>
…daptive backoff - Add __load_appconfig() helper: caches /v7/appconfig in LocalSettings (TTL = APPCONFIG_CACHE_TTL = 300s); logs warning if isUpdateRequired - Add __check_app_blocked(): on isAppBlocked=True shows appBlockedReason to user (Kodi notification) and signals caller to abort API work - Add __signal_iptv_manager(delay): always signals IPTV Manager after create_iptv_epg with an adaptive delay (30s – 600s) so EPG stays live forever; delay grows 30s per empty cycle, resets when work arrives - Add programme-location cache (nlziet_epg_progloc_cache, TTL 300s): during 30s drain cycles only the 10 item/detail calls hit the network instead of ~280 redundant programlocation fetches - Use appconfig.liveStreamRestartStartPadding (180s) as base for live_offset; user setting nlziet_live_start_offset becomes a +-120s adjustment relative to the server value (range updated accordingly) - epg_enrichment: add load/save_progloc_cache, load/save_backoff_cycles, compute_signal_delay helpers; api.py: new APPCONFIG_CACHE_KEY, APPCONFIG_CACHE_TTL, EPG_PROGLOC_CACHE_KEY, EPG_BACKOFF_CYCLES_KEY - CI test_20_appconfig_status: live test asserting isAppBlocked=False (hard fail) and isUpdateRequired=False (DeprecationWarning) - 11 new unit tests covering progloc cache and backoff helpers Co-authored-by: Claude Sonnet 4.6 (claude-sonnet-4.6) Signed-off-by: Your Name <you@example.com>
Addon.refresh() in service.iptv.manager writes last_refreshed after create_iptv_epg returns, clobbering any signal we write from within create_iptv_epg. The previous __signal_iptv_manager() approach was therefore a no-op: IPTV Manager would not refresh again for 24h. Fix: __signal_iptv_manager() now writes a plain-text file (nlziet_epg_signal_at) containing the target Unix timestamp. retroservice.py polls every 30s; when the time arrives it writes last_refreshed="0" to IPTV Manager — after Addon.refresh() has already exited — and removes the file. retroservice.py also handles the cold-start case: if settings.json has no nlziet_epg_progloc_cache (fresh install / first run), a signal file is written 5s after service startup so the first IPTV Manager refresh is triggered immediately instead of waiting 24h. Co-authored-by: Claude Sonnet 4.6 Signed-off-by: Oliver Kuckertz <oliver.kuckertz@mologie.de> Signed-off-by: Your Name <you@example.com>
Add a boolean channel setting (default: true) that hides channels requiring an additional subscription package from both the IPTV stream list and the EPG. The API exposes this via missingSubscriptionFeature on each channel entry: null = included in current subscription, non-null = extra package required (e.g. ExtraChannelPackage1). - create_iptv_streams: skip channels with missingSubscriptionFeature != null when setting is enabled - create_iptv_epg: accumulate subscribed channel IDs during the stale (fresh API fetch) pass and persist them in the progloc cache as 'subscribed_channels'; on cache-hit passes, reuse the stored set to filter EPG entries without an extra API call - chn_nlziet.json: add order-8 bool setting nlziet_epg_subscribed_only - resources/settings.xml: add corresponding setting entry (visible eq -11) - strings.po #30630: 'Only show subscribed channels' Co-authored-by: Claude Sonnet 4.6 (claude-sonnet-4.6) Signed-off-by: Your Name <you@example.com>
…uplicates epg_enrichment.py had its first block of functions duplicated verbatim starting at line 253. Because Python uses the last definition, build_enrich_queue and fetch_and_cache were the simpler broken versions. Removed the duplicate. Logging additions: - retroservice.py: _log() helper (xbmc.log); logs addon_data/signal path at init, tick counter, signal file found/remaining/fired, IPTV Manager signal success/failure, initial trigger write - create_iptv_streams: entry + returned/skipped channel counts - create_iptv_epg: entry with days/stale, drain step, per-day fetch vs cache, total channels+programmes collected, enrich queue saved count - __signal_iptv_manager: signal path + delay; exception as WARNING - __load_appconfig: cache hit (age) vs fresh fetch - load_progloc_cache: hit (age, date keys) vs stale/absent - build_enrich_queue: now/future/past breakdown with cached count - fetch_and_cache: batch size, per-item fetch, enriched/skipped counts Co-authored-by: Claude Sonnet 4.6 Signed-off-by: Oliver Kuckertz <oliver.kuckertz@mologie.de> Signed-off-by: Your Name <you@example.com>
The progloc cache (42 MB) and enrich queue (2.5 MB) were stored in settings.json via AddonSettings, causing Kodi to throw repeated 'EXCEPTION: Invalid setting type' errors on every PVR manager reload. Changes: - Move progloc cache to nlziet_epg_progloc.json (file I/O, no size limit) - Remove enrich queue persistence entirely: the queue is now built and drained within the same create_iptv_epg call, since build_enrich_queue already excludes items in the detail cache, making the saved position implicit - Remove cdata from progloc cache entries (was the bulk of the 42 MB); __create_replay_item now takes asset_id + title directly - Slim image field to store only the landscapeUrl string instead of the full image dict - Add _CACHE_DIR override in epg_enrichment.py for test isolation - Remove EPG_ENRICH_QUEUE_KEY from api.py (no longer used) - Update tests: TestQueueIO removed; TestProlocCache uses temp dir Co-authored-by: Claude Sonnet 4.6 Signed-off-by: Oliver Klomp <info@retrospect.tv> Signed-off-by: Your Name <you@example.com>
Future programmes tagged with WatchInAdvance in the progloc API now get a playable stream URL in the EPG, allowing users to watch upcoming content before its scheduled broadcast time. Implementation: - Store is_watch_ahead flag as field [11] in progloc cache entries (backward-compat: old caches default to False) - Add stream URL block for watch-ahead items in create_iptv_epg - Prefix EPG description with localized 'Watch in advance' label - Add string #30631 to en_gb and nl_nl translations - Add SubscribedOnly (#30630) and WatchInAdvance (#30631) to languagehelper Test improvements: - Fix EPG test fixtures to include contentItemId and assetId fields - Add cache isolation (temp _CACHE_DIR) to prevent inter-test leakage - Increase mock side_effect counts for 15-day EPG window - Add symlink for addon path assertion - Add test_iptv_epg_watch_ahead_gets_stream - Add test_iptv_epg_future_without_watch_ahead_no_stream Co-authored-by: Claude Opus 4.6 (high) Signed-off-by: Your Name <you@example.com>
A crash in any single channel's create_iptv_streams() or create_iptv_epg() was killing the entire IPTV Manager response, resulting in an empty playlist and no EPG data for all channels. Wrap each channel's contribution in try/except so one broken channel only loses its own streams/EPG, not everyone else's. Co-authored-by: Claude Opus 4.6 (high) Signed-off-by: Your Name <you@example.com>
- Add GENRE_MAP dict translating Dutch NLZIET genres to kodiDvbGenres.xml canonical English strings - Ship resources/data/pvr_genres.xml mapping English canonical genre text to DVB hex colour codes - retroservice.py installs pvr_genres.xml into pvr.iptvsimple's genreTextMappings directory on startup (version-tracked) - Fix: movie genre fallback now fires when enrichment cache exists but has no genre (was only firing when cache entry was absent) - Tests: genre translation, movie fallback, unknown passthrough, GENRE_MAP ↔ pvr_genres.xml consistency check Co-authored-by: Claude Opus 4.6 (high) Signed-off-by: Your Name <you@example.com>
When fetch_and_cache hits a network error (ConnectionError / DNS failure), UriHandler.open() raises rather than returning None. This exception was uncaught, propagating through create_iptv_epg and causing NLZIET to contribute an empty dict to the IPTV Manager EPG response. Fix: wrap UriHandler.open() in a try/except; on any exception, log a warning and break out of the batch loop early (all remaining items in the same batch will fail the same way, so aborting saves time). The rest of create_iptv_epg continues normally and returns whatever EPG data is already in the progloc cache. Co-authored-by: Claude Sonnet 4.6 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Your Name <you@example.com>
Replace the standalone inputstream.adaptive.live_offset property (which
ISA never read) with the manifest_config JSON blob approach introduced by
the local ISA patches (0003 + 0004 in ~/src/archlinux/kodi-addon-inputstream-adaptive).
The patched ISA adds a virtual +1 chapter to all live DASH streams, which
activates Kodi's |< and >| buttons:
- |< (SeekChapter 1) → seeks to m_liveOffset seconds from stream start
= start of current programme (~180 s buffer)
- >| (SeekChapter virtual) → iterates enabled streams, seeks to getMaxTimeMs()
= true live edge
On unpatched ISA, manifest_config already exists; unknown keys emit a
LOGERROR but are otherwise ignored, so this change is harmless there.
Co-authored-by: Claude Sonnet 4.6
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Your Name <you@example.com>
Server time (/v7/currenttime): - Add API_V7_CURRENT_TIME constant to api.py - Add Channel.__get_server_time(): fetches /v7/currenttime (ms float), converts to seconds; falls back to time.time() on any error. Intentionally private per-channel — other channels use their own time reference in create_iptv_epg. - Replace 'now_ts = time.time()' in create_iptv_epg with 'now_ts = self.__get_server_time()' so live/replay/watch-ahead boundary decisions use the authoritative NLZIET server clock. Per-channel genre XML: - Add channels/channel.nlziet/nlziet/genres.xml listing only the genres actually present in GENRE_MAP. retroservice discovers and merges these on top of the base pvr_genres.xml; channel entries override same-genreId entries from the base. Co-authored-by: Claude Sonnet 4.6 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Your Name <you@example.com>
…tance _PVR_GENRES_VERSION bumped to 2 to trigger reinstall on upgrade. _merge_genre_xmls(): - Parses base resources/data/pvr_genres.xml - Walks channels/**/genres.xml; channel entries override same-genreId entries from the base (channels control only genres they emit) - Builds a merged genres.xml with version header _set_xml_setting(): - String-level XML patcher that updates a <setting id=...> element (strips default='true' attribute), or appends the element when absent - Preserves file formatting _configure_pvr_instances(): - Scans all instance-settings-N.xml in pvr.iptvsimple's profile dir - For instances whose m3uPath contains 'service.iptv.manager' (i.e. the Retrospect IPTV instance), sets useEpgGenreText=true, genresPathType=0, genresPath to the installed genres.xml path _setup_pvr_genres(): - Version-checks merged genres.xml before reinstalling - When already at current version: still calls _configure_pvr_instances (idempotent — no-op if already correct) so fresh pvr.iptvsimple installs get configured on the next service start pvr_genres.xml: update comment to version 2, clarify role as base file. Co-authored-by: Claude Sonnet 4.6 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Your Name <you@example.com>
_configure_pvr_instances() now also sets catchupEnabled=true and catchupOnlyOnFinishedProgrammes=false in the instance XML. Previously these were only set in the global addon settings via configure(), but per-instance settings take precedence and the instance file had catchupEnabled=false (the pvr.iptvsimple default). Without catchup enabled at instance level, clicking a future EPG entry with a catchup-id always showed 'Record or Switch to Live' instead of offering to play the watch-ahead stream. Bump _PVR_GENRES_VERSION to 3 to trigger re-application on next startup. Co-authored-by: Claude Sonnet 4.6 Signed-off-by: Oliver Klee <klee@oliver> Signed-off-by: Your Name <you@example.com>
retroservice: add _has_epg_programme_data() that scans epg.xml for any <programme> element. On the first tick, if the progloc cache exists (has_epg_data=True) but the EPG file has no programme entries — which happens when the file was last written during a network outage — call _iptv_manager_signal() immediately to force service.iptv.manager to re-run create_iptv_epg and repopulate the file. chn_nlziet: guard __get_server_time() against implausible server responses. If the server-reported time differs from the local clock by more than 300 s (5 minutes), fall back to time.time(). This covers cases where the API returns malformed data, an unexpected payload (e.g. seconds instead of milliseconds), or a zero/future timestamp — the delta will naturally exceed the threshold, so no explicit format check is required. Co-authored-by: Claude Sonnet 4.6 Signed-off-by: Oliver Klee <klee@oliver> Signed-off-by: Your Name <you@example.com>
Future programmes can have both is_replay=True (will be replayable once aired) and is_watch_ahead=True (playable now in advance). The 'and not is_replay' guard excluded these, so Brugklas never got a catchup-id in the EPG. The time-based guards already make the two branches mutually exclusive: - is_replay fires on e_ts <= now_ts (programme has ended) - is_watch_ahead fires on s_ts > now_ts (programme hasn't started) There is no overlap, so 'not is_replay' is unnecessary and wrong. Co-authored-by: Claude Sonnet 4.6 Signed-off-by: Oliver Klee <klee@oliver> Signed-off-by: Your Name <you@example.com>
…ven refresh - OAuth2Handler.__init__ reads _access_token, _refresh_token, _expires_at from AddonSettings once at construction; no per-call settings I/O - _store_tokens() updates both settings and instance vars atomically - _do_token_refresh(): unconditional internal refresh (single _ so subclass can override); base uses refresh_token grant - refresh_access_token() -> bool: no-op when > 300s from expiry (_REFRESH_MARGIN), calls _do_token_refresh() otherwise; returns True/False instead of raising - get_valid_token(): pure getter returning self._access_token — no side-effects; callers that need a fresh token must call refresh_access_token() first - active_authentication(): calls refresh_access_token() before get_valid_token() - _clear_token_settings(): clears both settings and instance vars NLZIETOAuth2Handler: - __init__ caches _id_token from settings - _do_token_refresh() override: silent re-auth via id_token (web flow) or refresh_token grant (device flow); calls super()._do_token_refresh() for latter - 401 fallback in list_profiles() calls _do_token_refresh() directly (force-refresh, bypasses expiry check) - _store_tokens() and _clear_token_settings() maintain _id_token in sync chn_nlziet: - __set_auth_headers(): refresh_access_token() then get_valid_token() - create_iptv_streams() + create_iptv_epg(): refresh token on every heartbeat cycle so long IPTV Manager sessions (4+ hours) never hit stale tokens Tests (all 55 pass, 18 skipped = live-credential tests): - Updated test_06, test_16: set _expires_at on instance (not just settings), call refresh_access_token() explicitly - Updated test_refresh_no_tokens: assertFalse() instead of assertRaises(ValueError) - New test_refresh_access_token_noop_when_valid: monkey-patch verifies _do_token_refresh is NOT called when token is fresh - New test_get_valid_token_is_pure_getter: verifies no refresh side-effect - New test_instance_vars_loaded_from_settings_at_init: verifies all three instance vars populated from settings at construction Co-authored-by: Claude Sonnet 4.6 Signed-off-by: Oliver Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
|
|
just dropping a quick status update; while this branch may have not seen any pushes for a while; i've been busy with it (and suffering 'trying to do too much' ... also, there's one significant flaw in my approach, that I have no fixed. I'll try to push again here soon, and move this forward. |
|
Thanks for the update. I just merged some of the other PR's for you. Let me know if there are any other things I can help you with. |





Functional description
This merge requests adds support for NLZIET for both live streams, VOD, profile support with device flow and username/password authentication.
Reasoning
Closes #645
Technical description
A new channel was added, and some plumbing fixed.
Tasks & Activities