@@ -390,6 +390,8 @@ package SiriusXM;
390390 MAX_HOLD_COUNT => 3, # Consecutive no-new-segment fetches before treating as server failure
391391 # (3 × EXTINF/2 ≈ 15 s without new content per event;
392392 # 5 such events → SERVER_FAILURE_THRESHOLD → server switch)
393+ MAX_SEGMENT_RETRIES => 3, # Max consecutive fetch failures per segment before dropping it
394+ # (~1 s between each retry via process_segment_queues interval)
393395 SESSION_MAX_LIFE => 14400, # JSESSIONID estimated lifetime: 14400s (4 hours)
394396 CHANNEL_CACHE_TTL => 86400, # Channel list cache lifetime: 24 hours
395397};
@@ -419,6 +421,7 @@ sub new {
419421 channel_cookies => {}, # Store per-channel cookie jars
420422 segment_cache => {}, # Store cached segments per channel_id
421423 segment_queue => {}, # Track segments to be cached per channel_id
424+ segment_retry_count => {}, # Track consecutive fetch failures per segment (per channel)
422425 last_segment => {}, # Track last requested segment per channel_id
423426 playlist_cache => {}, # Store cached m3u8 content per channel_id
424427 playlist_channel_name => {}, # Store channel name for each channel_id for efficient lookup
@@ -2087,9 +2090,27 @@ sub cache_next_segment {
20872090 # Store in cache
20882091 $self -> {segment_cache }-> {$channel_id }-> {$segment_path } = $segment_data ;
20892092 main::log_info(" Cached segment: $segment_path (" . length ($segment_data ) . " bytes) for channel $channel_id " );
2093+ # Clear any retry counter on success
2094+ delete $self -> {segment_retry_count }-> {$channel_id }-> {$segment_path };
20902095 $cached_count ++;
20912096 } else {
2092- main::log_warn(" Failed to cache segment: $segment_path for channel $channel_id " );
2097+ # Track retry attempts for this segment
2098+ $self -> {segment_retry_count }-> {$channel_id } //= {};
2099+ my $retries = ++$self -> {segment_retry_count }-> {$channel_id }-> {$segment_path };
2100+
2101+ if ($retries <= MAX_SEGMENT_RETRIES) {
2102+ main::log_warn(" Failed to cache segment: $segment_path for channel $channel_id "
2103+ . " (attempt $retries /" . MAX_SEGMENT_RETRIES . " ), will retry in ~1s" );
2104+ # Put the segment back at the front of the queue so the next scheduler
2105+ # tick (~1 s) retries it, rather than waiting for the next playlist refresh.
2106+ unshift @$queue , $segment_path ;
2107+ } else {
2108+ main::log_warn(" Failed to cache segment: $segment_path for channel $channel_id "
2109+ . " after " . MAX_SEGMENT_RETRIES . " attempts, dropping segment" );
2110+ delete $self -> {segment_retry_count }-> {$channel_id }-> {$segment_path };
2111+ }
2112+ # Stop this batch — don't skip past a failed segment on this tick.
2113+ last ;
20932114 }
20942115 }
20952116
@@ -2839,6 +2860,7 @@ sub clear_channel_cache {
28392860 # Clear segment cache and queue
28402861 delete $self -> {segment_cache }-> {$channel_id };
28412862 delete $self -> {segment_queue }-> {$channel_id };
2863+ delete $self -> {segment_retry_count }-> {$channel_id };
28422864 delete $self -> {last_segment }-> {$channel_id };
28432865
28442866 # Clear activity tracking
0 commit comments