diff --git a/src/app/player/components/art-player/art-player.component.ts b/src/app/player/components/art-player/art-player.component.ts new file mode 100644 index 000000000..40b511ba4 --- /dev/null +++ b/src/app/player/components/art-player/art-player.component.ts @@ -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: `
`, + 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'; + } + } +} diff --git a/src/app/player/components/d-player/d-player.component.ts b/src/app/player/components/d-player/d-player.component.ts new file mode 100644 index 000000000..d9bc8048d --- /dev/null +++ b/src/app/player/components/d-player/d-player.component.ts @@ -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: ``, + 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'; + } + } +} diff --git a/src/app/player/components/video-player/video-player.component.html b/src/app/player/components/video-player/video-player.component.html index 8bbc053e6..b201edfcb 100644 --- a/src/app/player/components/video-player/video-player.component.html +++ b/src/app/player/components/video-player/video-player.component.html @@ -49,6 +49,23 @@ [volume]="volume" [showCaptions]="playerSettings.showCaptions" /> + + +