diff options
author | Chocobozzz <me@florianbigard.com> | 2020-08-07 13:43:48 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2020-08-07 13:43:48 +0200 |
commit | 951b582f52d0694865f020f0e53ccfad2d2d6033 (patch) | |
tree | e82f6eaf08a2add25a7807135a5b2351819ab3a0 | |
parent | 4891e4c77b72ac5a2f9d3d761a71eebe26d81357 (diff) | |
download | PeerTube-951b582f52d0694865f020f0e53ccfad2d2d6033.tar.gz PeerTube-951b582f52d0694865f020f0e53ccfad2d2d6033.tar.zst PeerTube-951b582f52d0694865f020f0e53ccfad2d2d6033.zip |
Add ability to share playlists in modal
13 files changed, 143 insertions, 43 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 3242bcf46..422d873c0 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 | |||
@@ -7,7 +7,7 @@ import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' | |||
7 | import { VideoBlockService } from '@app/shared/shared-moderation' | 7 | import { VideoBlockService } from '@app/shared/shared-moderation' |
8 | import { I18n } from '@ngx-translate/i18n-polyfill' | 8 | import { I18n } from '@ngx-translate/i18n-polyfill' |
9 | import { VideoBlacklist, VideoBlacklistType } from '@shared/models' | 9 | import { VideoBlacklist, VideoBlacklistType } from '@shared/models' |
10 | import { buildVideoEmbed, buildVideoLink } from 'src/assets/player/utils' | 10 | import { buildVideoOrPlaylistEmbed, buildVideoLink } from 'src/assets/player/utils' |
11 | import { environment } from 'src/environments/environment' | 11 | import { environment } from 'src/environments/environment' |
12 | import { DomSanitizer } from '@angular/platform-browser' | 12 | import { DomSanitizer } from '@angular/platform-browser' |
13 | 13 | ||
@@ -176,7 +176,7 @@ export class VideoBlockListComponent extends RestTable implements OnInit, AfterV | |||
176 | } | 176 | } |
177 | 177 | ||
178 | getVideoEmbed (entry: VideoBlacklist) { | 178 | getVideoEmbed (entry: VideoBlacklist) { |
179 | return buildVideoEmbed( | 179 | return buildVideoOrPlaylistEmbed( |
180 | buildVideoLink({ | 180 | buildVideoLink({ |
181 | baseUrl: `${environment.embedUrl}/videos/embed/${entry.video.uuid}`, | 181 | baseUrl: `${environment.embedUrl}/videos/embed/${entry.video.uuid}`, |
182 | title: false, | 182 | title: false, |
diff --git a/client/src/app/+videos/+video-watch/comment/video-comment.component.ts b/client/src/app/+videos/+video-watch/comment/video-comment.component.ts index 6744a0954..36ec6e9f9 100644 --- a/client/src/app/+videos/+video-watch/comment/video-comment.component.ts +++ b/client/src/app/+videos/+video-watch/comment/video-comment.component.ts | |||
@@ -135,7 +135,7 @@ export class VideoCommentComponent implements OnInit, OnChanges { | |||
135 | this.comment.account = null | 135 | this.comment.account = null |
136 | } | 136 | } |
137 | 137 | ||
138 | if (this.isUserLoggedIn() && this.authService.getUser().account.id !== this.comment.account.id) { | 138 | if (this.isUserLoggedIn() && this.comment.isDeleted === false && this.authService.getUser().account.id !== this.comment.account.id) { |
139 | this.prependModerationActions = [ | 139 | this.prependModerationActions = [ |
140 | { | 140 | { |
141 | label: this.i18n('Report comment'), | 141 | label: this.i18n('Report comment'), |
diff --git a/client/src/app/+videos/+video-watch/modal/video-share.component.html b/client/src/app/+videos/+video-watch/modal/video-share.component.html index 71ae6544f..946e8d8ca 100644 --- a/client/src/app/+videos/+video-watch/modal/video-share.component.html +++ b/client/src/app/+videos/+video-watch/modal/video-share.component.html | |||
@@ -6,18 +6,56 @@ | |||
6 | 6 | ||
7 | 7 | ||
8 | <div class="modal-body"> | 8 | <div class="modal-body"> |
9 | |||
9 | <div class="playlist" *ngIf="hasPlaylist()"> | 10 | <div class="playlist" *ngIf="hasPlaylist()"> |
10 | <div class="title-page title-page-single" i18n>Share the playlist</div> | 11 | <div class="title-page title-page-single" i18n>Share the playlist</div> |
11 | 12 | ||
12 | <my-input-readonly-copy [value]="getPlaylistUrl()"></my-input-readonly-copy> | 13 | <div ngbNav #nav="ngbNav" class="nav-tabs" [(activeId)]="activePlaylistId"> |
14 | |||
15 | <ng-container ngbNavItem="url"> | ||
16 | <a ngbNavLink i18n>URL</a> | ||
17 | |||
18 | <ng-template ngbNavContent> | ||
19 | <div class="nav-content"> | ||
20 | |||
21 | <my-input-readonly-copy [value]="getPlaylistUrl()"></my-input-readonly-copy> | ||
22 | </div> | ||
23 | </ng-template> | ||
24 | </ng-container> | ||
25 | |||
26 | <ng-container ngbNavItem="qrcode"> | ||
27 | <a ngbNavLink i18n>QR-Code</a> | ||
28 | |||
29 | <ng-template ngbNavContent> | ||
30 | <div class="nav-content"> | ||
31 | <qrcode [qrdata]="getPlaylistUrl()" [size]="256" level="Q"></qrcode> | ||
32 | </div> | ||
33 | </ng-template> | ||
34 | </ng-container> | ||
35 | |||
36 | <ng-container ngbNavItem="embed"> | ||
37 | <a ngbNavLink i18n>Embed</a> | ||
38 | |||
39 | <ng-template ngbNavContent> | ||
40 | <div class="nav-content"> | ||
41 | <my-input-readonly-copy [value]="getPlaylistIframeCode()"></my-input-readonly-copy> | ||
42 | |||
43 | <div i18n *ngIf="notSecure()" class="alert alert-warning"> | ||
44 | The url is not secured (no HTTPS), so the embed video won't work on HTTPS websites (web browsers block non secured HTTP requests on HTTPS websites). | ||
45 | </div> | ||
46 | </div> | ||
47 | </ng-template> | ||
48 | </ng-container> | ||
49 | |||
50 | </div> | ||
51 | |||
52 | <div [ngbNavOutlet]="nav"></div> | ||
13 | 53 | ||
14 | <div class="filters"> | 54 | <div class="filters"> |
15 | 55 | ||
16 | <div class="form-group"> | 56 | <div class="form-group"> |
17 | <my-peertube-checkbox | 57 | <my-peertube-checkbox inputName="includeVideoInPlaylist" [(ngModel)]="includeVideoInPlaylist" i18n-labelText |
18 | inputName="includeVideoInPlaylist" [(ngModel)]="includeVideoInPlaylist" | 58 | labelText="Share the playlist at this video position"></my-peertube-checkbox> |
19 | i18n-labelText labelText="Share the playlist at this video position" | ||
20 | ></my-peertube-checkbox> | ||
21 | </div> | 59 | </div> |
22 | 60 | ||
23 | </div> | 61 | </div> |
@@ -27,7 +65,7 @@ | |||
27 | <div class="video"> | 65 | <div class="video"> |
28 | <div class="title-page title-page-single" *ngIf="hasPlaylist()" i18n>Share the video</div> | 66 | <div class="title-page title-page-single" *ngIf="hasPlaylist()" i18n>Share the video</div> |
29 | 67 | ||
30 | <div ngbNav #nav="ngbNav" class="nav-tabs" [(activeId)]="activeId"> | 68 | <div ngbNav #nav="ngbNav" class="nav-tabs" [(activeId)]="activeVideoId"> |
31 | 69 | ||
32 | <ng-container ngbNavItem="url"> | 70 | <ng-container ngbNavItem="url"> |
33 | <a ngbNavLink i18n>URL</a> | 71 | <a ngbNavLink i18n>URL</a> |
@@ -137,7 +175,7 @@ | |||
137 | </div> | 175 | </div> |
138 | </div> | 176 | </div> |
139 | 177 | ||
140 | <ng-container *ngIf="isInEmbedTab()"> | 178 | <ng-container *ngIf="isVideoInEmbedTab()"> |
141 | <div class="form-group"> | 179 | <div class="form-group"> |
142 | <my-peertube-checkbox | 180 | <my-peertube-checkbox |
143 | inputName="title" [(ngModel)]="customizations.title" | 181 | inputName="title" [(ngModel)]="customizations.title" |
diff --git a/client/src/app/+videos/+video-watch/modal/video-share.component.ts b/client/src/app/+videos/+video-watch/modal/video-share.component.ts index 23c562273..d9171fe0e 100644 --- a/client/src/app/+videos/+video-watch/modal/video-share.component.ts +++ b/client/src/app/+videos/+video-watch/modal/video-share.component.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { Component, ElementRef, Input, ViewChild } from '@angular/core' | 1 | import { Component, ElementRef, Input, ViewChild } from '@angular/core' |
2 | import { buildVideoEmbed, buildVideoLink } from '../../../../assets/player/utils' | 2 | import { buildVideoOrPlaylistEmbed, buildVideoLink, buildPlaylistLink } from '../../../../assets/player/utils' |
3 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 3 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
4 | import { VideoCaption } from '@shared/models' | 4 | import { VideoCaption } from '@shared/models' |
5 | import { VideoDetails } from '@app/shared/shared-main' | 5 | import { VideoDetails } from '@app/shared/shared-main' |
@@ -24,6 +24,8 @@ type Customizations = { | |||
24 | peertubeLink: boolean | 24 | peertubeLink: boolean |
25 | } | 25 | } |
26 | 26 | ||
27 | type TabId = 'url' | 'qrcode' | 'embed' | ||
28 | |||
27 | @Component({ | 29 | @Component({ |
28 | selector: 'my-video-share', | 30 | selector: 'my-video-share', |
29 | templateUrl: './video-share.component.html', | 31 | templateUrl: './video-share.component.html', |
@@ -36,14 +38,18 @@ export class VideoShareComponent { | |||
36 | @Input() videoCaptions: VideoCaption[] = [] | 38 | @Input() videoCaptions: VideoCaption[] = [] |
37 | @Input() playlist: VideoPlaylist = null | 39 | @Input() playlist: VideoPlaylist = null |
38 | 40 | ||
39 | activeId: 'url' | 'qrcode' | 'embed' = 'url' | 41 | activeVideoId: TabId = 'url' |
42 | activePlaylistId: TabId = 'url' | ||
43 | |||
40 | customizations: Customizations | 44 | customizations: Customizations |
41 | isAdvancedCustomizationCollapsed = true | 45 | isAdvancedCustomizationCollapsed = true |
42 | includeVideoInPlaylist = false | 46 | includeVideoInPlaylist = false |
43 | 47 | ||
48 | private playlistPosition: number = null | ||
49 | |||
44 | constructor (private modalService: NgbModal) { } | 50 | constructor (private modalService: NgbModal) { } |
45 | 51 | ||
46 | show (currentVideoTimestamp?: number) { | 52 | show (currentVideoTimestamp?: number, currentPlaylistPosition?: number) { |
47 | let subtitle: string | 53 | let subtitle: string |
48 | if (this.videoCaptions.length !== 0) { | 54 | if (this.videoCaptions.length !== 0) { |
49 | subtitle = this.videoCaptions[0].language.id | 55 | subtitle = this.videoCaptions[0].language.id |
@@ -70,19 +76,28 @@ export class VideoShareComponent { | |||
70 | peertubeLink: true | 76 | peertubeLink: true |
71 | } | 77 | } |
72 | 78 | ||
79 | this.playlistPosition = currentPlaylistPosition | ||
80 | |||
73 | this.modalService.open(this.modal, { centered: true }) | 81 | this.modalService.open(this.modal, { centered: true }) |
74 | } | 82 | } |
75 | 83 | ||
76 | getVideoIframeCode () { | 84 | getVideoIframeCode () { |
77 | const options = this.getOptions(this.video.embedUrl) | 85 | const options = this.getVideoOptions(this.video.embedUrl) |
78 | 86 | ||
79 | const embedUrl = buildVideoLink(options) | 87 | const embedUrl = buildVideoLink(options) |
80 | return buildVideoEmbed(embedUrl) | 88 | return buildVideoOrPlaylistEmbed(embedUrl) |
89 | } | ||
90 | |||
91 | getPlaylistIframeCode () { | ||
92 | const options = this.getPlaylistOptions(this.playlist.embedUrl) | ||
93 | |||
94 | const embedUrl = buildPlaylistLink(options) | ||
95 | return buildVideoOrPlaylistEmbed(embedUrl) | ||
81 | } | 96 | } |
82 | 97 | ||
83 | getVideoUrl () { | 98 | getVideoUrl () { |
84 | const baseUrl = window.location.origin + '/videos/watch/' + this.video.uuid | 99 | const baseUrl = window.location.origin + '/videos/watch/' + this.video.uuid |
85 | const options = this.getOptions(baseUrl) | 100 | const options = this.getVideoOptions(baseUrl) |
86 | 101 | ||
87 | return buildVideoLink(options) | 102 | return buildVideoLink(options) |
88 | } | 103 | } |
@@ -99,15 +114,23 @@ export class VideoShareComponent { | |||
99 | return window.location.protocol === 'http:' | 114 | return window.location.protocol === 'http:' |
100 | } | 115 | } |
101 | 116 | ||
102 | isInEmbedTab () { | 117 | isVideoInEmbedTab () { |
103 | return this.activeId === 'embed' | 118 | return this.activeVideoId === 'embed' |
104 | } | 119 | } |
105 | 120 | ||
106 | hasPlaylist () { | 121 | hasPlaylist () { |
107 | return !!this.playlist | 122 | return !!this.playlist |
108 | } | 123 | } |
109 | 124 | ||
110 | private getOptions (baseUrl?: string) { | 125 | private getPlaylistOptions (baseUrl?: string) { |
126 | return { | ||
127 | baseUrl, | ||
128 | |||
129 | playlistPosition: this.playlistPosition || undefined | ||
130 | } | ||
131 | } | ||
132 | |||
133 | private getVideoOptions (baseUrl?: string) { | ||
111 | return { | 134 | return { |
112 | baseUrl, | 135 | baseUrl, |
113 | 136 | ||
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 dfe73d14d..d8136ab4f 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.ts +++ b/client/src/app/+videos/+video-watch/video-watch.component.ts | |||
@@ -244,7 +244,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
244 | showShareModal () { | 244 | showShareModal () { |
245 | this.pausePlayer() | 245 | this.pausePlayer() |
246 | 246 | ||
247 | this.videoShareModal.show(this.currentTime) | 247 | this.videoShareModal.show(this.currentTime, this.videoWatchPlaylist.currentPlaylistPosition) |
248 | } | 248 | } |
249 | 249 | ||
250 | isUserLoggedIn () { | 250 | isUserLoggedIn () { |
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 21d2ea47d..c7dc5f4d2 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 { buildVideoEmbed, buildVideoLink } from 'src/assets/player/utils' | 4 | import { buildVideoOrPlaylistEmbed, buildVideoLink } from 'src/assets/player/utils' |
5 | import { environment } from 'src/environments/environment' | 5 | import { environment } from 'src/environments/environment' |
6 | import { AfterViewInit, Component, OnInit, ViewChild, Input } from '@angular/core' | 6 | import { AfterViewInit, Component, OnInit, ViewChild, Input } from '@angular/core' |
7 | import { DomSanitizer } from '@angular/platform-browser' | 7 | import { DomSanitizer } from '@angular/platform-browser' |
@@ -141,7 +141,7 @@ export class AbuseListTableComponent extends RestTable implements OnInit, AfterV | |||
141 | } | 141 | } |
142 | 142 | ||
143 | getVideoEmbed (abuse: AdminAbuse) { | 143 | getVideoEmbed (abuse: AdminAbuse) { |
144 | return buildVideoEmbed( | 144 | return buildVideoOrPlaylistEmbed( |
145 | buildVideoLink({ | 145 | buildVideoLink({ |
146 | baseUrl: `${environment.embedUrl}/videos/embed/${abuse.video.uuid}`, | 146 | baseUrl: `${environment.embedUrl}/videos/embed/${abuse.video.uuid}`, |
147 | title: false, | 147 | title: false, |
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 09ab98dfe..794dd54bb 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 { buildVideoEmbed, buildVideoLink } from 'src/assets/player/utils' | 2 | import { buildVideoOrPlaylistEmbed, buildVideoLink } 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' |
@@ -58,7 +58,7 @@ export class VideoReportComponent extends FormReactive implements OnInit { | |||
58 | 58 | ||
59 | getVideoEmbed () { | 59 | getVideoEmbed () { |
60 | return this.sanitizer.bypassSecurityTrustHtml( | 60 | return this.sanitizer.bypassSecurityTrustHtml( |
61 | buildVideoEmbed( | 61 | buildVideoOrPlaylistEmbed( |
62 | buildVideoLink({ | 62 | buildVideoLink({ |
63 | baseUrl: this.video.embedUrl, | 63 | baseUrl: this.video.embedUrl, |
64 | title: false, | 64 | title: false, |
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist.model.ts b/client/src/app/shared/shared-video-playlist/video-playlist.model.ts index 7de379cdf..3db3b7a2e 100644 --- a/client/src/app/shared/shared-video-playlist/video-playlist.model.ts +++ b/client/src/app/shared/shared-video-playlist/video-playlist.model.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { getAbsoluteAPIUrl } from '@app/helpers' | 1 | import { getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers' |
2 | import { Actor } from '@app/shared/shared-main' | 2 | import { Actor } from '@app/shared/shared-main' |
3 | import { peertubeTranslate } from '@shared/core-utils/i18n' | 3 | import { peertubeTranslate } from '@shared/core-utils/i18n' |
4 | import { | 4 | import { |
@@ -33,6 +33,9 @@ export class VideoPlaylist implements ServerVideoPlaylist { | |||
33 | 33 | ||
34 | thumbnailUrl: string | 34 | thumbnailUrl: string |
35 | 35 | ||
36 | embedPath: string | ||
37 | embedUrl: string | ||
38 | |||
36 | ownerBy: string | 39 | ownerBy: string |
37 | ownerAvatarUrl: string | 40 | ownerAvatarUrl: string |
38 | 41 | ||
@@ -63,6 +66,9 @@ export class VideoPlaylist implements ServerVideoPlaylist { | |||
63 | this.thumbnailUrl = window.location.origin + '/client/assets/images/default-playlist.jpg' | 66 | this.thumbnailUrl = window.location.origin + '/client/assets/images/default-playlist.jpg' |
64 | } | 67 | } |
65 | 68 | ||
69 | this.embedPath = hash.embedPath | ||
70 | this.embedUrl = getAbsoluteEmbedUrl() + hash.embedPath | ||
71 | |||
66 | this.videosLength = hash.videosLength | 72 | this.videosLength = hash.videosLength |
67 | 73 | ||
68 | this.type = hash.type | 74 | this.type = hash.type |
diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts index c71b43415..15b2f420b 100644 --- a/client/src/assets/player/peertube-player-manager.ts +++ b/client/src/assets/player/peertube-player-manager.ts | |||
@@ -35,7 +35,7 @@ import { | |||
35 | VideoJSPluginOptions | 35 | VideoJSPluginOptions |
36 | } from './peertube-videojs-typings' | 36 | } from './peertube-videojs-typings' |
37 | import { TranslationsManager } from './translations-manager' | 37 | import { TranslationsManager } from './translations-manager' |
38 | import { buildVideoEmbed, buildVideoLink, copyToClipboard, getRtcConfig, isIOS, isSafari } from './utils' | 38 | import { buildVideoOrPlaylistEmbed, buildVideoLink, copyToClipboard, getRtcConfig, isIOS, isSafari } from './utils' |
39 | 39 | ||
40 | // Change 'Playback Rate' to 'Speed' (smaller for our settings menu) | 40 | // Change 'Playback Rate' to 'Speed' (smaller for our settings menu) |
41 | (videojs.getComponent('PlaybackRateMenuButton') as any).prototype.controlText_ = 'Speed' | 41 | (videojs.getComponent('PlaybackRateMenuButton') as any).prototype.controlText_ = 'Speed' |
@@ -492,7 +492,7 @@ export class PeertubePlayerManager { | |||
492 | { | 492 | { |
493 | label: player.localize('Copy embed code'), | 493 | label: player.localize('Copy embed code'), |
494 | listener: () => { | 494 | listener: () => { |
495 | copyToClipboard(buildVideoEmbed(videoEmbedUrl)) | 495 | copyToClipboard(buildVideoOrPlaylistEmbed(videoEmbedUrl)) |
496 | } | 496 | } |
497 | } | 497 | } |
498 | ] | 498 | ] |
diff --git a/client/src/assets/player/utils.ts b/client/src/assets/player/utils.ts index 115fdfa49..ce7a7fe6c 100644 --- a/client/src/assets/player/utils.ts +++ b/client/src/assets/player/utils.ts | |||
@@ -43,20 +43,20 @@ function isMobile () { | |||
43 | } | 43 | } |
44 | 44 | ||
45 | function buildVideoLink (options: { | 45 | function buildVideoLink (options: { |
46 | baseUrl?: string, | 46 | baseUrl?: string |
47 | 47 | ||
48 | startTime?: number, | 48 | startTime?: number |
49 | stopTime?: number, | 49 | stopTime?: number |
50 | 50 | ||
51 | subtitle?: string, | 51 | subtitle?: string |
52 | 52 | ||
53 | loop?: boolean, | 53 | loop?: boolean |
54 | autoplay?: boolean, | 54 | autoplay?: boolean |
55 | muted?: boolean, | 55 | muted?: boolean |
56 | 56 | ||
57 | // Embed options | 57 | // Embed options |
58 | title?: boolean, | 58 | title?: boolean |
59 | warningTitle?: boolean, | 59 | warningTitle?: boolean |
60 | controls?: boolean | 60 | controls?: boolean |
61 | peertubeLink?: boolean | 61 | peertubeLink?: boolean |
62 | } = {}) { | 62 | } = {}) { |
@@ -66,10 +66,7 @@ function buildVideoLink (options: { | |||
66 | ? baseUrl | 66 | ? baseUrl |
67 | : window.location.origin + window.location.pathname.replace('/embed/', '/watch/') | 67 | : window.location.origin + window.location.pathname.replace('/embed/', '/watch/') |
68 | 68 | ||
69 | const params = new URLSearchParams(window.location.search) | 69 | const params = generateParams(window.location.search) |
70 | // Remove these unused parameters when we are on a playlist page | ||
71 | params.delete('videoId') | ||
72 | params.delete('resume') | ||
73 | 70 | ||
74 | if (options.startTime) { | 71 | if (options.startTime) { |
75 | const startTimeInt = Math.floor(options.startTime) | 72 | const startTimeInt = Math.floor(options.startTime) |
@@ -91,6 +88,28 @@ function buildVideoLink (options: { | |||
91 | if (options.controls === false) params.set('controls', '0') | 88 | if (options.controls === false) params.set('controls', '0') |
92 | if (options.peertubeLink === false) params.set('peertubeLink', '0') | 89 | if (options.peertubeLink === false) params.set('peertubeLink', '0') |
93 | 90 | ||
91 | return buildUrl(url, params) | ||
92 | } | ||
93 | |||
94 | function buildPlaylistLink (options: { | ||
95 | baseUrl?: string | ||
96 | |||
97 | playlistPosition: number | ||
98 | }) { | ||
99 | const { baseUrl } = options | ||
100 | |||
101 | const url = baseUrl | ||
102 | ? baseUrl | ||
103 | : window.location.origin + window.location.pathname.replace('/video-playlists/embed/', '/videos/watch/playlist/') | ||
104 | |||
105 | const params = generateParams(window.location.search) | ||
106 | |||
107 | if (options.playlistPosition) params.set('playlistPosition', '' + options.playlistPosition) | ||
108 | |||
109 | return buildUrl(url, params) | ||
110 | } | ||
111 | |||
112 | function buildUrl (url: string, params: URLSearchParams) { | ||
94 | let hasParams = false | 113 | let hasParams = false |
95 | params.forEach(() => hasParams = true) | 114 | params.forEach(() => hasParams = true) |
96 | 115 | ||
@@ -99,6 +118,15 @@ function buildVideoLink (options: { | |||
99 | return url | 118 | return url |
100 | } | 119 | } |
101 | 120 | ||
121 | function generateParams (url: string) { | ||
122 | const params = new URLSearchParams(window.location.search) | ||
123 | // Unused parameters in embed | ||
124 | params.delete('videoId') | ||
125 | params.delete('resume') | ||
126 | |||
127 | return params | ||
128 | } | ||
129 | |||
102 | function timeToInt (time: number | string) { | 130 | function timeToInt (time: number | string) { |
103 | if (!time) return 0 | 131 | if (!time) return 0 |
104 | if (typeof time === 'number') return time | 132 | if (typeof time === 'number') return time |
@@ -140,7 +168,7 @@ function secondsToTime (seconds: number, full = false, symbol?: string) { | |||
140 | return time | 168 | return time |
141 | } | 169 | } |
142 | 170 | ||
143 | function buildVideoEmbed (embedUrl: string) { | 171 | function buildVideoOrPlaylistEmbed (embedUrl: string) { |
144 | return '<iframe width="560" height="315" ' + | 172 | return '<iframe width="560" height="315" ' + |
145 | 'sandbox="allow-same-origin allow-scripts allow-popups" ' + | 173 | 'sandbox="allow-same-origin allow-scripts allow-popups" ' + |
146 | 'src="' + embedUrl + '" ' + | 174 | 'src="' + embedUrl + '" ' + |
@@ -203,8 +231,9 @@ export { | |||
203 | timeToInt, | 231 | timeToInt, |
204 | secondsToTime, | 232 | secondsToTime, |
205 | isWebRTCDisabled, | 233 | isWebRTCDisabled, |
234 | buildPlaylistLink, | ||
206 | buildVideoLink, | 235 | buildVideoLink, |
207 | buildVideoEmbed, | 236 | buildVideoOrPlaylistEmbed, |
208 | videoFileMaxByResolution, | 237 | videoFileMaxByResolution, |
209 | videoFileMinByResolution, | 238 | videoFileMinByResolution, |
210 | copyToClipboard, | 239 | copyToClipboard, |
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts index f935bf4f0..581179640 100644 --- a/server/models/video/video-playlist.ts +++ b/server/models/video/video-playlist.ts | |||
@@ -528,6 +528,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
528 | }, | 528 | }, |
529 | 529 | ||
530 | thumbnailPath: this.getThumbnailStaticPath(), | 530 | thumbnailPath: this.getThumbnailStaticPath(), |
531 | embedPath: this.getEmbedStaticPath(), | ||
531 | 532 | ||
532 | type: { | 533 | type: { |
533 | id: this.type, | 534 | id: this.type, |
diff --git a/server/tests/api/videos/video-playlists.ts b/server/tests/api/videos/video-playlists.ts index 2bb97d7a8..52b32998d 100644 --- a/server/tests/api/videos/video-playlists.ts +++ b/server/tests/api/videos/video-playlists.ts | |||
@@ -236,7 +236,7 @@ describe('Test video playlists', function () { | |||
236 | const playlistFromList = res.body.data[0] as VideoPlaylist | 236 | const playlistFromList = res.body.data[0] as VideoPlaylist |
237 | 237 | ||
238 | const res2 = await getVideoPlaylist(server.url, playlistFromList.uuid) | 238 | const res2 = await getVideoPlaylist(server.url, playlistFromList.uuid) |
239 | const playlistFromGet = res2.body | 239 | const playlistFromGet = res2.body as VideoPlaylist |
240 | 240 | ||
241 | for (const playlist of [ playlistFromGet, playlistFromList ]) { | 241 | for (const playlist of [ playlistFromGet, playlistFromList ]) { |
242 | expect(playlist.id).to.be.a('number') | 242 | expect(playlist.id).to.be.a('number') |
@@ -250,6 +250,7 @@ describe('Test video playlists', function () { | |||
250 | expect(playlist.privacy.label).to.equal('Public') | 250 | expect(playlist.privacy.label).to.equal('Public') |
251 | expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR) | 251 | expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR) |
252 | expect(playlist.type.label).to.equal('Regular') | 252 | expect(playlist.type.label).to.equal('Regular') |
253 | expect(playlist.embedPath).to.equal('/video-playlists/embed/' + playlist.uuid) | ||
253 | 254 | ||
254 | expect(playlist.videosLength).to.equal(0) | 255 | expect(playlist.videosLength).to.equal(0) |
255 | 256 | ||
diff --git a/shared/models/videos/playlist/video-playlist.model.ts b/shared/models/videos/playlist/video-playlist.model.ts index c0941727a..f45d0ff88 100644 --- a/shared/models/videos/playlist/video-playlist.model.ts +++ b/shared/models/videos/playlist/video-playlist.model.ts | |||
@@ -18,6 +18,8 @@ export interface VideoPlaylist { | |||
18 | 18 | ||
19 | type: VideoConstant<VideoPlaylistType> | 19 | type: VideoConstant<VideoPlaylistType> |
20 | 20 | ||
21 | embedPath: string | ||
22 | |||
21 | createdAt: Date | string | 23 | createdAt: Date | string |
22 | updatedAt: Date | string | 24 | updatedAt: Date | string |
23 | 25 | ||