Skip to content

Commit c490aed

Browse files
committed
fix: Ensure pooled connection are shared correctly
Client A connects to Channel 1: Should create new stream Active Connections: 1/2 ✓ Client B connects to Channel 1: Should see log: Reusing existing pooled transcoded stream Active Connections: STILL 1/2 ✓ (this is the key!) Client C connects to Channel 2: Should create new stream (different channel) Active Connections: 2/2 ✓ Client D connects to Channel 1: Should reuse existing pool Active Connections: STILL 2/2 ✓ Client E connects to Channel 3: Should be REJECTED (2/2 full) Error: "Playlist has reached its maximum stream limit" Resolves #651
1 parent 851b983 commit c490aed

File tree

1 file changed

+38
-28
lines changed

1 file changed

+38
-28
lines changed

app/Services/M3uProxyService.php

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ public static function stopStreamsByMetadata(string $field, string $value, ?int
400400
// Invalidate cache since we just stopped streams
401401
self::invalidateMetadataCache($field, $value);
402402

403-
Log::info('Successfully stopped streams by metadata', [
403+
Log::debug('Successfully stopped streams by metadata', [
404404
'field' => $field,
405405
'value' => $value,
406406
'exclude_channel_id' => $excludeChannelId,
@@ -501,7 +501,7 @@ public static function stopOldestPlaylistStream(string $playlistUuid, ?int $excl
501501
self::invalidateMetadataCache('playlist_uuid', $playlistUuid);
502502
}
503503

504-
Log::info('Successfully stopped oldest stream for playlist', [
504+
Log::debug('Successfully stopped oldest stream for playlist', [
505505
'playlist_uuid' => $playlistUuid,
506506
'exclude_channel_id' => $excludeChannelId,
507507
'deleted_stream' => $data['deleted_stream'] ?? null,
@@ -581,39 +581,49 @@ public function getChannelUrl($playlist, $channel, ?Request $request = null, ?St
581581
$originalChannelId = $channel->id;
582582
$originalPlaylistUuid = $playlist->uuid;
583583

584-
// IMPORTANT: Check for existing pooled stream BEFORE capacity check
584+
// IMPORTANT: Check for existing pooled stream BEFORE capacity check AND provider profile selection
585585
// If a pooled stream exists, we can reuse it without consuming additional capacity
586-
// NOTE: We need to select the provider profile FIRST to check for pooled streams with the same provider
586+
// We search WITHOUT filtering by provider profile to maximize pooling opportunities:
587+
// - The whole point of pooling is to share streams across clients
588+
// - It doesn't matter which provider profile account is serving the stream
589+
// - This prevents selecting a different profile and failing to detect existing pools
587590
$existingStreamId = null;
588591
$selectedProfile = null;
589592

590593
if ($profile) {
591-
// Select provider profile if profiles are enabled
594+
// Search for pooled stream by ORIGINAL channel ID (handles cross-provider failovers)
595+
// Pass NULL for provider_profile_id to search across ALL profiles
596+
$existingStreamId = $this->findExistingPooledStream($originalChannelId, $originalPlaylistUuid, $profile->id, null);
597+
598+
if ($existingStreamId) {
599+
Log::debug('Reusing existing pooled transcoded stream (bypassing capacity check)', [
600+
'stream_id' => $existingStreamId,
601+
'original_channel_id' => $originalChannelId,
602+
'original_playlist_uuid' => $originalPlaylistUuid,
603+
'profile_id' => $profile->id,
604+
'note' => 'Pool reuse works across any provider profile',
605+
]);
606+
607+
return $this->buildTranscodeStreamUrl($existingStreamId, $profile->format ?? 'ts', $username);
608+
}
609+
610+
// Only select provider profile if we're creating a NEW stream (no pooled stream found)
592611
if ($playlist instanceof Playlist && $playlist->profiles_enabled) {
593612
$selectedProfile = ProfileService::selectProfile($playlist);
594613

595614
if (! $selectedProfile) {
596-
Log::warning('No profiles with capacity available (pooled stream check)', [
615+
Log::warning('No profiles with capacity available for new stream', [
597616
'playlist_id' => $playlist->id,
598617
'channel_id' => $id,
599618
]);
600619
abort(503, 'All provider profiles have reached their maximum stream limit. Please try again later.');
601620
}
602-
}
603-
604-
// Search for pooled stream by ORIGINAL channel ID (handles cross-provider failovers)
605-
$existingStreamId = $this->findExistingPooledStream($originalChannelId, $originalPlaylistUuid, $profile->id, $selectedProfile?->id);
606621

607-
if ($existingStreamId) {
608-
Log::info('Reusing existing pooled transcoded stream (bypassing capacity check)', [
609-
'stream_id' => $existingStreamId,
610-
'original_channel_id' => $originalChannelId,
611-
'original_playlist_uuid' => $originalPlaylistUuid,
612-
'profile_id' => $profile->id,
622+
Log::debug('Selected provider profile for new stream creation', [
623+
'playlist_id' => $playlist->id,
613624
'provider_profile_id' => $selectedProfile?->id,
625+
'channel_id' => $id,
614626
]);
615-
616-
return $this->buildTranscodeStreamUrl($existingStreamId, $profile->format ?? 'ts', $username);
617627
}
618628
}
619629

@@ -634,7 +644,7 @@ public function getChannelUrl($playlist, $channel, ?Request $request = null, ?St
634644
$stopResult = self::stopOldestPlaylistStream($playlist->uuid, $id);
635645

636646
if ($stopResult['deleted_count'] > 0) {
637-
Log::info('Stopped oldest stream to free capacity for new channel request', [
647+
Log::debug('Stopped oldest stream to free capacity for new channel request', [
638648
'channel_id' => $id,
639649
'playlist_uuid' => $playlist->uuid,
640650
'stopped_stream' => $stopResult['deleted_stream'] ?? null,
@@ -685,7 +695,7 @@ public function getChannelUrl($playlist, $channel, ?Request $request = null, ?St
685695

686696
// If we still have the original playlist, all are at capacity
687697
if ($playlist->uuid === $originalUuid) {
688-
Log::info('Channel stream request denied - all playlists at capacity', [
698+
Log::debug('Channel stream request denied - all playlists at capacity', [
689699
'channel_id' => $id,
690700
'primary_playlist' => $playlist->uuid,
691701
'primary_limit' => $playlist->available_streams,
@@ -859,7 +869,7 @@ public function getEpisodeUrl($playlist, $episode, ?StreamProfile $profile = nul
859869
$stopResult = self::stopOldestPlaylistStream($playlist->uuid, $id);
860870

861871
if ($stopResult['deleted_count'] > 0) {
862-
Log::info('Stopped oldest stream to free capacity for new episode request', [
872+
Log::debug('Stopped oldest stream to free capacity for new episode request', [
863873
'episode_id' => $id,
864874
'playlist_uuid' => $playlist->uuid,
865875
'stopped_stream' => $stopResult['deleted_stream'] ?? null,
@@ -874,7 +884,7 @@ public function getEpisodeUrl($playlist, $episode, ?StreamProfile $profile = nul
874884

875885
// If still at capacity (either setting disabled or stop failed), deny the request
876886
if ($activeStreams >= $playlist->available_streams) {
877-
Log::info('Episode stream request denied - playlist at capacity', [
887+
Log::debug('Episode stream request denied - playlist at capacity', [
878888
'episode_id' => $id,
879889
'playlist' => $playlist->uuid,
880890
'limit' => $playlist->available_streams,
@@ -928,7 +938,7 @@ public function getEpisodeUrl($playlist, $episode, ?StreamProfile $profile = nul
928938
$existingStreamId = $this->findExistingPooledStream($id, $playlist->uuid, $profile->id, $selectedProfile?->id);
929939

930940
if ($existingStreamId) {
931-
Log::info('Reusing existing pooled transcoded stream', [
941+
Log::debug('Reusing existing pooled transcoded stream', [
932942
'stream_id' => $existingStreamId,
933943
'episode_id' => $id,
934944
'playlist_uuid' => $playlist->uuid,
@@ -1025,7 +1035,7 @@ public function triggerFailover(string $streamId): bool
10251035
->post($endpoint);
10261036

10271037
if ($response->successful()) {
1028-
Log::info("Failover triggered successfully for stream {$streamId}");
1038+
Log::debug("Failover triggered successfully for stream {$streamId}");
10291039

10301040
return true;
10311041
}
@@ -1061,7 +1071,7 @@ public function stopStream(string $streamId): bool
10611071
->delete($endpoint);
10621072

10631073
if ($response->successful()) {
1064-
Log::info("Stream {$streamId} stopped successfully");
1074+
Log::debug("Stream {$streamId} stopped successfully");
10651075

10661076
return true;
10671077
}
@@ -1255,7 +1265,7 @@ protected function createStream(
12551265
$data = $response->json();
12561266

12571267
if (isset($data['stream_id'])) {
1258-
Log::info('m3u-proxy stream created/updated successfully', [
1268+
Log::debug('m3u-proxy stream created/updated successfully', [
12591269
'stream_id' => $data['stream_id'],
12601270
'url' => $url,
12611271
]);
@@ -1355,7 +1365,7 @@ protected function createTranscodedStream(
13551365
$data = $response->json();
13561366

13571367
if (isset($data['stream_id'])) {
1358-
Log::info('Created transcoded stream on m3u-proxy', [
1368+
Log::debug('Created transcoded stream on m3u-proxy', [
13591369
'stream_id' => $data['stream_id'],
13601370
'format' => $profile->format,
13611371
'payload' => $payload,
@@ -1549,7 +1559,7 @@ protected function findExistingPooledStream(int $channelId, string $playlistUuid
15491559
($profileId === null || ($metadata['profile_id'] ?? null) == $profileId) &&
15501560
($providerProfileId === null || ($metadata['provider_profile_id'] ?? null) == $providerProfileId)
15511561
) {
1552-
Log::info('Found existing pooled transcoded stream (cross-provider failover support)', [
1562+
Log::debug('Found existing pooled transcoded stream (cross-provider failover support)', [
15531563
'stream_id' => $stream['stream_id'],
15541564
'original_channel_id' => $channelId,
15551565
'original_playlist_uuid' => $playlistUuid,

0 commit comments

Comments
 (0)