diff options
22 files changed, 136 insertions, 67 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 @@ | |||
1 | import { SortMeta } from 'primeng/api' | 1 | import { SortMeta } from 'primeng/api' |
2 | import { switchMap } from 'rxjs/operators' | 2 | import { switchMap } from 'rxjs/operators' |
3 | import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' | 3 | import { buildVideoEmbedLink, buildVideoOrPlaylistEmbed, decorateVideoLink } from 'src/assets/player/utils' |
4 | import { environment } from 'src/environments/environment' | 4 | import { environment } from 'src/environments/environment' |
5 | import { Component, OnInit } from '@angular/core' | 5 | import { Component, OnInit } from '@angular/core' |
6 | import { DomSanitizer } from '@angular/platform-browser' | 6 | import { 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 @@ | |||
1 | import * as MarkdownIt from 'markdown-it' | 1 | import * as MarkdownIt from 'markdown-it' |
2 | import { buildVideoLink } from 'src/assets/player/utils' | 2 | import { buildVideoLink, decorateVideoLink } from 'src/assets/player/utils' |
3 | import { Injectable } from '@angular/core' | 3 | import { Injectable } from '@angular/core' |
4 | import { | 4 | import { |
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 @@ | |||
1 | import * as debug from 'debug' | 1 | import * as debug from 'debug' |
2 | import truncate from 'lodash-es/truncate' | 2 | import truncate from 'lodash-es/truncate' |
3 | import { SortMeta } from 'primeng/api' | 3 | import { SortMeta } from 'primeng/api' |
4 | import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' | 4 | import { buildVideoEmbedLink, buildVideoOrPlaylistEmbed, decorateVideoLink } from 'src/assets/player/utils' |
5 | import { environment } from 'src/environments/environment' | 5 | import { environment } from 'src/environments/environment' |
6 | import { Component, Input, OnInit, ViewChild } from '@angular/core' | 6 | import { Component, Input, OnInit, ViewChild } from '@angular/core' |
7 | import { DomSanitizer } from '@angular/platform-browser' | 7 | import { 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 @@ | |||
1 | import { buildPlaylistLink, buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' | 1 | import { buildPlaylistEmbedLink, buildVideoEmbedLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' |
2 | import { environment } from 'src/environments/environment' | 2 | import { environment } from 'src/environments/environment' |
3 | import { Component, ElementRef, Input, OnInit } from '@angular/core' | 3 | import { Component, ElementRef, Input, OnInit } from '@angular/core' |
4 | import { CustomMarkupComponent } from './shared' | 4 | import { 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 | |||
6 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' | 6 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' |
7 | import { SafeHtml } from '@angular/platform-browser' | 7 | import { SafeHtml } from '@angular/platform-browser' |
8 | import { MarkdownService, ScreenService } from '@app/core' | 8 | import { MarkdownService, ScreenService } from '@app/core' |
9 | import { 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 @@ | |||
1 | import { mapValues, pickBy } from 'lodash-es' | 1 | import { mapValues, pickBy } from 'lodash-es' |
2 | import { buildVideoLink, buildVideoOrPlaylistEmbed } from 'src/assets/player/utils' | 2 | import { buildVideoOrPlaylistEmbed, decorateVideoLink } from 'src/assets/player/utils' |
3 | import { Component, Input, OnInit, ViewChild } from '@angular/core' | 3 | import { Component, Input, OnInit, ViewChild } from '@angular/core' |
4 | import { DomSanitizer, SafeHtml } from '@angular/platform-browser' | 4 | import { DomSanitizer, SafeHtml } from '@angular/platform-browser' |
5 | import { Notifier } from '@app/core' | 5 | import { 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 @@ | |||
1 | import { Component, ElementRef, Input, ViewChild } from '@angular/core' | 1 | import { Component, ElementRef, Input, ViewChild } from '@angular/core' |
2 | import { Video, VideoDetails } from '@app/shared/shared-main' | 2 | import { VideoDetails } from '@app/shared/shared-main' |
3 | import { VideoPlaylist } from '@app/shared/shared-video-playlist' | 3 | import { VideoPlaylist } from '@app/shared/shared-video-playlist' |
4 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 4 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
5 | import { VideoCaption } from '@shared/models' | 5 | import { VideoCaption } from '@shared/models' |
6 | import { buildPlaylistLink, buildVideoLink, buildVideoOrPlaylistEmbed } from '../../../assets/player/utils' | 6 | import { |
7 | buildPlaylistLink, | ||
8 | buildVideoLink, | ||
9 | buildVideoOrPlaylistEmbed, | ||
10 | decoratePlaylistLink, | ||
11 | decorateVideoLink | ||
12 | } from '../../../assets/player/utils' | ||
7 | 13 | ||
8 | type Customizations = { | 14 | type 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' | |||
33 | import { | 33 | import { |
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' |
41 | import { TranslationsManager } from './translations-manager' | 42 | import { TranslationsManager } from './translations-manager' |
42 | import { buildVideoLink, buildVideoOrPlaylistEmbed, getRtcConfig, isIOS, isSafari } from './utils' | 43 | import { 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 | ||
135 | type PeerTubeLinkButtonOptions = { | ||
136 | shortUUID: string | ||
137 | } | ||
138 | |||
135 | type WebtorrentPluginOptions = { | 139 | type 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 @@ | |||
1 | import { VideoFile } from '@shared/models' | 1 | import { Video, VideoFile, VideoPlaylist } from '@shared/models' |
2 | import { escapeHTML } from '@shared/core-utils/renderer' | 2 | import { escapeHTML } from '@shared/core-utils/renderer' |
3 | 3 | ||
4 | function toTitleCase (str: string) { | 4 | function 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 | ||
46 | function buildVideoLink (options: { | 46 | function buildPlaylistLink (playlist: Pick<VideoPlaylist, 'shortUUID'>, base?: string) { |
47 | baseUrl?: string | 47 | return (base ?? window.location.origin) + '/w/p/' + playlist.shortUUID |
48 | } | ||
49 | |||
50 | function buildVideoLink (video: Pick<Video, 'shortUUID'>, base?: string) { | ||
51 | return (base ?? window.location.origin) + '/w/' + video.shortUUID | ||
52 | } | ||
53 | |||
54 | function buildPlaylistEmbedLink (playlist: Pick<VideoPlaylist, 'uuid'>, base?: string) { | ||
55 | return (base ?? window.location.origin) + '/video-playlists/embed/' + playlist.uuid | ||
56 | } | ||
57 | |||
58 | function buildVideoEmbedLink (video: Pick<Video, 'uuid'>, base?: string) { | ||
59 | return (base ?? window.location.origin) + '/videos/embed/' + video.uuid | ||
60 | } | ||
61 | |||
62 | function 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 | ||
95 | function buildPlaylistLink (options: { | 107 | function 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 @@ | |||
1 | import { buildVideoLink } from '../utils' | ||
2 | import videojs from 'video.js' | 1 | import videojs from 'video.js' |
2 | import { PeerTubeLinkButtonOptions } from '../peertube-videojs-typings' | ||
3 | import { buildVideoLink, decorateVideoLink } from '../utils' | ||
3 | 4 | ||
4 | const Button = videojs.getComponent('Button') | 5 | const Button = videojs.getComponent('Button') |
5 | class PeerTubeLinkButton extends Button { | 6 | class 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 | ||
38 | videojs.registerComponent('PeerTubeLinkButton', PeerTubeLinkButton) | 46 | videojs.registerComponent('PeerTubeLinkButton', PeerTubeLinkButton) |
diff --git a/shared/core-utils/miscs/date.ts b/shared/core-utils/common/date.ts index 4f92f758f..4f92f758f 100644 --- a/shared/core-utils/miscs/date.ts +++ b/shared/core-utils/common/date.ts | |||
diff --git a/shared/core-utils/miscs/index.ts b/shared/core-utils/common/index.ts index 7764e69ad..83f2ccbb6 100644 --- a/shared/core-utils/miscs/index.ts +++ b/shared/core-utils/common/index.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | export * from './date' | 1 | export * from './date' |
2 | export * from './miscs' | 2 | export * from './miscs' |
3 | export * from './types' | ||
4 | export * from './regexp' | 3 | export * from './regexp' |
4 | export * from './types' | ||
5 | export * from './url | ||
diff --git a/shared/core-utils/miscs/miscs.ts b/shared/core-utils/common/miscs.ts index 4780ca922..4780ca922 100644 --- a/shared/core-utils/miscs/miscs.ts +++ b/shared/core-utils/common/miscs.ts | |||
diff --git a/shared/core-utils/miscs/regexp.ts b/shared/core-utils/common/regexp.ts index 59eb87eb6..59eb87eb6 100644 --- a/shared/core-utils/miscs/regexp.ts +++ b/shared/core-utils/common/regexp.ts | |||
diff --git a/shared/core-utils/miscs/types.ts b/shared/core-utils/common/types.ts index bd2a97b98..bd2a97b98 100644 --- a/shared/core-utils/miscs/types.ts +++ b/shared/core-utils/common/types.ts | |||
diff --git a/shared/core-utils/common/url.ts b/shared/core-utils/common/url.ts new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/shared/core-utils/common/url.ts | |||
diff --git a/shared/core-utils/index.ts b/shared/core-utils/index.ts index 42d7cab1d..0b05dc9eb 100644 --- a/shared/core-utils/index.ts +++ b/shared/core-utils/index.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | export * from './abuse' | 1 | export * from './abuse' |
2 | export * from './common' | ||
2 | export * from './i18n' | 3 | export * from './i18n' |
3 | export * from './logs' | 4 | export * from './logs' |
4 | export * from './miscs' | ||
5 | export * from './plugins' | 5 | export * from './plugins' |
6 | export * from './renderer' | 6 | export * from './renderer' |
7 | export * from './users' | 7 | export * from './users' |