aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.ts9
-rw-r--r--client/src/app/modal/confirm.component.ts10
-rw-r--r--client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts21
-rw-r--r--client/src/assets/player/p2p-media-loader/segment-url-builder.ts4
-rw-r--r--client/src/assets/player/peertube-player-options-builder.ts61
-rw-r--r--server/controllers/feeds.ts26
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 @@
1import { Component, ElementRef, OnInit, ViewChild } from '@angular/core' 1import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'
2import { HtmlRendererService } from '@app/core'
2import { ConfirmService } from '@app/core/confirm/confirm.service' 3import { ConfirmService } from '@app/core/confirm/confirm.service'
3import { POP_STATE_MODAL_DISMISS } from '@app/helpers' 4import { POP_STATE_MODAL_DISMISS } from '@app/helpers'
4import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 5import { 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 @@
1import { Segment } from '@peertube/p2p-media-loader-core' 1import { Segment } from '@peertube/p2p-media-loader-core'
2import { RedundancyUrlManager } from './redundancy-url-manager' 2import { RedundancyUrlManager } from './redundancy-url-manager'
3 3
4function segmentUrlBuilderFactory (redundancyUrlManager: RedundancyUrlManager, requiredSegmentsPriority: number) { 4function 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'
21import { buildVideoOrPlaylistEmbed, getRtcConfig, isIOS, isSafari } from './utils' 21import { buildVideoOrPlaylistEmbed, getRtcConfig, isIOS, isSafari } from './utils'
22import { HybridLoaderSettings } from '@peertube/p2p-media-loader-core'
22 23
23export type PlayerMode = 'webtorrent' | 'p2p-media-loader' 24export 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: {