Skip to content

Add NLZIET support#1934

Draft
oliv3r wants to merge 29 commits intoretrospect-addon:masterfrom
oliv3r:nlziet
Draft

Add NLZIET support#1934
oliv3r wants to merge 29 commits intoretrospect-addon:masterfrom
oliv3r:nlziet

Conversation

@oliv3r
Copy link

@oliv3r oliv3r commented Feb 26, 2026

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

  • Review some commits, not everything is perfect yet
  • Fix the 'jump back to start of stream
  • Investigate if we can change the buttons to actual buttons in the authentication screen
  • Endurance testing (had a bug where after watching a Live stream for a long time (65 minutes) changing channels took significant time due to failure to refresh token, should be fixed, but not sure yet
  • Testing of 'extra' channels, i don't have that type of account
  • Setup a NLZIET account specific for retrospect. When creating the 'free for the first 2 weeks' account, it is possible to run the guarded tests. canceling the account within those 2 weeks breaks streaming capability, but the account and its API's still work. That would need to be added to github for the pipeline to run real API tests.
  • Fix a bug on the first/last/continue shortcut which is not de-duplicating.
  • Validate IPTV support (haven't gotten it to work yet)
  • Add 'up-next' support
  • Fixup QR code inclusion
  • Test 'big list' categories (just forgot to look at it)

Added None-checks in all Logger static methods to safely handle calls
during Python interpreter shutdown when the Logger singleton may already
be destroyed. Prevents AttributeError in __del__ destructors.

Co-authored-by: Anthropic Claude AI
Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
When a channel's main list requires login and log_on() fails (e.g.
user cancels device auth), return None from process_folder_list
instead of an empty list. FolderAction detects the None sentinel and
calls endOfDirectory(handle, False), causing Kodi to navigate back to
the channel overview.

A brief login-error notification is shown (matching the sub-item
login failure pattern at line 500-508). Previously the login failure
was silently ignored, leaving the user on an empty folder.

Co-authored-by: Anthropic Claude AI
Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
Container.Update triggers a separate plugin instance that races with
endOfDirectory. Slow responses (e.g. token exchange, profile switch)
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: Anthropic Claude AI
Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
Empty folder results previously showed an Error notification regardless
of the configured empty_list_behavior setting. Now only the 'error'
behavior triggers an Error notification; all other modes use Info.

Co-authored-by: Anthropic Claude AI
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
(dontGroup) or live items are present, matching the existing search
behavior.

Co-authored-by: Anthropic Claude AI
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 shown only when Widevine is not detected.

Co-authored-by: Anthropic Claude AI
Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
The if/elif chain dispatched on body type first, with the HTTP method
(PATCH) 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 ~20 callers. Ideally callers should specify the
method explicitly and the handler should reject ambiguous requests.

Co-authored-by: Anthropic Claude AI
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.

Co-authored-by: Anthropic Claude AI
Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
@oliv3r
Copy link
Author

oliv3r commented Feb 26, 2026

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.

@basrieter
Copy link
Collaborator

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.

@oliv3r oliv3r marked this pull request as draft February 26, 2026 21:51
@oliv3r
Copy link
Author

oliv3r commented Feb 26, 2026

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.
Please do; but realize even I am not happy with it yet ;)

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.

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.

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.

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.

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.

@oliv3r oliv3r changed the title [DRAFT] Add NLZIET support Add NLZIET support Feb 26, 2026
@oliv3r
Copy link
Author

oliv3r commented Feb 26, 2026 via email

Copy link
Collaborator

@basrieter basrieter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Author

@oliv3r oliv3r Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, fair enough; as mentioned above. i'll fix it sunday :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use the RETROSPECT_STDOUT_LOGGING to disable logging, not via these changes.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are old debug commits; as I said, I still need to review most of the code changes :p

@basrieter
Copy link
Collaborator

I also noticed that if you have a PVR add-on installed, the pop-up for NLZiet appears, even when NPO Start is active:

image

return None


class DeviceAuthDialog(xbmcgui.WindowDialog):
Copy link
Collaborator

@basrieter basrieter Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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")')?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@oliv3r
Copy link
Author

oliv3r commented Feb 27, 2026

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.

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.

@oliv3r
Copy link
Author

oliv3r commented Feb 27, 2026

I also noticed that if you have a PVR add-on installed, the pop-up for NLZiet appears, even when NPO Start is active:
image

interesting, this I will have to check why this is, right now, i think the pop-up comes when the channel gets loaded and we detect no configured session, not sure if it's related to what you mention though. I'll have to think what would work best, only when entering the NLZIET channel list and we're not being authenticated.

