]>
Commit | Line | Data |
---|---|---|
571389d4 C |
1 | import { join } from 'path' |
2 | import * as request from 'request' | |
e4f97bab | 3 | import * as url from 'url' |
571389d4 | 4 | import { ActivityIconObject } from '../../shared/index' |
e4f97bab C |
5 | import { ActivityPubActor } from '../../shared/models/activitypub/activitypub-actor' |
6 | import { ResultList } from '../../shared/models/result-list.model' | |
571389d4 C |
7 | import { database as db, REMOTE_SCHEME } from '../initializers' |
8 | import { CONFIG, STATIC_PATHS } from '../initializers/constants' | |
0d0e8dd0 | 9 | import { VideoInstance } from '../models/video/video-interface' |
571389d4 C |
10 | import { isRemoteAccountValid } from './custom-validators' |
11 | import { logger } from './logger' | |
12 | import { doRequest, doRequestAndSaveToFile } from './requests' | |
0d0e8dd0 C |
13 | |
14 | function generateThumbnailFromUrl (video: VideoInstance, icon: ActivityIconObject) { | |
15 | const thumbnailName = video.getThumbnailName() | |
16 | const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, thumbnailName) | |
17 | ||
18 | const options = { | |
19 | method: 'GET', | |
20 | uri: icon.url | |
21 | } | |
22 | return doRequestAndSaveToFile(options, thumbnailPath) | |
23 | } | |
24 | ||
571389d4 C |
25 | function getActivityPubUrl (type: 'video' | 'videoChannel' | 'account', id: string) { |
26 | if (type === 'video') return CONFIG.WEBSERVER.URL + '/videos/watch/' + id | |
27 | else if (type === 'videoChannel') return CONFIG.WEBSERVER.URL + '/video-channels/' + id | |
28 | else if (type === 'account') return CONFIG.WEBSERVER.URL + '/account/' + id | |
0d0e8dd0 C |
29 | |
30 | return '' | |
31 | } | |
32 | ||
33 | async function getOrCreateAccount (accountUrl: string) { | |
34 | let account = await db.Account.loadByUrl(accountUrl) | |
35 | ||
36 | // We don't have this account in our database, fetch it on remote | |
37 | if (!account) { | |
38 | const { account } = await fetchRemoteAccountAndCreatePod(accountUrl) | |
39 | ||
40 | if (!account) throw new Error('Cannot fetch remote account.') | |
41 | ||
42 | // Save our new account in database | |
43 | await account.save() | |
44 | } | |
45 | ||
46 | return account | |
47 | } | |
e4f97bab C |
48 | |
49 | async function fetchRemoteAccountAndCreatePod (accountUrl: string) { | |
50 | const options = { | |
51 | uri: accountUrl, | |
52 | method: 'GET' | |
53 | } | |
54 | ||
55 | let requestResult | |
56 | try { | |
57 | requestResult = await doRequest(options) | |
58 | } catch (err) { | |
59 | logger.warning('Cannot fetch remote account %s.', accountUrl, err) | |
60 | return undefined | |
61 | } | |
62 | ||
63 | const accountJSON: ActivityPubActor = requestResult.body | |
64 | if (isRemoteAccountValid(accountJSON) === false) return undefined | |
65 | ||
66 | const followersCount = await fetchAccountCount(accountJSON.followers) | |
67 | const followingCount = await fetchAccountCount(accountJSON.following) | |
68 | ||
69 | const account = db.Account.build({ | |
70 | uuid: accountJSON.uuid, | |
71 | name: accountJSON.preferredUsername, | |
72 | url: accountJSON.url, | |
73 | publicKey: accountJSON.publicKey.publicKeyPem, | |
74 | privateKey: null, | |
75 | followersCount: followersCount, | |
76 | followingCount: followingCount, | |
77 | inboxUrl: accountJSON.inbox, | |
78 | outboxUrl: accountJSON.outbox, | |
79 | sharedInboxUrl: accountJSON.endpoints.sharedInbox, | |
80 | followersUrl: accountJSON.followers, | |
81 | followingUrl: accountJSON.following | |
82 | }) | |
83 | ||
84 | const accountHost = url.parse(account.url).host | |
85 | const podOptions = { | |
86 | where: { | |
87 | host: accountHost | |
88 | }, | |
89 | defaults: { | |
90 | host: accountHost | |
91 | } | |
92 | } | |
93 | const pod = await db.Pod.findOrCreate(podOptions) | |
94 | ||
95 | return { account, pod } | |
96 | } | |
97 | ||
571389d4 C |
98 | function fetchRemoteVideoPreview (video: VideoInstance) { |
99 | // FIXME: use url | |
100 | const host = video.VideoChannel.Account.Pod.host | |
101 | const path = join(STATIC_PATHS.PREVIEWS, video.getPreviewName()) | |
102 | ||
103 | return request.get(REMOTE_SCHEME.HTTP + '://' + host + path) | |
104 | } | |
105 | ||
106 | async function fetchRemoteVideoDescription (video: VideoInstance) { | |
107 | const options = { | |
108 | uri: video.url | |
109 | } | |
110 | ||
111 | const { body } = await doRequest(options) | |
112 | return body.description ? body.description : '' | |
113 | } | |
114 | ||
115 | function activityPubContextify <T> (data: T) { | |
e4f97bab C |
116 | return Object.assign(data,{ |
117 | '@context': [ | |
118 | 'https://www.w3.org/ns/activitystreams', | |
119 | 'https://w3id.org/security/v1', | |
120 | { | |
121 | 'Hashtag': 'as:Hashtag', | |
122 | 'uuid': 'http://schema.org/identifier', | |
123 | 'category': 'http://schema.org/category', | |
124 | 'licence': 'http://schema.org/license', | |
125 | 'nsfw': 'as:sensitive', | |
126 | 'language': 'http://schema.org/inLanguage', | |
127 | 'views': 'http://schema.org/Number', | |
128 | 'size': 'http://schema.org/Number' | |
129 | } | |
130 | ] | |
131 | }) | |
132 | } | |
133 | ||
134 | function activityPubCollectionPagination (url: string, page: number, result: ResultList<any>) { | |
135 | const baseUrl = url.split('?').shift | |
136 | ||
137 | const obj = { | |
138 | id: baseUrl, | |
139 | type: 'Collection', | |
140 | totalItems: result.total, | |
141 | first: { | |
142 | id: baseUrl + '?page=' + page, | |
143 | type: 'CollectionPage', | |
144 | totalItems: result.total, | |
145 | next: baseUrl + '?page=' + (page + 1), | |
146 | partOf: baseUrl, | |
147 | items: result.data | |
148 | } | |
149 | } | |
150 | ||
151 | return activityPubContextify(obj) | |
152 | } | |
153 | ||
154 | // --------------------------------------------------------------------------- | |
155 | ||
156 | export { | |
157 | fetchRemoteAccountAndCreatePod, | |
158 | activityPubContextify, | |
0d0e8dd0 C |
159 | activityPubCollectionPagination, |
160 | getActivityPubUrl, | |
161 | generateThumbnailFromUrl, | |
571389d4 C |
162 | getOrCreateAccount, |
163 | fetchRemoteVideoPreview, | |
164 | fetchRemoteVideoDescription | |
e4f97bab C |
165 | } |
166 | ||
167 | // --------------------------------------------------------------------------- | |
168 | ||
169 | async function fetchAccountCount (url: string) { | |
170 | const options = { | |
171 | uri: url, | |
172 | method: 'GET' | |
173 | } | |
174 | ||
175 | let requestResult | |
176 | try { | |
177 | requestResult = await doRequest(options) | |
178 | } catch (err) { | |
179 | logger.warning('Cannot fetch remote account count %s.', url, err) | |
180 | return undefined | |
181 | } | |
182 | ||
183 | return requestResult.totalItems ? requestResult.totalItems : 0 | |
184 | } |