diff options
author | Chocobozzz <me@florianbigard.com> | 2022-02-22 13:48:09 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2022-02-22 13:48:09 +0100 |
commit | f7298d0dcc247fe3be94ec1e00351942a079a44b (patch) | |
tree | 6bcb9935ab00101115be003c988a8211fc260d8c | |
parent | 0d9a327df33e42a0f34caaf2cbff5f74e4d78426 (diff) | |
parent | ac5f679ad60fc48db6d9a9534a8ac5fd20eda36f (diff) | |
download | PeerTube-f7298d0dcc247fe3be94ec1e00351942a079a44b.tar.gz PeerTube-f7298d0dcc247fe3be94ec1e00351942a079a44b.tar.zst PeerTube-f7298d0dcc247fe3be94ec1e00351942a079a44b.zip |
Merge branch 'release/4.1.0' into develop
6 files changed, 90 insertions, 41 deletions
diff --git a/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.ts b/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.ts index 4a46f1ad9..2bae3499e 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.ts +++ b/client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.ts | |||
@@ -19,8 +19,13 @@ export class MyAccountDangerZoneComponent { | |||
19 | 19 | ||
20 | async deleteMe () { | 20 | async deleteMe () { |
21 | const res = await this.confirmService.confirmWithInput( | 21 | const res = await this.confirmService.confirmWithInput( |
22 | // eslint-disable-next-line max-len | 22 | $localize`Are you sure you want to delete your account?` + |
23 | $localize`Are you sure you want to delete your account? This will delete all your data, including channels, videos and comments. Content cached by other servers and other third-parties might make longer to be deleted.`, | 23 | '<br /><br />' + |
24 | // eslint-disable-next-line max-len | ||
25 | $localize`This will delete all your data, including channels, videos, comments and you won't be able to create another user on this instance with "${this.user.username}" username.` + | ||
26 | '<br /><br />' + | ||
27 | $localize`Content cached by other servers and other third-parties might make longer to be deleted.`, | ||
28 | |||
24 | $localize`Type your username to confirm`, | 29 | $localize`Type your username to confirm`, |
25 | this.user.username, | 30 | this.user.username, |
26 | $localize`Delete your account`, | 31 | $localize`Delete your account`, |
diff --git a/client/src/app/modal/confirm.component.ts b/client/src/app/modal/confirm.component.ts index 457dd1f3f..ec4e1d60f 100644 --- a/client/src/app/modal/confirm.component.ts +++ b/client/src/app/modal/confirm.component.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { Component, ElementRef, OnInit, ViewChild } from '@angular/core' | 1 | import { Component, ElementRef, OnInit, ViewChild } from '@angular/core' |
2 | import { HtmlRendererService } from '@app/core' | ||
2 | import { ConfirmService } from '@app/core/confirm/confirm.service' | 3 | import { ConfirmService } from '@app/core/confirm/confirm.service' |
3 | import { POP_STATE_MODAL_DISMISS } from '@app/helpers' | 4 | import { POP_STATE_MODAL_DISMISS } from '@app/helpers' |
4 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 5 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' |
@@ -24,6 +25,7 @@ export class ConfirmComponent implements OnInit { | |||
24 | 25 | ||
25 | constructor ( | 26 | constructor ( |
26 | private modalService: NgbModal, | 27 | private modalService: NgbModal, |
28 | private html: HtmlRendererService, | ||
27 | private confirmService: ConfirmService | 29 | private confirmService: ConfirmService |
28 | ) { } | 30 | ) { } |
29 | 31 | ||
@@ -31,14 +33,18 @@ export class ConfirmComponent implements OnInit { | |||
31 | this.confirmService.showConfirm.subscribe( | 33 | this.confirmService.showConfirm.subscribe( |
32 | ({ title, message, expectedInputValue, inputLabel, confirmButtonText }) => { | 34 | ({ title, message, expectedInputValue, inputLabel, confirmButtonText }) => { |
33 | this.title = title | 35 | this.title = title |
34 | this.message = message | ||
35 | 36 | ||
36 | this.inputLabel = inputLabel | 37 | this.inputLabel = inputLabel |
37 | this.expectedInputValue = expectedInputValue | 38 | this.expectedInputValue = expectedInputValue |
38 | 39 | ||
39 | this.confirmButtonText = confirmButtonText || $localize`Confirm` | 40 | this.confirmButtonText = confirmButtonText || $localize`Confirm` |
40 | 41 | ||
41 | this.showModal() | 42 | this.html.toSafeHtml(message) |
43 | .then(message => { | ||
44 | this.message = message | ||
45 | |||
46 | this.showModal() | ||
47 | }) | ||
42 | } | 48 | } |
43 | ) | 49 | ) |
44 | } | 50 | } |
diff --git a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts index 553930595..e4972ec10 100644 --- a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts +++ b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts | |||
@@ -56,6 +56,8 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
56 | private listenToPlaylistChangeSub: Subscription | 56 | private listenToPlaylistChangeSub: Subscription |
57 | private playlistsData: CachedPlaylist[] = [] | 57 | private playlistsData: CachedPlaylist[] = [] |
58 | 58 | ||
59 | private pendingAddId: number | ||
60 | |||
59 | constructor ( | 61 | constructor ( |
60 | protected formValidatorService: FormValidatorService, | 62 | protected formValidatorService: FormValidatorService, |
61 | private authService: AuthService, | 63 | private authService: AuthService, |
@@ -215,8 +217,9 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
215 | } | 217 | } |
216 | 218 | ||
217 | isPrimaryCheckboxChecked (playlist: PlaylistSummary) { | 219 | isPrimaryCheckboxChecked (playlist: PlaylistSummary) { |
218 | return playlist.elements.filter(e => e.enabled) | 220 | // Reduce latency when adding a video to a playlist using pendingAddId |
219 | .length !== 0 | 221 | return this.pendingAddId === playlist.id || |
222 | playlist.elements.filter(e => e.enabled).length !== 0 | ||
220 | } | 223 | } |
221 | 224 | ||
222 | toggleOptionalRow (playlist: PlaylistSummary) { | 225 | toggleOptionalRow (playlist: PlaylistSummary) { |
@@ -367,6 +370,8 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
367 | if (element.startTimestamp) body.startTimestamp = element.startTimestamp | 370 | if (element.startTimestamp) body.startTimestamp = element.startTimestamp |
368 | if (element.stopTimestamp && element.stopTimestamp !== this.video.duration) body.stopTimestamp = element.stopTimestamp | 371 | if (element.stopTimestamp && element.stopTimestamp !== this.video.duration) body.stopTimestamp = element.stopTimestamp |
369 | 372 | ||
373 | this.pendingAddId = playlist.id | ||
374 | |||
370 | this.videoPlaylistService.addVideoInPlaylist(playlist.id, body) | 375 | this.videoPlaylistService.addVideoInPlaylist(playlist.id, body) |
371 | .subscribe({ | 376 | .subscribe({ |
372 | next: res => { | 377 | next: res => { |
@@ -379,9 +384,17 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
379 | if (element) element.playlistElementId = res.videoPlaylistElement.id | 384 | if (element) element.playlistElementId = res.videoPlaylistElement.id |
380 | }, | 385 | }, |
381 | 386 | ||
382 | error: err => this.notifier.error(err.message), | 387 | error: err => { |
388 | this.pendingAddId = undefined | ||
389 | this.cd.markForCheck() | ||
390 | |||
391 | this.notifier.error(err.message) | ||
392 | }, | ||
383 | 393 | ||
384 | complete: () => this.cd.markForCheck() | 394 | complete: () => { |
395 | this.pendingAddId = undefined | ||
396 | this.cd.markForCheck() | ||
397 | } | ||
385 | }) | 398 | }) |
386 | } | 399 | } |
387 | 400 | ||
diff --git a/client/src/assets/player/p2p-media-loader/segment-url-builder.ts b/client/src/assets/player/p2p-media-loader/segment-url-builder.ts index 5ddc81ff6..9d324078a 100644 --- a/client/src/assets/player/p2p-media-loader/segment-url-builder.ts +++ b/client/src/assets/player/p2p-media-loader/segment-url-builder.ts | |||
@@ -1,10 +1,10 @@ | |||
1 | import { Segment } from '@peertube/p2p-media-loader-core' | 1 | import { Segment } from '@peertube/p2p-media-loader-core' |
2 | import { RedundancyUrlManager } from './redundancy-url-manager' | 2 | import { RedundancyUrlManager } from './redundancy-url-manager' |
3 | 3 | ||
4 | function segmentUrlBuilderFactory (redundancyUrlManager: RedundancyUrlManager, requiredSegmentsPriority: number) { | 4 | function segmentUrlBuilderFactory (redundancyUrlManager: RedundancyUrlManager, useOriginPriority: number) { |
5 | return function segmentBuilder (segment: Segment) { | 5 | return function segmentBuilder (segment: Segment) { |
6 | // Don't use redundancy for high priority segments | 6 | // Don't use redundancy for high priority segments |
7 | if (segment.priority <= requiredSegmentsPriority) return segment.url | 7 | if (segment.priority <= useOriginPriority) return segment.url |
8 | 8 | ||
9 | return redundancyUrlManager.buildUrl(segment.url) | 9 | return redundancyUrlManager.buildUrl(segment.url) |
10 | } | 10 | } |
diff --git a/client/src/assets/player/peertube-player-options-builder.ts b/client/src/assets/player/peertube-player-options-builder.ts index 901f6cd3b..71be5ccff 100644 --- a/client/src/assets/player/peertube-player-options-builder.ts +++ b/client/src/assets/player/peertube-player-options-builder.ts | |||
@@ -19,6 +19,7 @@ import { | |||
19 | VideoJSPluginOptions | 19 | VideoJSPluginOptions |
20 | } from './peertube-videojs-typings' | 20 | } from './peertube-videojs-typings' |
21 | import { buildVideoOrPlaylistEmbed, getRtcConfig, isIOS, isSafari } from './utils' | 21 | import { buildVideoOrPlaylistEmbed, getRtcConfig, isIOS, isSafari } from './utils' |
22 | import { HybridLoaderSettings } from '@peertube/p2p-media-loader-core' | ||
22 | 23 | ||
23 | export type PlayerMode = 'webtorrent' | 'p2p-media-loader' | 24 | export type PlayerMode = 'webtorrent' | 'p2p-media-loader' |
24 | 25 | ||
@@ -198,9 +199,6 @@ export class PeertubePlayerOptionsBuilder { | |||
198 | const p2pMediaLoaderOptions = this.options.p2pMediaLoader | 199 | const p2pMediaLoaderOptions = this.options.p2pMediaLoader |
199 | const commonOptions = this.options.common | 200 | const commonOptions = this.options.common |
200 | 201 | ||
201 | const trackerAnnounce = p2pMediaLoaderOptions.trackerAnnounce | ||
202 | .filter(t => t.startsWith('ws')) | ||
203 | |||
204 | const redundancyUrlManager = new RedundancyUrlManager(this.options.p2pMediaLoader.redundancyBaseUrls) | 202 | const redundancyUrlManager = new RedundancyUrlManager(this.options.p2pMediaLoader.redundancyBaseUrls) |
205 | 203 | ||
206 | const p2pMediaLoader: P2PMediaLoaderPluginOptions = { | 204 | const p2pMediaLoader: P2PMediaLoaderPluginOptions = { |
@@ -210,23 +208,8 @@ export class PeertubePlayerOptionsBuilder { | |||
210 | src: p2pMediaLoaderOptions.playlistUrl | 208 | src: p2pMediaLoaderOptions.playlistUrl |
211 | } | 209 | } |
212 | 210 | ||
213 | let consumeOnly = false | ||
214 | if ((navigator as any)?.connection?.type === 'cellular') { | ||
215 | console.log('We are on a cellular connection: disabling seeding.') | ||
216 | consumeOnly = true | ||
217 | } | ||
218 | |||
219 | const p2pMediaLoaderConfig: HlsJsEngineSettings = { | 211 | const p2pMediaLoaderConfig: HlsJsEngineSettings = { |
220 | loader: { | 212 | loader: this.getP2PMediaLoaderOptions(redundancyUrlManager), |
221 | trackerAnnounce, | ||
222 | segmentValidator: segmentValidatorFactory(this.options.p2pMediaLoader.segmentsSha256Url, this.options.common.isLive), | ||
223 | rtcConfig: getRtcConfig(), | ||
224 | requiredSegmentsPriority: 1, | ||
225 | simultaneousHttpDownloads: 1, | ||
226 | segmentUrlBuilder: segmentUrlBuilderFactory(redundancyUrlManager, 1), | ||
227 | useP2P: commonOptions.p2pEnabled, | ||
228 | consumeOnly | ||
229 | }, | ||
230 | segments: { | 213 | segments: { |
231 | swarmId: p2pMediaLoaderOptions.playlistUrl | 214 | swarmId: p2pMediaLoaderOptions.playlistUrl |
232 | } | 215 | } |
@@ -256,6 +239,46 @@ export class PeertubePlayerOptionsBuilder { | |||
256 | return toAssign | 239 | return toAssign |
257 | } | 240 | } |
258 | 241 | ||
242 | private getP2PMediaLoaderOptions (redundancyUrlManager: RedundancyUrlManager): Partial<HybridLoaderSettings> { | ||
243 | let consumeOnly = false | ||
244 | if ((navigator as any)?.connection?.type === 'cellular') { | ||
245 | console.log('We are on a cellular connection: disabling seeding.') | ||
246 | consumeOnly = true | ||
247 | } | ||
248 | |||
249 | const trackerAnnounce = this.options.p2pMediaLoader.trackerAnnounce | ||
250 | .filter(t => t.startsWith('ws')) | ||
251 | |||
252 | const specificLiveOrVODOptions = this.options.common.isLive | ||
253 | ? { // Live | ||
254 | requiredSegmentsPriority: 1 | ||
255 | } | ||
256 | : { // VOD | ||
257 | requiredSegmentsPriority: 3, | ||
258 | |||
259 | cachedSegmentExpiration: 86400000, | ||
260 | cachedSegmentsCount: 100, | ||
261 | |||
262 | httpDownloadMaxPriority: 9, | ||
263 | httpDownloadProbability: 0.06, | ||
264 | httpDownloadProbabilitySkipIfNoPeers: true, | ||
265 | |||
266 | p2pDownloadMaxPriority: 50 | ||
267 | } | ||
268 | |||
269 | return { | ||
270 | trackerAnnounce, | ||
271 | segmentValidator: segmentValidatorFactory(this.options.p2pMediaLoader.segmentsSha256Url, this.options.common.isLive), | ||
272 | rtcConfig: getRtcConfig(), | ||
273 | simultaneousHttpDownloads: 1, | ||
274 | segmentUrlBuilder: segmentUrlBuilderFactory(redundancyUrlManager, 1), | ||
275 | useP2P: this.options.common.p2pEnabled, | ||
276 | consumeOnly, | ||
277 | |||
278 | ...specificLiveOrVODOptions | ||
279 | } | ||
280 | } | ||
281 | |||
259 | private getHLSOptions (p2pMediaLoaderConfig: HlsJsEngineSettings) { | 282 | private getHLSOptions (p2pMediaLoaderConfig: HlsJsEngineSettings) { |
260 | const base = { | 283 | const base = { |
261 | capLevelToPlayerSize: true, | 284 | capLevelToPlayerSize: true, |
diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts index 90faaf024..c929a6726 100644 --- a/server/controllers/feeds.ts +++ b/server/controllers/feeds.ts | |||
@@ -104,7 +104,7 @@ async function generateVideoCommentsFeed (req: express.Request, res: express.Res | |||
104 | 104 | ||
105 | // Adding video items to the feed, one at a time | 105 | // Adding video items to the feed, one at a time |
106 | for (const comment of comments) { | 106 | for (const comment of comments) { |
107 | const link = WEBSERVER.URL + comment.getCommentStaticPath() | 107 | const localLink = WEBSERVER.URL + comment.getCommentStaticPath() |
108 | 108 | ||
109 | let title = comment.Video.name | 109 | let title = comment.Video.name |
110 | const author: { name: string, link: string }[] = [] | 110 | const author: { name: string, link: string }[] = [] |
@@ -119,8 +119,8 @@ async function generateVideoCommentsFeed (req: express.Request, res: express.Res | |||
119 | 119 | ||
120 | feed.addItem({ | 120 | feed.addItem({ |
121 | title, | 121 | title, |
122 | id: comment.url, | 122 | id: localLink, |
123 | link, | 123 | link: localLink, |
124 | content: toSafeHtml(comment.text), | 124 | content: toSafeHtml(comment.text), |
125 | author, | 125 | author, |
126 | date: comment.createdAt | 126 | date: comment.createdAt |
@@ -269,7 +269,7 @@ function addVideosToFeed (feed: Feed, videos: VideoModel[]) { | |||
269 | size_in_bytes: videoFile.size | 269 | size_in_bytes: videoFile.size |
270 | })) | 270 | })) |
271 | 271 | ||
272 | const videos = formattedVideoFiles.map(videoFile => { | 272 | const videoFiles = formattedVideoFiles.map(videoFile => { |
273 | const result = { | 273 | const result = { |
274 | type: MIMETYPES.VIDEO.EXT_MIMETYPE[extname(videoFile.fileUrl)], | 274 | type: MIMETYPES.VIDEO.EXT_MIMETYPE[extname(videoFile.fileUrl)], |
275 | medium: 'video', | 275 | medium: 'video', |
@@ -293,10 +293,12 @@ function addVideosToFeed (feed: Feed, videos: VideoModel[]) { | |||
293 | }) | 293 | }) |
294 | } | 294 | } |
295 | 295 | ||
296 | const localLink = WEBSERVER.URL + video.getWatchStaticPath() | ||
297 | |||
296 | feed.addItem({ | 298 | feed.addItem({ |
297 | title: video.name, | 299 | title: video.name, |
298 | id: video.url, | 300 | id: localLink, |
299 | link: WEBSERVER.URL + video.getWatchStaticPath(), | 301 | link: localLink, |
300 | description: mdToOneLinePlainText(video.getTruncatedDescription()), | 302 | description: mdToOneLinePlainText(video.getTruncatedDescription()), |
301 | content: toSafeHtml(video.description), | 303 | content: toSafeHtml(video.description), |
302 | author: [ | 304 | author: [ |
@@ -311,20 +313,20 @@ function addVideosToFeed (feed: Feed, videos: VideoModel[]) { | |||
311 | 313 | ||
312 | // Enclosure | 314 | // Enclosure |
313 | video: { | 315 | video: { |
314 | url: videos[0].url, | 316 | url: videoFiles[0].url, |
315 | length: videos[0].fileSize, | 317 | length: videoFiles[0].fileSize, |
316 | type: videos[0].type | 318 | type: videoFiles[0].type |
317 | }, | 319 | }, |
318 | 320 | ||
319 | // Media RSS | 321 | // Media RSS |
320 | videos, | 322 | videos: videoFiles, |
321 | 323 | ||
322 | embed: { | 324 | embed: { |
323 | url: video.getEmbedStaticPath(), | 325 | url: WEBSERVER.URL + video.getEmbedStaticPath(), |
324 | allowFullscreen: true | 326 | allowFullscreen: true |
325 | }, | 327 | }, |
326 | player: { | 328 | player: { |
327 | url: video.getWatchStaticPath() | 329 | url: WEBSERVER.URL + video.getWatchStaticPath() |
328 | }, | 330 | }, |
329 | categories, | 331 | categories, |
330 | community: { | 332 | community: { |