diff options
Diffstat (limited to 'server/lib/activitypub/videos.ts')
-rw-r--r-- | server/lib/activitypub/videos.ts | 54 |
1 files changed, 32 insertions, 22 deletions
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts index 998f90330..e1e523499 100644 --- a/server/lib/activitypub/videos.ts +++ b/server/lib/activitypub/videos.ts | |||
@@ -1,7 +1,6 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import * as sequelize from 'sequelize' | 2 | import * as sequelize from 'sequelize' |
3 | import * as magnetUtil from 'magnet-uri' | 3 | import * as magnetUtil from 'magnet-uri' |
4 | import { join } from 'path' | ||
5 | import * as request from 'request' | 4 | import * as request from 'request' |
6 | import { ActivityIconObject, ActivityUrlObject, ActivityVideoUrlObject, VideoState } from '../../../shared/index' | 5 | import { ActivityIconObject, ActivityUrlObject, ActivityVideoUrlObject, VideoState } from '../../../shared/index' |
7 | import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' | 6 | import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' |
@@ -11,7 +10,7 @@ import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos | |||
11 | import { resetSequelizeInstance, retryTransactionWrapper } from '../../helpers/database-utils' | 10 | import { resetSequelizeInstance, retryTransactionWrapper } from '../../helpers/database-utils' |
12 | import { logger } from '../../helpers/logger' | 11 | import { logger } from '../../helpers/logger' |
13 | import { doRequest, downloadImage } from '../../helpers/requests' | 12 | import { doRequest, downloadImage } from '../../helpers/requests' |
14 | import { ACTIVITY_PUB, CONFIG, REMOTE_SCHEME, sequelizeTypescript, THUMBNAILS_SIZE, VIDEO_MIMETYPE_EXT } from '../../initializers' | 13 | import { ACTIVITY_PUB, CONFIG, MIMETYPES, REMOTE_SCHEME, sequelizeTypescript, THUMBNAILS_SIZE } from '../../initializers' |
15 | import { ActorModel } from '../../models/activitypub/actor' | 14 | import { ActorModel } from '../../models/activitypub/actor' |
16 | import { TagModel } from '../../models/video/tag' | 15 | import { TagModel } from '../../models/video/tag' |
17 | import { VideoModel } from '../../models/video/video' | 16 | import { VideoModel } from '../../models/video/video' |
@@ -29,7 +28,8 @@ import { createRates } from './video-rates' | |||
29 | import { addVideoShares, shareVideoByServerAndChannel } from './share' | 28 | import { addVideoShares, shareVideoByServerAndChannel } from './share' |
30 | import { AccountModel } from '../../models/account/account' | 29 | import { AccountModel } from '../../models/account/account' |
31 | import { fetchVideoByUrl, VideoFetchByUrlType } from '../../helpers/video' | 30 | import { fetchVideoByUrl, VideoFetchByUrlType } from '../../helpers/video' |
32 | import { checkUrlsSameHost, getAPUrl } from '../../helpers/activitypub' | 31 | import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' |
32 | import { Notifier } from '../notifier' | ||
33 | 33 | ||
34 | async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) { | 34 | async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) { |
35 | // If the video is not private and published, we federate it | 35 | // If the video is not private and published, we federate it |
@@ -95,9 +95,8 @@ function fetchRemoteVideoStaticFile (video: VideoModel, path: string, reject: Fu | |||
95 | 95 | ||
96 | function generateThumbnailFromUrl (video: VideoModel, icon: ActivityIconObject) { | 96 | function generateThumbnailFromUrl (video: VideoModel, icon: ActivityIconObject) { |
97 | const thumbnailName = video.getThumbnailName() | 97 | const thumbnailName = video.getThumbnailName() |
98 | const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, thumbnailName) | ||
99 | 98 | ||
100 | return downloadImage(icon.url, thumbnailPath, THUMBNAILS_SIZE) | 99 | return downloadImage(icon.url, CONFIG.STORAGE.THUMBNAILS_DIR, thumbnailName, THUMBNAILS_SIZE) |
101 | } | 100 | } |
102 | 101 | ||
103 | function getOrCreateVideoChannelFromVideoObject (videoObject: VideoTorrentObject) { | 102 | function getOrCreateVideoChannelFromVideoObject (videoObject: VideoTorrentObject) { |
@@ -156,29 +155,34 @@ async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: Vid | |||
156 | } | 155 | } |
157 | 156 | ||
158 | async function getOrCreateVideoAndAccountAndChannel (options: { | 157 | async function getOrCreateVideoAndAccountAndChannel (options: { |
159 | videoObject: VideoTorrentObject | string, | 158 | videoObject: { id: string } | string, |
160 | syncParam?: SyncParam, | 159 | syncParam?: SyncParam, |
161 | fetchType?: VideoFetchByUrlType | 160 | fetchType?: VideoFetchByUrlType, |
161 | allowRefresh?: boolean // true by default | ||
162 | }) { | 162 | }) { |
163 | // Default params | 163 | // Default params |
164 | const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false } | 164 | const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false } |
165 | const fetchType = options.fetchType || 'all' | 165 | const fetchType = options.fetchType || 'all' |
166 | const allowRefresh = options.allowRefresh !== false | ||
166 | 167 | ||
167 | // Get video url | 168 | // Get video url |
168 | const videoUrl = getAPUrl(options.videoObject) | 169 | const videoUrl = getAPId(options.videoObject) |
169 | 170 | ||
170 | let videoFromDatabase = await fetchVideoByUrl(videoUrl, fetchType) | 171 | let videoFromDatabase = await fetchVideoByUrl(videoUrl, fetchType) |
171 | if (videoFromDatabase) { | 172 | if (videoFromDatabase) { |
172 | const refreshOptions = { | ||
173 | video: videoFromDatabase, | ||
174 | fetchedType: fetchType, | ||
175 | syncParam | ||
176 | } | ||
177 | 173 | ||
178 | if (syncParam.refreshVideo === true) videoFromDatabase = await refreshVideoIfNeeded(refreshOptions) | 174 | if (allowRefresh === true) { |
179 | else await JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', videoUrl: videoFromDatabase.url } }) | 175 | const refreshOptions = { |
176 | video: videoFromDatabase, | ||
177 | fetchedType: fetchType, | ||
178 | syncParam | ||
179 | } | ||
180 | |||
181 | if (syncParam.refreshVideo === true) videoFromDatabase = await refreshVideoIfNeeded(refreshOptions) | ||
182 | else await JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', url: videoFromDatabase.url } }) | ||
183 | } | ||
180 | 184 | ||
181 | return { video: videoFromDatabase } | 185 | return { video: videoFromDatabase, created: false } |
182 | } | 186 | } |
183 | 187 | ||
184 | const { videoObject: fetchedVideo } = await fetchRemoteVideo(videoUrl) | 188 | const { videoObject: fetchedVideo } = await fetchRemoteVideo(videoUrl) |
@@ -189,7 +193,7 @@ async function getOrCreateVideoAndAccountAndChannel (options: { | |||
189 | 193 | ||
190 | await syncVideoExternalAttributes(video, fetchedVideo, syncParam) | 194 | await syncVideoExternalAttributes(video, fetchedVideo, syncParam) |
191 | 195 | ||
192 | return { video } | 196 | return { video, created: true } |
193 | } | 197 | } |
194 | 198 | ||
195 | async function updateVideoFromAP (options: { | 199 | async function updateVideoFromAP (options: { |
@@ -200,13 +204,14 @@ async function updateVideoFromAP (options: { | |||
200 | overrideTo?: string[] | 204 | overrideTo?: string[] |
201 | }) { | 205 | }) { |
202 | logger.debug('Updating remote video "%s".', options.videoObject.uuid) | 206 | logger.debug('Updating remote video "%s".', options.videoObject.uuid) |
207 | |||
203 | let videoFieldsSave: any | 208 | let videoFieldsSave: any |
209 | const wasPrivateVideo = options.video.privacy === VideoPrivacy.PRIVATE | ||
210 | const wasUnlistedVideo = options.video.privacy === VideoPrivacy.UNLISTED | ||
204 | 211 | ||
205 | try { | 212 | try { |
206 | await sequelizeTypescript.transaction(async t => { | 213 | await sequelizeTypescript.transaction(async t => { |
207 | const sequelizeOptions = { | 214 | const sequelizeOptions = { transaction: t } |
208 | transaction: t | ||
209 | } | ||
210 | 215 | ||
211 | videoFieldsSave = options.video.toJSON() | 216 | videoFieldsSave = options.video.toJSON() |
212 | 217 | ||
@@ -276,6 +281,11 @@ async function updateVideoFromAP (options: { | |||
276 | } | 281 | } |
277 | }) | 282 | }) |
278 | 283 | ||
284 | // Notify our users? | ||
285 | if (wasPrivateVideo || wasUnlistedVideo) { | ||
286 | Notifier.Instance.notifyOnNewVideo(options.video) | ||
287 | } | ||
288 | |||
279 | logger.info('Remote video with uuid %s updated', options.videoObject.uuid) | 289 | logger.info('Remote video with uuid %s updated', options.videoObject.uuid) |
280 | } catch (err) { | 290 | } catch (err) { |
281 | if (options.video !== undefined && videoFieldsSave !== undefined) { | 291 | if (options.video !== undefined && videoFieldsSave !== undefined) { |
@@ -358,7 +368,7 @@ export { | |||
358 | // --------------------------------------------------------------------------- | 368 | // --------------------------------------------------------------------------- |
359 | 369 | ||
360 | function isActivityVideoUrlObject (url: ActivityUrlObject): url is ActivityVideoUrlObject { | 370 | function isActivityVideoUrlObject (url: ActivityUrlObject): url is ActivityVideoUrlObject { |
361 | const mimeTypes = Object.keys(VIDEO_MIMETYPE_EXT) | 371 | const mimeTypes = Object.keys(MIMETYPES.VIDEO.MIMETYPE_EXT) |
362 | 372 | ||
363 | const urlMediaType = url.mediaType || url.mimeType | 373 | const urlMediaType = url.mediaType || url.mimeType |
364 | return mimeTypes.indexOf(urlMediaType) !== -1 && urlMediaType.startsWith('video/') | 374 | return mimeTypes.indexOf(urlMediaType) !== -1 && urlMediaType.startsWith('video/') |
@@ -486,7 +496,7 @@ function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: Vid | |||
486 | 496 | ||
487 | const mediaType = fileUrl.mediaType || fileUrl.mimeType | 497 | const mediaType = fileUrl.mediaType || fileUrl.mimeType |
488 | const attribute = { | 498 | const attribute = { |
489 | extname: VIDEO_MIMETYPE_EXT[ mediaType ], | 499 | extname: MIMETYPES.VIDEO.MIMETYPE_EXT[ mediaType ], |
490 | infoHash: parsed.infoHash, | 500 | infoHash: parsed.infoHash, |
491 | resolution: fileUrl.height, | 501 | resolution: fileUrl.height, |
492 | size: fileUrl.size, | 502 | size: fileUrl.size, |