diff options
author | Chocobozzz <me@florianbigard.com> | 2018-06-12 20:04:58 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-06-12 20:37:51 +0200 |
commit | 2186386cca113506791583cb07d6ccacba7af4e0 (patch) | |
tree | 3c214c0b5fbd64332624267fa6e51fd4a9cf6474 /server/lib | |
parent | 6ccdf3a23ecec5ba2eeaf487fd1fafdc7606b4bf (diff) | |
download | PeerTube-2186386cca113506791583cb07d6ccacba7af4e0.tar.gz PeerTube-2186386cca113506791583cb07d6ccacba7af4e0.tar.zst PeerTube-2186386cca113506791583cb07d6ccacba7af4e0.zip |
Add concept of video state, and add ability to wait transcoding before
publishing a video
Diffstat (limited to 'server/lib')
-rw-r--r-- | server/lib/activitypub/audience.ts | 10 | ||||
-rw-r--r-- | server/lib/activitypub/crawl.ts | 2 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-update.ts | 27 | ||||
-rw-r--r-- | server/lib/activitypub/send/send-announce.ts | 14 | ||||
-rw-r--r-- | server/lib/activitypub/send/send-create.ts | 43 | ||||
-rw-r--r-- | server/lib/activitypub/send/send-like.ts | 33 | ||||
-rw-r--r-- | server/lib/activitypub/send/send-undo.ts | 42 | ||||
-rw-r--r-- | server/lib/activitypub/send/send-update.ts | 36 | ||||
-rw-r--r-- | server/lib/activitypub/videos.ts | 80 | ||||
-rw-r--r-- | server/lib/job-queue/handlers/video-file.ts | 127 | ||||
-rw-r--r-- | server/lib/job-queue/job-queue.ts | 1 |
11 files changed, 215 insertions, 200 deletions
diff --git a/server/lib/activitypub/audience.ts b/server/lib/activitypub/audience.ts index c1265dbcd..7164135b6 100644 --- a/server/lib/activitypub/audience.ts +++ b/server/lib/activitypub/audience.ts | |||
@@ -20,7 +20,7 @@ function getVideoCommentAudience ( | |||
20 | isOrigin = false | 20 | isOrigin = false |
21 | ) { | 21 | ) { |
22 | const to = [ ACTIVITY_PUB.PUBLIC ] | 22 | const to = [ ACTIVITY_PUB.PUBLIC ] |
23 | const cc = [ ] | 23 | const cc = [] |
24 | 24 | ||
25 | // Owner of the video we comment | 25 | // Owner of the video we comment |
26 | if (isOrigin === false) { | 26 | if (isOrigin === false) { |
@@ -55,7 +55,7 @@ async function getActorsInvolvedInVideo (video: VideoModel, t: Transaction) { | |||
55 | return actors | 55 | return actors |
56 | } | 56 | } |
57 | 57 | ||
58 | async function getAudience (actorSender: ActorModel, t: Transaction, isPublic = true) { | 58 | function getAudience (actorSender: ActorModel, isPublic = true) { |
59 | return buildAudience([ actorSender.followersUrl ], isPublic) | 59 | return buildAudience([ actorSender.followersUrl ], isPublic) |
60 | } | 60 | } |
61 | 61 | ||
@@ -67,14 +67,14 @@ function buildAudience (followerUrls: string[], isPublic = true) { | |||
67 | to = [ ACTIVITY_PUB.PUBLIC ] | 67 | to = [ ACTIVITY_PUB.PUBLIC ] |
68 | cc = followerUrls | 68 | cc = followerUrls |
69 | } else { // Unlisted | 69 | } else { // Unlisted |
70 | to = [ ] | 70 | to = [] |
71 | cc = [ ] | 71 | cc = [] |
72 | } | 72 | } |
73 | 73 | ||
74 | return { to, cc } | 74 | return { to, cc } |
75 | } | 75 | } |
76 | 76 | ||
77 | function audiencify <T> (object: T, audience: ActivityAudience) { | 77 | function audiencify<T> (object: T, audience: ActivityAudience) { |
78 | return Object.assign(object, audience) | 78 | return Object.assign(object, audience) |
79 | } | 79 | } |
80 | 80 | ||
diff --git a/server/lib/activitypub/crawl.ts b/server/lib/activitypub/crawl.ts index 7305b3969..d4fc786f7 100644 --- a/server/lib/activitypub/crawl.ts +++ b/server/lib/activitypub/crawl.ts | |||
@@ -28,7 +28,7 @@ async function crawlCollectionPage <T> (uri: string, handler: (items: T[]) => Pr | |||
28 | 28 | ||
29 | if (Array.isArray(body.orderedItems)) { | 29 | if (Array.isArray(body.orderedItems)) { |
30 | const items = body.orderedItems | 30 | const items = body.orderedItems |
31 | logger.info('Processing %i ActivityPub items for %s.', items.length, nextLink) | 31 | logger.info('Processing %i ActivityPub items for %s.', items.length, options.uri) |
32 | 32 | ||
33 | await handler(items) | 33 | await handler(items) |
34 | } | 34 | } |
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts index 2750f48c3..77de8c155 100644 --- a/server/lib/activitypub/process/process-update.ts +++ b/server/lib/activitypub/process/process-update.ts | |||
@@ -1,7 +1,6 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import { ActivityUpdate } from '../../../../shared/models/activitypub' | 2 | import { ActivityUpdate } from '../../../../shared/models/activitypub' |
3 | import { ActivityPubActor } from '../../../../shared/models/activitypub/activitypub-actor' | 3 | import { ActivityPubActor } from '../../../../shared/models/activitypub/activitypub-actor' |
4 | import { VideoTorrentObject } from '../../../../shared/models/activitypub/objects' | ||
5 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 4 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
6 | import { logger } from '../../../helpers/logger' | 5 | import { logger } from '../../../helpers/logger' |
7 | import { resetSequelizeInstance } from '../../../helpers/utils' | 6 | import { resetSequelizeInstance } from '../../../helpers/utils' |
@@ -13,6 +12,7 @@ import { VideoChannelModel } from '../../../models/video/video-channel' | |||
13 | import { VideoFileModel } from '../../../models/video/video-file' | 12 | import { VideoFileModel } from '../../../models/video/video-file' |
14 | import { fetchAvatarIfExists, getOrCreateActorAndServerAndModel, updateActorAvatarInstance, updateActorInstance } from '../actor' | 13 | import { fetchAvatarIfExists, getOrCreateActorAndServerAndModel, updateActorAvatarInstance, updateActorInstance } from '../actor' |
15 | import { | 14 | import { |
15 | fetchRemoteVideo, | ||
16 | generateThumbnailFromUrl, | 16 | generateThumbnailFromUrl, |
17 | getOrCreateAccountAndVideoAndChannel, | 17 | getOrCreateAccountAndVideoAndChannel, |
18 | getOrCreateVideoChannel, | 18 | getOrCreateVideoChannel, |
@@ -51,15 +51,18 @@ function processUpdateVideo (actor: ActorModel, activity: ActivityUpdate) { | |||
51 | } | 51 | } |
52 | 52 | ||
53 | async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) { | 53 | async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) { |
54 | const videoAttributesToUpdate = activity.object as VideoTorrentObject | 54 | const videoUrl = activity.object.id |
55 | 55 | ||
56 | const res = await getOrCreateAccountAndVideoAndChannel(videoAttributesToUpdate.id) | 56 | const videoObject = await fetchRemoteVideo(videoUrl) |
57 | if (!videoObject) throw new Error('Cannot fetch remote video with url: ' + videoUrl) | ||
58 | |||
59 | const res = await getOrCreateAccountAndVideoAndChannel(videoObject.id) | ||
57 | 60 | ||
58 | // Fetch video channel outside the transaction | 61 | // Fetch video channel outside the transaction |
59 | const newVideoChannelActor = await getOrCreateVideoChannel(videoAttributesToUpdate) | 62 | const newVideoChannelActor = await getOrCreateVideoChannel(videoObject) |
60 | const newVideoChannel = newVideoChannelActor.VideoChannel | 63 | const newVideoChannel = newVideoChannelActor.VideoChannel |
61 | 64 | ||
62 | logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid) | 65 | logger.debug('Updating remote video "%s".', videoObject.uuid) |
63 | let videoInstance = res.video | 66 | let videoInstance = res.video |
64 | let videoFieldsSave: any | 67 | let videoFieldsSave: any |
65 | 68 | ||
@@ -77,7 +80,7 @@ async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) { | |||
77 | throw new Error('Account ' + actor.url + ' does not own video channel ' + videoChannel.Actor.url) | 80 | throw new Error('Account ' + actor.url + ' does not own video channel ' + videoChannel.Actor.url) |
78 | } | 81 | } |
79 | 82 | ||
80 | const videoData = await videoActivityObjectToDBAttributes(newVideoChannel, videoAttributesToUpdate, activity.to) | 83 | const videoData = await videoActivityObjectToDBAttributes(newVideoChannel, videoObject, activity.to) |
81 | videoInstance.set('name', videoData.name) | 84 | videoInstance.set('name', videoData.name) |
82 | videoInstance.set('uuid', videoData.uuid) | 85 | videoInstance.set('uuid', videoData.uuid) |
83 | videoInstance.set('url', videoData.url) | 86 | videoInstance.set('url', videoData.url) |
@@ -88,6 +91,8 @@ async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) { | |||
88 | videoInstance.set('support', videoData.support) | 91 | videoInstance.set('support', videoData.support) |
89 | videoInstance.set('nsfw', videoData.nsfw) | 92 | videoInstance.set('nsfw', videoData.nsfw) |
90 | videoInstance.set('commentsEnabled', videoData.commentsEnabled) | 93 | videoInstance.set('commentsEnabled', videoData.commentsEnabled) |
94 | videoInstance.set('waitTranscoding', videoData.waitTranscoding) | ||
95 | videoInstance.set('state', videoData.state) | ||
91 | videoInstance.set('duration', videoData.duration) | 96 | videoInstance.set('duration', videoData.duration) |
92 | videoInstance.set('createdAt', videoData.createdAt) | 97 | videoInstance.set('createdAt', videoData.createdAt) |
93 | videoInstance.set('updatedAt', videoData.updatedAt) | 98 | videoInstance.set('updatedAt', videoData.updatedAt) |
@@ -98,8 +103,8 @@ async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) { | |||
98 | await videoInstance.save(sequelizeOptions) | 103 | await videoInstance.save(sequelizeOptions) |
99 | 104 | ||
100 | // Don't block on request | 105 | // Don't block on request |
101 | generateThumbnailFromUrl(videoInstance, videoAttributesToUpdate.icon) | 106 | generateThumbnailFromUrl(videoInstance, videoObject.icon) |
102 | .catch(err => logger.warn('Cannot generate thumbnail of %s.', videoAttributesToUpdate.id, { err })) | 107 | .catch(err => logger.warn('Cannot generate thumbnail of %s.', videoObject.id, { err })) |
103 | 108 | ||
104 | // Remove old video files | 109 | // Remove old video files |
105 | const videoFileDestroyTasks: Bluebird<void>[] = [] | 110 | const videoFileDestroyTasks: Bluebird<void>[] = [] |
@@ -108,16 +113,16 @@ async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) { | |||
108 | } | 113 | } |
109 | await Promise.all(videoFileDestroyTasks) | 114 | await Promise.all(videoFileDestroyTasks) |
110 | 115 | ||
111 | const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoInstance, videoAttributesToUpdate) | 116 | const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoInstance, videoObject) |
112 | const tasks = videoFileAttributes.map(f => VideoFileModel.create(f)) | 117 | const tasks = videoFileAttributes.map(f => VideoFileModel.create(f)) |
113 | await Promise.all(tasks) | 118 | await Promise.all(tasks) |
114 | 119 | ||
115 | const tags = videoAttributesToUpdate.tag.map(t => t.name) | 120 | const tags = videoObject.tag.map(t => t.name) |
116 | const tagInstances = await TagModel.findOrCreateTags(tags, t) | 121 | const tagInstances = await TagModel.findOrCreateTags(tags, t) |
117 | await videoInstance.$set('Tags', tagInstances, sequelizeOptions) | 122 | await videoInstance.$set('Tags', tagInstances, sequelizeOptions) |
118 | }) | 123 | }) |
119 | 124 | ||
120 | logger.info('Remote video with uuid %s updated', videoAttributesToUpdate.uuid) | 125 | logger.info('Remote video with uuid %s updated', videoObject.uuid) |
121 | } catch (err) { | 126 | } catch (err) { |
122 | if (videoInstance !== undefined && videoFieldsSave !== undefined) { | 127 | if (videoInstance !== undefined && videoFieldsSave !== undefined) { |
123 | resetSequelizeInstance(videoInstance, videoFieldsSave) | 128 | resetSequelizeInstance(videoInstance, videoFieldsSave) |
diff --git a/server/lib/activitypub/send/send-announce.ts b/server/lib/activitypub/send/send-announce.ts index fa1d47259..dfc099ff2 100644 --- a/server/lib/activitypub/send/send-announce.ts +++ b/server/lib/activitypub/send/send-announce.ts | |||
@@ -11,7 +11,7 @@ async function buildVideoAnnounce (byActor: ActorModel, videoShare: VideoShareMo | |||
11 | 11 | ||
12 | const accountsToForwardView = await getActorsInvolvedInVideo(video, t) | 12 | const accountsToForwardView = await getActorsInvolvedInVideo(video, t) |
13 | const audience = getObjectFollowersAudience(accountsToForwardView) | 13 | const audience = getObjectFollowersAudience(accountsToForwardView) |
14 | return announceActivityData(videoShare.url, byActor, announcedObject, t, audience) | 14 | return announceActivityData(videoShare.url, byActor, announcedObject, audience) |
15 | } | 15 | } |
16 | 16 | ||
17 | async function sendVideoAnnounce (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) { | 17 | async function sendVideoAnnounce (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) { |
@@ -20,16 +20,8 @@ async function sendVideoAnnounce (byActor: ActorModel, videoShare: VideoShareMod | |||
20 | return broadcastToFollowers(data, byActor, [ byActor ], t) | 20 | return broadcastToFollowers(data, byActor, [ byActor ], t) |
21 | } | 21 | } |
22 | 22 | ||
23 | async function announceActivityData ( | 23 | function announceActivityData (url: string, byActor: ActorModel, object: string, audience?: ActivityAudience): ActivityAnnounce { |
24 | url: string, | 24 | if (!audience) audience = getAudience(byActor) |
25 | byActor: ActorModel, | ||
26 | object: string, | ||
27 | t: Transaction, | ||
28 | audience?: ActivityAudience | ||
29 | ): Promise<ActivityAnnounce> { | ||
30 | if (!audience) { | ||
31 | audience = await getAudience(byActor, t) | ||
32 | } | ||
33 | 25 | ||
34 | return { | 26 | return { |
35 | type: 'Announce', | 27 | type: 'Announce', |
diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts index 3ef4fcd3b..293947b05 100644 --- a/server/lib/activitypub/send/send-create.ts +++ b/server/lib/activitypub/send/send-create.ts | |||
@@ -23,8 +23,8 @@ async function sendCreateVideo (video: VideoModel, t: Transaction) { | |||
23 | const byActor = video.VideoChannel.Account.Actor | 23 | const byActor = video.VideoChannel.Account.Actor |
24 | const videoObject = video.toActivityPubObject() | 24 | const videoObject = video.toActivityPubObject() |
25 | 25 | ||
26 | const audience = await getAudience(byActor, t, video.privacy === VideoPrivacy.PUBLIC) | 26 | const audience = getAudience(byActor, video.privacy === VideoPrivacy.PUBLIC) |
27 | const data = await createActivityData(video.url, byActor, videoObject, t, audience) | 27 | const data = createActivityData(video.url, byActor, videoObject, audience) |
28 | 28 | ||
29 | return broadcastToFollowers(data, byActor, [ byActor ], t) | 29 | return broadcastToFollowers(data, byActor, [ byActor ], t) |
30 | } | 30 | } |
@@ -33,7 +33,7 @@ async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel, | |||
33 | const url = getVideoAbuseActivityPubUrl(videoAbuse) | 33 | const url = getVideoAbuseActivityPubUrl(videoAbuse) |
34 | 34 | ||
35 | const audience = { to: [ video.VideoChannel.Account.Actor.url ], cc: [] } | 35 | const audience = { to: [ video.VideoChannel.Account.Actor.url ], cc: [] } |
36 | const data = await createActivityData(url, byActor, videoAbuse.toActivityPubObject(), t, audience) | 36 | const data = createActivityData(url, byActor, videoAbuse.toActivityPubObject(), audience) |
37 | 37 | ||
38 | return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl) | 38 | return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl) |
39 | } | 39 | } |
@@ -57,7 +57,7 @@ async function sendCreateVideoComment (comment: VideoCommentModel, t: Transactio | |||
57 | audience = getObjectFollowersAudience(actorsInvolvedInComment.concat(parentsCommentActors)) | 57 | audience = getObjectFollowersAudience(actorsInvolvedInComment.concat(parentsCommentActors)) |
58 | } | 58 | } |
59 | 59 | ||
60 | const data = await createActivityData(comment.url, byActor, commentObject, t, audience) | 60 | const data = createActivityData(comment.url, byActor, commentObject, audience) |
61 | 61 | ||
62 | // This was a reply, send it to the parent actors | 62 | // This was a reply, send it to the parent actors |
63 | const actorsException = [ byActor ] | 63 | const actorsException = [ byActor ] |
@@ -82,14 +82,14 @@ async function sendCreateView (byActor: ActorModel, video: VideoModel, t: Transa | |||
82 | // Send to origin | 82 | // Send to origin |
83 | if (video.isOwned() === false) { | 83 | if (video.isOwned() === false) { |
84 | const audience = getVideoAudience(video, actorsInvolvedInVideo) | 84 | const audience = getVideoAudience(video, actorsInvolvedInVideo) |
85 | const data = await createActivityData(url, byActor, viewActivityData, t, audience) | 85 | const data = createActivityData(url, byActor, viewActivityData, audience) |
86 | 86 | ||
87 | return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl) | 87 | return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl) |
88 | } | 88 | } |
89 | 89 | ||
90 | // Send to followers | 90 | // Send to followers |
91 | const audience = getObjectFollowersAudience(actorsInvolvedInVideo) | 91 | const audience = getObjectFollowersAudience(actorsInvolvedInVideo) |
92 | const data = await createActivityData(url, byActor, viewActivityData, t, audience) | 92 | const data = createActivityData(url, byActor, viewActivityData, audience) |
93 | 93 | ||
94 | // Use the server actor to send the view | 94 | // Use the server actor to send the view |
95 | const serverActor = await getServerActor() | 95 | const serverActor = await getServerActor() |
@@ -106,34 +106,31 @@ async function sendCreateDislike (byActor: ActorModel, video: VideoModel, t: Tra | |||
106 | // Send to origin | 106 | // Send to origin |
107 | if (video.isOwned() === false) { | 107 | if (video.isOwned() === false) { |
108 | const audience = getVideoAudience(video, actorsInvolvedInVideo) | 108 | const audience = getVideoAudience(video, actorsInvolvedInVideo) |
109 | const data = await createActivityData(url, byActor, dislikeActivityData, t, audience) | 109 | const data = createActivityData(url, byActor, dislikeActivityData, audience) |
110 | 110 | ||
111 | return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl) | 111 | return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl) |
112 | } | 112 | } |
113 | 113 | ||
114 | // Send to followers | 114 | // Send to followers |
115 | const audience = getObjectFollowersAudience(actorsInvolvedInVideo) | 115 | const audience = getObjectFollowersAudience(actorsInvolvedInVideo) |
116 | const data = await createActivityData(url, byActor, dislikeActivityData, t, audience) | 116 | const data = createActivityData(url, byActor, dislikeActivityData, audience) |
117 | 117 | ||
118 | const actorsException = [ byActor ] | 118 | const actorsException = [ byActor ] |
119 | return broadcastToFollowers(data, byActor, actorsInvolvedInVideo, t, actorsException) | 119 | return broadcastToFollowers(data, byActor, actorsInvolvedInVideo, t, actorsException) |
120 | } | 120 | } |
121 | 121 | ||
122 | async function createActivityData (url: string, | 122 | function createActivityData (url: string, byActor: ActorModel, object: any, audience?: ActivityAudience): ActivityCreate { |
123 | byActor: ActorModel, | 123 | if (!audience) audience = getAudience(byActor) |
124 | object: any, | 124 | |
125 | t: Transaction, | 125 | return audiencify( |
126 | audience?: ActivityAudience): Promise<ActivityCreate> { | 126 | { |
127 | if (!audience) { | 127 | type: 'Create' as 'Create', |
128 | audience = await getAudience(byActor, t) | 128 | id: url + '/activity', |
129 | } | 129 | actor: byActor.url, |
130 | 130 | object: audiencify(object, audience) | |
131 | return audiencify({ | 131 | }, |
132 | type: 'Create' as 'Create', | 132 | audience |
133 | id: url + '/activity', | 133 | ) |
134 | actor: byActor.url, | ||
135 | object: audiencify(object, audience) | ||
136 | }, audience) | ||
137 | } | 134 | } |
138 | 135 | ||
139 | function createDislikeActivityData (byActor: ActorModel, video: VideoModel) { | 136 | function createDislikeActivityData (byActor: ActorModel, video: VideoModel) { |
diff --git a/server/lib/activitypub/send/send-like.ts b/server/lib/activitypub/send/send-like.ts index ddeb1fcd2..37ee7c096 100644 --- a/server/lib/activitypub/send/send-like.ts +++ b/server/lib/activitypub/send/send-like.ts | |||
@@ -14,36 +14,31 @@ async function sendLike (byActor: ActorModel, video: VideoModel, t: Transaction) | |||
14 | // Send to origin | 14 | // Send to origin |
15 | if (video.isOwned() === false) { | 15 | if (video.isOwned() === false) { |
16 | const audience = getVideoAudience(video, accountsInvolvedInVideo) | 16 | const audience = getVideoAudience(video, accountsInvolvedInVideo) |
17 | const data = await likeActivityData(url, byActor, video, t, audience) | 17 | const data = likeActivityData(url, byActor, video, audience) |
18 | 18 | ||
19 | return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl) | 19 | return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl) |
20 | } | 20 | } |
21 | 21 | ||
22 | // Send to followers | 22 | // Send to followers |
23 | const audience = getObjectFollowersAudience(accountsInvolvedInVideo) | 23 | const audience = getObjectFollowersAudience(accountsInvolvedInVideo) |
24 | const data = await likeActivityData(url, byActor, video, t, audience) | 24 | const data = likeActivityData(url, byActor, video, audience) |
25 | 25 | ||
26 | const followersException = [ byActor ] | 26 | const followersException = [ byActor ] |
27 | return broadcastToFollowers(data, byActor, accountsInvolvedInVideo, t, followersException) | 27 | return broadcastToFollowers(data, byActor, accountsInvolvedInVideo, t, followersException) |
28 | } | 28 | } |
29 | 29 | ||
30 | async function likeActivityData ( | 30 | function likeActivityData (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityLike { |
31 | url: string, | 31 | if (!audience) audience = getAudience(byActor) |
32 | byActor: ActorModel, | 32 | |
33 | video: VideoModel, | 33 | return audiencify( |
34 | t: Transaction, | 34 | { |
35 | audience?: ActivityAudience | 35 | type: 'Like' as 'Like', |
36 | ): Promise<ActivityLike> { | 36 | id: url, |
37 | if (!audience) { | 37 | actor: byActor.url, |
38 | audience = await getAudience(byActor, t) | 38 | object: video.url |
39 | } | 39 | }, |
40 | 40 | audience | |
41 | return audiencify({ | 41 | ) |
42 | type: 'Like' as 'Like', | ||
43 | id: url, | ||
44 | actor: byActor.url, | ||
45 | object: video.url | ||
46 | }, audience) | ||
47 | } | 42 | } |
48 | 43 | ||
49 | // --------------------------------------------------------------------------- | 44 | // --------------------------------------------------------------------------- |
diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts index 9733e66dc..33c3d2429 100644 --- a/server/lib/activitypub/send/send-undo.ts +++ b/server/lib/activitypub/send/send-undo.ts | |||
@@ -27,7 +27,7 @@ async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) { | |||
27 | const undoUrl = getUndoActivityPubUrl(followUrl) | 27 | const undoUrl = getUndoActivityPubUrl(followUrl) |
28 | 28 | ||
29 | const object = followActivityData(followUrl, me, following) | 29 | const object = followActivityData(followUrl, me, following) |
30 | const data = await undoActivityData(undoUrl, me, object, t) | 30 | const data = undoActivityData(undoUrl, me, object) |
31 | 31 | ||
32 | return unicastTo(data, me, following.inboxUrl) | 32 | return unicastTo(data, me, following.inboxUrl) |
33 | } | 33 | } |
@@ -37,18 +37,18 @@ async function sendUndoLike (byActor: ActorModel, video: VideoModel, t: Transact | |||
37 | const undoUrl = getUndoActivityPubUrl(likeUrl) | 37 | const undoUrl = getUndoActivityPubUrl(likeUrl) |
38 | 38 | ||
39 | const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) | 39 | const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) |
40 | const object = await likeActivityData(likeUrl, byActor, video, t) | 40 | const object = likeActivityData(likeUrl, byActor, video) |
41 | 41 | ||
42 | // Send to origin | 42 | // Send to origin |
43 | if (video.isOwned() === false) { | 43 | if (video.isOwned() === false) { |
44 | const audience = getVideoAudience(video, actorsInvolvedInVideo) | 44 | const audience = getVideoAudience(video, actorsInvolvedInVideo) |
45 | const data = await undoActivityData(undoUrl, byActor, object, t, audience) | 45 | const data = undoActivityData(undoUrl, byActor, object, audience) |
46 | 46 | ||
47 | return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl) | 47 | return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl) |
48 | } | 48 | } |
49 | 49 | ||
50 | const audience = getObjectFollowersAudience(actorsInvolvedInVideo) | 50 | const audience = getObjectFollowersAudience(actorsInvolvedInVideo) |
51 | const data = await undoActivityData(undoUrl, byActor, object, t, audience) | 51 | const data = undoActivityData(undoUrl, byActor, object, audience) |
52 | 52 | ||
53 | const followersException = [ byActor ] | 53 | const followersException = [ byActor ] |
54 | return broadcastToFollowers(data, byActor, actorsInvolvedInVideo, t, followersException) | 54 | return broadcastToFollowers(data, byActor, actorsInvolvedInVideo, t, followersException) |
@@ -60,16 +60,16 @@ async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Trans | |||
60 | 60 | ||
61 | const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) | 61 | const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) |
62 | const dislikeActivity = createDislikeActivityData(byActor, video) | 62 | const dislikeActivity = createDislikeActivityData(byActor, video) |
63 | const object = await createActivityData(dislikeUrl, byActor, dislikeActivity, t) | 63 | const object = createActivityData(dislikeUrl, byActor, dislikeActivity) |
64 | 64 | ||
65 | if (video.isOwned() === false) { | 65 | if (video.isOwned() === false) { |
66 | const audience = getVideoAudience(video, actorsInvolvedInVideo) | 66 | const audience = getVideoAudience(video, actorsInvolvedInVideo) |
67 | const data = await undoActivityData(undoUrl, byActor, object, t, audience) | 67 | const data = undoActivityData(undoUrl, byActor, object, audience) |
68 | 68 | ||
69 | return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl) | 69 | return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl) |
70 | } | 70 | } |
71 | 71 | ||
72 | const data = await undoActivityData(undoUrl, byActor, object, t) | 72 | const data = undoActivityData(undoUrl, byActor, object) |
73 | 73 | ||
74 | const followersException = [ byActor ] | 74 | const followersException = [ byActor ] |
75 | return broadcastToFollowers(data, byActor, actorsInvolvedInVideo, t, followersException) | 75 | return broadcastToFollowers(data, byActor, actorsInvolvedInVideo, t, followersException) |
@@ -80,7 +80,7 @@ async function sendUndoAnnounce (byActor: ActorModel, videoShare: VideoShareMode | |||
80 | 80 | ||
81 | const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) | 81 | const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) |
82 | const object = await buildVideoAnnounce(byActor, videoShare, video, t) | 82 | const object = await buildVideoAnnounce(byActor, videoShare, video, t) |
83 | const data = await undoActivityData(undoUrl, byActor, object, t) | 83 | const data = undoActivityData(undoUrl, byActor, object) |
84 | 84 | ||
85 | const followersException = [ byActor ] | 85 | const followersException = [ byActor ] |
86 | return broadcastToFollowers(data, byActor, actorsInvolvedInVideo, t, followersException) | 86 | return broadcastToFollowers(data, byActor, actorsInvolvedInVideo, t, followersException) |
@@ -97,21 +97,21 @@ export { | |||
97 | 97 | ||
98 | // --------------------------------------------------------------------------- | 98 | // --------------------------------------------------------------------------- |
99 | 99 | ||
100 | async function undoActivityData ( | 100 | function undoActivityData ( |
101 | url: string, | 101 | url: string, |
102 | byActor: ActorModel, | 102 | byActor: ActorModel, |
103 | object: ActivityFollow | ActivityLike | ActivityCreate | ActivityAnnounce, | 103 | object: ActivityFollow | ActivityLike | ActivityCreate | ActivityAnnounce, |
104 | t: Transaction, | ||
105 | audience?: ActivityAudience | 104 | audience?: ActivityAudience |
106 | ): Promise<ActivityUndo> { | 105 | ): ActivityUndo { |
107 | if (!audience) { | 106 | if (!audience) audience = getAudience(byActor) |
108 | audience = await getAudience(byActor, t) | 107 | |
109 | } | 108 | return audiencify( |
110 | 109 | { | |
111 | return audiencify({ | 110 | type: 'Undo' as 'Undo', |
112 | type: 'Undo' as 'Undo', | 111 | id: url, |
113 | id: url, | 112 | actor: byActor.url, |
114 | actor: byActor.url, | 113 | object |
115 | object | 114 | }, |
116 | }, audience) | 115 | audience |
116 | ) | ||
117 | } | 117 | } |
diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts index d64b88343..2fd374ec6 100644 --- a/server/lib/activitypub/send/send-update.ts +++ b/server/lib/activitypub/send/send-update.ts | |||
@@ -15,9 +15,9 @@ async function sendUpdateVideo (video: VideoModel, t: Transaction) { | |||
15 | 15 | ||
16 | const url = getUpdateActivityPubUrl(video.url, video.updatedAt.toISOString()) | 16 | const url = getUpdateActivityPubUrl(video.url, video.updatedAt.toISOString()) |
17 | const videoObject = video.toActivityPubObject() | 17 | const videoObject = video.toActivityPubObject() |
18 | const audience = await getAudience(byActor, t, video.privacy === VideoPrivacy.PUBLIC) | 18 | const audience = getAudience(byActor, video.privacy === VideoPrivacy.PUBLIC) |
19 | 19 | ||
20 | const data = await updateActivityData(url, byActor, videoObject, t, audience) | 20 | const data = updateActivityData(url, byActor, videoObject, audience) |
21 | 21 | ||
22 | const actorsInvolved = await VideoShareModel.loadActorsByShare(video.id, t) | 22 | const actorsInvolved = await VideoShareModel.loadActorsByShare(video.id, t) |
23 | actorsInvolved.push(byActor) | 23 | actorsInvolved.push(byActor) |
@@ -30,8 +30,8 @@ async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelMod | |||
30 | 30 | ||
31 | const url = getUpdateActivityPubUrl(byActor.url, byActor.updatedAt.toISOString()) | 31 | const url = getUpdateActivityPubUrl(byActor.url, byActor.updatedAt.toISOString()) |
32 | const accountOrChannelObject = accountOrChannel.toActivityPubObject() | 32 | const accountOrChannelObject = accountOrChannel.toActivityPubObject() |
33 | const audience = await getAudience(byActor, t) | 33 | const audience = getAudience(byActor) |
34 | const data = await updateActivityData(url, byActor, accountOrChannelObject, t, audience) | 34 | const data = updateActivityData(url, byActor, accountOrChannelObject, audience) |
35 | 35 | ||
36 | let actorsInvolved: ActorModel[] | 36 | let actorsInvolved: ActorModel[] |
37 | if (accountOrChannel instanceof AccountModel) { | 37 | if (accountOrChannel instanceof AccountModel) { |
@@ -56,21 +56,17 @@ export { | |||
56 | 56 | ||
57 | // --------------------------------------------------------------------------- | 57 | // --------------------------------------------------------------------------- |
58 | 58 | ||
59 | async function updateActivityData ( | 59 | function updateActivityData (url: string, byActor: ActorModel, object: any, audience?: ActivityAudience): ActivityUpdate { |
60 | url: string, | 60 | if (!audience) audience = getAudience(byActor) |
61 | byActor: ActorModel, | ||
62 | object: any, | ||
63 | t: Transaction, | ||
64 | audience?: ActivityAudience | ||
65 | ): Promise<ActivityUpdate> { | ||
66 | if (!audience) { | ||
67 | audience = await getAudience(byActor, t) | ||
68 | } | ||
69 | 61 | ||
70 | return audiencify({ | 62 | return audiencify( |
71 | type: 'Update' as 'Update', | 63 | { |
72 | id: url, | 64 | type: 'Update' as 'Update', |
73 | actor: byActor.url, | 65 | id: url, |
74 | object: audiencify(object, audience) | 66 | actor: byActor.url, |
75 | }, audience) | 67 | object: audiencify(object, audience |
68 | ) | ||
69 | }, | ||
70 | audience | ||
71 | ) | ||
76 | } | 72 | } |
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts index 907f7e11e..7ec8ca193 100644 --- a/server/lib/activitypub/videos.ts +++ b/server/lib/activitypub/videos.ts | |||
@@ -1,8 +1,9 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import * as sequelize from 'sequelize' | ||
2 | import * as magnetUtil from 'magnet-uri' | 3 | import * as magnetUtil from 'magnet-uri' |
3 | import { join } from 'path' | 4 | import { join } from 'path' |
4 | import * as request from 'request' | 5 | import * as request from 'request' |
5 | import { ActivityIconObject } from '../../../shared/index' | 6 | import { ActivityIconObject, VideoState } from '../../../shared/index' |
6 | import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' | 7 | import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' |
7 | import { VideoPrivacy, VideoRateType } from '../../../shared/models/videos' | 8 | import { VideoPrivacy, VideoRateType } from '../../../shared/models/videos' |
8 | import { sanitizeAndCheckVideoTorrentObject } from '../../helpers/custom-validators/activitypub/videos' | 9 | import { sanitizeAndCheckVideoTorrentObject } from '../../helpers/custom-validators/activitypub/videos' |
@@ -21,6 +22,21 @@ import { VideoShareModel } from '../../models/video/video-share' | |||
21 | import { getOrCreateActorAndServerAndModel } from './actor' | 22 | import { getOrCreateActorAndServerAndModel } from './actor' |
22 | import { addVideoComments } from './video-comments' | 23 | import { addVideoComments } from './video-comments' |
23 | import { crawlCollectionPage } from './crawl' | 24 | import { crawlCollectionPage } from './crawl' |
25 | import { sendCreateVideo, sendUpdateVideo } from './send' | ||
26 | import { shareVideoByServerAndChannel } from './index' | ||
27 | |||
28 | async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) { | ||
29 | // If the video is not private and published, we federate it | ||
30 | if (video.privacy !== VideoPrivacy.PRIVATE && video.state === VideoState.PUBLISHED) { | ||
31 | if (isNewVideo === true) { | ||
32 | // Now we'll add the video's meta data to our followers | ||
33 | await sendCreateVideo(video, transaction) | ||
34 | await shareVideoByServerAndChannel(video, transaction) | ||
35 | } else { | ||
36 | await sendUpdateVideo(video, transaction) | ||
37 | } | ||
38 | } | ||
39 | } | ||
24 | 40 | ||
25 | function fetchRemoteVideoPreview (video: VideoModel, reject: Function) { | 41 | function fetchRemoteVideoPreview (video: VideoModel, reject: Function) { |
26 | const host = video.VideoChannel.Account.Actor.Server.host | 42 | const host = video.VideoChannel.Account.Actor.Server.host |
@@ -55,9 +71,11 @@ function generateThumbnailFromUrl (video: VideoModel, icon: ActivityIconObject) | |||
55 | return doRequestAndSaveToFile(options, thumbnailPath) | 71 | return doRequestAndSaveToFile(options, thumbnailPath) |
56 | } | 72 | } |
57 | 73 | ||
58 | async function videoActivityObjectToDBAttributes (videoChannel: VideoChannelModel, | 74 | async function videoActivityObjectToDBAttributes ( |
59 | videoObject: VideoTorrentObject, | 75 | videoChannel: VideoChannelModel, |
60 | to: string[] = []) { | 76 | videoObject: VideoTorrentObject, |
77 | to: string[] = [] | ||
78 | ) { | ||
61 | const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPrivacy.PUBLIC : VideoPrivacy.UNLISTED | 79 | const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPrivacy.PUBLIC : VideoPrivacy.UNLISTED |
62 | const duration = videoObject.duration.replace(/[^\d]+/, '') | 80 | const duration = videoObject.duration.replace(/[^\d]+/, '') |
63 | 81 | ||
@@ -90,6 +108,8 @@ async function videoActivityObjectToDBAttributes (videoChannel: VideoChannelMode | |||
90 | support, | 108 | support, |
91 | nsfw: videoObject.sensitive, | 109 | nsfw: videoObject.sensitive, |
92 | commentsEnabled: videoObject.commentsEnabled, | 110 | commentsEnabled: videoObject.commentsEnabled, |
111 | waitTranscoding: videoObject.waitTranscoding, | ||
112 | state: videoObject.state, | ||
93 | channelId: videoChannel.id, | 113 | channelId: videoChannel.id, |
94 | duration: parseInt(duration, 10), | 114 | duration: parseInt(duration, 10), |
95 | createdAt: new Date(videoObject.published), | 115 | createdAt: new Date(videoObject.published), |
@@ -185,22 +205,20 @@ async function getOrCreateVideo (videoObject: VideoTorrentObject, channelActor: | |||
185 | } | 205 | } |
186 | 206 | ||
187 | async function getOrCreateAccountAndVideoAndChannel (videoObject: VideoTorrentObject | string, actor?: ActorModel) { | 207 | async function getOrCreateAccountAndVideoAndChannel (videoObject: VideoTorrentObject | string, actor?: ActorModel) { |
188 | if (typeof videoObject === 'string') { | 208 | const videoUrl = typeof videoObject === 'string' ? videoObject : videoObject.id |
189 | const videoUrl = videoObject | 209 | |
190 | 210 | const videoFromDatabase = await VideoModel.loadByUrlAndPopulateAccount(videoUrl) | |
191 | const videoFromDatabase = await VideoModel.loadByUrlAndPopulateAccount(videoUrl) | 211 | if (videoFromDatabase) { |
192 | if (videoFromDatabase) { | 212 | return { |
193 | return { | 213 | video: videoFromDatabase, |
194 | video: videoFromDatabase, | 214 | actor: videoFromDatabase.VideoChannel.Account.Actor, |
195 | actor: videoFromDatabase.VideoChannel.Account.Actor, | 215 | channelActor: videoFromDatabase.VideoChannel.Actor |
196 | channelActor: videoFromDatabase.VideoChannel.Actor | ||
197 | } | ||
198 | } | 216 | } |
199 | |||
200 | videoObject = await fetchRemoteVideo(videoUrl) | ||
201 | if (!videoObject) throw new Error('Cannot fetch remote video with url: ' + videoUrl) | ||
202 | } | 217 | } |
203 | 218 | ||
219 | videoObject = await fetchRemoteVideo(videoUrl) | ||
220 | if (!videoObject) throw new Error('Cannot fetch remote video with url: ' + videoUrl) | ||
221 | |||
204 | if (!actor) { | 222 | if (!actor) { |
205 | const actorObj = videoObject.attributedTo.find(a => a.type === 'Person') | 223 | const actorObj = videoObject.attributedTo.find(a => a.type === 'Person') |
206 | if (!actorObj) throw new Error('Cannot find associated actor to video ' + videoObject.url) | 224 | if (!actorObj) throw new Error('Cannot find associated actor to video ' + videoObject.url) |
@@ -291,20 +309,6 @@ async function addVideoShares (shareUrls: string[], instance: VideoModel) { | |||
291 | } | 309 | } |
292 | } | 310 | } |
293 | 311 | ||
294 | export { | ||
295 | getOrCreateAccountAndVideoAndChannel, | ||
296 | fetchRemoteVideoPreview, | ||
297 | fetchRemoteVideoDescription, | ||
298 | generateThumbnailFromUrl, | ||
299 | videoActivityObjectToDBAttributes, | ||
300 | videoFileActivityUrlToDBAttributes, | ||
301 | getOrCreateVideo, | ||
302 | getOrCreateVideoChannel, | ||
303 | addVideoShares | ||
304 | } | ||
305 | |||
306 | // --------------------------------------------------------------------------- | ||
307 | |||
308 | async function fetchRemoteVideo (videoUrl: string): Promise<VideoTorrentObject> { | 312 | async function fetchRemoteVideo (videoUrl: string): Promise<VideoTorrentObject> { |
309 | const options = { | 313 | const options = { |
310 | uri: videoUrl, | 314 | uri: videoUrl, |
@@ -324,3 +328,17 @@ async function fetchRemoteVideo (videoUrl: string): Promise<VideoTorrentObject> | |||
324 | 328 | ||
325 | return body | 329 | return body |
326 | } | 330 | } |
331 | |||
332 | export { | ||
333 | federateVideoIfNeeded, | ||
334 | fetchRemoteVideo, | ||
335 | getOrCreateAccountAndVideoAndChannel, | ||
336 | fetchRemoteVideoPreview, | ||
337 | fetchRemoteVideoDescription, | ||
338 | generateThumbnailFromUrl, | ||
339 | videoActivityObjectToDBAttributes, | ||
340 | videoFileActivityUrlToDBAttributes, | ||
341 | getOrCreateVideo, | ||
342 | getOrCreateVideoChannel, | ||
343 | addVideoShares | ||
344 | } | ||
diff --git a/server/lib/job-queue/handlers/video-file.ts b/server/lib/job-queue/handlers/video-file.ts index 85f7dbfc2..f5ad076a6 100644 --- a/server/lib/job-queue/handlers/video-file.ts +++ b/server/lib/job-queue/handlers/video-file.ts | |||
@@ -1,17 +1,16 @@ | |||
1 | import * as kue from 'kue' | 1 | import * as kue from 'kue' |
2 | import { VideoResolution } from '../../../../shared' | 2 | import { VideoResolution, VideoState } from '../../../../shared' |
3 | import { VideoPrivacy } from '../../../../shared/models/videos' | ||
4 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
5 | import { computeResolutionsToTranscode } from '../../../helpers/utils' | 4 | import { computeResolutionsToTranscode } from '../../../helpers/utils' |
6 | import { sequelizeTypescript } from '../../../initializers' | ||
7 | import { VideoModel } from '../../../models/video/video' | 5 | import { VideoModel } from '../../../models/video/video' |
8 | import { shareVideoByServerAndChannel } from '../../activitypub' | ||
9 | import { sendCreateVideo, sendUpdateVideo } from '../../activitypub/send' | ||
10 | import { JobQueue } from '../job-queue' | 6 | import { JobQueue } from '../job-queue' |
7 | import { federateVideoIfNeeded } from '../../activitypub' | ||
8 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | ||
9 | import { sequelizeTypescript } from '../../../initializers' | ||
11 | 10 | ||
12 | export type VideoFilePayload = { | 11 | export type VideoFilePayload = { |
13 | videoUUID: string | 12 | videoUUID: string |
14 | isNewVideo: boolean | 13 | isNewVideo?: boolean |
15 | resolution?: VideoResolution | 14 | resolution?: VideoResolution |
16 | isPortraitMode?: boolean | 15 | isPortraitMode?: boolean |
17 | } | 16 | } |
@@ -52,10 +51,20 @@ async function processVideoFile (job: kue.Job) { | |||
52 | // Transcoding in other resolution | 51 | // Transcoding in other resolution |
53 | if (payload.resolution) { | 52 | if (payload.resolution) { |
54 | await video.transcodeOriginalVideofile(payload.resolution, payload.isPortraitMode) | 53 | await video.transcodeOriginalVideofile(payload.resolution, payload.isPortraitMode) |
55 | await onVideoFileTranscoderOrImportSuccess(video) | 54 | |
55 | const options = { | ||
56 | arguments: [ video ], | ||
57 | errorMessage: 'Cannot execute onVideoFileTranscoderOrImportSuccess with many retries.' | ||
58 | } | ||
59 | await retryTransactionWrapper(onVideoFileTranscoderOrImportSuccess, options) | ||
56 | } else { | 60 | } else { |
57 | await video.optimizeOriginalVideofile() | 61 | await video.optimizeOriginalVideofile() |
58 | await onVideoFileOptimizerSuccess(video, payload.isNewVideo) | 62 | |
63 | const options = { | ||
64 | arguments: [ video, payload.isNewVideo ], | ||
65 | errorMessage: 'Cannot execute onVideoFileOptimizerSuccess with many retries.' | ||
66 | } | ||
67 | await retryTransactionWrapper(onVideoFileOptimizerSuccess, options) | ||
59 | } | 68 | } |
60 | 69 | ||
61 | return video | 70 | return video |
@@ -64,68 +73,70 @@ async function processVideoFile (job: kue.Job) { | |||
64 | async function onVideoFileTranscoderOrImportSuccess (video: VideoModel) { | 73 | async function onVideoFileTranscoderOrImportSuccess (video: VideoModel) { |
65 | if (video === undefined) return undefined | 74 | if (video === undefined) return undefined |
66 | 75 | ||
67 | // Maybe the video changed in database, refresh it | 76 | return sequelizeTypescript.transaction(async t => { |
68 | const videoDatabase = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(video.uuid) | 77 | // Maybe the video changed in database, refresh it |
69 | // Video does not exist anymore | 78 | let videoDatabase = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(video.uuid, t) |
70 | if (!videoDatabase) return undefined | 79 | // Video does not exist anymore |
80 | if (!videoDatabase) return undefined | ||
71 | 81 | ||
72 | if (video.privacy !== VideoPrivacy.PRIVATE) { | 82 | // We transcoded the video file in another format, now we can publish it |
73 | await sendUpdateVideo(video, undefined) | 83 | const oldState = videoDatabase.state |
74 | } | 84 | videoDatabase.state = VideoState.PUBLISHED |
85 | videoDatabase = await videoDatabase.save({ transaction: t }) | ||
86 | |||
87 | // If the video was not published, we consider it is a new one for other instances | ||
88 | const isNewVideo = oldState !== VideoState.PUBLISHED | ||
89 | await federateVideoIfNeeded(videoDatabase, isNewVideo, t) | ||
75 | 90 | ||
76 | return undefined | 91 | return undefined |
92 | }) | ||
77 | } | 93 | } |
78 | 94 | ||
79 | async function onVideoFileOptimizerSuccess (video: VideoModel, isNewVideo: boolean) { | 95 | async function onVideoFileOptimizerSuccess (video: VideoModel, isNewVideo: boolean) { |
80 | if (video === undefined) return undefined | 96 | if (video === undefined) return undefined |
81 | 97 | ||
82 | // Maybe the video changed in database, refresh it | 98 | // Outside the transaction (IO on disk) |
83 | const videoDatabase = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(video.uuid) | 99 | const { videoFileResolution } = await video.getOriginalFileResolution() |
84 | // Video does not exist anymore | 100 | |
85 | if (!videoDatabase) return undefined | 101 | return sequelizeTypescript.transaction(async t => { |
86 | 102 | // Maybe the video changed in database, refresh it | |
87 | if (video.privacy !== VideoPrivacy.PRIVATE) { | 103 | const videoDatabase = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(video.uuid, t) |
88 | if (isNewVideo !== false) { | 104 | // Video does not exist anymore |
89 | // Now we'll add the video's meta data to our followers | 105 | if (!videoDatabase) return undefined |
90 | await sequelizeTypescript.transaction(async t => { | 106 | |
91 | await sendCreateVideo(video, t) | 107 | // Create transcoding jobs if there are enabled resolutions |
92 | await shareVideoByServerAndChannel(video, t) | 108 | const resolutionsEnabled = computeResolutionsToTranscode(videoFileResolution) |
93 | }) | 109 | logger.info( |
94 | } else { | 110 | 'Resolutions computed for video %s and origin file height of %d.', videoDatabase.uuid, videoFileResolution, |
95 | await sendUpdateVideo(video, undefined) | 111 | { resolutions: resolutionsEnabled } |
96 | } | 112 | ) |
97 | } | 113 | |
98 | 114 | if (resolutionsEnabled.length !== 0) { | |
99 | const { videoFileResolution } = await videoDatabase.getOriginalFileResolution() | 115 | const tasks: Promise<any>[] = [] |
100 | 116 | ||
101 | // Create transcoding jobs if there are enabled resolutions | 117 | for (const resolution of resolutionsEnabled) { |
102 | const resolutionsEnabled = computeResolutionsToTranscode(videoFileResolution) | 118 | const dataInput = { |
103 | logger.info( | 119 | videoUUID: videoDatabase.uuid, |
104 | 'Resolutions computed for video %s and origin file height of %d.', videoDatabase.uuid, videoFileResolution, | 120 | resolution |
105 | { resolutions: resolutionsEnabled } | 121 | } |
106 | ) | 122 | |
123 | const p = JobQueue.Instance.createJob({ type: 'video-file', payload: dataInput }) | ||
124 | tasks.push(p) | ||
125 | } | ||
107 | 126 | ||
108 | if (resolutionsEnabled.length !== 0) { | 127 | await Promise.all(tasks) |
109 | const tasks: Promise<any>[] = [] | ||
110 | 128 | ||
111 | for (const resolution of resolutionsEnabled) { | 129 | logger.info('Transcoding jobs created for uuid %s.', videoDatabase.uuid, { resolutionsEnabled }) |
112 | const dataInput = { | 130 | } else { |
113 | videoUUID: videoDatabase.uuid, | 131 | // No transcoding to do, it's now published |
114 | resolution, | 132 | video.state = VideoState.PUBLISHED |
115 | isNewVideo | 133 | video = await video.save({ transaction: t }) |
116 | } | ||
117 | 134 | ||
118 | const p = JobQueue.Instance.createJob({ type: 'video-file', payload: dataInput }) | 135 | logger.info('No transcoding jobs created for video %s (no resolutions).', video.uuid) |
119 | tasks.push(p) | ||
120 | } | 136 | } |
121 | 137 | ||
122 | await Promise.all(tasks) | 138 | return federateVideoIfNeeded(video, isNewVideo, t) |
123 | 139 | }) | |
124 | logger.info('Transcoding jobs created for uuid %s.', videoDatabase.uuid, { resolutionsEnabled }) | ||
125 | } else { | ||
126 | logger.info('No transcoding jobs created for video %s (no resolutions enabled).') | ||
127 | return undefined | ||
128 | } | ||
129 | } | 140 | } |
130 | 141 | ||
131 | // --------------------------------------------------------------------------- | 142 | // --------------------------------------------------------------------------- |
diff --git a/server/lib/job-queue/job-queue.ts b/server/lib/job-queue/job-queue.ts index bdfa19b61..695fe0eea 100644 --- a/server/lib/job-queue/job-queue.ts +++ b/server/lib/job-queue/job-queue.ts | |||
@@ -79,6 +79,7 @@ class JobQueue { | |||
79 | const res = await handlers[ handlerName ](job) | 79 | const res = await handlers[ handlerName ](job) |
80 | return done(null, res) | 80 | return done(null, res) |
81 | } catch (err) { | 81 | } catch (err) { |
82 | logger.error('Cannot execute job %d.', job.id, { err }) | ||
82 | return done(err) | 83 | return done(err) |
83 | } | 84 | } |
84 | }) | 85 | }) |