mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 10:06:15 -08:00
feat(players): enhance video player with audio track management and UI adjustments
Entire-Checkpoint: f35656857e95
This commit is contained in:
1 parent
95c32708ee
commit
538649feff
5 files changed
+150
-9
No files matched your search
@@ -93,9 +93,7 @@ export class VideoPlayerComponent implements OnInit, OnDestroy {
|
||||
private readonly store = inject(Store);
|
||||
|
||||
/** Active selected channel */
|
||||
readonly activeChannel$ = this.store
|
||||
.select(selectActive)
|
||||
.pipe(filter((channel) => Boolean((channel as Channel)?.url)));
|
||||
readonly activeChannel$ = this.store.select(selectActive);
|
||||
|
||||
/** Channels list */
|
||||
channels$!: Observable<Channel[]>;
|
||||
@@ -179,6 +177,7 @@ export class VideoPlayerComponent implements OnInit, OnDestroy {
|
||||
combineLatestWith(this.activatedRoute.queryParams),
|
||||
switchMap(([params, queryParams]) => {
|
||||
if (params['id']) {
|
||||
this.store.dispatch(ChannelActions.resetActiveChannel());
|
||||
this.store.dispatch(
|
||||
PlaylistActions.setActivePlaylist({
|
||||
playlistId: params['id'],
|
||||
@@ -402,8 +401,9 @@ export class VideoPlayerComponent implements OnInit, OnDestroy {
|
||||
|
||||
openSettings() {
|
||||
this.dialog.open(SettingsComponent, {
|
||||
width: '1000px',
|
||||
height: '90%',
|
||||
width: '1200px',
|
||||
maxWidth: '96vw',
|
||||
maxHeight: '92vh',
|
||||
data: { isDialog: true },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -111,6 +111,7 @@ export class ArtPlayerComponent implements OnInit, OnDestroy, OnChanges {
|
||||
this.hls = new Hls();
|
||||
this.hls.loadSource(url);
|
||||
this.hls.attachMedia(video);
|
||||
this.setupHlsAudioTracks();
|
||||
} else if (
|
||||
video.canPlayType('application/vnd.apple.mpegurl')
|
||||
) {
|
||||
@@ -119,10 +120,8 @@ export class ArtPlayerComponent implements OnInit, OnDestroy, OnChanges {
|
||||
},
|
||||
mkv: function (video: HTMLVideoElement, url: string) {
|
||||
video.src = url;
|
||||
// Add error handling
|
||||
video.onerror = () => {
|
||||
console.error('Error loading MKV file:', video.error);
|
||||
// Fallback to treating it as a regular video
|
||||
video.src = url;
|
||||
};
|
||||
},
|
||||
@@ -143,11 +142,49 @@ export class ArtPlayerComponent implements OnInit, OnDestroy, OnChanges {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for HLS.js audio tracks and adds a settings menu entry
|
||||
* to ArtPlayer for switching between available audio tracks.
|
||||
*/
|
||||
private setupHlsAudioTracks(): void {
|
||||
if (!this.hls) return;
|
||||
|
||||
const hls = this.hls;
|
||||
|
||||
hls.on(Hls.Events.AUDIO_TRACKS_UPDATED, () => {
|
||||
const tracks = hls.audioTracks;
|
||||
if (!tracks || tracks.length <= 1) return;
|
||||
|
||||
this.player.setting.add({
|
||||
html: 'Audio',
|
||||
icon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="22" height="22" fill="white">
|
||||
<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/>
|
||||
</svg>`,
|
||||
width: 220,
|
||||
tooltip: tracks[hls.audioTrack]?.name || '',
|
||||
selector: tracks.map((track, index) => ({
|
||||
html: track.name || track.lang || `Track ${index + 1}`,
|
||||
default: index === hls.audioTrack,
|
||||
})),
|
||||
onSelect: function (this: Artplayer, item: any) {
|
||||
const selectedIndex = tracks.findIndex(
|
||||
(t, i) =>
|
||||
(t.name || t.lang || `Track ${i + 1}`) === item.html
|
||||
);
|
||||
if (selectedIndex >= 0) {
|
||||
hls.audioTrack = selectedIndex;
|
||||
}
|
||||
return item.html;
|
||||
},
|
||||
} as any);
|
||||
});
|
||||
}
|
||||
|
||||
private getVideoType(url: string): string {
|
||||
const extension = url.split('.').pop()?.toLowerCase();
|
||||
switch (extension) {
|
||||
case 'mkv':
|
||||
return 'video/matroska'; // Changed from 'mkv'
|
||||
return 'video/matroska';
|
||||
case 'm3u8':
|
||||
return 'm3u8';
|
||||
case 'mp4':
|
||||
|
||||
@@ -176,6 +176,7 @@ export class ChannelListContainerComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.store.dispatch(ChannelActions.resetActiveChannel());
|
||||
this.store.dispatch(ChannelActions.setChannels({ channels: [] }));
|
||||
|
||||
if (this.epgRefreshInterval) {
|
||||
|
||||
@@ -13,9 +13,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
.video-js .vjs-menu-button-popup .vjs-menu {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 599px) {
|
||||
.video-js {
|
||||
height: calc(50vh - 65px) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,8 +61,34 @@ export class VjsPlayerComponent implements OnInit, OnChanges, OnDestroy {
|
||||
if (this.startTime > 0) {
|
||||
this.player.currentTime(this.startTime);
|
||||
}
|
||||
this.logAudioTracks();
|
||||
this.setupAudioTrackMenu();
|
||||
});
|
||||
|
||||
// Audio tracks may be added after loadedmetadata (e.g. HLS alternate audio)
|
||||
const audioTracks = this.player.audioTracks();
|
||||
if (audioTracks) {
|
||||
audioTracks.addEventListener('addtrack', () => {
|
||||
console.log(
|
||||
'[AudioTrack] addtrack event fired, total tracks:',
|
||||
this.player.audioTracks().length
|
||||
);
|
||||
this.logAudioTracks();
|
||||
this.setupAudioTrackMenu();
|
||||
});
|
||||
audioTracks.addEventListener('removetrack', () => {
|
||||
console.log(
|
||||
'[AudioTrack] removetrack event fired, total tracks:',
|
||||
this.player.audioTracks().length
|
||||
);
|
||||
this.logAudioTracks();
|
||||
this.setupAudioTrackMenu();
|
||||
});
|
||||
audioTracks.addEventListener('change', () => {
|
||||
this.logAudioTracks();
|
||||
});
|
||||
}
|
||||
|
||||
this.player.on('volumechange', () => {
|
||||
const currentVolume = this.player.volume();
|
||||
localStorage.setItem('volume', currentVolume.toString());
|
||||
@@ -119,4 +145,78 @@ export class VjsPlayerComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.player.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs all available audio tracks for debugging.
|
||||
*/
|
||||
private logAudioTracks(): void {
|
||||
const audioTracks = this.player.audioTracks();
|
||||
console.log('[AudioTrack] Audio tracks count:', audioTracks?.length ?? 0);
|
||||
for (let i = 0; i < (audioTracks?.length ?? 0); i++) {
|
||||
const t = audioTracks[i];
|
||||
console.log(
|
||||
`[AudioTrack] Track ${i}: label="${t.label}", language="${t.language}", enabled=${t.enabled}, kind="${t.kind}"`
|
||||
);
|
||||
}
|
||||
|
||||
// Also check the underlying tech for HLS audio tracks
|
||||
const tech =
|
||||
typeof this.player?.tech === 'function'
|
||||
? this.player.tech({ IWillNotUseThisInPlugins: true })
|
||||
: null;
|
||||
const audioMediaGroups =
|
||||
tech?.vhs?.playlists?.main?.mediaGroups?.AUDIO ??
|
||||
tech?.vhs?.playlists?.master?.mediaGroups?.AUDIO;
|
||||
|
||||
if (audioMediaGroups) {
|
||||
console.log(
|
||||
'[AudioTrack] HLS AUDIO media groups:',
|
||||
JSON.stringify(Object.keys(audioMediaGroups))
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
'[AudioTrack] HLS AUDIO media groups: none found in playlist metadata'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the audio track selection menu in the control bar.
|
||||
* Uses the Video.js audioTracks() API which works with both
|
||||
* native multi-audio streams and HLS.js alternate audio tracks.
|
||||
*/
|
||||
private setupAudioTrackMenu(): void {
|
||||
const audioTracks = this.player.audioTracks();
|
||||
console.log(
|
||||
'[AudioTrack] setupAudioTrackMenu called, tracks:',
|
||||
audioTracks?.length ?? 0
|
||||
);
|
||||
if (!audioTracks || audioTracks.length <= 1) {
|
||||
console.log(
|
||||
'[AudioTrack] Skipping menu: need >1 tracks, have',
|
||||
audioTracks?.length ?? 0
|
||||
);
|
||||
console.log(
|
||||
'[AudioTrack] If VLC/MPV show more tracks, the HLS manifest likely does not expose alternate audio via EXT-X-MEDIA'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const controlBar = this.player.getChild('controlBar');
|
||||
if (!controlBar) {
|
||||
return;
|
||||
}
|
||||
|
||||
let audioButton =
|
||||
controlBar.getChild('audioTrackButton') ??
|
||||
controlBar.getChild('AudioTrackButton');
|
||||
if (!audioButton) {
|
||||
audioButton = controlBar.addChild('audioTrackButton', {});
|
||||
}
|
||||
|
||||
if (audioButton) {
|
||||
audioButton.show?.();
|
||||
audioButton.update?.();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user