-
-
Notifications
You must be signed in to change notification settings - Fork 113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
save_last_sync try to convert HTTP date header with a localized python date-time parser which is incorrect #916
Comments
I haven't dug quite deep enough yet, but using ISO time here would be best. If not already available somewhere in the API response, it should be added either as a custom header, or an additional field, in the plugin. TODO: Investigate the relevant API responses and their structure, to see if ISO time is provided, or if the structure of the response allows for an additional field containing the server datetime. Messing with localized time string shall be avoided if at all possible, only trouble lies down that path. If I could decide, the whole world would switch to ISO 8601, UTC, with no DST. That is how big of a pain this is. |
I should note that this failure to parse datetime shouldn't be an issue unless the server is more than 2min out of sync with the client, something that is highly unlikely (should be near impossible) if both the server and client are properly synced with a time server. The consequence of the time being sufficiently out of sync (for this particular function anyways) is that content update might be missing during parial sync (events that happened in the window between server time and client time, minus the 2min tolerance that is put in the code here via a timedelta). The error in the log is otherwise harmless, except from potentially causing slightly slower sync times and larger log files (the error is caught, then logged – should probably get changed to only log the message as a warning, not the whole stack-trace as an error). |
OK. So I will lower its priority on my TODO.
I don't understand. The date string from HTTP is not localized, and is already UTC/GMT (only it is not IS0 8601, but is that critical?). I agree that with an ISO date we would be able to use a localized date parsing function like strptime without an error. Because there would be no string thus no localization (even though I am not confident numbers are always localized as indian/arabic numbers, then ISO would not help with strptime use). But I confirm that with this bug, jellyfin-kodi still works. Might be slower. I believe the best is to include a function to parse HTTP RFC9110 section 5.6.7 date format like django does. But I am uneasy copying and pasting it into jellyfin-kodi as django is BSD 3-Clause, not GPL v3. In the meantime, we could the closest available function in python libraries, email.utils.parsedate_to_datetime if requiring python 3.3 or above is OK. I confirm there is no "8601" string in KodiSyncQueue, so probably no API that returns an X-ISO-8601 date. |
@prahal I realize that you probably wanted to work on this, so I apologize for jumping in and doing it myself 🫠 I've implemented a Both PRs could do with some testing, especially the one for this repo, as I've yet to test the different code-paths of it, if you're interested in having a look at that. |
You use strptime in your PR which is wrong to me. You did not answer my comments about iso 8601 use not adding any value or to the use of strptime being wrong. So I cannot guess why you insist on using them. I am no expert especially on python. But this PR seems wrong to me. |
The HTTP date format does not make sense for software-to-software communication, it doesn't now, it didn't then. We could use We're not setting LC_TIME system global, only in the current python context (whatever that means in Kodi), and we re-set it to the previous value once done. @mcarlton00 could I get your take on this? |
Haven't dug super deeply, but from what I can gather from a quick search through the code this is only an issue for the I agree HTTP date format doesn't make much sense here. Just more work to parse and has edge cases like this (though how nobody has ran into this issue in the last 5 years is somewhat shocking), plus the fact that no user will ever view this value. Not sure about the locale changing shenanigans in the current implementation. Feels hacky. But as far as I can tell it would also only take effect when the user is running a newer version of the addon with an older version of the server plugin. That bit of code could definitely use some more comments explaining the "why". To me, Using the Personally not a fan of copy/pasting django's function here. imo straight copying functions from other projects is a bad habit to get into, because there's always potential for licensing violations (though it seems fine in this case), or not giving proper credit to where we got it. Also using regex for parsing dates is kinda overkill when there's other options. |
Updated to use the email library (inline import). |
It requires to read the Kodi logs to see the error. I expect no user does so. Only last_sync is saved and its format is orthogonal to the format of the KodiSyncQueue call HTTP header date used to compute time_now. Note that for time_now we parse the HTTP date header coming from the ASPnet server KodiSyncQueue runs in. It is definitely wrong to use strptime on an HTTP date.
Remains the question if we can use email.utils.parsedate_to_datetime (python >= 3.3) or if we should use email.utils.parsedate then convert it to datetime.
One don't need to copy paste it. All in all it is a regex with placeholders. But to avoid contamination, one would need to have someone that did not see the django imploementation to reimplement it based on instruction. Cumbersome but still. quiproquo: last_sync is also using a localized date function strftime which is in theory wrong but ... well. But as it is in the date format "%Y-%m-%dT%H:%M:%Sz" it should not have n adverse impact out of bad practice. Ie there is no string that can change between locales. The format last_sync is saved in could have matter if it was localized. , and if the python code changed locale between calls to the API (which is one reason more, the mangling of LC_TIME via setlocale to get strptime to not work as localized would be bad. It could affect all other date localized calls in the Kodi space). This is not the case for the strptime call to compute time_now which use Still, it is confusing and hinder debugging works. Better get it fixed. |
Are you sure? I was confident setlocale effect was global and that was the whole point its use was heavily discouraged. Or do you mean the plugin runs in its own process. Note: email.utils.parsedate or regex parsing as django would work with previous KodiSyncQueue API. Switching to ISO 8601 is a goal but totally orthogonal to this bug report. |
Could you make the two changes in two separate PRs? The format we parse KodiSyncQueue date from and the format last_sync is stored as are different matters. |
I see no benefit in splitting it up to separate PRs, and that would create extra work for @mcarlton00 and myself. |
Describe the bug
When the locale has a month name format "%b" which is not English save_last sync raise an exception because it cannot parse the server-date (server-date is the HTTP header "Date" of the HTTP request, from
.kodi/addons/plugin.video.jellyfin/jellyfin_kodi/jellyfin/http.py
:self.config.data["server-time"] = r.headers["Date"]
).HTTP Date has three format https://datatracker.ietf.org/doc/html/rfc9110.html#section-5.6.7, the "preferred format is a fixed-length and single-zone subset of the date and time specification used by the Internet Message Format [RFC5322]". That is https://datatracker.ietf.org/doc/html/rfc5322#section-3.3.
strptime is wrong as HTTP Date is not localized, we should use a dedicated function.
Seems the closest to parse HTTP date is to parse it as an email date with email.utils.parsedate_to_datetime. https://stackoverflow.com/questions/1471987/how-do-i-parse-an-http-date-string-in-python (not that I don't have enough reputation to comment on the wrong answers that is rfc822 which is not more appropriate that email.utils as rfc822 is the obsolete RFC for email dates (the current one is https://datatracker.ietf.org/doc/html/rfc2822#section-3.3 which is what email.utils.parse is about), not an RFC for HTTP dates. And the strptime answer that as this bug re'port shows does not work on localized systems, i.e. with an LC_TIME not English).
Also see the discussion in #288 (comment)
I disagree that ISO8601 is the way to go because server-date is about the HTTP date. We will have to parse the HTTP date format either way, so why not keep it the IMF format from RFC5322?
The issue is that we parse a non localized datetime string with a localized function, not that the format is wrong.
But do we agree to keep RFC5322 IMF date format?
Do we agree to use email.utils.parsedate_to_datetime (it is python 3.3 and above) or should I use email.utils.parse and then convert to datetime?
And is it fine to use email.utils.parsedate rfc2822 even if I believe it does not cope with the two obsolete HTTP date formats that the RFC9110 says we MUST be able to read?
Maybe test cases would be good too.
Probably a test case with a locale with non-English month names, one that 2 digit year, a non rfc822 date string. Do you see others?
Edit: wed could also include djjango parse_http_date codfe which seems straightforward:
https://github.com/django/django/blob/stable/5.1.x/django/utils/http.py#L97
To Reproduce
Expected behavior
No error in Kodi log and probably a working last sync guard.
Logs
Screenshots
System (please complete the following information):
Additional context
I tested this that seems to work.
The text was updated successfully, but these errors were encountered: