Move activitypub functions from helpers/ to lib/
authorChocobozzz <florian.bigard@gmail.com>
Mon, 20 Nov 2017 09:24:29 +0000 (10:24 +0100)
committerChocobozzz <florian.bigard@gmail.com>
Mon, 27 Nov 2017 18:40:52 +0000 (19:40 +0100)
26 files changed:
server/controllers/api/videos/index.ts
server/helpers/activitypub.ts
server/helpers/webfinger.ts
server/lib/activitypub/account.ts [new file with mode: 0644]
server/lib/activitypub/index.ts
server/lib/activitypub/process/process-add.ts
server/lib/activitypub/process/process-announce.ts
server/lib/activitypub/process/process-create.ts
server/lib/activitypub/process/process-delete.ts
server/lib/activitypub/process/process-follow.ts
server/lib/activitypub/process/process-update.ts
server/lib/activitypub/send/send-accept.ts
server/lib/activitypub/send/send-announce.ts
server/lib/activitypub/send/send-create.ts
server/lib/activitypub/send/send-follow.ts
server/lib/activitypub/send/send-undo.ts
server/lib/activitypub/send/send-update.ts
server/lib/activitypub/share.ts [new file with mode: 0644]
server/lib/activitypub/url.ts [new file with mode: 0644]
server/lib/activitypub/video-channels.ts [new file with mode: 0644]
server/lib/activitypub/videos.ts [new file with mode: 0644]
server/lib/cache/videos-preview-cache.ts
server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts
server/lib/user.ts
server/lib/video-channel.ts
server/middlewares/activitypub.ts

index 8c9b0aa5032499734d6f0b3a6a466698cbc55393..0d114dcd2d37fcb983299faff9824ad96675fdfe 100644 (file)
@@ -3,7 +3,6 @@ import * as multer from 'multer'
 import { extname, join } from 'path'
 import { VideoCreate, VideoPrivacy, VideoUpdate } from '../../../../shared'
 import {
-  fetchRemoteVideoDescription,
   generateRandomString,
   getFormattedObjects,
   getVideoFileHeight,
@@ -12,7 +11,6 @@ import {
   resetSequelizeInstance,
   retryTransactionWrapper
 } from '../../../helpers'
-import { getVideoActivityPubUrl, shareVideoByServer } from '../../../helpers/activitypub'
 import { CONFIG, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_MIMETYPE_EXT, VIDEO_PRIVACIES } from '../../../initializers'
 import { database as db } from '../../../initializers/database'
 import { sendAddVideo } from '../../../lib/activitypub/send/send-add'
@@ -37,6 +35,9 @@ import { abuseVideoRouter } from './abuse'
 import { blacklistRouter } from './blacklist'
 import { videoChannelRouter } from './channel'
 import { rateVideoRouter } from './rate'
+import { getVideoActivityPubUrl } from '../../../lib/activitypub/url'
+import { shareVideoByServer } from '../../../lib/activitypub/share'
+import { fetchRemoteVideoDescription } from '../../../lib/activitypub/videos'
 
 const videosRouter = express.Router()
 
index 9622a18012537552121c82d3c61e6bd405d18776..5c577bb612a559a3bf89bb8f8e4a32e639bc757e 100644 (file)
@@ -1,244 +1,7 @@
-import { join } from 'path'
-import * as request from 'request'
-import * as Sequelize from 'sequelize'
-import * as url from 'url'
-import { ActivityIconObject } from '../../shared/index'
 import { Activity } from '../../shared/models/activitypub/activity'
-import { ActivityPubActor } from '../../shared/models/activitypub/activitypub-actor'
-import { VideoChannelObject } from '../../shared/models/activitypub/objects/video-channel-object'
 import { ResultList } from '../../shared/models/result-list.model'
-import { database as db, REMOTE_SCHEME } from '../initializers'
-import { ACTIVITY_PUB, CONFIG, STATIC_PATHS } from '../initializers/constants'
-import { videoChannelActivityObjectToDBAttributes } from '../lib/activitypub/process/misc'
-import { sendVideoAnnounce } from '../lib/activitypub/send/send-announce'
-import { sendVideoChannelAnnounce } from '../lib/index'
-import { AccountFollowInstance } from '../models/account/account-follow-interface'
 import { AccountInstance } from '../models/account/account-interface'
-import { VideoAbuseInstance } from '../models/video/video-abuse-interface'
-import { VideoChannelInstance } from '../models/video/video-channel-interface'
-import { VideoInstance } from '../models/video/video-interface'
-import { isRemoteAccountValid } from './custom-validators'
-import { logger } from './logger'
 import { signObject } from './peertube-crypto'
-import { doRequest, doRequestAndSaveToFile } from './requests'
-import { getServerAccount } from './utils'
-import { isVideoChannelObjectValid } from './custom-validators/activitypub/video-channels'
-
-function generateThumbnailFromUrl (video: VideoInstance, icon: ActivityIconObject) {
-  const thumbnailName = video.getThumbnailName()
-  const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, thumbnailName)
-
-  const options = {
-    method: 'GET',
-    uri: icon.url
-  }
-  return doRequestAndSaveToFile(options, thumbnailPath)
-}
-
-async function shareVideoChannelByServer (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) {
-  const serverAccount = await getServerAccount()
-
-  await db.VideoChannelShare.create({
-    accountId: serverAccount.id,
-    videoChannelId: videoChannel.id
-  }, { transaction: t })
-
-  return sendVideoChannelAnnounce(serverAccount, videoChannel, t)
-}
-
-async function shareVideoByServer (video: VideoInstance, t: Sequelize.Transaction) {
-  const serverAccount = await getServerAccount()
-
-  await db.VideoShare.create({
-    accountId: serverAccount.id,
-    videoId: video.id
-  }, { transaction: t })
-
-  return sendVideoAnnounce(serverAccount, video, t)
-}
-
-function getVideoActivityPubUrl (video: VideoInstance) {
-  return CONFIG.WEBSERVER.URL + '/videos/watch/' + video.uuid
-}
-
-function getVideoChannelActivityPubUrl (videoChannel: VideoChannelInstance) {
-  return CONFIG.WEBSERVER.URL + '/video-channels/' + videoChannel.uuid
-}
-
-function getAccountActivityPubUrl (accountName: string) {
-  return CONFIG.WEBSERVER.URL + '/account/' + accountName
-}
-
-function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseInstance) {
-  return CONFIG.WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id
-}
-
-function getAccountFollowActivityPubUrl (accountFollow: AccountFollowInstance) {
-  const me = accountFollow.AccountFollower
-  const following = accountFollow.AccountFollowing
-
-  return me.url + '#follows/' + following.id
-}
-
-function getAccountFollowAcceptActivityPubUrl (accountFollow: AccountFollowInstance) {
-  const follower = accountFollow.AccountFollower
-  const me = accountFollow.AccountFollowing
-
-  return follower.url + '#accepts/follows/' + me.id
-}
-
-function getAnnounceActivityPubUrl (originalUrl: string, byAccount: AccountInstance) {
-  return originalUrl + '#announces/' + byAccount.id
-}
-
-function getUpdateActivityPubUrl (originalUrl: string, updatedAt: string) {
-  return originalUrl + '#updates/' + updatedAt
-}
-
-function getUndoActivityPubUrl (originalUrl: string) {
-  return originalUrl + '/undo'
-}
-
-async function getOrCreateAccount (accountUrl: string) {
-  let account = await db.Account.loadByUrl(accountUrl)
-
-  // We don't have this account in our database, fetch it on remote
-  if (!account) {
-    const res = await fetchRemoteAccountAndCreateServer(accountUrl)
-    if (res === undefined) throw new Error('Cannot fetch remote account.')
-
-    // Save our new account in database
-    account = await res.account.save()
-  }
-
-  return account
-}
-
-async function getOrCreateVideoChannel (ownerAccount: AccountInstance, videoChannelUrl: string) {
-  let videoChannel = await db.VideoChannel.loadByUrl(videoChannelUrl)
-
-  // We don't have this account in our database, fetch it on remote
-  if (!videoChannel) {
-    videoChannel = await fetchRemoteVideoChannel(ownerAccount, videoChannelUrl)
-    if (videoChannel === undefined) throw new Error('Cannot fetch remote video channel.')
-
-    // Save our new video channel in database
-    await videoChannel.save()
-  }
-
-  return videoChannel
-}
-
-async function fetchRemoteAccountAndCreateServer (accountUrl: string) {
-  const options = {
-    uri: accountUrl,
-    method: 'GET',
-    headers: {
-      'Accept': ACTIVITY_PUB.ACCEPT_HEADER
-    }
-  }
-
-  logger.info('Fetching remote account %s.', accountUrl)
-
-  let requestResult
-  try {
-    requestResult = await doRequest(options)
-  } catch (err) {
-    logger.warn('Cannot fetch remote account %s.', accountUrl, err)
-    return undefined
-  }
-
-  const accountJSON: ActivityPubActor = JSON.parse(requestResult.body)
-  if (isRemoteAccountValid(accountJSON) === false) {
-    logger.debug('Remote account JSON is not valid.', { accountJSON })
-    return undefined
-  }
-
-  const followersCount = await fetchAccountCount(accountJSON.followers)
-  const followingCount = await fetchAccountCount(accountJSON.following)
-
-  const account = db.Account.build({
-    uuid: accountJSON.uuid,
-    name: accountJSON.preferredUsername,
-    url: accountJSON.url,
-    publicKey: accountJSON.publicKey.publicKeyPem,
-    privateKey: null,
-    followersCount: followersCount,
-    followingCount: followingCount,
-    inboxUrl: accountJSON.inbox,
-    outboxUrl: accountJSON.outbox,
-    sharedInboxUrl: accountJSON.endpoints.sharedInbox,
-    followersUrl: accountJSON.followers,
-    followingUrl: accountJSON.following
-  })
-
-  const accountHost = url.parse(account.url).host
-  const serverOptions = {
-    where: {
-      host: accountHost
-    },
-    defaults: {
-      host: accountHost
-    }
-  }
-  const [ server ] = await db.Server.findOrCreate(serverOptions)
-  account.set('serverId', server.id)
-
-  return { account, server }
-}
-
-async function fetchRemoteVideoChannel (ownerAccount: AccountInstance, videoChannelUrl: string) {
-  const options = {
-    uri: videoChannelUrl,
-    method: 'GET',
-    headers: {
-      'Accept': ACTIVITY_PUB.ACCEPT_HEADER
-    }
-  }
-
-  logger.info('Fetching remote video channel %s.', videoChannelUrl)
-
-  let requestResult
-  try {
-    requestResult = await doRequest(options)
-  } catch (err) {
-    logger.warn('Cannot fetch remote video channel %s.', videoChannelUrl, err)
-    return undefined
-  }
-
-  const videoChannelJSON: VideoChannelObject = JSON.parse(requestResult.body)
-  if (isVideoChannelObjectValid(videoChannelJSON) === false) {
-    logger.debug('Remote video channel JSON is not valid.', { videoChannelJSON })
-    return undefined
-  }
-
-  const videoChannelAttributes = videoChannelActivityObjectToDBAttributes(videoChannelJSON, ownerAccount)
-  const videoChannel = db.VideoChannel.build(videoChannelAttributes)
-  videoChannel.Account = ownerAccount
-
-  return videoChannel
-}
-
-function fetchRemoteVideoPreview (video: VideoInstance) {
-  // FIXME: use url
-  const host = video.VideoChannel.Account.Server.host
-  const path = join(STATIC_PATHS.PREVIEWS, video.getPreviewName())
-
-  return request.get(REMOTE_SCHEME.HTTP + '://' + host + path)
-}
-
-async function fetchRemoteVideoDescription (video: VideoInstance) {
-  // FIXME: use url
-  const host = video.VideoChannel.Account.Server.host
-  const path = video.getDescriptionPath()
-  const options = {
-    uri: REMOTE_SCHEME.HTTP + '://' + host + path,
-    json: true
-  }
-
-  const { body } = await doRequest(options)
-  return body.description ? body.description : ''
-}
 
 function activityPubContextify <T> (data: T) {
   return Object.assign(data,{
@@ -289,43 +52,7 @@ function buildSignedActivity (byAccount: AccountInstance, data: Object) {
 // ---------------------------------------------------------------------------
 
 export {
-  fetchRemoteAccountAndCreateServer,
   activityPubContextify,
   activityPubCollectionPagination,
-  generateThumbnailFromUrl,
-  getOrCreateAccount,
-  fetchRemoteVideoPreview,
-  fetchRemoteVideoDescription,
-  shareVideoChannelByServer,
-  shareVideoByServer,
-  getOrCreateVideoChannel,
-  buildSignedActivity,
-  getVideoActivityPubUrl,
-  getVideoChannelActivityPubUrl,
-  getAccountActivityPubUrl,
-  getVideoAbuseActivityPubUrl,
-  getAccountFollowActivityPubUrl,
-  getAccountFollowAcceptActivityPubUrl,
-  getAnnounceActivityPubUrl,
-  getUpdateActivityPubUrl,
-  getUndoActivityPubUrl
-}
-
-// ---------------------------------------------------------------------------
-
-async function fetchAccountCount (url: string) {
-  const options = {
-    uri: url,
-    method: 'GET'
-  }
-
-  let requestResult
-  try {
-    requestResult = await doRequest(options)
-  } catch (err) {
-    logger.warn('Cannot fetch remote account count %s.', url, err)
-    return undefined
-  }
-
-  return requestResult.totalItems ? requestResult.totalItems : 0
+  buildSignedActivity
 }
index 31417e72883469d31d904ddcba2d63b72d2b003a..b7408c845b0c140e64002912700ace77397471de 100644 (file)
@@ -1,9 +1,9 @@
 import * as WebFinger from 'webfinger.js'
+import { WebFingerData } from '../../shared'
 
 import { isTestInstance } from './core-utils'
 import { isActivityPubUrlValid } from './custom-validators'
-import { WebFingerData } from '../../shared'
-import { fetchRemoteAccountAndCreateServer } from './activitypub'
+import { fetchRemoteAccountAndCreateServer } from '../lib/activitypub/account'
 
 const webfinger = new WebFinger({
   webfist_fallback: false,
diff --git a/server/lib/activitypub/account.ts b/server/lib/activitypub/account.ts
new file mode 100644 (file)
index 0000000..704a92e
--- /dev/null
@@ -0,0 +1,104 @@
+import * as url from 'url'
+import { ActivityPubActor } from '../../../shared/models/activitypub/activitypub-actor'
+import { isRemoteAccountValid } from '../../helpers/custom-validators/activitypub/account'
+import { logger } from '../../helpers/logger'
+import { doRequest } from '../../helpers/requests'
+import { ACTIVITY_PUB } from '../../initializers/constants'
+import { database as db } from '../../initializers/database'
+
+async function getOrCreateAccount (accountUrl: string) {
+  let account = await db.Account.loadByUrl(accountUrl)
+
+  // We don't have this account in our database, fetch it on remote
+  if (!account) {
+    const res = await fetchRemoteAccountAndCreateServer(accountUrl)
+    if (res === undefined) throw new Error('Cannot fetch remote account.')
+
+    // Save our new account in database
+    account = await res.account.save()
+  }
+
+  return account
+}
+
+async function fetchRemoteAccountAndCreateServer (accountUrl: string) {
+  const options = {
+    uri: accountUrl,
+    method: 'GET',
+    headers: {
+      'Accept': ACTIVITY_PUB.ACCEPT_HEADER
+    }
+  }
+
+  logger.info('Fetching remote account %s.', accountUrl)
+
+  let requestResult
+  try {
+    requestResult = await doRequest(options)
+  } catch (err) {
+    logger.warn('Cannot fetch remote account %s.', accountUrl, err)
+    return undefined
+  }
+
+  const accountJSON: ActivityPubActor = JSON.parse(requestResult.body)
+  if (isRemoteAccountValid(accountJSON) === false) {
+    logger.debug('Remote account JSON is not valid.', { accountJSON })
+    return undefined
+  }
+
+  const followersCount = await fetchAccountCount(accountJSON.followers)
+  const followingCount = await fetchAccountCount(accountJSON.following)
+
+  const account = db.Account.build({
+    uuid: accountJSON.uuid,
+    name: accountJSON.preferredUsername,
+    url: accountJSON.url,
+    publicKey: accountJSON.publicKey.publicKeyPem,
+    privateKey: null,
+    followersCount: followersCount,
+    followingCount: followingCount,
+    inboxUrl: accountJSON.inbox,
+    outboxUrl: accountJSON.outbox,
+    sharedInboxUrl: accountJSON.endpoints.sharedInbox,
+    followersUrl: accountJSON.followers,
+    followingUrl: accountJSON.following
+  })
+
+  const accountHost = url.parse(account.url).host
+  const serverOptions = {
+    where: {
+      host: accountHost
+    },
+    defaults: {
+      host: accountHost
+    }
+  }
+  const [ server ] = await db.Server.findOrCreate(serverOptions)
+  account.set('serverId', server.id)
+
+  return { account, server }
+}
+
+export {
+  getOrCreateAccount,
+  fetchRemoteAccountAndCreateServer
+}
+
+// ---------------------------------------------------------------------------
+
+async function fetchAccountCount (url: string) {
+  const options = {
+    uri: url,
+    method: 'GET'
+  }
+
+  let requestResult
+  try {
+    requestResult = await doRequest(options)
+  } catch (err) {
+    logger.warn('Cannot fetch remote account count %s.', url, err)
+    return undefined
+  }
+
+  return requestResult.totalItems ? requestResult.totalItems : 0
+}
index 1bea0a412e9235ba1bd5742952d638c97138b4c2..b1daa70bb49faefee711e5cfd9ba50c3a6cbceb5 100644 (file)
@@ -1,2 +1,7 @@
 export * from './process'
 export * from './send'
+export * from './account'
+export * from './share'
+export * from './video-channels'
+export * from './videos'
+export * from './url'
index f064c1ab61953e56b9772e2fcf9226c06c843a6b..2810362286b7eae7991153f9e13563a0863cb6dc 100644 (file)
@@ -1,11 +1,14 @@
 import * as Bluebird from 'bluebird'
 import { VideoTorrentObject } from '../../../../shared'
 import { ActivityAdd } from '../../../../shared/models/activitypub/activity'
-import { generateThumbnailFromUrl, getOrCreateAccount, logger, retryTransactionWrapper } from '../../../helpers'
-import { getOrCreateVideoChannel } from '../../../helpers/activitypub'
+import { retryTransactionWrapper } from '../../../helpers/database-utils'
+import { logger } from '../../../helpers/logger'
 import { database as db } from '../../../initializers'
 import { AccountInstance } from '../../../models/account/account-interface'
 import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
+import { getOrCreateAccount } from '../account'
+import { getOrCreateVideoChannel } from '../video-channels'
+import { generateThumbnailFromUrl } from '../videos'
 import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc'
 
 async function processAddActivity (activity: ActivityAdd) {
@@ -41,12 +44,10 @@ function processAddVideo (account: AccountInstance, activity: ActivityAdd, video
   return retryTransactionWrapper(addRemoteVideo, options)
 }
 
-function addRemoteVideo (
-  account: AccountInstance,
-  activity: ActivityAdd,
-  videoChannel: VideoChannelInstance,
-  videoToCreateData: VideoTorrentObject
-) {
+function addRemoteVideo (account: AccountInstance,
+                         activity: ActivityAdd,
+                         videoChannel: VideoChannelInstance,
+                         videoToCreateData: VideoTorrentObject) {
   logger.debug('Adding remote video %s.', videoToCreateData.url)
 
   return db.sequelize.transaction(async t => {
index 656db08a92185d5750488c709aa4d72ccbf0e2d7..40712ef031c35a7ed5f5a9e928e0b4fc029dd639 100644 (file)
@@ -1,11 +1,11 @@
 import { ActivityAnnounce } from '../../../../shared/models/activitypub/activity'
-import { getOrCreateAccount } from '../../../helpers/activitypub'
 import { logger } from '../../../helpers/logger'
 import { database as db } from '../../../initializers/index'
 import { VideoInstance } from '../../../models/index'
 import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
 import { processAddActivity } from './process-add'
 import { processCreateActivity } from './process-create'
+import { getOrCreateAccount } from '../account'
 
 async function processAnnounceActivity (activity: ActivityAnnounce) {
   const announcedActivity = activity.object
index aac941a6c9bf644f620ea5cfc5fc9c848e57cfe3..fc635eb1f64067b408b773f65fd9d07d91d78ab7 100644 (file)
@@ -1,9 +1,10 @@
 import { ActivityCreate, VideoChannelObject } from '../../../../shared'
 import { VideoAbuseObject } from '../../../../shared/models/activitypub/objects/video-abuse-object'
 import { logger, retryTransactionWrapper } from '../../../helpers'
-import { getOrCreateAccount, getVideoChannelActivityPubUrl } from '../../../helpers/activitypub'
 import { database as db } from '../../../initializers'
 import { AccountInstance } from '../../../models/account/account-interface'
+import { getOrCreateAccount } from '../account'
+import { getVideoChannelActivityPubUrl } from '../url'
 import { videoChannelActivityObjectToDBAttributes } from './misc'
 
 async function processCreateActivity (activity: ActivityCreate) {
index af5d964d48d8ead0fecccb4782bd38a28c4e676b..0328d1a7d508c8c4cd6c415743fa88082a09b976 100644 (file)
@@ -1,11 +1,11 @@
 import { ActivityDelete } from '../../../../shared/models/activitypub/activity'
-import { getOrCreateAccount } from '../../../helpers/activitypub'
 import { retryTransactionWrapper } from '../../../helpers/database-utils'
 import { logger } from '../../../helpers/logger'
 import { database as db } from '../../../initializers'
 import { AccountInstance } from '../../../models/account/account-interface'
 import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
 import { VideoInstance } from '../../../models/video/video-interface'
+import { getOrCreateAccount } from '../account'
 
 async function processDeleteActivity (activity: ActivityDelete) {
   const account = await getOrCreateAccount(activity.actor)
index 5536395800aa26db4f0651a2ffefd6f9d65f6237..41b38828c5bebcc83d8beefec52af141cdb482c6 100644 (file)
@@ -1,9 +1,10 @@
 import { ActivityFollow } from '../../../../shared/models/activitypub/activity'
-import { getOrCreateAccount, retryTransactionWrapper } from '../../../helpers'
+import { retryTransactionWrapper } from '../../../helpers'
 import { database as db } from '../../../initializers'
 import { AccountInstance } from '../../../models/account/account-interface'
 import { logger } from '../../../helpers/logger'
 import { sendAccept } from '../send/send-accept'
+import { getOrCreateAccount } from '../account'
 
 async function processFollowActivity (activity: ActivityFollow) {
   const activityObject = activity.object
index a3bfb1bafcf28524cf451bf6ec58df1178477ec3..4876735b860d1f1030d603ee16bd47f5f38a2f23 100644 (file)
@@ -1,6 +1,5 @@
 import { VideoChannelObject, VideoTorrentObject } from '../../../../shared'
 import { ActivityUpdate } from '../../../../shared/models/activitypub/activity'
-import { getOrCreateAccount } from '../../../helpers/activitypub'
 import { retryTransactionWrapper } from '../../../helpers/database-utils'
 import { logger } from '../../../helpers/logger'
 import { resetSequelizeInstance } from '../../../helpers/utils'
@@ -9,6 +8,7 @@ import { AccountInstance } from '../../../models/account/account-interface'
 import { VideoInstance } from '../../../models/video/video-interface'
 import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc'
 import Bluebird = require('bluebird')
+import { getOrCreateAccount } from '../account'
 
 async function processUpdateActivity (activity: ActivityUpdate) {
   const account = await getOrCreateAccount(activity.actor)
index 0324a30fa02397d46d87d150ff234317c1cf093f..eeab19ed01c59c94384bfa4751c35444dfe83207 100644 (file)
@@ -3,7 +3,7 @@ import { ActivityAccept } from '../../../../shared/models/activitypub/activity'
 import { AccountInstance } from '../../../models'
 import { AccountFollowInstance } from '../../../models/account/account-follow-interface'
 import { unicastTo } from './misc'
-import { getAccountFollowAcceptActivityPubUrl } from '../../../helpers/activitypub'
+import { getAccountFollowAcceptActivityPubUrl } from '../url'
 
 async function sendAccept (accountFollow: AccountFollowInstance, t: Transaction) {
   const follower = accountFollow.AccountFollower
index b9217e4f60e0e3ed7567446f46af188177cdb973..4b3a4ef758ea5438cab86bcbb698c616fb81b1ac 100644 (file)
@@ -4,7 +4,7 @@ import { VideoChannelInstance } from '../../../models/video/video-channel-interf
 import { broadcastToFollowers } from './misc'
 import { addActivityData } from './send-add'
 import { createActivityData } from './send-create'
-import { getAnnounceActivityPubUrl } from '../../../helpers/activitypub'
+import { getAnnounceActivityPubUrl } from '../url'
 
 async function sendVideoAnnounce (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
   const url = getAnnounceActivityPubUrl(video.url, byAccount)
index 66bfeee89371336c7bc8910f58eda9641a2240f0..14b666fc9edc61828a9795fa7cafb4464a69da48 100644 (file)
@@ -3,7 +3,7 @@ import { ActivityCreate } from '../../../../shared/models/activitypub/activity'
 import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models'
 import { VideoAbuseInstance } from '../../../models/video/video-abuse-interface'
 import { broadcastToFollowers, getAudience, unicastTo } from './misc'
-import { getVideoAbuseActivityPubUrl } from '../../../helpers/activitypub'
+import { getVideoAbuseActivityPubUrl } from '../url'
 
 async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
   const byAccount = videoChannel.Account
index 48d641c22d1f085e9b7a8fb4810054dda1db5890..3c01fb77c378cc71462c277b7fa0091c45444b7f 100644 (file)
@@ -2,8 +2,8 @@ import { Transaction } from 'sequelize'
 import { ActivityFollow } from '../../../../shared/models/activitypub/activity'
 import { AccountInstance } from '../../../models'
 import { AccountFollowInstance } from '../../../models/account/account-follow-interface'
+import { getAccountFollowActivityPubUrl } from '../url'
 import { unicastTo } from './misc'
-import { getAccountFollowActivityPubUrl } from '../../../helpers/activitypub'
 
 async function sendFollow (accountFollow: AccountFollowInstance, t: Transaction) {
   const me = accountFollow.AccountFollower
index 39da824f37eeb3310d317166a24f886fe0e812a5..77bee663957d034680cb6119bf46ff5fd184adb2 100644 (file)
@@ -3,8 +3,8 @@ import { ActivityFollow, ActivityUndo } from '../../../../shared/models/activity
 import { AccountInstance } from '../../../models'
 import { AccountFollowInstance } from '../../../models/account/account-follow-interface'
 import { unicastTo } from './misc'
-import { getAccountFollowActivityPubUrl, getUndoActivityPubUrl } from '../../../helpers/activitypub'
 import { followActivityData } from './send-follow'
+import { getAccountFollowActivityPubUrl, getUndoActivityPubUrl } from '../url'
 
 async function sendUndoFollow (accountFollow: AccountFollowInstance, t: Transaction) {
   const me = accountFollow.AccountFollower
index 42738f973e18485f9e94f69222c9511d1f6ab3f4..32cada06e3fb691a112dfc1ce46bcee74e6c999e 100644 (file)
@@ -1,8 +1,8 @@
 import { Transaction } from 'sequelize'
 import { ActivityUpdate } from '../../../../shared/models/activitypub/activity'
-import { getUpdateActivityPubUrl } from '../../../helpers/activitypub'
 import { database as db } from '../../../initializers'
 import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models'
+import { getUpdateActivityPubUrl } from '../url'
 import { broadcastToFollowers, getAudience } from './misc'
 
 async function sendUpdateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts
new file mode 100644 (file)
index 0000000..689e200
--- /dev/null
@@ -0,0 +1,33 @@
+import { Transaction } from 'sequelize'
+import { getServerAccount } from '../../helpers/utils'
+import { database as db } from '../../initializers'
+import { VideoChannelInstance } from '../../models/index'
+import { VideoInstance } from '../../models/video/video-interface'
+import { sendVideoAnnounce, sendVideoChannelAnnounce } from './send/send-announce'
+
+async function shareVideoChannelByServer (videoChannel: VideoChannelInstance, t: Transaction) {
+  const serverAccount = await getServerAccount()
+
+  await db.VideoChannelShare.create({
+    accountId: serverAccount.id,
+    videoChannelId: videoChannel.id
+  }, { transaction: t })
+
+  return sendVideoChannelAnnounce(serverAccount, videoChannel, t)
+}
+
+async function shareVideoByServer (video: VideoInstance, t: Transaction) {
+  const serverAccount = await getServerAccount()
+
+  await db.VideoShare.create({
+    accountId: serverAccount.id,
+    videoId: video.id
+  }, { transaction: t })
+
+  return sendVideoAnnounce(serverAccount, video, t)
+}
+
+export {
+  shareVideoChannelByServer,
+  shareVideoByServer
+}
diff --git a/server/lib/activitypub/url.ts b/server/lib/activitypub/url.ts
new file mode 100644 (file)
index 0000000..41ac0f9
--- /dev/null
@@ -0,0 +1,60 @@
+import { CONFIG } from '../../initializers/constants'
+import { VideoInstance } from '../../models/video/video-interface'
+import { VideoChannelInstance } from '../../models/video/video-channel-interface'
+import { VideoAbuseInstance } from '../../models/video/video-abuse-interface'
+import { AccountFollowInstance } from '../../models/account/account-follow-interface'
+import { AccountInstance } from '../../models/account/account-interface'
+
+function getVideoActivityPubUrl (video: VideoInstance) {
+  return CONFIG.WEBSERVER.URL + '/videos/watch/' + video.uuid
+}
+
+function getVideoChannelActivityPubUrl (videoChannel: VideoChannelInstance) {
+  return CONFIG.WEBSERVER.URL + '/video-channels/' + videoChannel.uuid
+}
+
+function getAccountActivityPubUrl (accountName: string) {
+  return CONFIG.WEBSERVER.URL + '/account/' + accountName
+}
+
+function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseInstance) {
+  return CONFIG.WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id
+}
+
+function getAccountFollowActivityPubUrl (accountFollow: AccountFollowInstance) {
+  const me = accountFollow.AccountFollower
+  const following = accountFollow.AccountFollowing
+
+  return me.url + '#follows/' + following.id
+}
+
+function getAccountFollowAcceptActivityPubUrl (accountFollow: AccountFollowInstance) {
+  const follower = accountFollow.AccountFollower
+  const me = accountFollow.AccountFollowing
+
+  return follower.url + '#accepts/follows/' + me.id
+}
+
+function getAnnounceActivityPubUrl (originalUrl: string, byAccount: AccountInstance) {
+  return originalUrl + '#announces/' + byAccount.id
+}
+
+function getUpdateActivityPubUrl (originalUrl: string, updatedAt: string) {
+  return originalUrl + '#updates/' + updatedAt
+}
+
+function getUndoActivityPubUrl (originalUrl: string) {
+  return originalUrl + '/undo'
+}
+
+export {
+  getVideoActivityPubUrl,
+  getVideoChannelActivityPubUrl,
+  getAccountActivityPubUrl,
+  getVideoAbuseActivityPubUrl,
+  getAccountFollowActivityPubUrl,
+  getAccountFollowAcceptActivityPubUrl,
+  getAnnounceActivityPubUrl,
+  getUpdateActivityPubUrl,
+  getUndoActivityPubUrl
+}
diff --git a/server/lib/activitypub/video-channels.ts b/server/lib/activitypub/video-channels.ts
new file mode 100644 (file)
index 0000000..7339d79
--- /dev/null
@@ -0,0 +1,60 @@
+import { VideoChannelObject } from '../../../shared/models/activitypub/objects/video-channel-object'
+import { isVideoChannelObjectValid } from '../../helpers/custom-validators/activitypub/video-channels'
+import { logger } from '../../helpers/logger'
+import { doRequest } from '../../helpers/requests'
+import { database as db } from '../../initializers'
+import { ACTIVITY_PUB } from '../../initializers/constants'
+import { AccountInstance } from '../../models/account/account-interface'
+import { videoChannelActivityObjectToDBAttributes } from './process/misc'
+
+async function getOrCreateVideoChannel (ownerAccount: AccountInstance, videoChannelUrl: string) {
+  let videoChannel = await db.VideoChannel.loadByUrl(videoChannelUrl)
+
+  // We don't have this account in our database, fetch it on remote
+  if (!videoChannel) {
+    videoChannel = await fetchRemoteVideoChannel(ownerAccount, videoChannelUrl)
+    if (videoChannel === undefined) throw new Error('Cannot fetch remote video channel.')
+
+    // Save our new video channel in database
+    await videoChannel.save()
+  }
+
+  return videoChannel
+}
+
+async function fetchRemoteVideoChannel (ownerAccount: AccountInstance, videoChannelUrl: string) {
+  const options = {
+    uri: videoChannelUrl,
+    method: 'GET',
+    headers: {
+      'Accept': ACTIVITY_PUB.ACCEPT_HEADER
+    }
+  }
+
+  logger.info('Fetching remote video channel %s.', videoChannelUrl)
+
+  let requestResult
+  try {
+    requestResult = await doRequest(options)
+  } catch (err) {
+    logger.warn('Cannot fetch remote video channel %s.', videoChannelUrl, err)
+    return undefined
+  }
+
+  const videoChannelJSON: VideoChannelObject = JSON.parse(requestResult.body)
+  if (isVideoChannelObjectValid(videoChannelJSON) === false) {
+    logger.debug('Remote video channel JSON is not valid.', { videoChannelJSON })
+    return undefined
+  }
+
+  const videoChannelAttributes = videoChannelActivityObjectToDBAttributes(videoChannelJSON, ownerAccount)
+  const videoChannel = db.VideoChannel.build(videoChannelAttributes)
+  videoChannel.Account = ownerAccount
+
+  return videoChannel
+}
+
+export {
+  getOrCreateVideoChannel,
+  fetchRemoteVideoChannel
+}
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts
new file mode 100644 (file)
index 0000000..9442448
--- /dev/null
@@ -0,0 +1,44 @@
+import { join } from 'path'
+import * as request from 'request'
+import { ActivityIconObject } from '../../../shared/index'
+import { doRequest, doRequestAndSaveToFile } from '../../helpers/requests'
+import { CONFIG, REMOTE_SCHEME, STATIC_PATHS } from '../../initializers/constants'
+import { VideoInstance } from '../../models/video/video-interface'
+
+function fetchRemoteVideoPreview (video: VideoInstance) {
+  // FIXME: use url
+  const host = video.VideoChannel.Account.Server.host
+  const path = join(STATIC_PATHS.PREVIEWS, video.getPreviewName())
+
+  return request.get(REMOTE_SCHEME.HTTP + '://' + host + path)
+}
+
+async function fetchRemoteVideoDescription (video: VideoInstance) {
+  // FIXME: use url
+  const host = video.VideoChannel.Account.Server.host
+  const path = video.getDescriptionPath()
+  const options = {
+    uri: REMOTE_SCHEME.HTTP + '://' + host + path,
+    json: true
+  }
+
+  const { body } = await doRequest(options)
+  return body.description ? body.description : ''
+}
+
+function generateThumbnailFromUrl (video: VideoInstance, icon: ActivityIconObject) {
+  const thumbnailName = video.getThumbnailName()
+  const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, thumbnailName)
+
+  const options = {
+    method: 'GET',
+    uri: icon.url
+  }
+  return doRequestAndSaveToFile(options, thumbnailPath)
+}
+
+export {
+  fetchRemoteVideoPreview,
+  fetchRemoteVideoDescription,
+  generateThumbnailFromUrl
+}
index 0570f51e8ec624f821f2a7db000a859e9e14c222..7f352f361df8e84905555eb22636212d216551b4 100644 (file)
@@ -3,8 +3,9 @@ import { join } from 'path'
 import { createWriteStream } from 'fs'
 
 import { database as db, CONFIG, CACHE } from '../../initializers'
-import { logger, unlinkPromise, fetchRemoteVideoPreview } from '../../helpers'
+import { logger, unlinkPromise } from '../../helpers'
 import { VideoInstance } from '../../models'
+import { fetchRemoteVideoPreview } from '../activitypub/videos'
 
 class VideosPreviewCache {
 
index f26110973cfc771ace370a6b0bbbd54a131dfc48..e65ab3ee1df995b00b063738a9b375414afaad8e 100644 (file)
@@ -1,13 +1,11 @@
 import * as Bluebird from 'bluebird'
 import { computeResolutionsToTranscode, logger } from '../../../helpers'
-
 import { database as db } from '../../../initializers/database'
 import { VideoInstance } from '../../../models'
-
+import { sendAddVideo } from '../../activitypub/send/send-add'
 import { JobScheduler } from '../job-scheduler'
 import { TranscodingJobPayload } from './transcoding-job-scheduler'
-import { shareVideoByServer } from '../../../helpers/activitypub'
-import { sendAddVideo } from '../../activitypub/send/send-add'
+import { shareVideoByServer } from '../../activitypub/share'
 
 async function process (data: TranscodingJobPayload, jobId: number) {
   const video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(data.videoUUID)
index d54ffc916d34bee45960655a06ddde8370229361..5653d8e655fae3bb796a9ac919933661b27e29e5 100644 (file)
@@ -5,7 +5,7 @@ import { CONFIG } from '../initializers/constants'
 import { UserInstance } from '../models'
 import { createVideoChannel } from './video-channel'
 import { logger } from '../helpers/logger'
-import { getAccountActivityPubUrl } from '../helpers/activitypub'
+import { getAccountActivityPubUrl } from './activitypub/url'
 
 async function createUserAccountAndChannel (user: UserInstance, validateUser = true) {
   const { account, videoChannel } = await db.sequelize.transaction(async t => {
index 5235d9cb56d2c56036a971e60c3d99ca5ae95124..a939a23d5893ef043e3c15ff879a221753cb0574 100644 (file)
@@ -3,7 +3,7 @@ import { VideoChannelCreate } from '../../shared/models'
 import { logger } from '../helpers'
 import { database as db } from '../initializers'
 import { AccountInstance } from '../models'
-import { getVideoChannelActivityPubUrl } from '../helpers/activitypub'
+import { getVideoChannelActivityPubUrl } from './activitypub/url'
 
 async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountInstance, t: Sequelize.Transaction) {
   const videoChannelData = {
index 8e8a3961b3c5f70c7ef077dac7b54b546cad5022..29bec0c974d13a90d147ee849c3261e5c2f430b0 100644 (file)
@@ -2,9 +2,9 @@ import { eachSeries } from 'async'
 import { NextFunction, Request, RequestHandler, Response } from 'express'
 import { ActivityPubSignature } from '../../shared'
 import { isSignatureVerified, logger } from '../helpers'
-import { fetchRemoteAccountAndCreateServer } from '../helpers/activitypub'
 import { database as db } from '../initializers'
 import { ACTIVITY_PUB } from '../initializers/constants'
+import { fetchRemoteAccountAndCreateServer } from '../lib/activitypub/account'
 
 async function checkSignature (req: Request, res: Response, next: NextFunction) {
   const signatureObject: ActivityPubSignature = req.body.signature