Channels that support user profiles (e.g. kids, adult, named) need isolated
search histories so queries from one profile do not appear in another.

Add a search_profile_id property to Channel (returns None by default).
When a channel overrides it, SearchAction uses 'search:<profile_id>'
as the settings key instead of the plain 'search' key.

Co-authored-by: Anthropic Claude AI
Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
oliv3r and others added 8 commits March 1, 2026 17:57
Implemented complete OAuth2 authentication for NLZIET channel supporting
both headless login (PKCE flow) and device flow (RFC 8628) for TV devices.

Key features:
- Headless browser-based username/password login
- Session detection to skip login when already authenticated
- Device flow for devices (nlziet.nl/koppel)
- Automatic token refresh with silent re-authentication
- Comprehensive test suite with AUTO/MANUAL user/pass authentication

Based on OAuth2 implementation [0] by ArjenD and dut-iptv.

[0]: https://github.com/dut-sees/kodi-nlziet

Co-authored-by: Anthropic Claude AI
Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
Signed-off-by: Your Name <you@example.com>
Minimal NLZIET channel implementation:
- Channel registration with dynamic channel discovery
- Username/password authentication
- Device flow authentication
- Automatic profile selection after login (auto-selects if single profile)

Co-authored-by: Anthropic Claude AI
Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
Signed-off-by: Your Name <you@example.com>
Add optional QR code display to DeviceAuthDialog for device flow
authentication. When a caller supplies the qr_url parameter, a QR code
for that parameter is inserted into the dialog box.

Co-authored-by: Anthropic Claude AI
Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
Signed-off-by: Your Name <you@example.com>
Adds live TV channel listing with DASH/Widevine playback, VOD
categories (Movies, TV Shows, Trending, Recommended), Continue
Watching, My List (watchlist), series/season/episode lists,
and search functionality.

Rich metadata: descriptions, poster/landscape artwork, channel
logos, studio labels, broadcast dates, availability windows, and
expiration dates. Items marked unavailable are filtered out.

Co-authored-by: Anthropic Claude AI
Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
Signed-off-by: Your Name <you@example.com>
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: Anthropic Claude AI
Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
Signed-off-by: Your Name <you@example.com>
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: Anthropic Claude AI
Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
Signed-off-by: Your Name <you@example.com>
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
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Your Name <you@example.com>
Co-authored-by: Anthropic Claude AI
Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
oliv3r added 2 commits March 2, 2026 00:03
Co-authored-by: Anthropic Claude AI
Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
Co-authored-by: Anthropic Claude AI
Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
@powermarcel10
Copy link

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.

Your Name added 10 commits March 2, 2026 20:17
- start_device_flow(): re-raise OSError (catches requests.ConnectionError,
  socket.gaierror, etc.) so callers can distinguish network failures from
  other errors.  Non-OSError exceptions still return None as before.

- __run_device_flow(): wrap start_device_flow() — OSError shows #30615
  'Could not connect to service' instead of #30620 'Failed to start device
  setup', which was misleading when the real cause was no internet.

- DeviceAuthDialog: replace onControl(control)/control.getId() with the
  standard Kodi idiom onClick(controlId) (integer dispatch).

- DeviceAuthDialog: add threading.Event stop_event, set unconditionally
  in onClosed() before the safety-net logic.  Fires for every close path:
  button click, Escape/Back, programmatic close(), success, timeout.

- __poll_with_progress(): replace monitor.waitForAbort(0.5) with
  dialog.stop_event.wait(0.5) so the poll thread wakes immediately when
  the dialog closes rather than sleeping through its 0.5 s tick.
  Kodi abort is still checked via monitor.abortRequested() after the wait.

- DeviceAuthDialog.xml: add <onclick>Close()</onclick> to the Cancel
  button — standard Kodi UX convention.

Co-authored-by: Claude Sonnet 4.6
Signed-off-by: Your Name <you@example.com>
- Shift visit-instruction, URL, enter-code, and user-code labels down
  64 px (+64 from top=166→230, 208→272, 250→314, 292→356) to create
  roughly two visual lines of breathing room between the QR instruction
  text and the URL/code section.

- Replace hardcoded texturenofocus (0xFF404040) with <texturenofocus />
  (no background when unfocused) — the idiomatic Kodi pattern used in
  skin.estuary/Custom_1100_AddonLauncher.xml; eliminates the hardcoded
  hex value entirely.  texturefocus keeps the named button_focus colour.

- Pair texturenofocus / with textcolor=grey + focusedcolor=white so:
  unfocused state: grey label text, no background (readable on light bg)
  focused state:   white label text on button_focus coloured rectangle

