a775c858a12d5640bb943650577e43c2193510ec
[oweals/peertube.git] / server / lib / activitypub / process / misc.ts
1 import * as magnetUtil from 'magnet-uri'
2 import { VideoTorrentObject } from '../../../../shared'
3 import { VideoChannelObject } from '../../../../shared/models/activitypub/objects'
4 import { VideoPrivacy } from '../../../../shared/models/videos'
5 import { doRequest } from '../../../helpers'
6 import { isVideoFileInfoHashValid } from '../../../helpers/custom-validators/videos'
7 import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../../initializers'
8 import { AccountModel } from '../../../models/account/account'
9 import { VideoModel } from '../../../models/video/video'
10 import { VideoChannelModel } from '../../../models/video/video-channel'
11 import { VideoChannelShareModel } from '../../../models/video/video-channel-share'
12 import { VideoShareModel } from '../../../models/video/video-share'
13 import { getOrCreateAccountAndServer } from '../account'
14
15 function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountModel) {
16   return {
17     name: videoChannelObject.name,
18     description: videoChannelObject.content,
19     uuid: videoChannelObject.uuid,
20     url: videoChannelObject.id,
21     createdAt: new Date(videoChannelObject.published),
22     updatedAt: new Date(videoChannelObject.updated),
23     remote: true,
24     accountId: account.id
25   }
26 }
27
28 async function videoActivityObjectToDBAttributes (
29   videoChannel: VideoChannelModel,
30   videoObject: VideoTorrentObject,
31   to: string[] = [],
32   cc: string[] = []
33 ) {
34   let privacy = VideoPrivacy.PRIVATE
35   if (to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1) privacy = VideoPrivacy.PUBLIC
36   else if (cc.indexOf(ACTIVITY_PUB.PUBLIC) !== -1) privacy = VideoPrivacy.UNLISTED
37
38   const duration = videoObject.duration.replace(/[^\d]+/, '')
39   let language = null
40   if (videoObject.language) {
41     language = parseInt(videoObject.language.identifier, 10)
42   }
43
44   let category = null
45   if (videoObject.category) {
46     category = parseInt(videoObject.category.identifier, 10)
47   }
48
49   let licence = null
50   if (videoObject.licence) {
51     licence = parseInt(videoObject.licence.identifier, 10)
52   }
53
54   let description = null
55   if (videoObject.content) {
56     description = videoObject.content
57   }
58
59   return {
60     name: videoObject.name,
61     uuid: videoObject.uuid,
62     url: videoObject.id,
63     category,
64     licence,
65     language,
66     description,
67     nsfw: videoObject.nsfw,
68     channelId: videoChannel.id,
69     duration: parseInt(duration, 10),
70     createdAt: new Date(videoObject.published),
71     // FIXME: updatedAt does not seems to be considered by Sequelize
72     updatedAt: new Date(videoObject.updated),
73     views: videoObject.views,
74     likes: 0,
75     dislikes: 0,
76     remote: true,
77     privacy
78   }
79 }
80
81 function videoFileActivityUrlToDBAttributes (videoCreated: VideoModel, videoObject: VideoTorrentObject) {
82   const mimeTypes = Object.keys(VIDEO_MIMETYPE_EXT)
83   const fileUrls = videoObject.url.filter(u => {
84     return mimeTypes.indexOf(u.mimeType) !== -1 && u.mimeType.startsWith('video/')
85   })
86
87   if (fileUrls.length === 0) {
88     throw new Error('Cannot find video files for ' + videoCreated.url)
89   }
90
91   const attributes = []
92   for (const fileUrl of fileUrls) {
93     // Fetch associated magnet uri
94     const magnet = videoObject.url.find(u => {
95       return u.mimeType === 'application/x-bittorrent;x-scheme-handler/magnet' && u.width === fileUrl.width
96     })
97
98     if (!magnet) throw new Error('Cannot find associated magnet uri for file ' + fileUrl.url)
99
100     const parsed = magnetUtil.decode(magnet.url)
101     if (!parsed || isVideoFileInfoHashValid(parsed.infoHash) === false) throw new Error('Cannot parse magnet URI ' + magnet.url)
102
103     const attribute = {
104       extname: VIDEO_MIMETYPE_EXT[fileUrl.mimeType],
105       infoHash: parsed.infoHash,
106       resolution: fileUrl.width,
107       size: fileUrl.size,
108       videoId: videoCreated.id
109     }
110     attributes.push(attribute)
111   }
112
113   return attributes
114 }
115
116 async function addVideoShares (instance: VideoModel, shares: string[]) {
117   for (const share of shares) {
118     // Fetch url
119     const json = await doRequest({
120       uri: share,
121       json: true
122     })
123     const actor = json['actor']
124     if (!actor) continue
125
126     const account = await getOrCreateAccountAndServer(actor)
127
128     const entry = {
129       accountId: account.id,
130       videoId: instance.id
131     }
132
133     await VideoShareModel.findOrCreate({
134       where: entry,
135       defaults: entry
136     })
137   }
138 }
139
140 async function addVideoChannelShares (instance: VideoChannelModel, shares: string[]) {
141   for (const share of shares) {
142     // Fetch url
143     const json = await doRequest({
144       uri: share,
145       json: true
146     })
147     const actor = json['actor']
148     if (!actor) continue
149
150     const account = await getOrCreateAccountAndServer(actor)
151
152     const entry = {
153       accountId: account.id,
154       videoChannelId: instance.id
155     }
156
157     await VideoChannelShareModel.findOrCreate({
158       where: entry,
159       defaults: entry
160     })
161   }
162 }
163
164 // ---------------------------------------------------------------------------
165
166 export {
167   videoFileActivityUrlToDBAttributes,
168   videoActivityObjectToDBAttributes,
169   videoChannelActivityObjectToDBAttributes,
170   addVideoChannelShares,
171   addVideoShares
172 }