mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-15 05:36:17 -08:00
feat(player): detect codec errors and support mpegts streams
Add codec error detection and reporting to the Video.js player component and propagate codec error events to parent components. Introduce a codecError event emitter with a CodecError interface and logic to avoid duplicated or noisy error reporting. Improve player initialization to handle raw MPEG-TS streams via mpegts.js: detect mpegts URLs, prevent autoplay for those streams, and initialize a dedicated mpegts player instance. Wire codec error output up in template so host components can handle codec failures. Also: - Replace hardcoded HLS MIME type with runtime getMimeType() in templates. - Swap to videojs-quality-selector-hls plugin and add safe init of plugins. - Add safeguards for player destruction and more robust error handling (track fatal errors, prevent races). - Persist volume changes and listen to video element errors to surface codec problems. These changes enable better handling of unsupported audio/video codecs, support raw .ts streams, and allow UI-level responses to codec failures.
This commit is contained in:
1 parent
80a2649a8b
commit
aaabe6023a
5 files changed
+667
-44
No files matched your search
@@ -36,12 +36,13 @@
|
||||
src:
|
||||
activeChannel.url +
|
||||
activeChannel.epgParams,
|
||||
type: 'application/x-mpegURL',
|
||||
type: getMimeType(activeChannel.url),
|
||||
},
|
||||
],
|
||||
}"
|
||||
[volume]="volume"
|
||||
[class.hide-captions]="!playerSettings.showCaptions"
|
||||
(codecError)="handleCodecError($event)"
|
||||
/>
|
||||
}
|
||||
<!-- default html player component -->
|
||||
@@ -58,6 +59,7 @@
|
||||
[channel]="activeChannel"
|
||||
[volume]="volume"
|
||||
[showCaptions]="playerSettings.showCaptions!"
|
||||
(codecError)="handleCodecError($event)"
|
||||
/>
|
||||
}
|
||||
@if (playerSettings.player === 'mpv') {
|
||||
|
||||
@@ -12,12 +12,15 @@ import {
|
||||
} from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { StorageMap } from '@ngx-pwa/local-storage';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import {
|
||||
ArtPlayerComponent,
|
||||
AudioPlayerComponent,
|
||||
CodecError,
|
||||
COMPONENT_OVERLAY_REF,
|
||||
EpgListComponent,
|
||||
HtmlVideoPlayerComponent,
|
||||
@@ -80,8 +83,10 @@ export class VideoPlayerComponent implements OnInit {
|
||||
private readonly playlistsService = inject(PlaylistsService);
|
||||
private readonly router = inject(Router);
|
||||
private readonly settingsStore = inject(SettingsStore);
|
||||
private readonly snackBar = inject(MatSnackBar);
|
||||
private readonly storage = inject(StorageMap);
|
||||
private readonly store = inject(Store);
|
||||
private readonly translateService = inject(TranslateService);
|
||||
|
||||
/** Active selected channel */
|
||||
readonly activeChannel$ = this.store
|
||||
@@ -474,4 +479,65 @@ export class VideoPlayerComponent implements OnInit {
|
||||
target instanceof HTMLTextAreaElement
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the MIME type for a stream URL based on its extension
|
||||
* @param url Stream URL
|
||||
* @returns MIME type string for Video.js
|
||||
*/
|
||||
getMimeType(url: string): string {
|
||||
const extension = url.split(/[#?]/)[0].split('.').pop()?.toLowerCase();
|
||||
switch (extension) {
|
||||
case 'ts':
|
||||
return 'video/mp2t';
|
||||
case 'm3u8':
|
||||
return 'application/x-mpegURL';
|
||||
case 'mp4':
|
||||
return 'video/mp4';
|
||||
case 'webm':
|
||||
return 'video/webm';
|
||||
case 'mkv':
|
||||
return 'video/x-matroska';
|
||||
case 'flv':
|
||||
return 'video/x-flv';
|
||||
default:
|
||||
return 'application/x-mpegURL';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle codec error from video players
|
||||
* Shows a snackbar suggesting to use VLC or MPV for unsupported codecs
|
||||
*/
|
||||
handleCodecError(error: CodecError): void {
|
||||
console.log('handleCodecError received:', error);
|
||||
const codecName = error.codec || 'unknown';
|
||||
const isAudio = error.type === 'unsupported_audio';
|
||||
const codecType = isAudio ? 'audio' : 'video';
|
||||
|
||||
// Show snackbar with helpful message
|
||||
const message = this.translateService.instant('PLAYER.UNSUPPORTED_CODEC', {
|
||||
codecType: codecType,
|
||||
codec: codecName
|
||||
}) || `Unsupported ${codecType} codec: ${codecName}. Use VLC or MPV for full codec support.`;
|
||||
|
||||
const actionText = this.isDesktop
|
||||
? this.translateService.instant('PLAYER.SWITCH_TO_VLC') || 'Switch to VLC'
|
||||
: this.translateService.instant('CLOSE') || 'Close';
|
||||
|
||||
const snackBarRef = this.snackBar.open(message, actionText, {
|
||||
duration: 10000,
|
||||
horizontalPosition: 'center',
|
||||
verticalPosition: 'bottom',
|
||||
panelClass: ['codec-error-snackbar']
|
||||
});
|
||||
|
||||
// If on desktop, offer to switch to VLC
|
||||
if (this.isDesktop) {
|
||||
snackBarRef.onAction().subscribe(() => {
|
||||
// Update player setting to VLC
|
||||
this.settingsStore.updateSettings({ player: VideoPlayer.VLC });
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user