Open prahal opened 2 days ago
I haven't dug quite deep enough yet, but using ISO time here would be best. The requests the dates are coming from are presumably handled by the Jellyfin plugin, KodiSyncQueue.
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.
Failing that, a response header with the ISO datetime should be added. X-ISO-8601
or some such if no (defacto) standard header for this exists.
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).
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.
OK. So I will lower its priority on my TODO.
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 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?).
strptime in save_last_sync expects the current month for a user session with a French locale to be sept.
(%b
) while the HTTP Date header returns a non localized "Sep"' for the month of September.
Thus, I get:
ValueError: time data '16 Sep 2024 10:40:33 GMT' does not match format '%d %b %Y %H:%M:%S GMT'
because strptime expect
16 sept. 2024 10:40:33 GMT
for '%d %b %Y %H:%M:%S GMT
It bugs because the date is not localized, not the opposite.
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 in my opinion, the issue is not the date format. It is about using strptime to parse a date that is not localized. I don't think these functions are intended to be used in this context (even for an ISO8601 date format).
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. We could ask one to rewrite a similar function based on general guidelines, i.e. parse the date with regex placeholders, support 2 digit years. Or even ask django developers for permission to reuse this code. But now that I saw their code, I believe I cannot write this function myself (probably I should not have pasted the function here to avoid other having the same issue, do you want me to edit it out?).
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. Or email.utils.parsedate then convert to datetime. Are you fine with this option? Or only an ISO 8601 patch would be merged?
I confirm there is no "8601" string in KodiSyncQueue, so probably no API that returns an X-ISO-8601 date. But as far as I looked, KodiSyncQeue does add or modify HTTP headers. I believe the HTTP Date header comes from Microsoft ASP dotnet.
@prahal I realize that you probably wanted to work on this, so I apologize for jumping in and doing it myself :melting_face:
I've implemented a Server-Time
header in KodiSyncQueue, in addition to (probably) fixing the parsing of the standard Date header.
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. Feel free to comment on my code too, if you have questions around any of it :slightly_smiling_face:
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 https://github.com/jellyfin/jellyfin-kodi/issues/288#issuecomment-617950518 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.