Conversation
- Add CHANNEL_CACHE_TTL constant (24 hours) to SiriusXM package - Add File::Basename and File::Spec imports to SiriusXM package - Add channel_cache_file and channel_cache_expires fields to new() - Load channel cache from disk at startup via load_channel_cache() - New load_channel_cache(): reads channels.json, handles expired cache by still serving old data while scheduling immediate background refresh - New save_channel_cache(): persists channel list as JSON with fetched_at/expires_at envelope in the same dir as the cookie file - get_channels() now calls save_channel_cache() after successful API fetch - New refresh_channel_cache_if_expired(): background refresh that keeps old channel list available for serving during the refresh; retries in 5 minutes on failure - Wire refresh_channel_cache_if_expired() into start_http_daemon() loop Co-authored-by: paul-1 <6473457+paul-1@users.noreply.github.com>
Call get_channels() in start_server() after authentication so that: - No cache file: channels are fetched from the API and saved to disk - Corrupt cache: channels are fetched from the API and saved to disk - Expired cache: channels were already loaded by load_channel_cache() and the background refresh loop updates them asynchronously Co-authored-by: paul-1 <6473457+paul-1@users.noreply.github.com>
Fixed in 16dce82.
|
paul-1
left a comment
There was a problem hiding this comment.
@copilot When the client requests channel data, just return the cached channel information, do not expire the cache.
See:
[22.02.2026 22:22:17.656] DEBUG SiriusHandler::handle_http_request:2720: GET request: /channel/all
[22.02.2026 22:22:17.656] DEBUG SiriusHandler::handle_http_request:2811: Channel info request for: all
[22.02.2026 22:22:17.656] DEBUG SiriusXM::refresh_channels:2410: Refreshing channel data (clearing cache)
…g refresh Replace refresh_channels() with get_channels() in the /channel/all HTTP handler so client requests always return the cached list. The background refresh loop (refresh_channel_cache_if_expired) is the only path that triggers a live API fetch. Co-authored-by: paul-1 <6473457+paul-1@users.noreply.github.com>
Fixed in 3f95ce3. The |
|
@copilot can you check to make sure that before trying to refresh channel data, that we check the session cookies in the global cookie to make sure they are not expired. |
In refresh_channel_cache_if_expired(), check is_session_authenticated() against the global cookie jar before attempting the API call. If the session cookies are expired, re-authenticate first; if re-auth fails, skip this cycle and let the 5-minute retry handle it. Co-authored-by: paul-1 <6473457+paul-1@users.noreply.github.com>
Done in 8eb328e. |
paul-1
left a comment
There was a problem hiding this comment.
@copilot Something is wrong with checking session cookie expiration.
The log shows the system was idle for almost 24 hours, when the system tried to update the channel data. There is no way a Session Cookie lasts 24hours, infact we manually set the expiration at 14400 seconds.
[26.02.2026 22:25:49.746] DEBUG <SiriusHandler::start_http_daemon>:2717: Client connection closed
[27.02.2026 19:41:11.087] INFO <SiriusXM::refresh_channel_cache_if_expired>:2204: Channel cache expired – fetching fresh channel list in background...
[27.02.2026 19:41:11.612] WARN <SiriusXM::refresh_channel_cache_if_expired>:2254: Background channel refresh: invalid or empty response – keeping existing channel list
[27.02.2026 19:46:11.934] INFO <SiriusXM::refresh_channel_cache_if_expired>:2204: Channel cache expired – fetching fresh channel list in background...
[27.02.2026 19:46:12.229] WARN <SiriusXM::refresh_channel_cache_if_expired>:2254: Background channel refresh: invalid or empty response – keeping existing channel list
[27.02.2026 19:51:11.544] INFO <SiriusXM::refresh_channel_cache_if_expired>:2204: Channel cache expired – fetching fresh channel list in background...
[27.02.2026 19:51:11.890] WARN <SiriusXM::refresh_channel_cache_if_expired>:2254: Background channel refresh: invalid or empty response – keeping existing channel list
[27.02.2026 19:56:11.204] INFO <SiriusXM::refresh_channel_cache_if_expired>:2204: Channel cache expired – fetching fresh channel list in background...
This same log messages go on for a whole day.. Restating sxm.pl the system properly renews the session and updates the channels.
[28.02.2026 11:13:08.399] INFO <main::signal_handler>: 349: Received signal TERM, shutting down gracefully...
[28.02.2026 11:13:10.538] INFO <main::init_logging>: 264: SiriusXM Proxy logging initialized with level: DEBUG
[28.02.2026 11:13:10.538] INFO <main::init_logging>: 265: File logging enabled: /opt/slimserver/Logs/sxm-proxy.log
[28.02.2026 11:13:10.538] INFO <main::init_logging>: 272: Cookie file: /opt/slimserver/Cache/siriusxm/sxm.txt
[28.02.2026 11:13:10.538] INFO <main::main>:3089: Starting SiriusXM Perl proxy v1.1.0
[28.02.2026 11:13:10.538] INFO <SiriusXM::new>: 442: Loaded cookies from: /opt/slimserver/Cache/siriusxm/sxm.txt
[28.02.2026 11:13:10.538] INFO <SiriusXM::__ANON__>: 518: Cookie SXMDATA (global): expires 2094-03-17 04:07:52 UTC (in 24853d 11h 54m)
[28.02.2026 11:13:10.538] INFO <SiriusXM::__ANON__>: 518: Cookie AWSALB (global): expires 2026-03-06 00:54:47 UTC (in 5d 8h 41m)
[28.02.2026 11:13:10.538] INFO <SiriusXM::__ANON__>: 507: Cookie JSESSIONID (global): no expiration set, estimated lifetime ~4h (expires ~2026-02-28 20:13:10 UTC)
[28.02.2026 11:13:10.539] DEBUG <SiriusXM::new>: 465: SiriusXM object created for user: phermann2@outlook.com, region: US
[28.02.2026 11:13:10.539] DEBUG <SiriusXM::new>: 471: Channel cache file: /opt/slimserver/Cache/siriusxm/channels.json
[28.02.2026 11:13:10.562] INFO <SiriusXM::load_channel_cache>:2143: Loaded 435 channels from expired cache (expired: 2026-02-28 00:41:11 UTC) – background refresh will run
[28.02.2026 11:13:10.563] INFO <main::start_server>:3048: Starting HTTP server on port 8888
[28.02.2026 11:13:10.563] DEBUG <SiriusXM::__ANON__>: 876: SXMDATA cookie valid for 24853d 11h
[28.02.2026 11:13:10.563] DEBUG <SiriusXM::authenticate>:1123: Attempting to authenticate session for global
[28.02.2026 11:13:10.940] INFO <SiriusXM::authenticate>:1158: Session authentication successful for global
[28.02.2026 11:13:10.941] INFO <SiriusXM::__ANON__>: 518: Cookie SXMDATA (global): expires 2094-03-18 19:27:17 UTC (in 24855d 3h 14m)
[28.02.2026 11:13:10.941] INFO <SiriusXM::__ANON__>: 518: Cookie AWSALB (global): expires 2026-03-07 16:13:10 UTC (in 7d 0h 0m)
[28.02.2026 11:13:10.941] INFO <SiriusXM::__ANON__>: 507: Cookie JSESSIONID (global): no expiration set, estimated lifetime ~4h (expires ~2026-02-28 20:13:10 UTC)
[28.02.2026 11:13:10.941] INFO <main::start_server>:3056: Authentication successful - starting server
[28.02.2026 11:13:10.941] INFO <main::start_server>:3068: Channel list ready at startup: 435 channels
[28.02.2026 11:13:10.941] INFO <SiriusHandler::start_http_daemon>:2654: HTTP server started on port 8888
[28.02.2026 11:13:10.941] INFO <SiriusHandler::start_http_daemon>:2655: Access channels at: http://127.0.0.1:8888/channel.m3u8
[28.02.2026 11:13:11.942] INFO <SiriusXM::refresh_channel_cache_if_expired>:2204: Channel cache expired – fetching fresh channel list in background...
[28.02.2026 11:13:12.556] INFO <SiriusXM::refresh_channel_cache_if_expired>:2260: Background channel refresh complete: 435 channels loaded
[28.02.2026 11:13:12.571] INFO <SiriusXM::save_channel_cache>:2184: Saved 435 channels to cache /opt/slimserver/Cache/siriusxm/channels.json (expires: 2026-03-01 16:13:12 UTC)
Replace the conditional is_session_authenticated() guard with an unconditional authenticate() call in refresh_channel_cache_if_expired(). Root cause: JSESSIONID has no explicit expiry timestamp in the cookie jar, so is_session_authenticated() always returned true even after the server had invalidated the session (after ~4h of idle). This caused the background refresh to call the API with a dead session, getting an empty response every 5 minutes indefinitely. Fix: always call authenticate(undef) before the channel fetch, mirroring what start_server() does on every startup. This ensures a live session regardless of cookie timestamp availability. Co-authored-by: paul-1 <6473457+paul-1@users.noreply.github.com>
Fixed in 84ef5e2. Root cause: Fix: replaced the conditional |
|
@copilot I do not like unconditional authentication, this can lead to rate blocking (Especially when debugging). The JSESSIONID cookie does not contain an expiry, but can we not track it ourselves. If would be best if this tracking is retained (saved on disk) across sessions either in the cookie itself, or a separate cache file |
Replace unconditional authenticate() in background channel refresh with a conditional check against a tracked jsessionid_expires timestamp. Changes: - Add jsessionid_expires field to SiriusXM object (default 0 = expired) - In authenticate(): when global context (channel_id undef) succeeds, set jsessionid_expires = time() + SESSION_MAX_LIFE (4 hours) - In save_channel_cache(): include jsessionid_expires in channels.json so the tracked expiry survives proxy restarts - In load_channel_cache(): restore jsessionid_expires from channels.json (defaults to 0 if absent, triggering re-auth on first background refresh) - In refresh_channel_cache_if_expired(): only call authenticate() when time() >= jsessionid_expires; otherwise log remaining validity and skip Co-authored-by: paul-1 <6473457+paul-1@users.noreply.github.com>
Done in 6093243. Instead of unconditional authentication, we now track the JSESSIONID lifetime ourselves:
The updated {
"fetched_at": 1740000000,
"expires_at": 1740086400,
"jsessionid_expires": 1740014400,
"channels": [ ... ]
} |
The proxy fetched the channel list from the SiriusXM API on every cold start and never persisted it, adding unnecessary latency and API calls. This adds a full cache lifecycle: save on fetch, load at startup, and async background refresh on expiry.
Cache format
channels.jsonis written to the same directory as the cookie file:{ "fetched_at": 1740000000, "expires_at": 1740086400, "jsessionid_expires": 1740014400, "channels": [ ... ] }Changes
CHANNEL_CACHE_TTL— new constant (86400 s / 24 h)SiriusXM::new()— deriveschannel_cache_filepath (<cookie_dir>/channels.json), initialiseschannel_cache_expiresandjsessionid_expiresfields, callsload_channel_cache()at construction timeload_channel_cache()— reads and validateschannels.jsonon startup; restores both the channel list and the trackedjsessionid_expirestimestamp; if the channel cache is expired it still populates$self->{channels}so requests are served immediately and setschannel_cache_expires = 0to trigger an immediate background refreshsave_channel_cache()— serialises the channel array withfetched_at/expires_at/jsessionid_expiresenvelope so the tracked session expiry survives proxy restartsauthenticate()— when a global-context authentication succeeds, recordsjsessionid_expires = time() + SESSION_MAX_LIFE(4 hours); JSESSIONID carries no explicit cookie timestamp so we track it ourselvesget_channels()— callssave_channel_cache()after every successful API fetchrefresh_channel_cache_if_expired()— background refresh called from the server loop; checksjsessionid_expires(our own tracked timestamp) and only callsauthenticate()when the session has elapsed, avoiding unnecessary re-authentication; keeps old channel list in place while fetching, falls back to old data on any failure and retries in 5 minutesstart_http_daemon()loop — wiresrefresh_channel_cache_if_expired()alongside the existingrefresh_expired_playlists()/process_segment_queues()callsstart_server()— callsget_channels()after authentication to ensure the channel list is always available at startup, covering: no cache file, corrupt cache, and expired cache/channel/allHTTP handler — callsget_channels()(cached) instead ofrefresh_channels()so client requests always return the in-memory list without triggering a live API fetchOriginal prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.