Co-authored-by: Claude Sonnet 4.6
Signed-off-by: Your Name <you@example.com>
The 2-of-3 majority vote in __pick_boundary_episode failed when
broadcastAt timestamps were corrupted by re-broadcasts (De Luizenmoeder
S1) or bulk imports (Flikken Maastricht S1).  With firstAvailable always
empty, the vote degraded to a 1-vs-1 tie that fell back to broadcastAt
— exactly the wrong signal.

Replace the vote with a simpler strategy: trust API order (always
correct) and use broadcastAt monotonicity only to detect ascending vs
descending direction.  Non-monotonic or absent timestamps default to
descending (the most common ordering across the NLZIET catalogue).

Changes:
- __fetch_season_episodes: return 2-tuples (broadcastAt, item) instead
  of 3-tuples, since firstAvailable is always empty.
- __pick_boundary_episode: replaced vote logic with direction detection.
- Added __is_ascending static method for broadcastAt monotonicity check.
- Updated existing tests for new tuple format.
- Added 15 new unit tests covering descending, ascending, corrupted
  broadcastAt (Flikken M. S1), re-broadcasts (De Luizenmoeder S1),
  single/empty lists, and __is_ascending edge cases.

Co-authored-by: Claude Opus 4.6
Signed-off-by: Oliver Bramley <oliver@bramley.dev>
Signed-off-by: Your Name <you@example.com>
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>
- Switch from broken qrcode.image.pure (requires pymaging) to
  qrcode.make() default which uses PilImage when PIL is available
- Generate QR PNG in set_content() (before onInit) to keep UI thread fast
- Remove <visible>false</visible> from XML; manage all control visibility
  exclusively from Python onInit() per Kodi best practice
- Hide QR text label when no qr_url is set (was showing blank)
- Adjust QR image top position to sit just above the progress bar

Co-authored-by: Claude Sonnet 4.6
Signed-off-by: Your Name <you@example.com>
- Auth: initialise _time_since_poll = _interval so the first device-flow
  poll fires within ~0.5 s instead of waiting a full interval (~5 s).
  Approval is detected almost immediately after the user scans the QR code.

- Live stream big-skip navigation (DiagnosticPlayer / DiagnosticMonitor):
  * onAVStarted detects NLZIET live streams by URL pattern (nlziet-linear).
  * onPlayBackSeek redirects big backward skips (>4 min) to seekTime(0)
    (start of 2-hour timeshift buffer) and big forward skips (>4 min) to
    seekTime(getTotalTime()-5) (live edge).
  * DiagnosticMonitor.onNotification intercepts Player.OnSeek for blocked
    forward seeks at the live edge (inputstream.adaptive silently rejects
    these, so onPlayBackSeek never fires, but the JSON-RPC notification
    carries seekoffset and still arrives).
  * A 2-second re-entry guard (_redirect_until) prevents the redirect
    seekTime() call from triggering a second redirect.
  * DiagnosticMonitor now receives a player reference via __init__
    (player is created before monitor to satisfy the dependency).
  * onPlayBackStopped / onPlayBackEnded / onPlayBackError all clear
    _nlziet_live so the overrides do not bleed into the next stream.

Co-authored-by: Claude Sonnet 4.6
Signed-off-by: Oliver Schillings <o.schillings@outlook.com>
Signed-off-by: Your Name <you@example.com>
- Add xbmcgui.Dialog().notification() before seekTime() calls in
  DiagnosticPlayer and DiagnosticMonitor so the user sees a toast
  ("Naar live" / "Naar begin uitzending") even when the buffer jump
  is subtle (fresh stream with little history).
- Fix nlziet_password setting type: type="password" was dropped in
  Kodi Nexus (20) and generated a parse warning on every startup.
  Changed to type="text" -- the vault handles encryption on disk.

Co-authored-by: Claude Sonnet 4.6
Signed-off-by: Your Name <you@example.com>
When a live start offset is configured (> 0), inject the
inputstream.adaptive.live_offset property onto the resolved stream.
ISA uses this value in GetChapterPos() so that |< seeks to the
post-ads position instead of the raw buffer start (second 0).

Co-authored-by: Claude Sonnet 4.6
Signed-off-by: Oliver Knieps <ok@example.com>
Signed-off-by: Your Name <you@example.com>
Change type from 'number' to 'slider' with option='int' and
range='-60,10,300' so Kodi enforces min/max bounds visually
and won't accept out-of-range values (which previously caused
CSettingsManager to return None, yielding start_offset=0).

Co-authored-by: Claude Sonnet 4.6
Signed-off-by: Your Name <you@example.com>
@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 2, 2026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NLZiet addition to retrospect

3 participants