Finish admin design
[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/video-channel-object'
4 import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
5 import { isVideoFileInfoHashValid } from '../../../helpers/custom-validators/videos'
6 import { doRequest } from '../../../helpers/requests'
7 import { database as db } from '../../../initializers'
8 import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../../initializers/constants'
9 import { AccountInstance } from '../../../models/account/account-interface'
10 import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
11 import { VideoFileAttributes } from '../../../models/video/video-file-interface'
12 import { VideoAttributes, VideoInstance } from '../../../models/video/video-interface'
13 import { getOrCreateAccountAndServer } from '../account'
14
15 function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountInstance) {
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: VideoChannelInstance,
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   const videoData: VideoAttributes = {
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   return videoData
81 }
82
83 function videoFileActivityUrlToDBAttributes (videoCreated: VideoInstance, videoObject: VideoTorrentObject) {
84   const mimeTypes = Object.keys(VIDEO_MIMETYPE_EXT)
85   const fileUrls = videoObject.url.filter(u => {
86     return mimeTypes.indexOf(u.mimeType) !== -1 && u.mimeType.startsWith('video/')
87   })
88
89   if (fileUrls.length === 0) {
90     throw new Error('Cannot find video files for ' + videoCreated.url)
91   }
92
93   const attributes: VideoFileAttributes[] = []
94   for (const fileUrl of fileUrls) {
95     // Fetch associated magnet uri
96     const magnet = videoObject.url.find(u => {
97       return u.mimeType === 'application/x-bittorrent;x-scheme-handler/magnet' && u.width === fileUrl.width
98     })
99
100     if (!magnet) throw new Error('Cannot find associated magnet uri for file ' + fileUrl.url)
101
102     const parsed = magnetUtil.decode(magnet.url)
103     if (!parsed || isVideoFileInfoHashValid(parsed.infoHash) === false) throw new Error('Cannot parse magnet URI ' + magnet.url)
104
105     const attribute = {
106       extname: VIDEO_MIMETYPE_EXT[fileUrl.mimeType],
107       infoHash: parsed.infoHash,
108       resolution: fileUrl.width,
109       size: fileUrl.size,
110       videoId: videoCreated.id
111     }
112     attributes.push(attribute)
113   }
114
115   return attributes
116 }
117
118 async function addVideoShares (instance: VideoInstance, shares: string[]) {
119   for (const share of shares) {
120     // Fetch url
121     const json = await doRequest({
122       uri: share,
123       json: true
124     })
125     const actor = json['actor']
126     if (!actor) continue
127
128     const account = await getOrCreateAccountAndServer(actor)
129
130     const entry = {
131       accountId: account.id,
132       videoId: instance.id
133     }
134
135     await db.VideoShare.findOrCreate({
136       where: entry,
137       defaults: entry
138     })
139   }
140 }
141
142 async function addVideoChannelShares (instance: VideoChannelInstance, shares: string[]) {
143   for (const share of shares) {
144     // Fetch url
145     const json = await doRequest({
146       uri: share,
147       json: true
148     })
149     const actor = json['actor']
150     if (!actor) continue
151
152     const account = await getOrCreateAccountAndServer(actor)
153
154     const entry = {
155       accountId: account.id,
156       videoChannelId: instance.id
157     }
158
159     await db.VideoChannelShare.findOrCreate({
160       where: entry,
161       defaults: entry
162     })
163   }
164 }
165
166 // ---------------------------------------------------------------------------
167
168 export {
169   videoFileActivityUrlToDBAttributes,
170   videoActivityObjectToDBAttributes,
171   videoChannelActivityObjectToDBAttributes,
172   addVideoChannelShares,
173   addVideoShares
174 }