diff options
author | Chocobozzz <florian.bigard@gmail.com> | 2017-11-10 14:34:45 +0100 |
---|---|---|
committer | Chocobozzz <florian.bigard@gmail.com> | 2017-11-27 19:40:51 +0100 |
commit | 0d0e8dd0904b380b70e19ebcb4763d65601c4632 (patch) | |
tree | acb625d7c88fbe863fa14bf6783fafe4a8e35137 /server/controllers | |
parent | e4f97babf701481b55cc10fb3448feab5f97c867 (diff) | |
download | PeerTube-0d0e8dd0904b380b70e19ebcb4763d65601c4632.tar.gz PeerTube-0d0e8dd0904b380b70e19ebcb4763d65601c4632.tar.zst PeerTube-0d0e8dd0904b380b70e19ebcb4763d65601c4632.zip |
Continue activitypub
Diffstat (limited to 'server/controllers')
-rw-r--r-- | server/controllers/activitypub/inbox.ts | 30 | ||||
-rw-r--r-- | server/controllers/activitypub/pods.ts | 138 | ||||
-rw-r--r-- | server/controllers/activitypub/videos.ts | 928 | ||||
-rw-r--r-- | server/controllers/api/videos/index.ts | 30 |
4 files changed, 435 insertions, 691 deletions
diff --git a/server/controllers/activitypub/inbox.ts b/server/controllers/activitypub/inbox.ts index 79d989c2c..eee217650 100644 --- a/server/controllers/activitypub/inbox.ts +++ b/server/controllers/activitypub/inbox.ts | |||
@@ -1,26 +1,15 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | 2 | import { Activity, ActivityPubCollection, ActivityPubOrderedCollection, ActivityType, RootActivity } from '../../../shared' | |
3 | import { | ||
4 | processCreateActivity, | ||
5 | processUpdateActivity, | ||
6 | processFlagActivity | ||
7 | } from '../../lib' | ||
8 | import { | ||
9 | Activity, | ||
10 | ActivityType, | ||
11 | RootActivity, | ||
12 | ActivityPubCollection, | ||
13 | ActivityPubOrderedCollection | ||
14 | } from '../../../shared' | ||
15 | import { | ||
16 | signatureValidator, | ||
17 | checkSignature, | ||
18 | asyncMiddleware | ||
19 | } from '../../middlewares' | ||
20 | import { logger } from '../../helpers' | 3 | import { logger } from '../../helpers' |
4 | import { isActivityValid } from '../../helpers/custom-validators/activitypub/activity' | ||
5 | import { processCreateActivity, processFlagActivity, processUpdateActivity } from '../../lib' | ||
6 | import { processAddActivity } from '../../lib/activitypub/process-add' | ||
7 | import { asyncMiddleware, checkSignature, signatureValidator } from '../../middlewares' | ||
8 | import { activityPubValidator } from '../../middlewares/validators/activitypub/activity' | ||
21 | 9 | ||
22 | const processActivity: { [ P in ActivityType ]: (activity: Activity) => Promise<any> } = { | 10 | const processActivity: { [ P in ActivityType ]: (activity: Activity) => Promise<any> } = { |
23 | Create: processCreateActivity, | 11 | Create: processCreateActivity, |
12 | Add: processAddActivity, | ||
24 | Update: processUpdateActivity, | 13 | Update: processUpdateActivity, |
25 | Flag: processFlagActivity | 14 | Flag: processFlagActivity |
26 | } | 15 | } |
@@ -30,7 +19,7 @@ const inboxRouter = express.Router() | |||
30 | inboxRouter.post('/', | 19 | inboxRouter.post('/', |
31 | signatureValidator, | 20 | signatureValidator, |
32 | asyncMiddleware(checkSignature), | 21 | asyncMiddleware(checkSignature), |
33 | // inboxValidator, | 22 | activityPubValidator, |
34 | asyncMiddleware(inboxController) | 23 | asyncMiddleware(inboxController) |
35 | ) | 24 | ) |
36 | 25 | ||
@@ -54,6 +43,9 @@ async function inboxController (req: express.Request, res: express.Response, nex | |||
54 | activities = [ rootActivity as Activity ] | 43 | activities = [ rootActivity as Activity ] |
55 | } | 44 | } |
56 | 45 | ||
46 | // Only keep activities we are able to process | ||
47 | activities = activities.filter(a => isActivityValid(a)) | ||
48 | |||
57 | await processActivities(activities) | 49 | await processActivities(activities) |
58 | 50 | ||
59 | res.status(204).end() | 51 | res.status(204).end() |
diff --git a/server/controllers/activitypub/pods.ts b/server/controllers/activitypub/pods.ts index 326eb61ac..6cce57c1c 100644 --- a/server/controllers/activitypub/pods.ts +++ b/server/controllers/activitypub/pods.ts | |||
@@ -1,69 +1,69 @@ | |||
1 | import * as express from 'express' | 1 | // import * as express from 'express' |
2 | 2 | // | |
3 | import { database as db } from '../../../initializers/database' | 3 | // import { database as db } from '../../../initializers/database' |
4 | import { | 4 | // import { |
5 | checkSignature, | 5 | // checkSignature, |
6 | signatureValidator, | 6 | // signatureValidator, |
7 | setBodyHostPort, | 7 | // setBodyHostPort, |
8 | remotePodsAddValidator, | 8 | // remotePodsAddValidator, |
9 | asyncMiddleware | 9 | // asyncMiddleware |
10 | } from '../../../middlewares' | 10 | // } from '../../../middlewares' |
11 | import { sendOwnedDataToPod } from '../../../lib' | 11 | // import { sendOwnedDataToPod } from '../../../lib' |
12 | import { getMyPublicCert, getFormattedObjects } from '../../../helpers' | 12 | // import { getMyPublicCert, getFormattedObjects } from '../../../helpers' |
13 | import { CONFIG } from '../../../initializers' | 13 | // import { CONFIG } from '../../../initializers' |
14 | import { PodInstance } from '../../../models' | 14 | // import { PodInstance } from '../../../models' |
15 | import { PodSignature, Pod as FormattedPod } from '../../../../shared' | 15 | // import { PodSignature, Pod as FormattedPod } from '../../../../shared' |
16 | 16 | // | |
17 | const remotePodsRouter = express.Router() | 17 | // const remotePodsRouter = express.Router() |
18 | 18 | // | |
19 | remotePodsRouter.post('/remove', | 19 | // remotePodsRouter.post('/remove', |
20 | signatureValidator, | 20 | // signatureValidator, |
21 | checkSignature, | 21 | // checkSignature, |
22 | asyncMiddleware(removePods) | 22 | // asyncMiddleware(removePods) |
23 | ) | 23 | // ) |
24 | 24 | // | |
25 | remotePodsRouter.post('/list', | 25 | // remotePodsRouter.post('/list', |
26 | asyncMiddleware(remotePodsList) | 26 | // asyncMiddleware(remotePodsList) |
27 | ) | 27 | // ) |
28 | 28 | // | |
29 | remotePodsRouter.post('/add', | 29 | // remotePodsRouter.post('/add', |
30 | setBodyHostPort, // We need to modify the host before running the validator! | 30 | // setBodyHostPort, // We need to modify the host before running the validator! |
31 | remotePodsAddValidator, | 31 | // remotePodsAddValidator, |
32 | asyncMiddleware(addPods) | 32 | // asyncMiddleware(addPods) |
33 | ) | 33 | // ) |
34 | 34 | // | |
35 | // --------------------------------------------------------------------------- | 35 | // // --------------------------------------------------------------------------- |
36 | 36 | // | |
37 | export { | 37 | // export { |
38 | remotePodsRouter | 38 | // remotePodsRouter |
39 | } | 39 | // } |
40 | 40 | // | |
41 | // --------------------------------------------------------------------------- | 41 | // // --------------------------------------------------------------------------- |
42 | 42 | // | |
43 | async function addPods (req: express.Request, res: express.Response, next: express.NextFunction) { | 43 | // async function addPods (req: express.Request, res: express.Response, next: express.NextFunction) { |
44 | const information = req.body | 44 | // const information = req.body |
45 | 45 | // | |
46 | const pod = db.Pod.build(information) | 46 | // const pod = db.Pod.build(information) |
47 | const podCreated = await pod.save() | 47 | // const podCreated = await pod.save() |
48 | 48 | // | |
49 | await sendOwnedDataToPod(podCreated.id) | 49 | // await sendOwnedDataToPod(podCreated.id) |
50 | 50 | // | |
51 | const cert = await getMyPublicCert() | 51 | // const cert = await getMyPublicCert() |
52 | return res.json({ cert, email: CONFIG.ADMIN.EMAIL }) | 52 | // return res.json({ cert, email: CONFIG.ADMIN.EMAIL }) |
53 | } | 53 | // } |
54 | 54 | // | |
55 | async function remotePodsList (req: express.Request, res: express.Response, next: express.NextFunction) { | 55 | // async function remotePodsList (req: express.Request, res: express.Response, next: express.NextFunction) { |
56 | const pods = await db.Pod.list() | 56 | // const pods = await db.Pod.list() |
57 | 57 | // | |
58 | return res.json(getFormattedObjects<FormattedPod, PodInstance>(pods, pods.length)) | 58 | // return res.json(getFormattedObjects<FormattedPod, PodInstance>(pods, pods.length)) |
59 | } | 59 | // } |
60 | 60 | // | |
61 | async function removePods (req: express.Request, res: express.Response, next: express.NextFunction) { | 61 | // async function removePods (req: express.Request, res: express.Response, next: express.NextFunction) { |
62 | const signature: PodSignature = req.body.signature | 62 | // const signature: PodSignature = req.body.signature |
63 | const host = signature.host | 63 | // const host = signature.host |
64 | 64 | // | |
65 | const pod = await db.Pod.loadByHost(host) | 65 | // const pod = await db.Pod.loadByHost(host) |
66 | await pod.destroy() | 66 | // await pod.destroy() |
67 | 67 | // | |
68 | return res.type('json').status(204).end() | 68 | // return res.type('json').status(204).end() |
69 | } | 69 | // } |
diff --git a/server/controllers/activitypub/videos.ts b/server/controllers/activitypub/videos.ts index cba47f0a1..9a1868ff7 100644 --- a/server/controllers/activitypub/videos.ts +++ b/server/controllers/activitypub/videos.ts | |||
@@ -1,589 +1,339 @@ | |||
1 | import * as express from 'express' | 1 | // import * as express from 'express' |
2 | import * as Bluebird from 'bluebird' | 2 | // import * as Bluebird from 'bluebird' |
3 | import * as Sequelize from 'sequelize' | 3 | // import * as Sequelize from 'sequelize' |
4 | 4 | // | |
5 | import { database as db } from '../../../initializers/database' | 5 | // import { database as db } from '../../../initializers/database' |
6 | import { | 6 | // import { |
7 | REQUEST_ENDPOINT_ACTIONS, | 7 | // REQUEST_ENDPOINT_ACTIONS, |
8 | REQUEST_ENDPOINTS, | 8 | // REQUEST_ENDPOINTS, |
9 | REQUEST_VIDEO_EVENT_TYPES, | 9 | // REQUEST_VIDEO_EVENT_TYPES, |
10 | REQUEST_VIDEO_QADU_TYPES | 10 | // REQUEST_VIDEO_QADU_TYPES |
11 | } from '../../../initializers' | 11 | // } from '../../../initializers' |
12 | import { | 12 | // import { |
13 | checkSignature, | 13 | // checkSignature, |
14 | signatureValidator, | 14 | // signatureValidator, |
15 | remoteVideosValidator, | 15 | // remoteVideosValidator, |
16 | remoteQaduVideosValidator, | 16 | // remoteQaduVideosValidator, |
17 | remoteEventsVideosValidator | 17 | // remoteEventsVideosValidator |
18 | } from '../../../middlewares' | 18 | // } from '../../../middlewares' |
19 | import { logger, retryTransactionWrapper, resetSequelizeInstance } from '../../../helpers' | 19 | // import { logger, retryTransactionWrapper, resetSequelizeInstance } from '../../../helpers' |
20 | import { quickAndDirtyUpdatesVideoToFriends, fetchVideoChannelByHostAndUUID } from '../../../lib' | 20 | // import { quickAndDirtyUpdatesVideoToFriends, fetchVideoChannelByHostAndUUID } from '../../../lib' |
21 | import { PodInstance, VideoFileInstance } from '../../../models' | 21 | // import { PodInstance, VideoFileInstance } from '../../../models' |
22 | import { | 22 | // import { |
23 | RemoteVideoRequest, | 23 | // RemoteVideoRequest, |
24 | RemoteVideoCreateData, | 24 | // RemoteVideoCreateData, |
25 | RemoteVideoUpdateData, | 25 | // RemoteVideoUpdateData, |
26 | RemoteVideoRemoveData, | 26 | // RemoteVideoRemoveData, |
27 | RemoteVideoReportAbuseData, | 27 | // RemoteVideoReportAbuseData, |
28 | RemoteQaduVideoRequest, | 28 | // RemoteQaduVideoRequest, |
29 | RemoteQaduVideoData, | 29 | // RemoteQaduVideoData, |
30 | RemoteVideoEventRequest, | 30 | // RemoteVideoEventRequest, |
31 | RemoteVideoEventData, | 31 | // RemoteVideoEventData, |
32 | RemoteVideoChannelCreateData, | 32 | // RemoteVideoChannelCreateData, |
33 | RemoteVideoChannelUpdateData, | 33 | // RemoteVideoChannelUpdateData, |
34 | RemoteVideoChannelRemoveData, | 34 | // RemoteVideoChannelRemoveData, |
35 | RemoteVideoAuthorRemoveData, | 35 | // RemoteVideoAuthorRemoveData, |
36 | RemoteVideoAuthorCreateData | 36 | // RemoteVideoAuthorCreateData |
37 | } from '../../../../shared' | 37 | // } from '../../../../shared' |
38 | import { VideoInstance } from '../../../models/video/video-interface' | 38 | // import { VideoInstance } from '../../../models/video/video-interface' |
39 | 39 | // | |
40 | const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS] | 40 | // const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS] |
41 | 41 | // | |
42 | // Functions to call when processing a remote request | 42 | // // Functions to call when processing a remote request |
43 | // FIXME: use RemoteVideoRequestType as id type | 43 | // // FIXME: use RemoteVideoRequestType as id type |
44 | const functionsHash: { [ id: string ]: (...args) => Promise<any> } = {} | 44 | // const functionsHash: { [ id: string ]: (...args) => Promise<any> } = {} |
45 | functionsHash[ENDPOINT_ACTIONS.ADD_VIDEO] = addRemoteVideoRetryWrapper | 45 | // functionsHash[ENDPOINT_ACTIONS.ADD_VIDEO] = addRemoteVideoRetryWrapper |
46 | functionsHash[ENDPOINT_ACTIONS.UPDATE_VIDEO] = updateRemoteVideoRetryWrapper | 46 | // functionsHash[ENDPOINT_ACTIONS.UPDATE_VIDEO] = updateRemoteVideoRetryWrapper |
47 | functionsHash[ENDPOINT_ACTIONS.REMOVE_VIDEO] = removeRemoteVideoRetryWrapper | 47 | // functionsHash[ENDPOINT_ACTIONS.REMOVE_VIDEO] = removeRemoteVideoRetryWrapper |
48 | functionsHash[ENDPOINT_ACTIONS.ADD_CHANNEL] = addRemoteVideoChannelRetryWrapper | 48 | // functionsHash[ENDPOINT_ACTIONS.ADD_CHANNEL] = addRemoteVideoChannelRetryWrapper |
49 | functionsHash[ENDPOINT_ACTIONS.UPDATE_CHANNEL] = updateRemoteVideoChannelRetryWrapper | 49 | // functionsHash[ENDPOINT_ACTIONS.UPDATE_CHANNEL] = updateRemoteVideoChannelRetryWrapper |
50 | functionsHash[ENDPOINT_ACTIONS.REMOVE_CHANNEL] = removeRemoteVideoChannelRetryWrapper | 50 | // functionsHash[ENDPOINT_ACTIONS.REMOVE_CHANNEL] = removeRemoteVideoChannelRetryWrapper |
51 | functionsHash[ENDPOINT_ACTIONS.REPORT_ABUSE] = reportAbuseRemoteVideoRetryWrapper | 51 | // functionsHash[ENDPOINT_ACTIONS.REPORT_ABUSE] = reportAbuseRemoteVideoRetryWrapper |
52 | functionsHash[ENDPOINT_ACTIONS.ADD_AUTHOR] = addRemoteVideoAuthorRetryWrapper | 52 | // functionsHash[ENDPOINT_ACTIONS.ADD_AUTHOR] = addRemoteVideoAuthorRetryWrapper |
53 | functionsHash[ENDPOINT_ACTIONS.REMOVE_AUTHOR] = removeRemoteVideoAuthorRetryWrapper | 53 | // functionsHash[ENDPOINT_ACTIONS.REMOVE_AUTHOR] = removeRemoteVideoAuthorRetryWrapper |
54 | 54 | // | |
55 | const remoteVideosRouter = express.Router() | 55 | // const remoteVideosRouter = express.Router() |
56 | 56 | // | |
57 | remoteVideosRouter.post('/', | 57 | // remoteVideosRouter.post('/', |
58 | signatureValidator, | 58 | // signatureValidator, |
59 | checkSignature, | 59 | // checkSignature, |
60 | remoteVideosValidator, | 60 | // remoteVideosValidator, |
61 | remoteVideos | 61 | // remoteVideos |
62 | ) | 62 | // ) |
63 | 63 | // | |
64 | remoteVideosRouter.post('/qadu', | 64 | // remoteVideosRouter.post('/qadu', |
65 | signatureValidator, | 65 | // signatureValidator, |
66 | checkSignature, | 66 | // checkSignature, |
67 | remoteQaduVideosValidator, | 67 | // remoteQaduVideosValidator, |
68 | remoteVideosQadu | 68 | // remoteVideosQadu |
69 | ) | 69 | // ) |
70 | 70 | // | |
71 | remoteVideosRouter.post('/events', | 71 | // remoteVideosRouter.post('/events', |
72 | signatureValidator, | 72 | // signatureValidator, |
73 | checkSignature, | 73 | // checkSignature, |
74 | remoteEventsVideosValidator, | 74 | // remoteEventsVideosValidator, |
75 | remoteVideosEvents | 75 | // remoteVideosEvents |
76 | ) | 76 | // ) |
77 | 77 | // | |
78 | // --------------------------------------------------------------------------- | 78 | // // --------------------------------------------------------------------------- |
79 | 79 | // | |
80 | export { | 80 | // export { |
81 | remoteVideosRouter | 81 | // remoteVideosRouter |
82 | } | 82 | // } |
83 | 83 | // | |
84 | // --------------------------------------------------------------------------- | 84 | // // --------------------------------------------------------------------------- |
85 | 85 | // | |
86 | function remoteVideos (req: express.Request, res: express.Response, next: express.NextFunction) { | 86 | // function remoteVideos (req: express.Request, res: express.Response, next: express.NextFunction) { |
87 | const requests: RemoteVideoRequest[] = req.body.data | 87 | // const requests: RemoteVideoRequest[] = req.body.data |
88 | const fromPod = res.locals.secure.pod | 88 | // const fromPod = res.locals.secure.pod |
89 | 89 | // | |
90 | // We need to process in the same order to keep consistency | 90 | // // We need to process in the same order to keep consistency |
91 | Bluebird.each(requests, request => { | 91 | // Bluebird.each(requests, request => { |
92 | const data = request.data | 92 | // const data = request.data |
93 | 93 | // | |
94 | // Get the function we need to call in order to process the request | 94 | // // Get the function we need to call in order to process the request |
95 | const fun = functionsHash[request.type] | 95 | // const fun = functionsHash[request.type] |
96 | if (fun === undefined) { | 96 | // if (fun === undefined) { |
97 | logger.error('Unknown remote request type %s.', request.type) | 97 | // logger.error('Unknown remote request type %s.', request.type) |
98 | return | 98 | // return |
99 | } | 99 | // } |
100 | 100 | // | |
101 | return fun.call(this, data, fromPod) | 101 | // return fun.call(this, data, fromPod) |
102 | }) | 102 | // }) |
103 | .catch(err => logger.error('Error managing remote videos.', err)) | 103 | // .catch(err => logger.error('Error managing remote videos.', err)) |
104 | 104 | // | |
105 | // Don't block the other pod | 105 | // // Don't block the other pod |
106 | return res.type('json').status(204).end() | 106 | // return res.type('json').status(204).end() |
107 | } | 107 | // } |
108 | 108 | // | |
109 | function remoteVideosQadu (req: express.Request, res: express.Response, next: express.NextFunction) { | 109 | // function remoteVideosQadu (req: express.Request, res: express.Response, next: express.NextFunction) { |
110 | const requests: RemoteQaduVideoRequest[] = req.body.data | 110 | // const requests: RemoteQaduVideoRequest[] = req.body.data |
111 | const fromPod = res.locals.secure.pod | 111 | // const fromPod = res.locals.secure.pod |
112 | 112 | // | |
113 | Bluebird.each(requests, request => { | 113 | // Bluebird.each(requests, request => { |
114 | const videoData = request.data | 114 | // const videoData = request.data |
115 | 115 | // | |
116 | return quickAndDirtyUpdateVideoRetryWrapper(videoData, fromPod) | 116 | // return quickAndDirtyUpdateVideoRetryWrapper(videoData, fromPod) |
117 | }) | 117 | // }) |
118 | .catch(err => logger.error('Error managing remote videos.', err)) | 118 | // .catch(err => logger.error('Error managing remote videos.', err)) |
119 | 119 | // | |
120 | return res.type('json').status(204).end() | 120 | // return res.type('json').status(204).end() |
121 | } | 121 | // } |
122 | 122 | // | |
123 | function remoteVideosEvents (req: express.Request, res: express.Response, next: express.NextFunction) { | 123 | // function remoteVideosEvents (req: express.Request, res: express.Response, next: express.NextFunction) { |
124 | const requests: RemoteVideoEventRequest[] = req.body.data | 124 | // const requests: RemoteVideoEventRequest[] = req.body.data |
125 | const fromPod = res.locals.secure.pod | 125 | // const fromPod = res.locals.secure.pod |
126 | 126 | // | |
127 | Bluebird.each(requests, request => { | 127 | // Bluebird.each(requests, request => { |
128 | const eventData = request.data | 128 | // const eventData = request.data |
129 | 129 | // | |
130 | return processVideosEventsRetryWrapper(eventData, fromPod) | 130 | // return processVideosEventsRetryWrapper(eventData, fromPod) |
131 | }) | 131 | // }) |
132 | .catch(err => logger.error('Error managing remote videos.', err)) | 132 | // .catch(err => logger.error('Error managing remote videos.', err)) |
133 | 133 | // | |
134 | return res.type('json').status(204).end() | 134 | // return res.type('json').status(204).end() |
135 | } | 135 | // } |
136 | 136 | // | |
137 | async function processVideosEventsRetryWrapper (eventData: RemoteVideoEventData, fromPod: PodInstance) { | 137 | // async function processVideosEventsRetryWrapper (eventData: RemoteVideoEventData, fromPod: PodInstance) { |
138 | const options = { | 138 | // const options = { |
139 | arguments: [ eventData, fromPod ], | 139 | // arguments: [ eventData, fromPod ], |
140 | errorMessage: 'Cannot process videos events with many retries.' | 140 | // errorMessage: 'Cannot process videos events with many retries.' |
141 | } | 141 | // } |
142 | 142 | // | |
143 | await retryTransactionWrapper(processVideosEvents, options) | 143 | // await retryTransactionWrapper(processVideosEvents, options) |
144 | } | 144 | // } |
145 | 145 | // | |
146 | async function processVideosEvents (eventData: RemoteVideoEventData, fromPod: PodInstance) { | 146 | // async function processVideosEvents (eventData: RemoteVideoEventData, fromPod: PodInstance) { |
147 | await db.sequelize.transaction(async t => { | 147 | // await db.sequelize.transaction(async t => { |
148 | const sequelizeOptions = { transaction: t } | 148 | // const sequelizeOptions = { transaction: t } |
149 | const videoInstance = await fetchLocalVideoByUUID(eventData.uuid, t) | 149 | // const videoInstance = await fetchLocalVideoByUUID(eventData.uuid, t) |
150 | 150 | // | |
151 | let columnToUpdate | 151 | // let columnToUpdate |
152 | let qaduType | 152 | // let qaduType |
153 | 153 | // | |
154 | switch (eventData.eventType) { | 154 | // switch (eventData.eventType) { |
155 | case REQUEST_VIDEO_EVENT_TYPES.VIEWS: | 155 | // case REQUEST_VIDEO_EVENT_TYPES.VIEWS: |
156 | columnToUpdate = 'views' | 156 | // columnToUpdate = 'views' |
157 | qaduType = REQUEST_VIDEO_QADU_TYPES.VIEWS | 157 | // qaduType = REQUEST_VIDEO_QADU_TYPES.VIEWS |
158 | break | 158 | // break |
159 | 159 | // | |
160 | case REQUEST_VIDEO_EVENT_TYPES.LIKES: | 160 | // case REQUEST_VIDEO_EVENT_TYPES.LIKES: |
161 | columnToUpdate = 'likes' | 161 | // columnToUpdate = 'likes' |
162 | qaduType = REQUEST_VIDEO_QADU_TYPES.LIKES | 162 | // qaduType = REQUEST_VIDEO_QADU_TYPES.LIKES |
163 | break | 163 | // break |
164 | 164 | // | |
165 | case REQUEST_VIDEO_EVENT_TYPES.DISLIKES: | 165 | // case REQUEST_VIDEO_EVENT_TYPES.DISLIKES: |
166 | columnToUpdate = 'dislikes' | 166 | // columnToUpdate = 'dislikes' |
167 | qaduType = REQUEST_VIDEO_QADU_TYPES.DISLIKES | 167 | // qaduType = REQUEST_VIDEO_QADU_TYPES.DISLIKES |
168 | break | 168 | // break |
169 | 169 | // | |
170 | default: | 170 | // default: |
171 | throw new Error('Unknown video event type.') | 171 | // throw new Error('Unknown video event type.') |
172 | } | 172 | // } |
173 | 173 | // | |
174 | const query = {} | 174 | // const query = {} |
175 | query[columnToUpdate] = eventData.count | 175 | // query[columnToUpdate] = eventData.count |
176 | 176 | // | |
177 | await videoInstance.increment(query, sequelizeOptions) | 177 | // await videoInstance.increment(query, sequelizeOptions) |
178 | 178 | // | |
179 | const qadusParams = [ | 179 | // const qadusParams = [ |
180 | { | 180 | // { |
181 | videoId: videoInstance.id, | 181 | // videoId: videoInstance.id, |
182 | type: qaduType | 182 | // type: qaduType |
183 | } | 183 | // } |
184 | ] | 184 | // ] |
185 | await quickAndDirtyUpdatesVideoToFriends(qadusParams, t) | 185 | // await quickAndDirtyUpdatesVideoToFriends(qadusParams, t) |
186 | }) | 186 | // }) |
187 | 187 | // | |
188 | logger.info('Remote video event processed for video with uuid %s.', eventData.uuid) | 188 | // logger.info('Remote video event processed for video with uuid %s.', eventData.uuid) |
189 | } | 189 | // } |
190 | 190 | // | |
191 | async function quickAndDirtyUpdateVideoRetryWrapper (videoData: RemoteQaduVideoData, fromPod: PodInstance) { | 191 | // async function quickAndDirtyUpdateVideoRetryWrapper (videoData: RemoteQaduVideoData, fromPod: PodInstance) { |
192 | const options = { | 192 | // const options = { |
193 | arguments: [ videoData, fromPod ], | 193 | // arguments: [ videoData, fromPod ], |
194 | errorMessage: 'Cannot update quick and dirty the remote video with many retries.' | 194 | // errorMessage: 'Cannot update quick and dirty the remote video with many retries.' |
195 | } | 195 | // } |
196 | 196 | // | |
197 | await retryTransactionWrapper(quickAndDirtyUpdateVideo, options) | 197 | // await retryTransactionWrapper(quickAndDirtyUpdateVideo, options) |
198 | } | 198 | // } |
199 | 199 | // | |
200 | async function quickAndDirtyUpdateVideo (videoData: RemoteQaduVideoData, fromPod: PodInstance) { | 200 | // async function quickAndDirtyUpdateVideo (videoData: RemoteQaduVideoData, fromPod: PodInstance) { |
201 | let videoUUID = '' | 201 | // let videoUUID = '' |
202 | 202 | // | |
203 | await db.sequelize.transaction(async t => { | 203 | // await db.sequelize.transaction(async t => { |
204 | const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoData.uuid, t) | 204 | // const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoData.uuid, t) |
205 | const sequelizeOptions = { transaction: t } | 205 | // const sequelizeOptions = { transaction: t } |
206 | 206 | // | |
207 | videoUUID = videoInstance.uuid | 207 | // videoUUID = videoInstance.uuid |
208 | 208 | // | |
209 | if (videoData.views) { | 209 | // if (videoData.views) { |
210 | videoInstance.set('views', videoData.views) | 210 | // videoInstance.set('views', videoData.views) |
211 | } | 211 | // } |
212 | 212 | // | |
213 | if (videoData.likes) { | 213 | // if (videoData.likes) { |
214 | videoInstance.set('likes', videoData.likes) | 214 | // videoInstance.set('likes', videoData.likes) |
215 | } | 215 | // } |
216 | 216 | // | |
217 | if (videoData.dislikes) { | 217 | // if (videoData.dislikes) { |
218 | videoInstance.set('dislikes', videoData.dislikes) | 218 | // videoInstance.set('dislikes', videoData.dislikes) |
219 | } | 219 | // } |
220 | 220 | // | |
221 | await videoInstance.save(sequelizeOptions) | 221 | // await videoInstance.save(sequelizeOptions) |
222 | }) | 222 | // }) |
223 | 223 | // | |
224 | logger.info('Remote video with uuid %s quick and dirty updated', videoUUID) | 224 | // logger.info('Remote video with uuid %s quick and dirty updated', videoUUID) |
225 | } | 225 | // } |
226 | 226 | // | |
227 | // Handle retries on fail | 227 | // async function removeRemoteVideoRetryWrapper (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) { |
228 | async function addRemoteVideoRetryWrapper (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) { | 228 | // const options = { |
229 | const options = { | 229 | // arguments: [ videoToRemoveData, fromPod ], |
230 | arguments: [ videoToCreateData, fromPod ], | 230 | // errorMessage: 'Cannot remove the remote video channel with many retries.' |
231 | errorMessage: 'Cannot insert the remote video with many retries.' | 231 | // } |
232 | } | 232 | // |
233 | 233 | // await retryTransactionWrapper(removeRemoteVideo, options) | |
234 | await retryTransactionWrapper(addRemoteVideo, options) | 234 | // } |
235 | } | 235 | // |
236 | 236 | // async function removeRemoteVideo (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) { | |
237 | async function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) { | 237 | // logger.debug('Removing remote video "%s".', videoToRemoveData.uuid) |
238 | logger.debug('Adding remote video "%s".', videoToCreateData.uuid) | 238 | // |
239 | 239 | // await db.sequelize.transaction(async t => { | |
240 | await db.sequelize.transaction(async t => { | 240 | // // We need the instance because we have to remove some other stuffs (thumbnail etc) |
241 | const sequelizeOptions = { | 241 | // const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoToRemoveData.uuid, t) |
242 | transaction: t | 242 | // await videoInstance.destroy({ transaction: t }) |
243 | } | 243 | // }) |
244 | 244 | // | |
245 | const videoFromDatabase = await db.Video.loadByUUID(videoToCreateData.uuid) | 245 | // logger.info('Remote video with uuid %s removed.', videoToRemoveData.uuid) |
246 | if (videoFromDatabase) throw new Error('UUID already exists.') | 246 | // } |
247 | 247 | // | |
248 | const videoChannel = await db.VideoChannel.loadByHostAndUUID(fromPod.host, videoToCreateData.channelUUID, t) | 248 | // async function removeRemoteVideoAuthorRetryWrapper (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) { |
249 | if (!videoChannel) throw new Error('Video channel ' + videoToCreateData.channelUUID + ' not found.') | 249 | // const options = { |
250 | 250 | // arguments: [ authorAttributesToRemove, fromPod ], | |
251 | const tags = videoToCreateData.tags | 251 | // errorMessage: 'Cannot remove the remote video author with many retries.' |
252 | const tagInstances = await db.Tag.findOrCreateTags(tags, t) | 252 | // } |
253 | 253 | // | |
254 | const videoData = { | 254 | // await retryTransactionWrapper(removeRemoteVideoAuthor, options) |
255 | name: videoToCreateData.name, | 255 | // } |
256 | uuid: videoToCreateData.uuid, | 256 | // |
257 | category: videoToCreateData.category, | 257 | // async function removeRemoteVideoAuthor (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) { |
258 | licence: videoToCreateData.licence, | 258 | // logger.debug('Removing remote video author "%s".', authorAttributesToRemove.uuid) |
259 | language: videoToCreateData.language, | 259 | // |
260 | nsfw: videoToCreateData.nsfw, | 260 | // await db.sequelize.transaction(async t => { |
261 | description: videoToCreateData.truncatedDescription, | 261 | // const videoAuthor = await db.Author.loadAuthorByPodAndUUID(authorAttributesToRemove.uuid, fromPod.id, t) |
262 | channelId: videoChannel.id, | 262 | // await videoAuthor.destroy({ transaction: t }) |
263 | duration: videoToCreateData.duration, | 263 | // }) |
264 | createdAt: videoToCreateData.createdAt, | 264 | // |
265 | // FIXME: updatedAt does not seems to be considered by Sequelize | 265 | // logger.info('Remote video author with uuid %s removed.', authorAttributesToRemove.uuid) |
266 | updatedAt: videoToCreateData.updatedAt, | 266 | // } |
267 | views: videoToCreateData.views, | 267 | // |
268 | likes: videoToCreateData.likes, | 268 | // async function removeRemoteVideoChannelRetryWrapper (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) { |
269 | dislikes: videoToCreateData.dislikes, | 269 | // const options = { |
270 | remote: true, | 270 | // arguments: [ videoChannelAttributesToRemove, fromPod ], |
271 | privacy: videoToCreateData.privacy | 271 | // errorMessage: 'Cannot remove the remote video channel with many retries.' |
272 | } | 272 | // } |
273 | 273 | // | |
274 | const video = db.Video.build(videoData) | 274 | // await retryTransactionWrapper(removeRemoteVideoChannel, options) |
275 | await db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData) | 275 | // } |
276 | const videoCreated = await video.save(sequelizeOptions) | 276 | // |
277 | 277 | // async function removeRemoteVideoChannel (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) { | |
278 | const tasks = [] | 278 | // logger.debug('Removing remote video channel "%s".', videoChannelAttributesToRemove.uuid) |
279 | for (const fileData of videoToCreateData.files) { | 279 | // |
280 | const videoFileInstance = db.VideoFile.build({ | 280 | // await db.sequelize.transaction(async t => { |
281 | extname: fileData.extname, | 281 | // const videoChannel = await fetchVideoChannelByHostAndUUID(fromPod.host, videoChannelAttributesToRemove.uuid, t) |
282 | infoHash: fileData.infoHash, | 282 | // await videoChannel.destroy({ transaction: t }) |
283 | resolution: fileData.resolution, | 283 | // }) |
284 | size: fileData.size, | 284 | // |
285 | videoId: videoCreated.id | 285 | // logger.info('Remote video channel with uuid %s removed.', videoChannelAttributesToRemove.uuid) |
286 | }) | 286 | // } |
287 | 287 | // | |
288 | tasks.push(videoFileInstance.save(sequelizeOptions)) | 288 | // async function reportAbuseRemoteVideoRetryWrapper (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) { |
289 | } | 289 | // const options = { |
290 | 290 | // arguments: [ reportData, fromPod ], | |
291 | await Promise.all(tasks) | 291 | // errorMessage: 'Cannot create remote abuse video with many retries.' |
292 | 292 | // } | |
293 | await videoCreated.setTags(tagInstances, sequelizeOptions) | 293 | // |
294 | }) | 294 | // await retryTransactionWrapper(reportAbuseRemoteVideo, options) |
295 | 295 | // } | |
296 | logger.info('Remote video with uuid %s inserted.', videoToCreateData.uuid) | 296 | // |
297 | } | 297 | // async function reportAbuseRemoteVideo (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) { |
298 | 298 | // logger.debug('Reporting remote abuse for video %s.', reportData.videoUUID) | |
299 | // Handle retries on fail | 299 | // |
300 | async function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) { | 300 | // await db.sequelize.transaction(async t => { |
301 | const options = { | 301 | // const videoInstance = await fetchLocalVideoByUUID(reportData.videoUUID, t) |
302 | arguments: [ videoAttributesToUpdate, fromPod ], | 302 | // const videoAbuseData = { |
303 | errorMessage: 'Cannot update the remote video with many retries' | 303 | // reporterUsername: reportData.reporterUsername, |
304 | } | 304 | // reason: reportData.reportReason, |
305 | 305 | // reporterPodId: fromPod.id, | |
306 | await retryTransactionWrapper(updateRemoteVideo, options) | 306 | // videoId: videoInstance.id |
307 | } | 307 | // } |
308 | 308 | // | |
309 | async function updateRemoteVideo (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) { | 309 | // await db.VideoAbuse.create(videoAbuseData) |
310 | logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid) | 310 | // |
311 | let videoInstance: VideoInstance | 311 | // }) |
312 | let videoFieldsSave: object | 312 | // |
313 | 313 | // logger.info('Remote abuse for video uuid %s created', reportData.videoUUID) | |
314 | try { | 314 | // } |
315 | await db.sequelize.transaction(async t => { | 315 | // |
316 | const sequelizeOptions = { | 316 | // async function fetchLocalVideoByUUID (id: string, t: Sequelize.Transaction) { |
317 | transaction: t | 317 | // try { |
318 | } | 318 | // const video = await db.Video.loadLocalVideoByUUID(id, t) |
319 | 319 | // | |
320 | const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoAttributesToUpdate.uuid, t) | 320 | // if (!video) throw new Error('Video ' + id + ' not found') |
321 | videoFieldsSave = videoInstance.toJSON() | 321 | // |
322 | const tags = videoAttributesToUpdate.tags | 322 | // return video |
323 | 323 | // } catch (err) { | |
324 | const tagInstances = await db.Tag.findOrCreateTags(tags, t) | 324 | // logger.error('Cannot load owned video from id.', { error: err.stack, id }) |
325 | 325 | // throw err | |
326 | videoInstance.set('name', videoAttributesToUpdate.name) | 326 | // } |
327 | videoInstance.set('category', videoAttributesToUpdate.category) | 327 | // } |
328 | videoInstance.set('licence', videoAttributesToUpdate.licence) | 328 | // |
329 | videoInstance.set('language', videoAttributesToUpdate.language) | 329 | // async function fetchVideoByHostAndUUID (podHost: string, uuid: string, t: Sequelize.Transaction) { |
330 | videoInstance.set('nsfw', videoAttributesToUpdate.nsfw) | 330 | // try { |
331 | videoInstance.set('description', videoAttributesToUpdate.truncatedDescription) | 331 | // const video = await db.Video.loadByHostAndUUID(podHost, uuid, t) |
332 | videoInstance.set('duration', videoAttributesToUpdate.duration) | 332 | // if (!video) throw new Error('Video not found') |
333 | videoInstance.set('createdAt', videoAttributesToUpdate.createdAt) | 333 | // |
334 | videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt) | 334 | // return video |
335 | videoInstance.set('views', videoAttributesToUpdate.views) | 335 | // } catch (err) { |
336 | videoInstance.set('likes', videoAttributesToUpdate.likes) | 336 | // logger.error('Cannot load video from host and uuid.', { error: err.stack, podHost, uuid }) |
337 | videoInstance.set('dislikes', videoAttributesToUpdate.dislikes) | 337 | // throw err |
338 | videoInstance.set('privacy', videoAttributesToUpdate.privacy) | 338 | // } |
339 | 339 | // } | |
340 | await videoInstance.save(sequelizeOptions) | ||
341 | |||
342 | // Remove old video files | ||
343 | const videoFileDestroyTasks: Bluebird<void>[] = [] | ||
344 | for (const videoFile of videoInstance.VideoFiles) { | ||
345 | videoFileDestroyTasks.push(videoFile.destroy(sequelizeOptions)) | ||
346 | } | ||
347 | await Promise.all(videoFileDestroyTasks) | ||
348 | |||
349 | const videoFileCreateTasks: Bluebird<VideoFileInstance>[] = [] | ||
350 | for (const fileData of videoAttributesToUpdate.files) { | ||
351 | const videoFileInstance = db.VideoFile.build({ | ||
352 | extname: fileData.extname, | ||
353 | infoHash: fileData.infoHash, | ||
354 | resolution: fileData.resolution, | ||
355 | size: fileData.size, | ||
356 | videoId: videoInstance.id | ||
357 | }) | ||
358 | |||
359 | videoFileCreateTasks.push(videoFileInstance.save(sequelizeOptions)) | ||
360 | } | ||
361 | |||
362 | await Promise.all(videoFileCreateTasks) | ||
363 | |||
364 | await videoInstance.setTags(tagInstances, sequelizeOptions) | ||
365 | }) | ||
366 | |||
367 | logger.info('Remote video with uuid %s updated', videoAttributesToUpdate.uuid) | ||
368 | } catch (err) { | ||
369 | if (videoInstance !== undefined && videoFieldsSave !== undefined) { | ||
370 | resetSequelizeInstance(videoInstance, videoFieldsSave) | ||
371 | } | ||
372 | |||
373 | // This is just a debug because we will retry the insert | ||
374 | logger.debug('Cannot update the remote video.', err) | ||
375 | throw err | ||
376 | } | ||
377 | } | ||
378 | |||
379 | async function removeRemoteVideoRetryWrapper (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) { | ||
380 | const options = { | ||
381 | arguments: [ videoToRemoveData, fromPod ], | ||
382 | errorMessage: 'Cannot remove the remote video channel with many retries.' | ||
383 | } | ||
384 | |||
385 | await retryTransactionWrapper(removeRemoteVideo, options) | ||
386 | } | ||
387 | |||
388 | async function removeRemoteVideo (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) { | ||
389 | logger.debug('Removing remote video "%s".', videoToRemoveData.uuid) | ||
390 | |||
391 | await db.sequelize.transaction(async t => { | ||
392 | // We need the instance because we have to remove some other stuffs (thumbnail etc) | ||
393 | const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoToRemoveData.uuid, t) | ||
394 | await videoInstance.destroy({ transaction: t }) | ||
395 | }) | ||
396 | |||
397 | logger.info('Remote video with uuid %s removed.', videoToRemoveData.uuid) | ||
398 | } | ||
399 | |||
400 | async function addRemoteVideoAuthorRetryWrapper (authorToCreateData: RemoteVideoAuthorCreateData, fromPod: PodInstance) { | ||
401 | const options = { | ||
402 | arguments: [ authorToCreateData, fromPod ], | ||
403 | errorMessage: 'Cannot insert the remote video author with many retries.' | ||
404 | } | ||
405 | |||
406 | await retryTransactionWrapper(addRemoteVideoAuthor, options) | ||
407 | } | ||
408 | |||
409 | async function addRemoteVideoAuthor (authorToCreateData: RemoteVideoAuthorCreateData, fromPod: PodInstance) { | ||
410 | logger.debug('Adding remote video author "%s".', authorToCreateData.uuid) | ||
411 | |||
412 | await db.sequelize.transaction(async t => { | ||
413 | const authorInDatabase = await db.Author.loadAuthorByPodAndUUID(authorToCreateData.uuid, fromPod.id, t) | ||
414 | if (authorInDatabase) throw new Error('Author with UUID ' + authorToCreateData.uuid + ' already exists.') | ||
415 | |||
416 | const videoAuthorData = { | ||
417 | name: authorToCreateData.name, | ||
418 | uuid: authorToCreateData.uuid, | ||
419 | userId: null, // Not on our pod | ||
420 | podId: fromPod.id | ||
421 | } | ||
422 | |||
423 | const author = db.Author.build(videoAuthorData) | ||
424 | await author.save({ transaction: t }) | ||
425 | }) | ||
426 | |||
427 | logger.info('Remote video author with uuid %s inserted.', authorToCreateData.uuid) | ||
428 | } | ||
429 | |||
430 | async function removeRemoteVideoAuthorRetryWrapper (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) { | ||
431 | const options = { | ||
432 | arguments: [ authorAttributesToRemove, fromPod ], | ||
433 | errorMessage: 'Cannot remove the remote video author with many retries.' | ||
434 | } | ||
435 | |||
436 | await retryTransactionWrapper(removeRemoteVideoAuthor, options) | ||
437 | } | ||
438 | |||
439 | async function removeRemoteVideoAuthor (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) { | ||
440 | logger.debug('Removing remote video author "%s".', authorAttributesToRemove.uuid) | ||
441 | |||
442 | await db.sequelize.transaction(async t => { | ||
443 | const videoAuthor = await db.Author.loadAuthorByPodAndUUID(authorAttributesToRemove.uuid, fromPod.id, t) | ||
444 | await videoAuthor.destroy({ transaction: t }) | ||
445 | }) | ||
446 | |||
447 | logger.info('Remote video author with uuid %s removed.', authorAttributesToRemove.uuid) | ||
448 | } | ||
449 | |||
450 | async function addRemoteVideoChannelRetryWrapper (videoChannelToCreateData: RemoteVideoChannelCreateData, fromPod: PodInstance) { | ||
451 | const options = { | ||
452 | arguments: [ videoChannelToCreateData, fromPod ], | ||
453 | errorMessage: 'Cannot insert the remote video channel with many retries.' | ||
454 | } | ||
455 | |||
456 | await retryTransactionWrapper(addRemoteVideoChannel, options) | ||
457 | } | ||
458 | |||
459 | async function addRemoteVideoChannel (videoChannelToCreateData: RemoteVideoChannelCreateData, fromPod: PodInstance) { | ||
460 | logger.debug('Adding remote video channel "%s".', videoChannelToCreateData.uuid) | ||
461 | |||
462 | await db.sequelize.transaction(async t => { | ||
463 | const videoChannelInDatabase = await db.VideoChannel.loadByUUID(videoChannelToCreateData.uuid) | ||
464 | if (videoChannelInDatabase) { | ||
465 | throw new Error('Video channel with UUID ' + videoChannelToCreateData.uuid + ' already exists.') | ||
466 | } | ||
467 | |||
468 | const authorUUID = videoChannelToCreateData.ownerUUID | ||
469 | const podId = fromPod.id | ||
470 | |||
471 | const author = await db.Author.loadAuthorByPodAndUUID(authorUUID, podId, t) | ||
472 | if (!author) throw new Error('Unknown author UUID' + authorUUID + '.') | ||
473 | |||
474 | const videoChannelData = { | ||
475 | name: videoChannelToCreateData.name, | ||
476 | description: videoChannelToCreateData.description, | ||
477 | uuid: videoChannelToCreateData.uuid, | ||
478 | createdAt: videoChannelToCreateData.createdAt, | ||
479 | updatedAt: videoChannelToCreateData.updatedAt, | ||
480 | remote: true, | ||
481 | authorId: author.id | ||
482 | } | ||
483 | |||
484 | const videoChannel = db.VideoChannel.build(videoChannelData) | ||
485 | await videoChannel.save({ transaction: t }) | ||
486 | }) | ||
487 | |||
488 | logger.info('Remote video channel with uuid %s inserted.', videoChannelToCreateData.uuid) | ||
489 | } | ||
490 | |||
491 | async function updateRemoteVideoChannelRetryWrapper (videoChannelAttributesToUpdate: RemoteVideoChannelUpdateData, fromPod: PodInstance) { | ||
492 | const options = { | ||
493 | arguments: [ videoChannelAttributesToUpdate, fromPod ], | ||
494 | errorMessage: 'Cannot update the remote video channel with many retries.' | ||
495 | } | ||
496 | |||
497 | await retryTransactionWrapper(updateRemoteVideoChannel, options) | ||
498 | } | ||
499 | |||
500 | async function updateRemoteVideoChannel (videoChannelAttributesToUpdate: RemoteVideoChannelUpdateData, fromPod: PodInstance) { | ||
501 | logger.debug('Updating remote video channel "%s".', videoChannelAttributesToUpdate.uuid) | ||
502 | |||
503 | await db.sequelize.transaction(async t => { | ||
504 | const sequelizeOptions = { transaction: t } | ||
505 | |||
506 | const videoChannelInstance = await fetchVideoChannelByHostAndUUID(fromPod.host, videoChannelAttributesToUpdate.uuid, t) | ||
507 | videoChannelInstance.set('name', videoChannelAttributesToUpdate.name) | ||
508 | videoChannelInstance.set('description', videoChannelAttributesToUpdate.description) | ||
509 | videoChannelInstance.set('createdAt', videoChannelAttributesToUpdate.createdAt) | ||
510 | videoChannelInstance.set('updatedAt', videoChannelAttributesToUpdate.updatedAt) | ||
511 | |||
512 | await videoChannelInstance.save(sequelizeOptions) | ||
513 | }) | ||
514 | |||
515 | logger.info('Remote video channel with uuid %s updated', videoChannelAttributesToUpdate.uuid) | ||
516 | } | ||
517 | |||
518 | async function removeRemoteVideoChannelRetryWrapper (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) { | ||
519 | const options = { | ||
520 | arguments: [ videoChannelAttributesToRemove, fromPod ], | ||
521 | errorMessage: 'Cannot remove the remote video channel with many retries.' | ||
522 | } | ||
523 | |||
524 | await retryTransactionWrapper(removeRemoteVideoChannel, options) | ||
525 | } | ||
526 | |||
527 | async function removeRemoteVideoChannel (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) { | ||
528 | logger.debug('Removing remote video channel "%s".', videoChannelAttributesToRemove.uuid) | ||
529 | |||
530 | await db.sequelize.transaction(async t => { | ||
531 | const videoChannel = await fetchVideoChannelByHostAndUUID(fromPod.host, videoChannelAttributesToRemove.uuid, t) | ||
532 | await videoChannel.destroy({ transaction: t }) | ||
533 | }) | ||
534 | |||
535 | logger.info('Remote video channel with uuid %s removed.', videoChannelAttributesToRemove.uuid) | ||
536 | } | ||
537 | |||
538 | async function reportAbuseRemoteVideoRetryWrapper (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) { | ||
539 | const options = { | ||
540 | arguments: [ reportData, fromPod ], | ||
541 | errorMessage: 'Cannot create remote abuse video with many retries.' | ||
542 | } | ||
543 | |||
544 | await retryTransactionWrapper(reportAbuseRemoteVideo, options) | ||
545 | } | ||
546 | |||
547 | async function reportAbuseRemoteVideo (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) { | ||
548 | logger.debug('Reporting remote abuse for video %s.', reportData.videoUUID) | ||
549 | |||
550 | await db.sequelize.transaction(async t => { | ||
551 | const videoInstance = await fetchLocalVideoByUUID(reportData.videoUUID, t) | ||
552 | const videoAbuseData = { | ||
553 | reporterUsername: reportData.reporterUsername, | ||
554 | reason: reportData.reportReason, | ||
555 | reporterPodId: fromPod.id, | ||
556 | videoId: videoInstance.id | ||
557 | } | ||
558 | |||
559 | await db.VideoAbuse.create(videoAbuseData) | ||
560 | |||
561 | }) | ||
562 | |||
563 | logger.info('Remote abuse for video uuid %s created', reportData.videoUUID) | ||
564 | } | ||
565 | |||
566 | async function fetchLocalVideoByUUID (id: string, t: Sequelize.Transaction) { | ||
567 | try { | ||
568 | const video = await db.Video.loadLocalVideoByUUID(id, t) | ||
569 | |||
570 | if (!video) throw new Error('Video ' + id + ' not found') | ||
571 | |||
572 | return video | ||
573 | } catch (err) { | ||
574 | logger.error('Cannot load owned video from id.', { error: err.stack, id }) | ||
575 | throw err | ||
576 | } | ||
577 | } | ||
578 | |||
579 | async function fetchVideoByHostAndUUID (podHost: string, uuid: string, t: Sequelize.Transaction) { | ||
580 | try { | ||
581 | const video = await db.Video.loadByHostAndUUID(podHost, uuid, t) | ||
582 | if (!video) throw new Error('Video not found') | ||
583 | |||
584 | return video | ||
585 | } catch (err) { | ||
586 | logger.error('Cannot load video from host and uuid.', { error: err.stack, podHost, uuid }) | ||
587 | throw err | ||
588 | } | ||
589 | } | ||
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 4dd09917b..964db151d 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts | |||
@@ -10,7 +10,8 @@ import { | |||
10 | VIDEO_CATEGORIES, | 10 | VIDEO_CATEGORIES, |
11 | VIDEO_LICENCES, | 11 | VIDEO_LICENCES, |
12 | VIDEO_LANGUAGES, | 12 | VIDEO_LANGUAGES, |
13 | VIDEO_PRIVACIES | 13 | VIDEO_PRIVACIES, |
14 | VIDEO_MIMETYPE_EXT | ||
14 | } from '../../../initializers' | 15 | } from '../../../initializers' |
15 | import { | 16 | import { |
16 | addEventToRemoteVideo, | 17 | addEventToRemoteVideo, |
@@ -50,6 +51,7 @@ import { abuseVideoRouter } from './abuse' | |||
50 | import { blacklistRouter } from './blacklist' | 51 | import { blacklistRouter } from './blacklist' |
51 | import { rateVideoRouter } from './rate' | 52 | import { rateVideoRouter } from './rate' |
52 | import { videoChannelRouter } from './channel' | 53 | import { videoChannelRouter } from './channel' |
54 | import { getActivityPubUrl } from '../../../helpers/activitypub' | ||
53 | 55 | ||
54 | const videosRouter = express.Router() | 56 | const videosRouter = express.Router() |
55 | 57 | ||
@@ -59,19 +61,18 @@ const storage = multer.diskStorage({ | |||
59 | cb(null, CONFIG.STORAGE.VIDEOS_DIR) | 61 | cb(null, CONFIG.STORAGE.VIDEOS_DIR) |
60 | }, | 62 | }, |
61 | 63 | ||
62 | filename: (req, file, cb) => { | 64 | filename: async (req, file, cb) => { |
63 | let extension = '' | 65 | const extension = VIDEO_MIMETYPE_EXT[file.mimetype] |
64 | if (file.mimetype === 'video/webm') extension = 'webm' | 66 | let randomString = '' |
65 | else if (file.mimetype === 'video/mp4') extension = 'mp4' | 67 | |
66 | else if (file.mimetype === 'video/ogg') extension = 'ogv' | 68 | try { |
67 | generateRandomString(16) | 69 | randomString = await generateRandomString(16) |
68 | .then(randomString => { | 70 | } catch (err) { |
69 | cb(null, randomString + '.' + extension) | 71 | logger.error('Cannot generate random string for file name.', err) |
70 | }) | 72 | randomString = 'fake-random-string' |
71 | .catch(err => { | 73 | } |
72 | logger.error('Cannot generate random string for file name.', err) | 74 | |
73 | throw err | 75 | cb(null, randomString + '.' + extension) |
74 | }) | ||
75 | } | 76 | } |
76 | }) | 77 | }) |
77 | 78 | ||
@@ -190,6 +191,7 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi | |||
190 | channelId: res.locals.videoChannel.id | 191 | channelId: res.locals.videoChannel.id |
191 | } | 192 | } |
192 | const video = db.Video.build(videoData) | 193 | const video = db.Video.build(videoData) |
194 | video.url = getActivityPubUrl('video', video.uuid) | ||
193 | 195 | ||
194 | const videoFilePath = join(CONFIG.STORAGE.VIDEOS_DIR, videoPhysicalFile.filename) | 196 | const videoFilePath = join(CONFIG.STORAGE.VIDEOS_DIR, videoPhysicalFile.filename) |
195 | const videoFileHeight = await getVideoFileHeight(videoFilePath) | 197 | const videoFileHeight = await getVideoFileHeight(videoFilePath) |