import { Component, ElementRef, Input, ViewChild } from '@angular/core' import { DomSanitizer, SafeHtml } from '@angular/platform-browser' import { HooksService, ServerService } from '@app/core' import { VideoDetails } from '@app/shared/shared-main' import { VideoPlaylist } from '@app/shared/shared-video-playlist' import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { buildVideoOrPlaylistEmbed } from '@root-helpers/video' import { buildPlaylistLink, buildVideoLink, decoratePlaylistLink, decorateVideoLink } from '@shared/core-utils' import { VideoCaption, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models' type Customizations = { startAtCheckbox: boolean startAt: number stopAtCheckbox: boolean stopAt: number subtitleCheckbox: boolean subtitle: string loop: boolean originUrl: boolean autoplay: boolean muted: boolean embedP2P: boolean onlyEmbedUrl: boolean title: boolean warningTitle: boolean controlBar: boolean peertubeLink: boolean responsive: boolean includeVideoInPlaylist: boolean } type TabId = 'url' | 'qrcode' | 'embed' @Component({ selector: 'my-video-share', templateUrl: './video-share.component.html', styleUrls: [ './video-share.component.scss' ] }) export class VideoShareComponent { @ViewChild('modal', { static: true }) modal: ElementRef @Input() video: VideoDetails = null @Input() videoCaptions: VideoCaption[] = [] @Input() playlist: VideoPlaylist = null @Input() playlistPosition: number = null activeVideoId: TabId = 'url' activePlaylistId: TabId = 'url' customizations: Customizations isAdvancedCustomizationCollapsed = true videoUrl: string playlistUrl: string videoEmbedUrl: string playlistEmbedUrl: string videoEmbedHTML: string videoEmbedSafeHTML: SafeHtml playlistEmbedHTML: string playlistEmbedSafeHTML: SafeHtml constructor ( private modalService: NgbModal, private sanitizer: DomSanitizer, private server: ServerService, private hooks: HooksService ) { } show (currentVideoTimestamp?: number, currentPlaylistPosition?: number) { let subtitle: string if (this.videoCaptions && this.videoCaptions.length !== 0) { subtitle = this.videoCaptions[0].language.id } this.customizations = new Proxy({ startAtCheckbox: false, startAt: currentVideoTimestamp ? Math.floor(currentVideoTimestamp) : 0, stopAtCheckbox: false, stopAt: this.video?.duration, subtitleCheckbox: false, subtitle, loop: false, originUrl: false, autoplay: false, muted: false, // Embed options embedP2P: this.server.getHTMLConfig().defaults.p2p.embed.enabled, onlyEmbedUrl: false, title: true, warningTitle: true, controlBar: true, peertubeLink: true, responsive: false, includeVideoInPlaylist: false }, { set: (target, prop, value) => { // FIXME: typings (target as any)[prop] = value if (prop === 'embedP2P') { // Auto enabled warning title if P2P is enabled this.customizations.warningTitle = value } this.onUpdate() return true } }) this.playlistPosition = currentPlaylistPosition this.onUpdate() this.modalService.open(this.modal, { centered: true }).shown.subscribe(() => { this.hooks.runAction('action:modal.share.shown', 'video-watch', { video: this.video, playlist: this.playlist }) }) } // --------------------------------------------------------------------------- getVideoUrl () { const url = this.customizations.originUrl ? this.video.url : buildVideoLink(this.video, window.location.origin) return this.hooks.wrapFun( decorateVideoLink, { url, ...this.getVideoOptions(false) }, 'video-watch', 'filter:share.video-url.build.params', 'filter:share.video-url.build.result' ) } getVideoEmbedUrl () { return this.hooks.wrapFun( decorateVideoLink, { url: this.video.embedUrl, ...this.getVideoOptions(true) }, 'video-watch', 'filter:share.video-embed-url.build.params', 'filter:share.video-embed-url.build.result' ) } async getVideoEmbedCode (options: { responsive: boolean }) { const { responsive } = options return this.hooks.wrapFun( buildVideoOrPlaylistEmbed, { embedUrl: await this.getVideoEmbedUrl(), embedTitle: this.video.name, responsive }, 'video-watch', 'filter:share.video-embed-code.build.params', 'filter:share.video-embed-code.build.result' ) } // --------------------------------------------------------------------------- getPlaylistUrl () { const url = buildPlaylistLink(this.playlist) return this.hooks.wrapFun( decoratePlaylistLink, { url, ...this.getPlaylistOptions() }, 'video-watch', 'filter:share.video-playlist-url.build.params', 'filter:share.video-playlist-url.build.result' ) } getPlaylistEmbedUrl () { return this.hooks.wrapFun( decoratePlaylistLink, { url: this.playlist.embedUrl, ...this.getPlaylistOptions() }, 'video-watch', 'filter:share.video-playlist-embed-url.build.params', 'filter:share.video-playlist-embed-url.build.result' ) } async getPlaylistEmbedCode (options: { responsive: boolean }) { const { responsive } = options return this.hooks.wrapFun( buildVideoOrPlaylistEmbed, { embedUrl: await this.getPlaylistEmbedUrl(), embedTitle: this.playlist.displayName, responsive }, 'video-watch', 'filter:share.video-playlist-embed-code.build.params', 'filter:share.video-playlist-embed-code.build.result' ) } // --------------------------------------------------------------------------- async onUpdate () { if (this.playlist) { this.playlistUrl = await this.getPlaylistUrl() this.playlistEmbedUrl = await this.getPlaylistEmbedUrl() this.playlistEmbedHTML = await this.getPlaylistEmbedCode({ responsive: this.customizations.responsive }) this.playlistEmbedSafeHTML = this.sanitizer.bypassSecurityTrustHtml(await this.getPlaylistEmbedCode({ responsive: false })) } if (this.video) { this.videoUrl = await this.getVideoUrl() this.videoEmbedUrl = await this.getVideoEmbedUrl() this.videoEmbedHTML = await this.getVideoEmbedCode({ responsive: this.customizations.responsive }) this.videoEmbedSafeHTML = this.sanitizer.bypassSecurityTrustHtml(await this.getVideoEmbedCode({ responsive: false })) } } notSecure () { return window.location.protocol === 'http:' } isInVideoEmbedTab () { return this.activeVideoId === 'embed' } isInPlaylistEmbedTab () { return this.activePlaylistId === 'embed' } isLocalVideo () { return this.video.isLocal } isPrivateVideo () { return this.video.privacy.id === VideoPrivacy.PRIVATE } isPrivatePlaylist () { return this.playlist.privacy.id === VideoPlaylistPrivacy.PRIVATE } private getPlaylistOptions (baseUrl?: string) { return { baseUrl, playlistPosition: this.playlistPosition && this.customizations.includeVideoInPlaylist ? this.playlistPosition : undefined } } private getVideoOptions (forEmbed: boolean) { const embedOptions = forEmbed ? { title: this.customizations.title, warningTitle: this.customizations.warningTitle, controlBar: this.customizations.controlBar, peertubeLink: this.customizations.peertubeLink, // If using default value, we don't need to specify it p2p: this.customizations.embedP2P === this.server.getHTMLConfig().defaults.p2p.embed.enabled ? undefined : this.customizations.embedP2P } : {} return { startTime: this.customizations.startAtCheckbox ? this.customizations.startAt : undefined, stopTime: this.customizations.stopAtCheckbox ? this.customizations.stopAt : undefined, subtitle: this.customizations.subtitleCheckbox ? this.customizations.subtitle : undefined, loop: this.customizations.loop, autoplay: this.customizations.autoplay, muted: this.customizations.muted, ...embedOptions } } }