import { CommonModule } from '@angular/common'; import { Component, input, output, ViewEncapsulation, ChangeDetectionStrategy, } from '@angular/core'; import { FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { MatTooltipModule } from '@angular/material/tooltip'; import { TranslateModule } from '@ngx-translate/core'; import { StreamFormat, VideoPlayer, reportsPlaybackFailures, } from '@iptvnator/shared/interfaces'; import { SettingsPlayerOption } from './settings.models'; import { markSectionForCheckOnFormEvents } from './settings-section-form-render'; import { provideFormFieldDefaults } from '@iptvnator/ui/components/form-field-defaults'; @Component({ selector: 'app-settings-playback-section', providers: [provideFormFieldDefaults()], imports: [ CommonModule, MatButtonModule, MatFormFieldModule, MatIconModule, MatInputModule, MatSelectModule, MatSlideToggleModule, MatTooltipModule, ReactiveFormsModule, TranslateModule, ], templateUrl: './settings-playback-section.component.html', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, styles: [':host { display: contents; }'], }) export class SettingsPlaybackSectionComponent { readonly mpvPlayerArgumentsPlaceholder = [ '--ontop', '--autofit=640x360', '--geometry=+80+80', ].join('\n'); readonly vlcPlayerArgumentsPlaceholder = [ '--video-on-top', '--width=640', '--height=360', ].join('\n'); readonly form = input.required(); constructor() { // Parent patches (Discard, backup import) change the form outside // this OnPush section's events. markSectionForCheckOnFormEvents(this.form); } readonly players = input.required(); readonly streamFormatEnum = input.required(); readonly isDesktop = input(false); /** Frame-copy embedded MPV engine is possible on this machine */ readonly frameCopyAvailable = input(false); /** Frame-copy engine is what the current app run actually uses */ readonly frameCopyActive = input(false); readonly supportsManagedExternalPlayers = input(false); readonly supportsExternalPlayerPathSettings = input(false); /** * Cross-playlist movie matching is Electron-only, so the auto-failover * toggle would control nothing in the PWA. */ readonly supportsVodMultiSource = input(false); readonly selectRecordingFolder = output(); isWebPlayerSelected(): boolean { return reportsPlaybackFailures(this.form().value.player); } /** * The fullscreen channel panel lives inside the fullscreen element the * shared controls own (the player view host). The legacy vendor chrome * fullscreens the engine's own element and external MPV/VLC own their * own window, so in both cases the toggle would control nothing. * Embedded MPV always renders the shared controls. */ supportsFullscreenChannelPanel(): boolean { const value = this.form().value; return ( (this.isWebPlayerSelected() && value.webPlayerSharedControls !== false) || value.player === VideoPlayer.EmbeddedMpv ); } /** * The Up next card is part of the shared controls. Embedded MPV mounts * them only under the frame-copy engine; the native-view engine keeps * its own dock, where the toggle would control nothing. */ supportsUpNextCard(): boolean { const value = this.form().value; return ( (this.isWebPlayerSelected() && value.webPlayerSharedControls !== false) || (value.player === VideoPlayer.EmbeddedMpv && this.frameCopyActive()) ); } isExternalPlayerSelected(): boolean { const player = this.form().value.player; return player === VideoPlayer.MPV || player === VideoPlayer.VLC; } isEmbeddedMpvSelected(): boolean { return this.form().value.player === VideoPlayer.EmbeddedMpv; } }