1 import { Component, ElementRef, Input, ViewChild } from '@angular/core'
2 import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
3 import { HooksService, ServerService } from '@app/core'
4 import { VideoDetails } from '@app/shared/shared-main'
5 import { VideoPlaylist } from '@app/shared/shared-video-playlist'
6 import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
7 import { buildVideoOrPlaylistEmbed } from '@root-helpers/video'
8 import { buildPlaylistLink, buildVideoLink, decoratePlaylistLink, decorateVideoLink } from '@shared/core-utils'
9 import { VideoCaption, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models'
11 type Customizations = {
12 startAtCheckbox: boolean
15 stopAtCheckbox: boolean
18 subtitleCheckbox: boolean
34 includeVideoInPlaylist: boolean
37 type TabId = 'url' | 'qrcode' | 'embed'
40 selector: 'my-video-share',
41 templateUrl: './video-share.component.html',
42 styleUrls: [ './video-share.component.scss' ]
44 export class VideoShareComponent {
45 @ViewChild('modal', { static: true }) modal: ElementRef
47 @Input() video: VideoDetails = null
48 @Input() videoCaptions: VideoCaption[] = []
49 @Input() playlist: VideoPlaylist = null
50 @Input() playlistPosition: number = null
52 activeVideoId: TabId = 'url'
53 activePlaylistId: TabId = 'url'
55 customizations: Customizations
56 isAdvancedCustomizationCollapsed = true
62 playlistEmbedUrl: string
64 videoEmbedHTML: string
65 videoEmbedSafeHTML: SafeHtml
66 playlistEmbedHTML: string
67 playlistEmbedSafeHTML: SafeHtml
70 private modalService: NgbModal,
71 private sanitizer: DomSanitizer,
72 private server: ServerService,
73 private hooks: HooksService
76 show (currentVideoTimestamp?: number, currentPlaylistPosition?: number) {
78 if (this.videoCaptions && this.videoCaptions.length !== 0) {
79 subtitle = this.videoCaptions[0].language.id
82 this.customizations = new Proxy({
83 startAtCheckbox: false,
84 startAt: currentVideoTimestamp ? Math.floor(currentVideoTimestamp) : 0,
86 stopAtCheckbox: false,
87 stopAt: this.video?.duration,
89 subtitleCheckbox: false,
98 embedP2P: this.server.getHTMLConfig().defaults.p2p.embed.enabled,
106 includeVideoInPlaylist: false
108 set: (target, prop, value) => {
110 (target as any)[prop] = value
112 if (prop === 'embedP2P') {
113 // Auto enabled warning title if P2P is enabled
114 this.customizations.warningTitle = value
123 this.playlistPosition = currentPlaylistPosition
127 this.modalService.open(this.modal, { centered: true }).shown.subscribe(() => {
128 this.hooks.runAction('action:modal.share.shown', 'video-watch', { video: this.video, playlist: this.playlist })
132 // ---------------------------------------------------------------------------
135 const url = this.customizations.originUrl
137 : buildVideoLink(this.video, window.location.origin)
139 return this.hooks.wrapFun(
141 { url, ...this.getVideoOptions(false) },
143 'filter:share.video-url.build.params',
144 'filter:share.video-url.build.result'
148 getVideoEmbedUrl () {
149 return this.hooks.wrapFun(
151 { url: this.video.embedUrl, ...this.getVideoOptions(true) },
153 'filter:share.video-embed-url.build.params',
154 'filter:share.video-embed-url.build.result'
158 async getVideoEmbedCode (options: { responsive: boolean }) {
159 const { responsive } = options
160 return this.hooks.wrapFun(
161 buildVideoOrPlaylistEmbed,
162 { embedUrl: await this.getVideoEmbedUrl(), embedTitle: this.video.name, responsive },
164 'filter:share.video-embed-code.build.params',
165 'filter:share.video-embed-code.build.result'
169 // ---------------------------------------------------------------------------
172 const url = buildPlaylistLink(this.playlist)
174 return this.hooks.wrapFun(
175 decoratePlaylistLink,
176 { url, ...this.getPlaylistOptions() },
178 'filter:share.video-playlist-url.build.params',
179 'filter:share.video-playlist-url.build.result'
183 getPlaylistEmbedUrl () {
184 return this.hooks.wrapFun(
185 decoratePlaylistLink,
186 { url: this.playlist.embedUrl, ...this.getPlaylistOptions() },
188 'filter:share.video-playlist-embed-url.build.params',
189 'filter:share.video-playlist-embed-url.build.result'
193 async getPlaylistEmbedCode (options: { responsive: boolean }) {
194 const { responsive } = options
195 return this.hooks.wrapFun(
196 buildVideoOrPlaylistEmbed,
197 { embedUrl: await this.getPlaylistEmbedUrl(), embedTitle: this.playlist.displayName, responsive },
199 'filter:share.video-playlist-embed-code.build.params',
200 'filter:share.video-playlist-embed-code.build.result'
204 // ---------------------------------------------------------------------------
208 this.playlistUrl = await this.getPlaylistUrl()
209 this.playlistEmbedUrl = await this.getPlaylistEmbedUrl()
210 this.playlistEmbedHTML = await this.getPlaylistEmbedCode({ responsive: this.customizations.responsive })
211 this.playlistEmbedSafeHTML = this.sanitizer.bypassSecurityTrustHtml(await this.getPlaylistEmbedCode({ responsive: false }))
215 this.videoUrl = await this.getVideoUrl()
216 this.videoEmbedUrl = await this.getVideoEmbedUrl()
217 this.videoEmbedHTML = await this.getVideoEmbedCode({ responsive: this.customizations.responsive })
218 this.videoEmbedSafeHTML = this.sanitizer.bypassSecurityTrustHtml(await this.getVideoEmbedCode({ responsive: false }))
223 return window.location.protocol === 'http:'
226 isInVideoEmbedTab () {
227 return this.activeVideoId === 'embed'
230 isInPlaylistEmbedTab () {
231 return this.activePlaylistId === 'embed'
235 return this.video.isLocal
239 return this.video.privacy.id === VideoPrivacy.PRIVATE
242 isPrivatePlaylist () {
243 return this.playlist.privacy.id === VideoPlaylistPrivacy.PRIVATE
246 private getPlaylistOptions (baseUrl?: string) {
250 playlistPosition: this.playlistPosition && this.customizations.includeVideoInPlaylist
251 ? this.playlistPosition
256 private getVideoOptions (forEmbed: boolean) {
257 const embedOptions = forEmbed
259 title: this.customizations.title,
260 warningTitle: this.customizations.warningTitle,
261 controlBar: this.customizations.controlBar,
262 peertubeLink: this.customizations.peertubeLink,
264 // If using default value, we don't need to specify it
265 p2p: this.customizations.embedP2P === this.server.getHTMLConfig().defaults.p2p.embed.enabled
267 : this.customizations.embedP2P
272 startTime: this.customizations.startAtCheckbox ? this.customizations.startAt : undefined,
273 stopTime: this.customizations.stopAtCheckbox ? this.customizations.stopAt : undefined,
275 subtitle: this.customizations.subtitleCheckbox ? this.customizations.subtitle : undefined,
277 loop: this.customizations.loop,
278 autoplay: this.customizations.autoplay,
279 muted: this.customizations.muted,