aboutsummaryrefslogtreecommitdiffhomepage
path: root/client
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-07-26 14:12:39 +0200
committerChocobozzz <me@florianbigard.com>2021-07-26 14:12:50 +0200
commit9162fdd36300d2478f13d6ad346ec2c323f40faa (patch)
treedea0ca43f3ea2fb72a73ca77338f5b7c990acdd7 /client
parent764b1a14fc494f2cfd7ea590d2f07b01df65c7ad (diff)
downloadPeerTube-9162fdd36300d2478f13d6ad346ec2c323f40faa.tar.gz
PeerTube-9162fdd36300d2478f13d6ad346ec2c323f40faa.tar.zst
PeerTube-9162fdd36300d2478f13d6ad346ec2c323f40faa.zip
Refactor video links building
Diffstat (limited to 'client')
-rw-r--r--client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts7
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.html2
-rw-r--r--client/src/app/+videos/+video-watch/shared/comment/video-comment.component.ts2
-rw-r--r--client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts3
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.ts1
-rw-r--r--client/src/app/core/renderer/markdown.service.ts10
-rw-r--r--client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts6
-rw-r--r--client/src/app/shared/shared-custom-markup/peertube-custom-tags/embed-markup.component.ts6
-rw-r--r--client/src/app/shared/shared-forms/markdown-textarea.component.ts5
-rw-r--r--client/src/app/shared/shared-moderation/report-modals/video-report.component.ts7
-rw-r--r--client/src/app/shared/shared-share-modal/video-share.component.ts38
-rw-r--r--client/src/assets/player/peertube-player-manager.ts42
-rw-r--r--client/src/assets/player/peertube-videojs-typings.ts5
-rw-r--r--client/src/assets/player/utils.ts46
-rw-r--r--client/src/assets/player/videojs-components/peertube-link-button.ts18
15 files changed, 133 insertions, 65 deletions
diff --git a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
index 08500ef5c..3af20ea0a 100644
--- a/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
+++ b/client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
@@ -1,6 +1,6 @@
1import { SortMeta } from 'primeng/api' 1import { SortMeta } from 'primeng/api'
2import { switchMap } from 'rxjs/operators' 2import { switchMap } from 'rxjs/operators'
3import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' 3import { buildVideoEmbedLink, buildVideoOrPlaylistEmbed, decorateVideoLink } from 'src/assets/player/utils'
4import { environment } from 'src/environments/environment' 4import { environment } from 'src/environments/environment'
5import { Component, OnInit } from '@angular/core' 5import { Component, OnInit } from '@angular/core'
6import { DomSanitizer } from '@angular/platform-browser' 6import { DomSanitizer } from '@angular/platform-browser'
@@ -147,8 +147,9 @@ export class VideoBlockListComponent extends RestTable implements OnInit {
147 147
148 getVideoEmbed (entry: VideoBlacklist) { 148 getVideoEmbed (entry: VideoBlacklist) {
149 return buildVideoOrPlaylistEmbed( 149 return buildVideoOrPlaylistEmbed(
150 buildVideoLink({ 150 decorateVideoLink({
151 baseUrl: `${environment.originServerUrl}/videos/embed/${entry.video.uuid}`, 151 url: buildVideoEmbedLink(entry.video, environment.originServerUrl),
152
152 title: false, 153 title: false,
153 warningTitle: false 154 warningTitle: false
154 }), 155 }),
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.html b/client/src/app/+videos/+video-edit/shared/video-edit.component.html
index 50d030ac9..ee5a50611 100644
--- a/client/src/app/+videos/+video-edit/shared/video-edit.component.html
+++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.html
@@ -45,7 +45,7 @@
45 </ng-template> 45 </ng-template>
46 </my-help> 46 </my-help>
47 47
48 <my-markdown-textarea [truncate]="250" formControlName="description" [markdownVideo]="true"></my-markdown-textarea> 48 <my-markdown-textarea [truncate]="250" formControlName="description" [markdownVideo]="videoToUpdate"></my-markdown-textarea>
49 49
50 <div *ngIf="formErrors.description" class="form-error"> 50 <div *ngIf="formErrors.description" class="form-error">
51 {{ formErrors.description }} 51 {{ formErrors.description }}
diff --git a/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.ts b/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.ts
index 04f8f0d58..0e1c4c207 100644
--- a/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.ts
+++ b/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.ts
@@ -161,7 +161,7 @@ export class VideoCommentComponent implements OnInit, OnChanges {
161 // Before HTML rendering restore line feed for markdown list compatibility 161 // Before HTML rendering restore line feed for markdown list compatibility
162 const commentText = this.comment.text.replace(/<br.?\/?>/g, '\r\n') 162 const commentText = this.comment.text.replace(/<br.?\/?>/g, '\r\n')
163 const html = await this.markdownService.textMarkdownToHTML(commentText, true, true) 163 const html = await this.markdownService.textMarkdownToHTML(commentText, true, true)
164 this.sanitizedCommentHTML = this.markdownService.processVideoTimestamps(html) 164 this.sanitizedCommentHTML = this.markdownService.processVideoTimestamps(this.video.shortUUID, html)
165 this.newParentComments = this.parentComments.concat([ this.comment ]) 165 this.newParentComments = this.parentComments.concat([ this.comment ])
166 166
167 if (this.comment.account) { 167 if (this.comment.account) {
diff --git a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts
index 23d00d31a..870c7ae3f 100644
--- a/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts
+++ b/client/src/app/+videos/+video-watch/shared/metadata/video-description.component.ts
@@ -80,6 +80,7 @@ export class VideoDescriptionComponent implements OnChanges {
80 80
81 private async setVideoDescriptionHTML () { 81 private async setVideoDescriptionHTML () {
82 const html = await this.markdownService.textMarkdownToHTML(this.video.description) 82 const html = await this.markdownService.textMarkdownToHTML(this.video.description)
83 this.videoHTMLDescription = this.markdownService.processVideoTimestamps(html) 83
84 this.videoHTMLDescription = this.markdownService.processVideoTimestamps(this.video.shortUUID, html)
84 } 85 }
85} 86}
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.ts b/client/src/app/+videos/+video-watch/video-watch.component.ts
index 7460ae3fc..9212b78be 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/+videos/+video-watch/video-watch.component.ts
@@ -582,6 +582,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
582 582
583 videoCaptions: playerCaptions, 583 videoCaptions: playerCaptions,
584 584
585 videoShortUUID: video.shortUUID,
585 videoUUID: video.uuid 586 videoUUID: video.uuid
586 }, 587 },
587 588
diff --git a/client/src/app/core/renderer/markdown.service.ts b/client/src/app/core/renderer/markdown.service.ts
index ca1bf4eb9..01d44864b 100644
--- a/client/src/app/core/renderer/markdown.service.ts
+++ b/client/src/app/core/renderer/markdown.service.ts
@@ -1,5 +1,5 @@
1import * as MarkdownIt from 'markdown-it' 1import * as MarkdownIt from 'markdown-it'
2import { buildVideoLink } from 'src/assets/player/utils' 2import { buildVideoLink, decorateVideoLink } from 'src/assets/player/utils'
3import { Injectable } from '@angular/core' 3import { Injectable } from '@angular/core'
4import { 4import {
5 COMPLETE_RULES, 5 COMPLETE_RULES,
@@ -82,10 +82,14 @@ export class MarkdownService {
82 return this.render({ name: 'customPageMarkdownIt', markdown, withEmoji: true, additionalAllowedTags }) 82 return this.render({ name: 'customPageMarkdownIt', markdown, withEmoji: true, additionalAllowedTags })
83 } 83 }
84 84
85 processVideoTimestamps (html: string) { 85 processVideoTimestamps (videoShortUUID: string, html: string) {
86 return html.replace(/((\d{1,2}):)?(\d{1,2}):(\d{1,2})/g, function (str, _, h, m, s) { 86 return html.replace(/((\d{1,2}):)?(\d{1,2}):(\d{1,2})/g, function (str, _, h, m, s) {
87 const t = (3600 * +(h || 0)) + (60 * +(m || 0)) + (+(s || 0)) 87 const t = (3600 * +(h || 0)) + (60 * +(m || 0)) + (+(s || 0))
88 const url = buildVideoLink({ startTime: t }) 88
89 const url = decorateVideoLink({
90 url: buildVideoLink({ shortUUID: videoShortUUID }),
91 startTime: t
92 })
89 return `<a class="video-timestamp" href="${url}">${str}</a>` 93 return `<a class="video-timestamp" href="${url}">${str}</a>`
90 }) 94 })
91 } 95 }
diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
index 67aa0e399..393108ac9 100644
--- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
+++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
@@ -1,7 +1,7 @@
1import * as debug from 'debug' 1import * as debug from 'debug'
2import truncate from 'lodash-es/truncate' 2import truncate from 'lodash-es/truncate'
3import { SortMeta } from 'primeng/api' 3import { SortMeta } from 'primeng/api'
4import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' 4import { buildVideoEmbedLink, buildVideoOrPlaylistEmbed, decorateVideoLink } from 'src/assets/player/utils'
5import { environment } from 'src/environments/environment' 5import { environment } from 'src/environments/environment'
6import { Component, Input, OnInit, ViewChild } from '@angular/core' 6import { Component, Input, OnInit, ViewChild } from '@angular/core'
7import { DomSanitizer } from '@angular/platform-browser' 7import { DomSanitizer } from '@angular/platform-browser'
@@ -129,8 +129,8 @@ export class AbuseListTableComponent extends RestTable implements OnInit {
129 129
130 getVideoEmbed (abuse: AdminAbuse) { 130 getVideoEmbed (abuse: AdminAbuse) {
131 return buildVideoOrPlaylistEmbed( 131 return buildVideoOrPlaylistEmbed(
132 buildVideoLink({ 132 decorateVideoLink({
133 baseUrl: `${environment.originServerUrl}/videos/embed/${abuse.video.uuid}`, 133 url: buildVideoEmbedLink(abuse.video, environment.originServerUrl),
134 title: false, 134 title: false,
135 warningTitle: false, 135 warningTitle: false,
136 startTime: abuse.video.startAt, 136 startTime: abuse.video.startAt,
diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/embed-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/embed-markup.component.ts
index 4462903db..ba8969d5b 100644
--- a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/embed-markup.component.ts
+++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/embed-markup.component.ts
@@ -1,4 +1,4 @@
1import { buildPlaylistLink, buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' 1import { buildPlaylistEmbedLink, buildVideoEmbedLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils'
2import { environment } from 'src/environments/environment' 2import { environment } from 'src/environments/environment'
3import { Component, ElementRef, Input, OnInit } from '@angular/core' 3import { Component, ElementRef, Input, OnInit } from '@angular/core'
4import { CustomMarkupComponent } from './shared' 4import { CustomMarkupComponent } from './shared'
@@ -17,8 +17,8 @@ export class EmbedMarkupComponent implements CustomMarkupComponent, OnInit {
17 17
18 ngOnInit () { 18 ngOnInit () {
19 const link = this.type === 'video' 19 const link = this.type === 'video'
20 ? buildVideoLink({ baseUrl: `${environment.originServerUrl}/videos/embed/${this.uuid}` }) 20 ? buildVideoEmbedLink({ uuid: this.uuid }, environment.originServerUrl)
21 : buildPlaylistLink({ baseUrl: `${environment.originServerUrl}/video-playlists/embed/${this.uuid}` }) 21 : buildPlaylistEmbedLink({ uuid: this.uuid }, environment.originServerUrl)
22 22
23 this.el.nativeElement.innerHTML = buildVideoOrPlaylistEmbed(link, this.uuid) 23 this.el.nativeElement.innerHTML = buildVideoOrPlaylistEmbed(link, this.uuid)
24 } 24 }
diff --git a/client/src/app/shared/shared-forms/markdown-textarea.component.ts b/client/src/app/shared/shared-forms/markdown-textarea.component.ts
index a233a4205..8f51d47df 100644
--- a/client/src/app/shared/shared-forms/markdown-textarea.component.ts
+++ b/client/src/app/shared/shared-forms/markdown-textarea.component.ts
@@ -6,6 +6,7 @@ import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@an
6import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' 6import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
7import { SafeHtml } from '@angular/platform-browser' 7import { SafeHtml } from '@angular/platform-browser'
8import { MarkdownService, ScreenService } from '@app/core' 8import { MarkdownService, ScreenService } from '@app/core'
9import { Video } from '@shared/models'
9 10
10@Component({ 11@Component({
11 selector: 'my-markdown-textarea', 12 selector: 'my-markdown-textarea',
@@ -33,7 +34,7 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
33 @Input() markdownType: 'text' | 'enhanced' = 'text' 34 @Input() markdownType: 'text' | 'enhanced' = 'text'
34 @Input() customMarkdownRenderer?: (text: string) => Promise<string | HTMLElement> 35 @Input() customMarkdownRenderer?: (text: string) => Promise<string | HTMLElement>
35 36
36 @Input() markdownVideo = false 37 @Input() markdownVideo: Video
37 38
38 @Input() name = 'description' 39 @Input() name = 'description'
39 40
@@ -147,7 +148,7 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
147 } 148 }
148 149
149 if (this.markdownVideo) { 150 if (this.markdownVideo) {
150 html = this.markdownService.processVideoTimestamps(html) 151 html = this.markdownService.processVideoTimestamps(this.markdownVideo.shortUUID, html)
151 } 152 }
152 153
153 return html 154 return html
diff --git a/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts b/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts
index 4ca6f52ad..41f4fa30d 100644
--- a/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts
+++ b/client/src/app/shared/shared-moderation/report-modals/video-report.component.ts
@@ -1,5 +1,5 @@
1import { mapValues, pickBy } from 'lodash-es' 1import { mapValues, pickBy } from 'lodash-es'
2import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' 2import { buildVideoOrPlaylistEmbed, decorateVideoLink } from 'src/assets/player/utils'
3import { Component, Input, OnInit, ViewChild } from '@angular/core' 3import { Component, Input, OnInit, ViewChild } from '@angular/core'
4import { DomSanitizer, SafeHtml } from '@angular/platform-browser' 4import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
5import { Notifier } from '@app/core' 5import { Notifier } from '@app/core'
@@ -57,11 +57,12 @@ export class VideoReportComponent extends FormReactive implements OnInit {
57 getVideoEmbed () { 57 getVideoEmbed () {
58 return this.sanitizer.bypassSecurityTrustHtml( 58 return this.sanitizer.bypassSecurityTrustHtml(
59 buildVideoOrPlaylistEmbed( 59 buildVideoOrPlaylistEmbed(
60 buildVideoLink({ 60 decorateVideoLink({
61 baseUrl: this.video.embedUrl, 61 url: this.video.embedUrl,
62 title: false, 62 title: false,
63 warningTitle: false 63 warningTitle: false
64 }), 64 }),
65
65 this.video.name 66 this.video.name
66 ) 67 )
67 ) 68 )
diff --git a/client/src/app/shared/shared-share-modal/video-share.component.ts b/client/src/app/shared/shared-share-modal/video-share.component.ts
index a41ff248b..cdfe50836 100644
--- a/client/src/app/shared/shared-share-modal/video-share.component.ts
+++ b/client/src/app/shared/shared-share-modal/video-share.component.ts
@@ -1,9 +1,15 @@
1import { Component, ElementRef, Input, ViewChild } from '@angular/core' 1import { Component, ElementRef, Input, ViewChild } from '@angular/core'
2import { Video, VideoDetails } from '@app/shared/shared-main' 2import { VideoDetails } from '@app/shared/shared-main'
3import { VideoPlaylist } from '@app/shared/shared-video-playlist' 3import { VideoPlaylist } from '@app/shared/shared-video-playlist'
4import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 4import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
5import { VideoCaption } from '@shared/models' 5import { VideoCaption } from '@shared/models'
6import { buildPlaylistLink, buildVideoLink, buildVideoOrPlaylistEmbed } from '../../../assets/player/utils' 6import {
7 buildPlaylistLink,
8 buildVideoLink,
9 buildVideoOrPlaylistEmbed,
10 decoratePlaylistLink,
11 decorateVideoLink
12} from '../../../assets/player/utils'
7 13
8type Customizations = { 14type Customizations = {
9 startAtCheckbox: boolean 15 startAtCheckbox: boolean
@@ -83,34 +89,34 @@ export class VideoShareComponent {
83 } 89 }
84 90
85 getVideoIframeCode () { 91 getVideoIframeCode () {
86 const options = this.getVideoOptions(this.video.embedUrl) 92 const embedUrl = decorateVideoLink({ url: this.video.embedUrl, ...this.getVideoOptions() })
87 93
88 const embedUrl = buildVideoLink(options)
89 return buildVideoOrPlaylistEmbed(embedUrl, this.video.name) 94 return buildVideoOrPlaylistEmbed(embedUrl, this.video.name)
90 } 95 }
91 96
92 getPlaylistIframeCode () { 97 getPlaylistIframeCode () {
93 const options = this.getPlaylistOptions(this.playlist.embedUrl) 98 const embedUrl = decoratePlaylistLink({ url: this.playlist.embedUrl, ...this.getPlaylistOptions() })
94 99
95 const embedUrl = buildPlaylistLink(options)
96 return buildVideoOrPlaylistEmbed(embedUrl, this.playlist.displayName) 100 return buildVideoOrPlaylistEmbed(embedUrl, this.playlist.displayName)
97 } 101 }
98 102
99 getVideoUrl () { 103 getVideoUrl () {
100 let baseUrl = this.customizations.originUrl ? this.video.originInstanceUrl : window.location.origin 104 const baseUrl = this.customizations.originUrl
101 baseUrl += Video.buildWatchUrl(this.video) 105 ? this.video.originInstanceUrl
106 : window.location.origin
102 107
103 const options = this.getVideoOptions(baseUrl) 108 return decorateVideoLink({
109 url: buildVideoLink(this.video, baseUrl),
104 110
105 return buildVideoLink(options) 111 ...this.getVideoOptions()
112 })
106 } 113 }
107 114
108 getPlaylistUrl () { 115 getPlaylistUrl () {
109 const base = window.location.origin + VideoPlaylist.buildWatchUrl(this.playlist) 116 const url = buildPlaylistLink(this.playlist)
117 if (!this.includeVideoInPlaylist) return url
110 118
111 if (!this.includeVideoInPlaylist) return base 119 return decoratePlaylistLink({ url, playlistPosition: this.playlistPosition })
112
113 return base + '?playlistPosition=' + this.playlistPosition
114 } 120 }
115 121
116 notSecure () { 122 notSecure () {
@@ -133,10 +139,8 @@ export class VideoShareComponent {
133 } 139 }
134 } 140 }
135 141
136 private getVideoOptions (baseUrl?: string) { 142 private getVideoOptions () {
137 return { 143 return {
138 baseUrl,
139
140 startTime: this.customizations.startAtCheckbox ? this.customizations.startAt : undefined, 144 startTime: this.customizations.startAtCheckbox ? this.customizations.startAt : undefined,
141 stopTime: this.customizations.stopAtCheckbox ? this.customizations.stopAt : undefined, 145 stopTime: this.customizations.stopAtCheckbox ? this.customizations.stopAt : undefined,
142 146
diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts
index b071a0938..6f0b804cd 100644
--- a/client/src/assets/player/peertube-player-manager.ts
+++ b/client/src/assets/player/peertube-player-manager.ts
@@ -33,13 +33,14 @@ import { getStoredP2PEnabled } from './peertube-player-local-storage'
33import { 33import {
34 NextPreviousVideoButtonOptions, 34 NextPreviousVideoButtonOptions,
35 P2PMediaLoaderPluginOptions, 35 P2PMediaLoaderPluginOptions,
36 PeerTubeLinkButtonOptions,
36 PlaylistPluginOptions, 37 PlaylistPluginOptions,
37 UserWatching, 38 UserWatching,
38 VideoJSCaption, 39 VideoJSCaption,
39 VideoJSPluginOptions 40 VideoJSPluginOptions
40} from './peertube-videojs-typings' 41} from './peertube-videojs-typings'
41import { TranslationsManager } from './translations-manager' 42import { TranslationsManager } from './translations-manager'
42import { buildVideoLink, buildVideoOrPlaylistEmbed, getRtcConfig, isIOS, isSafari } from './utils' 43import { buildVideoLink, buildVideoOrPlaylistEmbed, decorateVideoLink, getRtcConfig, isIOS, isSafari } from './utils'
43 44
44// Change 'Playback Rate' to 'Speed' (smaller for our settings menu) 45// Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
45(videojs.getComponent('PlaybackRateMenuButton') as any).prototype.controlText_ = 'Speed' 46(videojs.getComponent('PlaybackRateMenuButton') as any).prototype.controlText_ = 'Speed'
@@ -110,6 +111,7 @@ export interface CommonOptions extends CustomizationOptions {
110 videoCaptions: VideoJSCaption[] 111 videoCaptions: VideoJSCaption[]
111 112
112 videoUUID: string 113 videoUUID: string
114 videoShortUUID: string
113 115
114 userWatching?: UserWatching 116 userWatching?: UserWatching
115 117
@@ -175,7 +177,13 @@ export class PeertubePlayerManager {
175 PeertubePlayerManager.alreadyPlayed = true 177 PeertubePlayerManager.alreadyPlayed = true
176 }) 178 })
177 179
178 self.addContextMenu(mode, player, options.common.embedUrl, options.common.embedTitle) 180 self.addContextMenu({
181 mode,
182 player,
183 videoShortUUID: options.common.videoShortUUID,
184 videoEmbedUrl: options.common.embedUrl,
185 videoEmbedTitle: options.common.embedTitle
186 })
179 187
180 player.bezels() 188 player.bezels()
181 player.stats({ 189 player.stats({
@@ -218,7 +226,13 @@ export class PeertubePlayerManager {
218 videojs(newVideoElement, videojsOptions, function (this: videojs.Player) { 226 videojs(newVideoElement, videojsOptions, function (this: videojs.Player) {
219 const player = this 227 const player = this
220 228
221 self.addContextMenu(mode, player, options.common.embedUrl, options.common.embedTitle) 229 self.addContextMenu({
230 mode,
231 player,
232 videoShortUUID: options.common.videoShortUUID,
233 videoEmbedUrl: options.common.embedUrl,
234 videoEmbedTitle: options.common.embedTitle
235 })
222 236
223 PeertubePlayerManager.onPlayerChange(player) 237 PeertubePlayerManager.onPlayerChange(player)
224 }) 238 })
@@ -295,6 +309,8 @@ export class PeertubePlayerManager {
295 309
296 controlBar: { 310 controlBar: {
297 children: this.getControlBarChildren(mode, { 311 children: this.getControlBarChildren(mode, {
312 videoShortUUID: commonOptions.videoShortUUID,
313
298 captions: commonOptions.captions, 314 captions: commonOptions.captions,
299 peertubeLink: commonOptions.peertubeLink, 315 peertubeLink: commonOptions.peertubeLink,
300 theaterButton: commonOptions.theaterButton, 316 theaterButton: commonOptions.theaterButton,
@@ -409,6 +425,8 @@ export class PeertubePlayerManager {
409 } 425 }
410 426
411 private static getControlBarChildren (mode: PlayerMode, options: { 427 private static getControlBarChildren (mode: PlayerMode, options: {
428 videoShortUUID: string
429
412 peertubeLink: boolean 430 peertubeLink: boolean
413 theaterButton: boolean 431 theaterButton: boolean
414 captions: boolean 432 captions: boolean
@@ -497,7 +515,7 @@ export class PeertubePlayerManager {
497 515
498 if (options.peertubeLink === true) { 516 if (options.peertubeLink === true) {
499 Object.assign(children, { 517 Object.assign(children, {
500 'peerTubeLinkButton': {} 518 'peerTubeLinkButton': { shortUUID: options.videoShortUUID } as PeerTubeLinkButtonOptions
501 }) 519 })
502 } 520 }
503 521
@@ -514,7 +532,15 @@ export class PeertubePlayerManager {
514 return children 532 return children
515 } 533 }
516 534
517 private static addContextMenu (mode: PlayerMode, player: videojs.Player, videoEmbedUrl: string, videoEmbedTitle: string) { 535 private static addContextMenu (options: {
536 mode: PlayerMode
537 player: videojs.Player
538 videoShortUUID: string
539 videoEmbedUrl: string
540 videoEmbedTitle: string
541 }) {
542 const { mode, player, videoEmbedTitle, videoEmbedUrl, videoShortUUID } = options
543
518 const content = () => { 544 const content = () => {
519 const isLoopEnabled = player.options_['loop'] 545 const isLoopEnabled = player.options_['loop']
520 const items = [ 546 const items = [
@@ -528,13 +554,15 @@ export class PeertubePlayerManager {
528 { 554 {
529 label: player.localize('Copy the video URL'), 555 label: player.localize('Copy the video URL'),
530 listener: function () { 556 listener: function () {
531 copyToClipboard(buildVideoLink()) 557 copyToClipboard(buildVideoLink({ shortUUID: videoShortUUID }))
532 } 558 }
533 }, 559 },
534 { 560 {
535 label: player.localize('Copy the video URL at the current time'), 561 label: player.localize('Copy the video URL at the current time'),
536 listener: function (this: videojs.Player) { 562 listener: function (this: videojs.Player) {
537 copyToClipboard(buildVideoLink({ startTime: this.currentTime() })) 563 const url = buildVideoLink({ shortUUID: videoShortUUID })
564
565 copyToClipboard(decorateVideoLink({ url, startTime: this.currentTime() }))
538 } 566 }
539 }, 567 },
540 { 568 {
diff --git a/client/src/assets/player/peertube-videojs-typings.ts b/client/src/assets/player/peertube-videojs-typings.ts
index 8afb424a7..d3c75990b 100644
--- a/client/src/assets/player/peertube-videojs-typings.ts
+++ b/client/src/assets/player/peertube-videojs-typings.ts
@@ -132,6 +132,10 @@ type NextPreviousVideoButtonOptions = {
132 isDisabled: () => boolean 132 isDisabled: () => boolean
133} 133}
134 134
135type PeerTubeLinkButtonOptions = {
136 shortUUID: string
137}
138
135type WebtorrentPluginOptions = { 139type WebtorrentPluginOptions = {
136 playerElement: HTMLVideoElement 140 playerElement: HTMLVideoElement
137 141
@@ -225,5 +229,6 @@ export {
225 VideoJSPluginOptions, 229 VideoJSPluginOptions,
226 LoadedQualityData, 230 LoadedQualityData,
227 QualityLevelRepresentation, 231 QualityLevelRepresentation,
232 PeerTubeLinkButtonOptions,
228 QualityLevels 233 QualityLevels
229} 234}
diff --git a/client/src/assets/player/utils.ts b/client/src/assets/player/utils.ts
index 2bb70d1fa..eb9302493 100644
--- a/client/src/assets/player/utils.ts
+++ b/client/src/assets/player/utils.ts
@@ -1,4 +1,4 @@
1import { VideoFile } from '@shared/models' 1import { Video, VideoFile, VideoPlaylist } from '@shared/models'
2import { escapeHTML } from '@shared/core-utils/renderer' 2import { escapeHTML } from '@shared/core-utils/renderer'
3 3
4function toTitleCase (str: string) { 4function toTitleCase (str: string) {
@@ -43,8 +43,24 @@ function isMobile () {
43 return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent) 43 return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent)
44} 44}
45 45
46function buildVideoLink (options: { 46function buildPlaylistLink (playlist: Pick<VideoPlaylist, 'shortUUID'>, base?: string) {
47 baseUrl?: string 47 return (base ?? window.location.origin) + '/w/p/' + playlist.shortUUID
48}
49
50function buildVideoLink (video: Pick<Video, 'shortUUID'>, base?: string) {
51 return (base ?? window.location.origin) + '/w/' + video.shortUUID
52}
53
54function buildPlaylistEmbedLink (playlist: Pick<VideoPlaylist, 'uuid'>, base?: string) {
55 return (base ?? window.location.origin) + '/video-playlists/embed/' + playlist.uuid
56}
57
58function buildVideoEmbedLink (video: Pick<Video, 'uuid'>, base?: string) {
59 return (base ?? window.location.origin) + '/videos/embed/' + video.uuid
60}
61
62function decorateVideoLink (options: {
63 url: string
48 64
49 startTime?: number 65 startTime?: number
50 stopTime?: number 66 stopTime?: number
@@ -60,12 +76,8 @@ function buildVideoLink (options: {
60 warningTitle?: boolean 76 warningTitle?: boolean
61 controls?: boolean 77 controls?: boolean
62 peertubeLink?: boolean 78 peertubeLink?: boolean
63} = {}) { 79}) {
64 const { baseUrl } = options 80 const { url } = options
65
66 const url = baseUrl
67 ? baseUrl
68 : window.location.origin + window.location.pathname.replace('/embed/', '/w/')
69 81
70 const params = generateParams(window.location.search) 82 const params = generateParams(window.location.search)
71 83
@@ -92,16 +104,12 @@ function buildVideoLink (options: {
92 return buildUrl(url, params) 104 return buildUrl(url, params)
93} 105}
94 106
95function buildPlaylistLink (options: { 107function decoratePlaylistLink (options: {
96 baseUrl?: string 108 url: string
97 109
98 playlistPosition?: number 110 playlistPosition?: number
99}) { 111}) {
100 const { baseUrl } = options 112 const { url } = options
101
102 const url = baseUrl
103 ? baseUrl
104 : window.location.origin + window.location.pathname.replace('/video-playlists/embed/', '/w/p/')
105 113
106 const params = generateParams(window.location.search) 114 const params = generateParams(window.location.search)
107 115
@@ -224,8 +232,14 @@ export {
224 timeToInt, 232 timeToInt,
225 secondsToTime, 233 secondsToTime,
226 isWebRTCDisabled, 234 isWebRTCDisabled,
235
227 buildPlaylistLink, 236 buildPlaylistLink,
228 buildVideoLink, 237 buildVideoLink,
238 decorateVideoLink,
239 decoratePlaylistLink,
240 buildPlaylistEmbedLink,
241 buildVideoEmbedLink,
242
229 buildVideoOrPlaylistEmbed, 243 buildVideoOrPlaylistEmbed,
230 videoFileMaxByResolution, 244 videoFileMaxByResolution,
231 videoFileMinByResolution, 245 videoFileMinByResolution,
diff --git a/client/src/assets/player/videojs-components/peertube-link-button.ts b/client/src/assets/player/videojs-components/peertube-link-button.ts
index e73c95900..f47c165d9 100644
--- a/client/src/assets/player/videojs-components/peertube-link-button.ts
+++ b/client/src/assets/player/videojs-components/peertube-link-button.ts
@@ -1,11 +1,13 @@
1import { buildVideoLink } from '../utils'
2import videojs from 'video.js' 1import videojs from 'video.js'
2import { PeerTubeLinkButtonOptions } from '../peertube-videojs-typings'
3import { buildVideoLink, decorateVideoLink } from '../utils'
3 4
4const Button = videojs.getComponent('Button') 5const Button = videojs.getComponent('Button')
5class PeerTubeLinkButton extends Button { 6class PeerTubeLinkButton extends Button {
7 private shortUUID: string
6 8
7 constructor (player: videojs.Player, options?: videojs.ComponentOptions) { 9 constructor (player: videojs.Player, options?: PeerTubeLinkButtonOptions) {
8 super(player, options) 10 super(player, options as any)
9 } 11 }
10 12
11 createEl () { 13 createEl () {
@@ -13,7 +15,7 @@ class PeerTubeLinkButton extends Button {
13 } 15 }
14 16
15 updateHref () { 17 updateHref () {
16 this.el().setAttribute('href', buildVideoLink({ startTime: this.player().currentTime() })) 18 this.el().setAttribute('href', this.buildLink())
17 } 19 }
18 20
19 handleClick () { 21 handleClick () {
@@ -22,7 +24,7 @@ class PeerTubeLinkButton extends Button {
22 24
23 private buildElement () { 25 private buildElement () {
24 const el = videojs.dom.createEl('a', { 26 const el = videojs.dom.createEl('a', {
25 href: buildVideoLink(), 27 href: this.buildLink(),
26 innerHTML: 'PeerTube', 28 innerHTML: 'PeerTube',
27 title: this.player().localize('Video page (new window)'), 29 title: this.player().localize('Video page (new window)'),
28 className: 'vjs-peertube-link', 30 className: 'vjs-peertube-link',
@@ -33,6 +35,12 @@ class PeerTubeLinkButton extends Button {
33 35
34 return el as HTMLButtonElement 36 return el as HTMLButtonElement
35 } 37 }
38
39 private buildLink () {
40 const url = buildVideoLink({ shortUUID: this.shortUUID })
41
42 return decorateVideoLink({ url, startTime: this.player().currentTime() })
43 }
36} 44}
37 45
38videojs.registerComponent('PeerTubeLinkButton', PeerTubeLinkButton) 46videojs.registerComponent('PeerTubeLinkButton', PeerTubeLinkButton)