mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
feat(player): add ArtPlayer and DPlayer components with integration in video player
This commit is contained in:
1 parent
b1f11a57bc
commit
2e8a7494b4
7 files changed
+247
-2
No files matched your search
@@ -0,0 +1,122 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import {
|
||||
Component,
|
||||
ElementRef,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
SimpleChanges,
|
||||
} from '@angular/core';
|
||||
import Artplayer from 'artplayer';
|
||||
import Hls from 'hls.js';
|
||||
import { Channel } from '../../../../../shared/channel.interface';
|
||||
|
||||
@Component({
|
||||
selector: 'app-art-player',
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
template: `<div #artplayer class="artplayer-container"></div>`,
|
||||
styles: [
|
||||
`
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.artplayer-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
`,
|
||||
],
|
||||
})
|
||||
export class ArtPlayerComponent implements OnInit, OnDestroy, OnChanges {
|
||||
@Input() channel: Channel;
|
||||
@Input() volume = 1;
|
||||
@Input() showCaptions = false;
|
||||
|
||||
private player: Artplayer;
|
||||
|
||||
constructor(private elementRef: ElementRef) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.initPlayer();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.player) {
|
||||
this.player.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes['channel'] && !changes['channel'].firstChange) {
|
||||
if (this.player) {
|
||||
this.player.destroy();
|
||||
}
|
||||
this.initPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
private initPlayer(): void {
|
||||
const el = this.elementRef.nativeElement.querySelector(
|
||||
'.artplayer-container'
|
||||
);
|
||||
const isLive = this.channel.url.toLowerCase().includes('m3u8');
|
||||
|
||||
this.player = new Artplayer({
|
||||
container: el,
|
||||
url: this.channel.url + (this.channel.epgParams || ''),
|
||||
volume: this.volume,
|
||||
isLive: isLive,
|
||||
autoplay: true,
|
||||
type: this.getVideoType(this.channel.url),
|
||||
pip: true,
|
||||
autoSize: true,
|
||||
autoMini: true,
|
||||
screenshot: true,
|
||||
setting: true,
|
||||
playbackRate: true,
|
||||
aspectRatio: true,
|
||||
fullscreen: true,
|
||||
fullscreenWeb: true,
|
||||
customType: {
|
||||
m3u8: function (video: HTMLVideoElement, url: string) {
|
||||
if (Hls.isSupported()) {
|
||||
const hls = new Hls();
|
||||
hls.loadSource(url);
|
||||
hls.attachMedia(video);
|
||||
} else if (
|
||||
video.canPlayType('application/vnd.apple.mpegurl')
|
||||
) {
|
||||
video.src = url;
|
||||
}
|
||||
},
|
||||
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;
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private getVideoType(url: string): string {
|
||||
const extension = url.split('.').pop()?.toLowerCase();
|
||||
switch (extension) {
|
||||
case 'mkv':
|
||||
return 'video/matroska'; // Changed from 'mkv'
|
||||
case 'm3u8':
|
||||
return 'm3u8';
|
||||
case 'mp4':
|
||||
return 'mp4';
|
||||
default:
|
||||
return 'auto';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import {
|
||||
Component,
|
||||
ElementRef,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
SimpleChanges,
|
||||
} from '@angular/core';
|
||||
import DPlayer from 'dplayer';
|
||||
import { Channel } from '../../../../../shared/channel.interface';
|
||||
|
||||
@Component({
|
||||
selector: 'app-d-player',
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
template: `<div #dplayer class="dplayer-container"></div>`,
|
||||
styles: [
|
||||
`
|
||||
.dplayer-container {
|
||||
width: 100%;
|
||||
height: calc(100vh - 64px);
|
||||
}
|
||||
`,
|
||||
],
|
||||
})
|
||||
export class DPlayerComponent implements OnInit, OnDestroy, OnChanges {
|
||||
@Input() channel: Channel;
|
||||
@Input() volume = 1;
|
||||
@Input() showCaptions = false;
|
||||
|
||||
private player: any;
|
||||
|
||||
constructor(private elementRef: ElementRef) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.initPlayer();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.player) {
|
||||
this.player.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes['channel'] && !changes['channel'].firstChange) {
|
||||
// Destroy existing player
|
||||
if (this.player) {
|
||||
this.player.destroy();
|
||||
}
|
||||
// Reinitialize with new channel
|
||||
this.initPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
private initPlayer(): void {
|
||||
const el =
|
||||
this.elementRef.nativeElement.querySelector('.dplayer-container');
|
||||
const isLive = this.channel.url.toLowerCase().includes('m3u8');
|
||||
|
||||
this.player = new DPlayer({
|
||||
container: el,
|
||||
live: isLive,
|
||||
volume: this.volume,
|
||||
autoplay: true,
|
||||
video: {
|
||||
url: this.channel.url + (this.channel.epgParams || ''),
|
||||
type: this.getVideoType(this.channel.url),
|
||||
},
|
||||
logo: this.channel.tvg?.logo,
|
||||
});
|
||||
}
|
||||
|
||||
private getVideoType(url: string): string {
|
||||
const extension = url.split('.').pop()?.toLowerCase();
|
||||
switch (extension) {
|
||||
case 'mkv':
|
||||
return 'video/matroska';
|
||||
case 'm3u8':
|
||||
return 'hls';
|
||||
case 'mp4':
|
||||
return 'auto';
|
||||
default:
|
||||
return 'auto';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,23 @@
|
||||
[volume]="volume"
|
||||
[showCaptions]="playerSettings.showCaptions"
|
||||
/>
|
||||
|
||||
<!-- Add DPlayer component -->
|
||||
<app-d-player
|
||||
*ngIf="playerSettings.player === 'dplayer'"
|
||||
[channel]="activeChannel"
|
||||
[volume]="volume"
|
||||
[showCaptions]="playerSettings.showCaptions"
|
||||
/>
|
||||
|
||||
<!-- Add ArtPlayer component -->
|
||||
<app-art-player
|
||||
*ngIf="playerSettings.player === 'artplayer'"
|
||||
[channel]="activeChannel"
|
||||
[volume]="volume"
|
||||
[showCaptions]="playerSettings.showCaptions"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="custom-player"
|
||||
*ngIf="playerSettings.player === 'mpv'"
|
||||
|
||||
@@ -35,7 +35,9 @@ import {
|
||||
selectChannels,
|
||||
selectCurrentEpgProgram,
|
||||
} from '../../../state/selectors';
|
||||
import { ArtPlayerComponent } from '../art-player/art-player.component';
|
||||
import { AudioPlayerComponent } from '../audio-player/audio-player.component';
|
||||
import { DPlayerComponent } from '../d-player/d-player.component';
|
||||
import { EpgListComponent } from '../epg-list/epg-list.component';
|
||||
import { HtmlVideoPlayerComponent } from '../html-video-player/html-video-player.component';
|
||||
import { InfoOverlayComponent } from '../info-overlay/info-overlay.component';
|
||||
@@ -65,6 +67,8 @@ export const COMPONENT_OVERLAY_REF = new InjectionToken(
|
||||
SidebarComponent,
|
||||
ToolbarComponent,
|
||||
VjsPlayerComponent,
|
||||
DPlayerComponent,
|
||||
ArtPlayerComponent,
|
||||
],
|
||||
templateUrl: './video-player.component.html',
|
||||
styleUrls: ['./video-player.component.scss'],
|
||||
|
||||
@@ -2,4 +2,8 @@
|
||||
<app-vjs-player [options]="vjsOptions" />
|
||||
} @else if (player === 'html5') {
|
||||
<app-html-video-player *ngIf="player === 'html5'" [channel]="channel" />
|
||||
} @else if (player === 'dplayer') {
|
||||
<app-d-player [channel]="channel" [volume]="1" [showCaptions]="false" />
|
||||
} @else if (player === 'artplayer') {
|
||||
<app-art-player [channel]="channel" [volume]="1" [showCaptions]="false" />
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import {
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { StorageMap } from '@ngx-pwa/local-storage';
|
||||
import { getExtensionFromUrl } from '../../../../shared/playlist.utils';
|
||||
import { ArtPlayerComponent } from '../../player/components/art-player/art-player.component';
|
||||
import { DPlayerComponent } from '../../player/components/d-player/d-player.component';
|
||||
import { HtmlVideoPlayerComponent } from '../../player/components/html-video-player/html-video-player.component';
|
||||
import { VjsPlayerComponent } from '../../player/components/vjs-player/vjs-player.component';
|
||||
import { Settings, VideoPlayer } from '../../settings/settings.interface';
|
||||
@@ -20,7 +22,13 @@ import { STORE_KEY } from '../../shared/enums/store-keys.enum';
|
||||
selector: 'app-web-player-view',
|
||||
templateUrl: './web-player-view.component.html',
|
||||
styleUrls: ['./web-player-view.component.scss'],
|
||||
imports: [HtmlVideoPlayerComponent, NgIf, VjsPlayerComponent],
|
||||
imports: [
|
||||
HtmlVideoPlayerComponent,
|
||||
NgIf,
|
||||
VjsPlayerComponent,
|
||||
DPlayerComponent,
|
||||
ArtPlayerComponent,
|
||||
],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class WebPlayerViewComponent {
|
||||
|
||||
@@ -3,13 +3,14 @@ import { Theme } from './theme.enum';
|
||||
|
||||
/**
|
||||
* Contains all types of supported video players
|
||||
* TODO: extract to separate file
|
||||
*/
|
||||
export enum VideoPlayer {
|
||||
VideoJs = 'videojs',
|
||||
Html5Player = 'html5',
|
||||
MPV = 'mpv',
|
||||
VLC = 'vlc',
|
||||
DPlayer = 'dplayer',
|
||||
ArtPlayer = 'artplayer',
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user