aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib')
-rw-r--r--server/lib/activitypub/videos.ts59
-rw-r--r--server/lib/schedulers/videos-redundancy-scheduler.ts5
-rw-r--r--server/lib/thumbnail.ts16
3 files changed, 68 insertions, 12 deletions
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts
index a5f6537eb..66330a964 100644
--- a/server/lib/activitypub/videos.ts
+++ b/server/lib/activitypub/videos.ts
@@ -3,7 +3,8 @@ import { maxBy, minBy } from 'lodash'
3import * as magnetUtil from 'magnet-uri' 3import * as magnetUtil from 'magnet-uri'
4import { basename, join } from 'path' 4import { basename, join } from 'path'
5import * as request from 'request' 5import * as request from 'request'
6import * as sequelize from 'sequelize' 6import { Transaction } from 'sequelize/types'
7import { TrackerModel } from '@server/models/server/tracker'
7import { VideoLiveModel } from '@server/models/video/video-live' 8import { VideoLiveModel } from '@server/models/video/video-live'
8import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 9import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
9import { 10import {
@@ -16,12 +17,16 @@ import {
16 ActivityUrlObject, 17 ActivityUrlObject,
17 ActivityVideoUrlObject 18 ActivityVideoUrlObject
18} from '../../../shared/index' 19} from '../../../shared/index'
19import { ActivityIconObject, VideoObject } from '../../../shared/models/activitypub/objects' 20import { ActivityIconObject, ActivityTrackerUrlObject, VideoObject } from '../../../shared/models/activitypub/objects'
20import { VideoPrivacy } from '../../../shared/models/videos' 21import { VideoPrivacy } from '../../../shared/models/videos'
21import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' 22import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
22import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' 23import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
23import { buildRemoteVideoBaseUrl, checkUrlsSameHost, getAPId } from '../../helpers/activitypub' 24import { buildRemoteVideoBaseUrl, checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
24import { isAPVideoFileMetadataObject, sanitizeAndCheckVideoTorrentObject } from '../../helpers/custom-validators/activitypub/videos' 25import {
26 isAPVideoFileUrlMetadataObject,
27 isAPVideoTrackerUrlObject,
28 sanitizeAndCheckVideoTorrentObject
29} from '../../helpers/custom-validators/activitypub/videos'
25import { isArray } from '../../helpers/custom-validators/misc' 30import { isArray } from '../../helpers/custom-validators/misc'
26import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' 31import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos'
27import { deleteNonExistingModels, resetSequelizeInstance, retryTransactionWrapper } from '../../helpers/database-utils' 32import { deleteNonExistingModels, resetSequelizeInstance, retryTransactionWrapper } from '../../helpers/database-utils'
@@ -83,7 +88,7 @@ import { addVideoShares, shareVideoByServerAndChannel } from './share'
83import { addVideoComments } from './video-comments' 88import { addVideoComments } from './video-comments'
84import { createRates } from './video-rates' 89import { createRates } from './video-rates'
85 90
86async function federateVideoIfNeeded (videoArg: MVideoAPWithoutCaption, isNewVideo: boolean, transaction?: sequelize.Transaction) { 91async function federateVideoIfNeeded (videoArg: MVideoAPWithoutCaption, isNewVideo: boolean, transaction?: Transaction) {
87 const video = videoArg as MVideoAP 92 const video = videoArg as MVideoAP
88 93
89 if ( 94 if (
@@ -433,6 +438,12 @@ async function updateVideoFromAP (options: {
433 await setVideoTags({ video: videoUpdated, tags, transaction: t, defaultValue: videoUpdated.Tags }) 438 await setVideoTags({ video: videoUpdated, tags, transaction: t, defaultValue: videoUpdated.Tags })
434 } 439 }
435 440
441 // Update trackers
442 {
443 const trackers = getTrackerUrls(videoObject, videoUpdated)
444 await setVideoTrackers({ video: videoUpdated, trackers, transaction: t })
445 }
446
436 { 447 {
437 // Update captions 448 // Update captions
438 await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(videoUpdated.id, t) 449 await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(videoUpdated.id, t)
@@ -577,7 +588,7 @@ function isAPVideoUrlObject (url: any): url is ActivityVideoUrlObject {
577 return MIMETYPES.VIDEO.MIMETYPE_EXT[urlMediaType] && urlMediaType.startsWith('video/') 588 return MIMETYPES.VIDEO.MIMETYPE_EXT[urlMediaType] && urlMediaType.startsWith('video/')
578} 589}
579 590
580function isAPStreamingPlaylistUrlObject (url: ActivityUrlObject): url is ActivityPlaylistUrlObject { 591function isAPStreamingPlaylistUrlObject (url: any): url is ActivityPlaylistUrlObject {
581 return url && url.mediaType === 'application/x-mpegURL' 592 return url && url.mediaType === 'application/x-mpegURL'
582} 593}
583 594
@@ -671,6 +682,12 @@ async function createVideo (videoObject: VideoObject, channel: MChannelAccountLi
671 }) 682 })
672 await Promise.all(videoCaptionsPromises) 683 await Promise.all(videoCaptionsPromises)
673 684
685 // Process trackers
686 {
687 const trackers = getTrackerUrls(videoObject, videoCreated)
688 await setVideoTrackers({ video: videoCreated, trackers, transaction: t })
689 }
690
674 videoCreated.VideoFiles = videoFiles 691 videoCreated.VideoFiles = videoFiles
675 692
676 if (videoCreated.isLive) { 693 if (videoCreated.isLive) {
@@ -797,7 +814,7 @@ function videoFileActivityUrlToDBAttributes (
797 : parsed.xs 814 : parsed.xs
798 815
799 // Fetch associated metadata url, if any 816 // Fetch associated metadata url, if any
800 const metadata = urls.filter(isAPVideoFileMetadataObject) 817 const metadata = urls.filter(isAPVideoFileUrlMetadataObject)
801 .find(u => { 818 .find(u => {
802 return u.height === fileUrl.height && 819 return u.height === fileUrl.height &&
803 u.fps === fileUrl.fps && 820 u.fps === fileUrl.fps &&
@@ -889,3 +906,33 @@ function getPreviewUrl (previewIcon: ActivityIconObject, video: MVideoWithHost)
889 ? previewIcon.url 906 ? previewIcon.url
890 : buildRemoteVideoBaseUrl(video, join(LAZY_STATIC_PATHS.PREVIEWS, video.generatePreviewName())) 907 : buildRemoteVideoBaseUrl(video, join(LAZY_STATIC_PATHS.PREVIEWS, video.generatePreviewName()))
891} 908}
909
910function getTrackerUrls (object: VideoObject, video: MVideoWithHost) {
911 let wsFound = false
912
913 const trackers = object.url.filter(u => isAPVideoTrackerUrlObject(u))
914 .map((u: ActivityTrackerUrlObject) => {
915 if (u.rel.includes('websocket')) wsFound = true
916
917 return u.href
918 })
919
920 if (wsFound) return trackers
921
922 return [
923 buildRemoteVideoBaseUrl(video, '/tracker/socket', REMOTE_SCHEME.WS),
924 buildRemoteVideoBaseUrl(video, '/tracker/announce')
925 ]
926}
927
928async function setVideoTrackers (options: {
929 video: MVideo
930 trackers: string[]
931 transaction?: Transaction
932}) {
933 const { video, trackers, transaction } = options
934
935 const trackerInstances = await TrackerModel.findOrCreateTrackers(trackers, transaction)
936
937 await video.$set('Trackers', trackerInstances, { transaction })
938}
diff --git a/server/lib/schedulers/videos-redundancy-scheduler.ts b/server/lib/schedulers/videos-redundancy-scheduler.ts
index 60008e695..9e2667416 100644
--- a/server/lib/schedulers/videos-redundancy-scheduler.ts
+++ b/server/lib/schedulers/videos-redundancy-scheduler.ts
@@ -1,6 +1,7 @@
1import { move } from 'fs-extra' 1import { move } from 'fs-extra'
2import { join } from 'path' 2import { join } from 'path'
3import { getServerActor } from '@server/models/application/application' 3import { getServerActor } from '@server/models/application/application'
4import { TrackerModel } from '@server/models/server/tracker'
4import { VideoModel } from '@server/models/video/video' 5import { VideoModel } from '@server/models/video/video'
5import { 6import {
6 MStreamingPlaylist, 7 MStreamingPlaylist,
@@ -221,8 +222,8 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
221 222
222 logger.info('Duplicating %s - %d in videos redundancy with "%s" strategy.', video.url, file.resolution, strategy) 223 logger.info('Duplicating %s - %d in videos redundancy with "%s" strategy.', video.url, file.resolution, strategy)
223 224
224 const { baseUrlHttp, baseUrlWs } = video.getBaseUrls() 225 const trackerUrls = await TrackerModel.listUrlsByVideoId(video.id)
225 const magnetUri = generateMagnetUri(video, video, file, baseUrlHttp, baseUrlWs) 226 const magnetUri = generateMagnetUri(video, file, trackerUrls)
226 227
227 const tmpPath = await downloadWebTorrentVideo({ magnetUri }, VIDEO_IMPORT_TIMEOUT) 228 const tmpPath = await downloadWebTorrentVideo({ magnetUri }, VIDEO_IMPORT_TIMEOUT)
228 229
diff --git a/server/lib/thumbnail.ts b/server/lib/thumbnail.ts
index 4bad8d6ca..49317df28 100644
--- a/server/lib/thumbnail.ts
+++ b/server/lib/thumbnail.ts
@@ -1,5 +1,6 @@
1import { copy } from 'fs-extra' 1import { copy } from 'fs-extra'
2import { join } from 'path' 2import { join } from 'path'
3import { logger } from '@server/helpers/logger'
3import { ThumbnailType } from '../../shared/models/videos/thumbnail.type' 4import { ThumbnailType } from '../../shared/models/videos/thumbnail.type'
4import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils' 5import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils'
5import { processImage } from '../helpers/image-utils' 6import { processImage } from '../helpers/image-utils'
@@ -62,7 +63,7 @@ function createVideoMiniatureFromUrl (options: {
62 size?: ImageSize 63 size?: ImageSize
63}) { 64}) {
64 const { downloadUrl, video, type, size } = options 65 const { downloadUrl, video, type, size } = options
65 const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size) 66 const { filename: updatedFilename, basePath, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size)
66 67
67 // Only save the file URL if it is a remote video 68 // Only save the file URL if it is a remote video
68 const fileUrl = video.isOwned() 69 const fileUrl = video.isOwned()
@@ -76,10 +77,16 @@ function createVideoMiniatureFromUrl (options: {
76 77
77 // If the thumbnail URL did not change and has a unique filename (introduced in 3.2), avoid thumbnail processing 78 // If the thumbnail URL did not change and has a unique filename (introduced in 3.2), avoid thumbnail processing
78 const thumbnailUrlChanged = !existingUrl || existingUrl !== downloadUrl || downloadUrl.endsWith(`${video.uuid}.jpg`) 79 const thumbnailUrlChanged = !existingUrl || existingUrl !== downloadUrl || downloadUrl.endsWith(`${video.uuid}.jpg`)
80
81 // Do not change the thumbnail filename if the file did not change
82 const filename = thumbnailUrlChanged
83 ? updatedFilename
84 : existingThumbnail.filename
85
79 const thumbnailCreator = () => { 86 const thumbnailCreator = () => {
80 if (thumbnailUrlChanged) return downloadImage(downloadUrl, basePath, filename, { width, height }) 87 if (thumbnailUrlChanged) return downloadImage(downloadUrl, basePath, filename, { width, height })
81 88
82 return copy(existingThumbnail.getPath(), ThumbnailModel.buildPath(type, filename)) 89 return Promise.resolve()
83 } 90 }
84 91
85 return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail, fileUrl }) 92 return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail, fileUrl })
@@ -236,7 +243,7 @@ async function createThumbnailFromFunction (parameters: {
236 fileUrl = null 243 fileUrl = null
237 } = parameters 244 } = parameters
238 245
239 const oldFilename = existingThumbnail 246 const oldFilename = existingThumbnail && existingThumbnail.filename !== filename
240 ? existingThumbnail.filename 247 ? existingThumbnail.filename
241 : undefined 248 : undefined
242 249
@@ -248,7 +255,8 @@ async function createThumbnailFromFunction (parameters: {
248 thumbnail.type = type 255 thumbnail.type = type
249 thumbnail.fileUrl = fileUrl 256 thumbnail.fileUrl = fileUrl
250 thumbnail.automaticallyGenerated = automaticallyGenerated 257 thumbnail.automaticallyGenerated = automaticallyGenerated
251 thumbnail.previousThumbnailFilename = oldFilename 258
259 if (oldFilename) thumbnail.previousThumbnailFilename = oldFilename
252 260
253 await thumbnailCreator() 261 await thumbnailCreator()
254 262