From 8dc8a34ee8428e7657414115d1c137592efa174d Mon Sep 17 00:00:00 2001
From: Chocobozzz <me@florianbigard.com>
Date: Thu, 23 Apr 2020 09:32:53 +0200
Subject: [PATCH] Avoir some circular dependencies

---
 scripts/create-transcoding-job.ts             |   2 +-
 server/controllers/activitypub/client.ts      |   6 +-
 server/controllers/api/accounts.ts            |   3 +-
 server/controllers/api/search.ts              |   6 +-
 server/controllers/api/server/follows.ts      |   3 +-
 .../api/server/server-blocklist.ts            |   3 +-
 server/controllers/api/video-channel.ts       |   5 +-
 server/controllers/api/video-playlist.ts      |   3 +-
 server/controllers/api/videos/abuse.ts        |   3 +-
 server/controllers/api/videos/blacklist.ts    |   2 +-
 server/controllers/api/videos/captions.ts     |   2 +-
 server/controllers/api/videos/import.ts       |   2 +-
 server/controllers/api/videos/index.ts        |  14 ++-
 server/controllers/api/videos/ownership.ts    |   2 +-
 server/controllers/api/videos/rate.ts         |   2 +-
 server/helpers/peertube-crypto.ts             |   8 +-
 server/helpers/utils.ts                       |  13 ---
 server/helpers/video.ts                       |  42 +++++++-
 server/helpers/webtorrent.ts                  |   2 +-
 server/initializers/checker-after-init.ts     |   3 +-
 server/lib/activitypub/actor.ts               |   2 +-
 server/lib/activitypub/follow.ts              |   2 +-
 server/lib/activitypub/index.ts               |   9 --
 .../lib/activitypub/process/process-follow.ts |   2 +-
 server/lib/activitypub/send/send-create.ts    |   2 +-
 server/lib/activitypub/send/send-delete.ts    |   2 +-
 server/lib/activitypub/send/send-update.ts    |   2 +-
 server/lib/activitypub/send/utils.ts          |   2 +-
 server/lib/activitypub/share.ts               |   2 +-
 server/lib/activitypub/videos.ts              |   3 +-
 server/lib/avatar.ts                          |   2 +-
 server/lib/emailer.ts                         |  14 +--
 .../job-queue/handlers/activitypub-follow.ts  |   9 +-
 .../handlers/activitypub-http-broadcast.ts    |   9 +-
 .../handlers/activitypub-http-fetcher.ts      |  13 +--
 .../handlers/activitypub-http-unicast.ts      |   9 +-
 .../handlers/activitypub-refresher.ts         |  10 +-
 server/lib/job-queue/handlers/email.ts        |   5 +-
 .../handlers/utils/activitypub-http-utils.ts  |  11 +-
 .../job-queue/handlers/video-file-import.ts   |   6 +-
 server/lib/job-queue/handlers/video-import.ts |  23 +---
 .../job-queue/handlers/video-redundancy.ts    |   5 +-
 .../job-queue/handlers/video-transcoding.ts   |  42 ++------
 server/lib/job-queue/handlers/video-views.ts  |   2 +-
 server/lib/job-queue/job-queue.ts             |  28 +++--
 server/lib/notifier.ts                        |   2 +-
 server/lib/redundancy.ts                      |   2 +-
 .../schedulers/auto-follow-index-instances.ts |   2 +-
 .../lib/schedulers/update-videos-scheduler.ts |   2 +-
 .../schedulers/videos-redundancy-scheduler.ts |   4 +-
 server/lib/video-comment.ts                   |   2 +-
 server/lib/video-paths.ts                     |   2 +-
 server/lib/video-playlist.ts                  |   2 +-
 server/lib/videos.ts                          |  36 -------
 server/middlewares/activitypub.ts             |   2 +-
 .../validators/activitypub/activity.ts        |   2 +-
 server/middlewares/validators/blocklist.ts    |   2 +-
 server/middlewares/validators/follows.ts      |   2 +-
 .../middlewares/validators/videos/videos.ts   |   2 +-
 server/models/activitypub/actor-follow.ts     |   2 +-
 server/models/application/application.ts      |  11 ++
 server/models/redundancy/video-redundancy.ts  |   2 +-
 server/models/video/video-comment.ts          |   2 +-
 server/models/video/video-format-utils.ts     |   4 +-
 server/models/video/video.ts                  |   2 +-
 server/tests/api/activitypub/security.ts      |   3 +-
 shared/models/server/emailer.model.ts         |   8 ++
 shared/models/server/index.ts                 |   1 +
 shared/models/server/job.model.ts             | 100 ++++++++++++++++++
 69 files changed, 278 insertions(+), 263 deletions(-)
 delete mode 100644 server/lib/activitypub/index.ts
 delete mode 100644 server/lib/videos.ts
 create mode 100644 shared/models/server/emailer.model.ts

diff --git a/scripts/create-transcoding-job.ts b/scripts/create-transcoding-job.ts
index fec58da2e..1312e8952 100755
--- a/scripts/create-transcoding-job.ts
+++ b/scripts/create-transcoding-job.ts
@@ -5,8 +5,8 @@ import * as program from 'commander'
 import { VideoModel } from '../server/models/video/video'
 import { initDatabaseModels } from '../server/initializers'
 import { JobQueue } from '../server/lib/job-queue'
-import { VideoTranscodingPayload } from '../server/lib/job-queue/handlers/video-transcoding'
 import { computeResolutionsToTranscode } from '@server/helpers/ffmpeg-utils'
+import { VideoTranscodingPayload } from '@shared/models'
 
 program
   .option('-v, --video [videoUUID]', 'Video UUID')
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts
index c3aeeebf5..e44f1c6ab 100644
--- a/server/controllers/activitypub/client.ts
+++ b/server/controllers/activitypub/client.ts
@@ -24,20 +24,20 @@ import { cacheRoute } from '../../middlewares/cache'
 import { activityPubResponse } from './utils'
 import { AccountVideoRateModel } from '../../models/account/account-video-rate'
 import {
-  getRateUrl,
   getVideoCommentsActivityPubUrl,
   getVideoDislikesActivityPubUrl,
   getVideoLikesActivityPubUrl,
   getVideoSharesActivityPubUrl
-} from '../../lib/activitypub'
+} from '../../lib/activitypub/url'
 import { VideoCaptionModel } from '../../models/video/video-caption'
 import { videoFileRedundancyGetValidator, videoPlaylistRedundancyGetValidator } from '../../middlewares/validators/redundancy'
-import { getServerActor } from '../../helpers/utils'
 import { buildDislikeActivity } from '../../lib/activitypub/send/send-dislike'
 import { videoPlaylistElementAPGetValidator, videoPlaylistsGetValidator } from '../../middlewares/validators/videos/video-playlists'
 import { VideoPlaylistModel } from '../../models/video/video-playlist'
 import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
 import { MAccountId, MActorId, MVideoAPWithoutCaption, MVideoId } from '@server/typings/models'
+import { getServerActor } from '@server/models/application/application'
+import { getRateUrl } from '@server/lib/activitypub/video-rates'
 
 const activityPubClientRouter = express.Router()
 activityPubClientRouter.use(cors())
diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts
index f8d2bad8b..3bbb0a43e 100644
--- a/server/controllers/api/accounts.ts
+++ b/server/controllers/api/accounts.ts
@@ -1,5 +1,5 @@
 import * as express from 'express'
-import { getFormattedObjects, getServerActor } from '../../helpers/utils'
+import { getFormattedObjects} from '../../helpers/utils'
 import {
   asyncMiddleware,
   authenticate,
@@ -28,6 +28,7 @@ import { VideoChannelModel } from '../../models/video/video-channel'
 import { JobQueue } from '../../lib/job-queue'
 import { VideoPlaylistModel } from '../../models/video/video-playlist'
 import { commonVideoPlaylistFiltersValidator, videoPlaylistsSearchValidator } from '../../middlewares/validators/videos/video-playlists'
+import { getServerActor } from '@server/models/application/application'
 
 const accountsRouter = express.Router()
 
diff --git a/server/controllers/api/search.ts b/server/controllers/api/search.ts
index 16ffbf683..35d94d747 100644
--- a/server/controllers/api/search.ts
+++ b/server/controllers/api/search.ts
@@ -1,6 +1,6 @@
 import * as express from 'express'
 import { buildNSFWFilter, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils'
-import { getFormattedObjects, getServerActor } from '../../helpers/utils'
+import { getFormattedObjects } from '../../helpers/utils'
 import { VideoModel } from '../../models/video/video'
 import {
   asyncMiddleware,
@@ -15,11 +15,13 @@ import {
   videosSearchValidator
 } from '../../middlewares'
 import { VideoChannelsSearchQuery, VideosSearchQuery } from '../../../shared/models/search'
-import { getOrCreateActorAndServerAndModel, getOrCreateVideoAndAccountAndChannel } from '../../lib/activitypub'
+import { getOrCreateActorAndServerAndModel } from '../../lib/activitypub/actor'
 import { logger } from '../../helpers/logger'
 import { VideoChannelModel } from '../../models/video/video-channel'
 import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger'
 import { MChannelAccountDefault, MVideoAccountLightBlacklistAllFiles } from '../../typings/models'
+import { getServerActor } from '@server/models/application/application'
+import { getOrCreateVideoAndAccountAndChannel } from '@server/lib/activitypub/videos'
 
 const searchRouter = express.Router()
 
diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts
index 0bc20bd60..82e9ef898 100644
--- a/server/controllers/api/server/follows.ts
+++ b/server/controllers/api/server/follows.ts
@@ -1,7 +1,7 @@
 import * as express from 'express'
 import { UserRight } from '../../../../shared/models/users'
 import { logger } from '../../../helpers/logger'
-import { getFormattedObjects, getServerActor } from '../../../helpers/utils'
+import { getFormattedObjects} from '../../../helpers/utils'
 import { SERVER_ACTOR_NAME } from '../../../initializers/constants'
 import { sendAccept, sendReject, sendUndoFollow } from '../../../lib/activitypub/send'
 import {
@@ -27,6 +27,7 @@ import { JobQueue } from '../../../lib/job-queue'
 import { removeRedundanciesOfServer } from '../../../lib/redundancy'
 import { sequelizeTypescript } from '../../../initializers/database'
 import { autoFollowBackIfNeeded } from '../../../lib/activitypub/follow'
+import { getServerActor } from '@server/models/application/application'
 
 const serverFollowsRouter = express.Router()
 serverFollowsRouter.get('/following',
diff --git a/server/controllers/api/server/server-blocklist.ts b/server/controllers/api/server/server-blocklist.ts
index ffb7814fa..008b8d4ea 100644
--- a/server/controllers/api/server/server-blocklist.ts
+++ b/server/controllers/api/server/server-blocklist.ts
@@ -1,6 +1,6 @@
 import * as express from 'express'
 import 'multer'
-import { getFormattedObjects, getServerActor } from '../../../helpers/utils'
+import { getFormattedObjects} from '../../../helpers/utils'
 import {
   asyncMiddleware,
   asyncRetryTransactionMiddleware,
@@ -22,6 +22,7 @@ import { AccountBlocklistModel } from '../../../models/account/account-blocklist
 import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist'
 import { ServerBlocklistModel } from '../../../models/server/server-blocklist'
 import { UserRight } from '../../../../shared/models/users'
+import { getServerActor } from '@server/models/application/application'
 
 const serverBlocklistRouter = express.Router()
 
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts
index a808896ff..faef5ba4b 100644
--- a/server/controllers/api/video-channel.ts
+++ b/server/controllers/api/video-channel.ts
@@ -1,5 +1,5 @@
 import * as express from 'express'
-import { getFormattedObjects, getServerActor } from '../../helpers/utils'
+import { getFormattedObjects} from '../../helpers/utils'
 import {
   asyncMiddleware,
   asyncRetryTransactionMiddleware,
@@ -21,7 +21,7 @@ import { sendUpdateActor } from '../../lib/activitypub/send'
 import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared'
 import { createLocalVideoChannel, federateAllVideosOfChannel } from '../../lib/video-channel'
 import { buildNSFWFilter, createReqFiles, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils'
-import { setAsyncActorKeys } from '../../lib/activitypub'
+import { setAsyncActorKeys } from '../../lib/activitypub/actor'
 import { AccountModel } from '../../models/account/account'
 import { MIMETYPES } from '../../initializers/constants'
 import { logger } from '../../helpers/logger'
@@ -36,6 +36,7 @@ import { commonVideoPlaylistFiltersValidator } from '../../middlewares/validator
 import { CONFIG } from '../../initializers/config'
 import { sequelizeTypescript } from '../../initializers/database'
 import { MChannelAccountDefault } from '@server/typings/models'
+import { getServerActor } from '@server/models/application/application'
 
 const auditLogger = auditLoggerFactory('channels')
 const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR })
diff --git a/server/controllers/api/video-playlist.ts b/server/controllers/api/video-playlist.ts
index aa9053372..49ac3c80e 100644
--- a/server/controllers/api/video-playlist.ts
+++ b/server/controllers/api/video-playlist.ts
@@ -1,5 +1,5 @@
 import * as express from 'express'
-import { getFormattedObjects, getServerActor } from '../../helpers/utils'
+import { getFormattedObjects} from '../../helpers/utils'
 import {
   asyncMiddleware,
   asyncRetryTransactionMiddleware,
@@ -41,6 +41,7 @@ import { CONFIG } from '../../initializers/config'
 import { sequelizeTypescript } from '../../initializers/database'
 import { createPlaylistMiniatureFromExisting } from '../../lib/thumbnail'
 import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/typings/models'
+import { getServerActor } from '@server/models/application/application'
 
 const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { thumbnailfile: CONFIG.STORAGE.TMP_DIR })
 
diff --git a/server/controllers/api/videos/abuse.ts b/server/controllers/api/videos/abuse.ts
index f37d90896..bc7df48c8 100644
--- a/server/controllers/api/videos/abuse.ts
+++ b/server/controllers/api/videos/abuse.ts
@@ -1,7 +1,7 @@
 import * as express from 'express'
 import { UserRight, VideoAbuseCreate, VideoAbuseState } from '../../../../shared'
 import { logger } from '../../../helpers/logger'
-import { getFormattedObjects, getServerActor } from '../../../helpers/utils'
+import { getFormattedObjects } from '../../../helpers/utils'
 import { sequelizeTypescript } from '../../../initializers'
 import {
   asyncMiddleware,
@@ -22,6 +22,7 @@ import { auditLoggerFactory, VideoAbuseAuditView } from '../../../helpers/audit-
 import { Notifier } from '../../../lib/notifier'
 import { sendVideoAbuse } from '../../../lib/activitypub/send/send-flag'
 import { MVideoAbuseAccountVideo } from '../../../typings/models/video'
+import { getServerActor } from '@server/models/application/application'
 
 const auditLogger = auditLoggerFactory('abuse')
 const abuseVideoRouter = express.Router()
diff --git a/server/controllers/api/videos/blacklist.ts b/server/controllers/api/videos/blacklist.ts
index c4aa79cd2..abd09387c 100644
--- a/server/controllers/api/videos/blacklist.ts
+++ b/server/controllers/api/videos/blacklist.ts
@@ -19,7 +19,7 @@ import { VideoBlacklistModel } from '../../../models/video/video-blacklist'
 import { sequelizeTypescript } from '../../../initializers'
 import { Notifier } from '../../../lib/notifier'
 import { sendDeleteVideo } from '../../../lib/activitypub/send'
-import { federateVideoIfNeeded } from '../../../lib/activitypub'
+import { federateVideoIfNeeded } from '../../../lib/activitypub/videos'
 import { MVideoBlacklistVideo } from '@server/typings/models'
 
 const blacklistRouter = express.Router()
diff --git a/server/controllers/api/videos/captions.ts b/server/controllers/api/videos/captions.ts
index fd7b165fb..8c1d12ca8 100644
--- a/server/controllers/api/videos/captions.ts
+++ b/server/controllers/api/videos/captions.ts
@@ -6,7 +6,7 @@ import { MIMETYPES } from '../../../initializers/constants'
 import { getFormattedObjects } from '../../../helpers/utils'
 import { VideoCaptionModel } from '../../../models/video/video-caption'
 import { logger } from '../../../helpers/logger'
-import { federateVideoIfNeeded } from '../../../lib/activitypub'
+import { federateVideoIfNeeded } from '../../../lib/activitypub/videos'
 import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils'
 import { CONFIG } from '../../../initializers/config'
 import { sequelizeTypescript } from '../../../initializers/database'
diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts
index be96ef42c..b4f70a086 100644
--- a/server/controllers/api/videos/import.ts
+++ b/server/controllers/api/videos/import.ts
@@ -10,7 +10,7 @@ import { VideoImportCreate, VideoImportState, VideoPrivacy, VideoState } from '.
 import { VideoModel } from '../../../models/video/video'
 import { VideoCaptionModel } from '../../../models/video/video-caption'
 import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils'
-import { getVideoActivityPubUrl } from '../../../lib/activitypub'
+import { getVideoActivityPubUrl } from '../../../lib/activitypub/url'
 import { TagModel } from '../../../models/video/tag'
 import { VideoImportModel } from '../../../models/video/video-import'
 import { JobQueue } from '../../../lib/job-queue/job-queue'
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 04d775cbf..8048c568c 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -4,7 +4,7 @@ import { VideoCreate, VideoPrivacy, VideoState, VideoUpdate } from '../../../../
 import { getMetadataFromFile, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils'
 import { logger } from '../../../helpers/logger'
 import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger'
-import { getFormattedObjects, getServerActor } from '../../../helpers/utils'
+import { getFormattedObjects } from '../../../helpers/utils'
 import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist'
 import {
   DEFAULT_AUDIO_RESOLUTION,
@@ -14,12 +14,7 @@ import {
   VIDEO_LICENCES,
   VIDEO_PRIVACIES
 } from '../../../initializers/constants'
-import {
-  changeVideoChannelShare,
-  federateVideoIfNeeded,
-  fetchRemoteVideoDescription,
-  getVideoActivityPubUrl
-} from '../../../lib/activitypub'
+import { federateVideoIfNeeded, fetchRemoteVideoDescription } from '../../../lib/activitypub/videos'
 import { JobQueue } from '../../../lib/job-queue'
 import { Redis } from '../../../lib/redis'
 import {
@@ -67,7 +62,10 @@ import { MVideoDetails, MVideoFullLight } from '@server/typings/models'
 import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
 import { getVideoFilePath } from '@server/lib/video-paths'
 import toInt from 'validator/lib/toInt'
-import { addOptimizeOrMergeAudioJob } from '@server/lib/videos'
+import { addOptimizeOrMergeAudioJob } from '@server/helpers/video'
+import { getServerActor } from '@server/models/application/application'
+import { changeVideoChannelShare } from '@server/lib/activitypub/share'
+import { getVideoActivityPubUrl } from '@server/lib/activitypub/url'
 
 const auditLogger = auditLoggerFactory('videos')
 const videosRouter = express.Router()
diff --git a/server/controllers/api/videos/ownership.ts b/server/controllers/api/videos/ownership.ts
index 41d7cdc43..190036f85 100644
--- a/server/controllers/api/videos/ownership.ts
+++ b/server/controllers/api/videos/ownership.ts
@@ -15,7 +15,7 @@ import { VideoChangeOwnershipModel } from '../../../models/video/video-change-ow
 import { VideoChangeOwnershipStatus, VideoState } from '../../../../shared/models/videos'
 import { VideoChannelModel } from '../../../models/video/video-channel'
 import { getFormattedObjects } from '../../../helpers/utils'
-import { changeVideoChannelShare } from '../../../lib/activitypub'
+import { changeVideoChannelShare } from '../../../lib/activitypub/share'
 import { sendUpdateVideo } from '../../../lib/activitypub/send'
 import { VideoModel } from '../../../models/video/video'
 import { MVideoFullLight } from '@server/typings/models'
diff --git a/server/controllers/api/videos/rate.ts b/server/controllers/api/videos/rate.ts
index 3d2f3d728..3ee365289 100644
--- a/server/controllers/api/videos/rate.ts
+++ b/server/controllers/api/videos/rate.ts
@@ -2,7 +2,7 @@ import * as express from 'express'
 import { UserVideoRateUpdate } from '../../../../shared'
 import { logger } from '../../../helpers/logger'
 import { VIDEO_RATE_TYPES } from '../../../initializers/constants'
-import { getRateUrl, sendVideoRateChange } from '../../../lib/activitypub'
+import { getRateUrl, sendVideoRateChange } from '../../../lib/activitypub/video-rates'
 import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoUpdateRateValidator } from '../../../middlewares'
 import { AccountModel } from '../../../models/account/account'
 import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
diff --git a/server/helpers/peertube-crypto.ts b/server/helpers/peertube-crypto.ts
index 89c0ab151..394e97fd5 100644
--- a/server/helpers/peertube-crypto.ts
+++ b/server/helpers/peertube-crypto.ts
@@ -5,7 +5,6 @@ import { jsonld } from './custom-jsonld-signature'
 import { logger } from './logger'
 import { cloneDeep } from 'lodash'
 import { createSign, createVerify } from 'crypto'
-import { buildDigest } from '../lib/job-queue/handlers/utils/activitypub-http-utils'
 import * as bcrypt from 'bcrypt'
 import { MActor } from '../typings/models'
 
@@ -104,12 +103,19 @@ async function signJsonLDObject (byActor: MActor, data: any) {
   return Object.assign(data, { signature })
 }
 
+function buildDigest (body: any) {
+  const rawBody = typeof body === 'string' ? body : JSON.stringify(body)
+
+  return 'SHA-256=' + sha256(rawBody, 'base64')
+}
+
 // ---------------------------------------------------------------------------
 
 export {
   isHTTPSignatureDigestValid,
   parseHTTPSignature,
   isHTTPSignatureVerified,
+  buildDigest,
   isJsonLDSignatureVerified,
   comparePassword,
   createPrivateAndPublicKeys,
diff --git a/server/helpers/utils.ts b/server/helpers/utils.ts
index 11c118292..ad3b7949e 100644
--- a/server/helpers/utils.ts
+++ b/server/helpers/utils.ts
@@ -1,11 +1,9 @@
 import { ResultList } from '../../shared'
-import { ApplicationModel } from '../models/application/application'
 import { execPromise, execPromise2, randomBytesPromise, sha256 } from './core-utils'
 import { logger } from './logger'
 import { join } from 'path'
 import { Instance as ParseTorrent } from 'parse-torrent'
 import { remove } from 'fs-extra'
-import * as memoizee from 'memoizee'
 import { CONFIG } from '../initializers/config'
 import { isVideoFileExtnameValid } from './custom-validators/videos'
 
@@ -33,16 +31,6 @@ function getFormattedObjects<U, V, T extends FormattableToJSON<U, V>> (objects:
   } as ResultList<V>
 }
 
-const getServerActor = memoizee(async function () {
-  const application = await ApplicationModel.load()
-  if (!application) throw Error('Could not load Application from database.')
-
-  const actor = application.Account.Actor
-  actor.Account = application.Account
-
-  return actor
-}, { promise: true })
-
 function generateVideoImportTmpPath (target: string | ParseTorrent, extensionArg?: string) {
   const id = typeof target === 'string'
     ? target
@@ -105,7 +93,6 @@ export {
   generateRandomString,
   getFormattedObjects,
   getSecureTorrentName,
-  getServerActor,
   getServerCommit,
   generateVideoImportTmpPath,
   getUUIDFromFilename
diff --git a/server/helpers/video.ts b/server/helpers/video.ts
index 4fe2a60f0..6f76cbdfc 100644
--- a/server/helpers/video.ts
+++ b/server/helpers/video.ts
@@ -1,14 +1,21 @@
 import { VideoModel } from '../models/video/video'
 import * as Bluebird from 'bluebird'
 import {
+  isStreamingPlaylist,
+  MStreamingPlaylistVideo,
+  MVideo,
   MVideoAccountLightBlacklistAllFiles,
+  MVideoFile,
   MVideoFullLight,
   MVideoIdThumbnail,
+  MVideoImmutable,
   MVideoThumbnail,
-  MVideoWithRights,
-  MVideoImmutable
+  MVideoWithRights
 } from '@server/typings/models'
 import { Response } from 'express'
+import { DEFAULT_AUDIO_RESOLUTION } from '@server/initializers/constants'
+import { JobQueue } from '@server/lib/job-queue'
+import { VideoTranscodingPayload } from '@shared/models'
 
 type VideoFetchType = 'all' | 'only-video' | 'only-video-with-rights' | 'id' | 'none' | 'only-immutable-attributes'
 
@@ -62,10 +69,39 @@ function getVideoWithAttributes (res: Response) {
   return res.locals.videoAll || res.locals.onlyVideo || res.locals.onlyVideoWithRights
 }
 
+function addOptimizeOrMergeAudioJob (video: MVideo, videoFile: MVideoFile) {
+  let dataInput: VideoTranscodingPayload
+
+  if (videoFile.isAudio()) {
+    dataInput = {
+      type: 'merge-audio' as 'merge-audio',
+      resolution: DEFAULT_AUDIO_RESOLUTION,
+      videoUUID: video.uuid,
+      isNewVideo: true
+    }
+  } else {
+    dataInput = {
+      type: 'optimize' as 'optimize',
+      videoUUID: video.uuid,
+      isNewVideo: true
+    }
+  }
+
+  return JobQueue.Instance.createJobWithPromise({ type: 'video-transcoding', payload: dataInput })
+}
+
+function extractVideo (videoOrPlaylist: MVideo | MStreamingPlaylistVideo) {
+  return isStreamingPlaylist(videoOrPlaylist)
+    ? videoOrPlaylist.Video
+    : videoOrPlaylist
+}
+
 export {
   VideoFetchType,
   VideoFetchByUrlType,
   fetchVideo,
   getVideoWithAttributes,
-  fetchVideoByUrl
+  fetchVideoByUrl,
+  addOptimizeOrMergeAudioJob,
+  extractVideo
 }
diff --git a/server/helpers/webtorrent.ts b/server/helpers/webtorrent.ts
index b25e44fcd..7cd76d708 100644
--- a/server/helpers/webtorrent.ts
+++ b/server/helpers/webtorrent.ts
@@ -13,8 +13,8 @@ import { WEBSERVER } from '@server/initializers/constants'
 import * as parseTorrent from 'parse-torrent'
 import * as magnetUtil from 'magnet-uri'
 import { isArray } from '@server/helpers/custom-validators/misc'
-import { extractVideo } from '@server/lib/videos'
 import { getTorrentFileName, getVideoFilePath } from '@server/lib/video-paths'
+import { extractVideo } from '@server/helpers/video'
 
 const createTorrentPromise = promisify2<string, any, any>(createTorrent)
 
diff --git a/server/initializers/checker-after-init.ts b/server/initializers/checker-after-init.ts
index a57d552df..f111be2ae 100644
--- a/server/initializers/checker-after-init.ts
+++ b/server/initializers/checker-after-init.ts
@@ -1,12 +1,11 @@
 import * as config from 'config'
 import { isProdInstance, isTestInstance } from '../helpers/core-utils'
 import { UserModel } from '../models/account/user'
-import { ApplicationModel } from '../models/application/application'
+import { getServerActor, ApplicationModel } from '../models/application/application'
 import { OAuthClientModel } from '../models/oauth/oauth-client'
 import { URL } from 'url'
 import { CONFIG, isEmailEnabled } from './config'
 import { logger } from '../helpers/logger'
-import { getServerActor } from '../helpers/utils'
 import { RecentlyAddedStrategy } from '../../shared/models/redundancy'
 import { isArray } from '../helpers/custom-validators/misc'
 import { uniq } from 'lodash'
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts
index 8132ac135..c743dcf3f 100644
--- a/server/lib/activitypub/actor.ts
+++ b/server/lib/activitypub/actor.ts
@@ -19,7 +19,6 @@ import { AvatarModel } from '../../models/avatar/avatar'
 import { ServerModel } from '../../models/server/server'
 import { VideoChannelModel } from '../../models/video/video-channel'
 import { JobQueue } from '../job-queue'
-import { getServerActor } from '../../helpers/utils'
 import { ActorFetchByUrlType, fetchActorByUrl } from '../../helpers/actor'
 import { sequelizeTypescript } from '../../initializers/database'
 import {
@@ -36,6 +35,7 @@ import {
   MChannel
 } from '../../typings/models'
 import { extname } from 'path'
+import { getServerActor } from '@server/models/application/application'
 
 // Set account keys, this could be long so process after the account creation and do not block the client
 function setAsyncActorKeys <T extends MActor> (actor: T) {
diff --git a/server/lib/activitypub/follow.ts b/server/lib/activitypub/follow.ts
index a1c95504e..3b5ad47c9 100644
--- a/server/lib/activitypub/follow.ts
+++ b/server/lib/activitypub/follow.ts
@@ -3,8 +3,8 @@ import { CONFIG } from '../../initializers/config'
 import { SERVER_ACTOR_NAME } from '../../initializers/constants'
 import { JobQueue } from '../job-queue'
 import { logger } from '../../helpers/logger'
-import { getServerActor } from '../../helpers/utils'
 import { ServerModel } from '../../models/server/server'
+import { getServerActor } from '@server/models/application/application'
 
 async function autoFollowBackIfNeeded (actorFollow: MActorFollowActors) {
   if (!CONFIG.FOLLOWINGS.INSTANCE.AUTO_FOLLOW_BACK.ENABLED) return
diff --git a/server/lib/activitypub/index.ts b/server/lib/activitypub/index.ts
deleted file mode 100644
index d8c7d83b7..000000000
--- a/server/lib/activitypub/index.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-export * from './process'
-export * from './send'
-export * from './actor'
-export * from './share'
-export * from './playlist'
-export * from './videos'
-export * from './video-comments'
-export * from './video-rates'
-export * from './url'
diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts
index db7fb8568..8f7828e41 100644
--- a/server/lib/activitypub/process/process-follow.ts
+++ b/server/lib/activitypub/process/process-follow.ts
@@ -7,11 +7,11 @@ import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
 import { sendAccept, sendReject } from '../send'
 import { Notifier } from '../../notifier'
 import { getAPId } from '../../../helpers/activitypub'
-import { getServerActor } from '../../../helpers/utils'
 import { CONFIG } from '../../../initializers/config'
 import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
 import { MActorFollowActors, MActorSignature } from '../../../typings/models'
 import { autoFollowBackIfNeeded } from '../follow'
+import { getServerActor } from '@server/models/application/application'
 
 async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) {
   const { activity, byActor } = options
diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts
index e2fa061e8..0635c7b66 100644
--- a/server/lib/activitypub/send/send-create.ts
+++ b/server/lib/activitypub/send/send-create.ts
@@ -6,7 +6,6 @@ import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unic
 import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf, getVideoCommentAudience } from '../audience'
 import { logger } from '../../../helpers/logger'
 import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
-import { getServerActor } from '../../../helpers/utils'
 import {
   MActorLight,
   MCommentOwnerVideo,
@@ -17,6 +16,7 @@ import {
   MVideoRedundancyStreamingPlaylistVideo
 } from '../../../typings/models'
 import { ContextType } from '@server/helpers/activitypub'
+import { getServerActor } from '@server/models/application/application'
 
 async function sendCreateVideo (video: MVideoAP, t: Transaction) {
   if (!video.hasPrivacyForFederation()) return undefined
diff --git a/server/lib/activitypub/send/send-delete.ts b/server/lib/activitypub/send/send-delete.ts
index 3225ebf32..fd3f06dec 100644
--- a/server/lib/activitypub/send/send-delete.ts
+++ b/server/lib/activitypub/send/send-delete.ts
@@ -7,9 +7,9 @@ import { getDeleteActivityPubUrl } from '../url'
 import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
 import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience'
 import { logger } from '../../../helpers/logger'
-import { getServerActor } from '../../../helpers/utils'
 import { MCommentOwnerVideoReply, MVideoAccountLight, MVideoPlaylistFullSummary } from '../../../typings/models/video'
 import { MActorUrl } from '../../../typings/models'
+import { getServerActor } from '@server/models/application/application'
 
 async function sendDeleteVideo (video: MVideoAccountLight, transaction: Transaction) {
   logger.info('Creating job to broadcast delete of video %s.', video.url)
diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts
index 2b01ca5e7..7a4cf3f56 100644
--- a/server/lib/activitypub/send/send-update.ts
+++ b/server/lib/activitypub/send/send-update.ts
@@ -9,7 +9,6 @@ import { broadcastToFollowers, sendVideoRelatedActivity } from './utils'
 import { audiencify, getActorsInvolvedInVideo, getAudience } from '../audience'
 import { logger } from '../../../helpers/logger'
 import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
-import { getServerActor } from '../../../helpers/utils'
 import {
   MAccountDefault,
   MActor,
@@ -20,6 +19,7 @@ import {
   MVideoPlaylistFull,
   MVideoRedundancyVideo
 } from '../../../typings/models'
+import { getServerActor } from '@server/models/application/application'
 
 async function sendUpdateVideo (videoArg: MVideoAPWithoutCaption, t: Transaction, overrodeByActor?: MActor) {
   const video = videoArg as MVideoAP
diff --git a/server/lib/activitypub/send/utils.ts b/server/lib/activitypub/send/utils.ts
index 6fd53d71b..0dfcc51be 100644
--- a/server/lib/activitypub/send/utils.ts
+++ b/server/lib/activitypub/send/utils.ts
@@ -5,10 +5,10 @@ import { ActorModel } from '../../../models/activitypub/actor'
 import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
 import { JobQueue } from '../../job-queue'
 import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience'
-import { getServerActor } from '../../../helpers/utils'
 import { afterCommitIfTransaction } from '../../../helpers/database-utils'
 import { MActor, MActorId, MActorLight, MActorWithInboxes, MVideoAccountLight, MVideoId, MVideoImmutable } from '../../../typings/models'
 import { ContextType } from '@server/helpers/activitypub'
+import { getServerActor } from '@server/models/application/application'
 
 async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: {
   byActor: MActorLight
diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts
index a7c645062..d2cbc59a8 100644
--- a/server/lib/activitypub/share.ts
+++ b/server/lib/activitypub/share.ts
@@ -1,5 +1,4 @@
 import { Transaction } from 'sequelize'
-import { getServerActor } from '../../helpers/utils'
 import { VideoShareModel } from '../../models/video/video-share'
 import { sendUndoAnnounce, sendVideoAnnounce } from './send'
 import { getVideoAnnounceActivityPubUrl } from './url'
@@ -10,6 +9,7 @@ import { logger } from '../../helpers/logger'
 import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
 import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
 import { MChannelActorLight, MVideo, MVideoAccountLight, MVideoId } from '../../typings/models/video'
+import { getServerActor } from '@server/models/application/application'
 
 async function shareVideoByServerAndChannel (video: MVideoAccountLight, t: Transaction) {
   if (!video.hasPrivacyForFederation()) return undefined
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts
index 62f589272..7d16bd390 100644
--- a/server/lib/activitypub/videos.ts
+++ b/server/lib/activitypub/videos.ts
@@ -6,7 +6,7 @@ import {
   ActivityHashTagObject,
   ActivityMagnetUrlObject,
   ActivityPlaylistSegmentHashesObject,
-  ActivityPlaylistUrlObject,
+  ActivityPlaylistUrlObject, ActivitypubHttpFetcherPayload,
   ActivityTagObject,
   ActivityUrlObject,
   ActivityVideoUrlObject,
@@ -38,7 +38,6 @@ import { sendCreateVideo, sendUpdateVideo } from './send'
 import { isArray } from '../../helpers/custom-validators/misc'
 import { VideoCaptionModel } from '../../models/video/video-caption'
 import { JobQueue } from '../job-queue'
-import { ActivitypubHttpFetcherPayload } from '../job-queue/handlers/activitypub-http-fetcher'
 import { createRates } from './video-rates'
 import { addVideoShares, shareVideoByServerAndChannel } from './share'
 import { fetchVideoByUrl, VideoFetchByUrlType } from '../../helpers/video'
diff --git a/server/lib/avatar.ts b/server/lib/avatar.ts
index 3de45dd19..282d834a2 100644
--- a/server/lib/avatar.ts
+++ b/server/lib/avatar.ts
@@ -1,7 +1,7 @@
 import 'multer'
 import { sendUpdateActor } from './activitypub/send'
 import { AVATARS_SIZE, LRU_CACHE, QUEUE_CONCURRENCY } from '../initializers/constants'
-import { updateActorAvatarInstance } from './activitypub'
+import { updateActorAvatarInstance } from './activitypub/actor'
 import { processImage } from '../helpers/image-utils'
 import { extname, join } from 'path'
 import { retryTransactionWrapper } from '../helpers/database-utils'
diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts
index 5a99edc7f..45d57fd28 100644
--- a/server/lib/emailer.ts
+++ b/server/lib/emailer.ts
@@ -3,7 +3,6 @@ import { isTestInstance } from '../helpers/core-utils'
 import { bunyanLogger, logger } from '../helpers/logger'
 import { CONFIG, isEmailEnabled } from '../initializers/config'
 import { JobQueue } from './job-queue'
-import { EmailPayload } from './job-queue/handlers/email'
 import { readFileSync } from 'fs-extra'
 import { WEBSERVER } from '../initializers/constants'
 import {
@@ -16,15 +15,7 @@ import {
 } from '../typings/models/video'
 import { MActorFollowActors, MActorFollowFull, MUser } from '../typings/models'
 import { MVideoImport, MVideoImportVideo } from '@server/typings/models/video/video-import'
-
-type SendEmailOptions = {
-  to: string[]
-  subject: string
-  text: string
-
-  fromDisplayName?: string
-  replyTo?: string
-}
+import { EmailPayload } from '@shared/models'
 
 class Emailer {
 
@@ -507,6 +498,5 @@ class Emailer {
 // ---------------------------------------------------------------------------
 
 export {
-  Emailer,
-  SendEmailOptions
+  Emailer
 }
diff --git a/server/lib/job-queue/handlers/activitypub-follow.ts b/server/lib/job-queue/handlers/activitypub-follow.ts
index e467c5b1b..7034c10d0 100644
--- a/server/lib/job-queue/handlers/activitypub-follow.ts
+++ b/server/lib/job-queue/handlers/activitypub-follow.ts
@@ -11,14 +11,7 @@ import { ActorModel } from '../../../models/activitypub/actor'
 import { Notifier } from '../../notifier'
 import { sequelizeTypescript } from '../../../initializers/database'
 import { MActor, MActorFollowActors, MActorFull } from '../../../typings/models'
-
-export type ActivitypubFollowPayload = {
-  followerActorId: number
-  name: string
-  host: string
-  isAutoFollow?: boolean
-  assertIsChannel?: boolean
-}
+import { ActivitypubFollowPayload } from '@shared/models'
 
 async function processActivityPubFollow (job: Bull.Job) {
   const payload = job.data as ActivitypubFollowPayload
diff --git a/server/lib/job-queue/handlers/activitypub-http-broadcast.ts b/server/lib/job-queue/handlers/activitypub-http-broadcast.ts
index 7d9dd61e9..e4d3dbbff 100644
--- a/server/lib/job-queue/handlers/activitypub-http-broadcast.ts
+++ b/server/lib/job-queue/handlers/activitypub-http-broadcast.ts
@@ -5,14 +5,7 @@ import { doRequest } from '../../../helpers/requests'
 import { buildGlobalHeaders, buildSignedRequestOptions, computeBody } from './utils/activitypub-http-utils'
 import { BROADCAST_CONCURRENCY, JOB_REQUEST_TIMEOUT } from '../../../initializers/constants'
 import { ActorFollowScoreCache } from '../../files-cache'
-import { ContextType } from '@server/helpers/activitypub'
-
-export type ActivitypubHttpBroadcastPayload = {
-  uris: string[]
-  signatureActorId?: number
-  body: any
-  contextType?: ContextType
-}
+import { ActivitypubHttpBroadcastPayload } from '@shared/models'
 
 async function processActivityPubHttpBroadcast (job: Bull.Job) {
   logger.info('Processing ActivityPub broadcast in job %d.', job.id)
diff --git a/server/lib/job-queue/handlers/activitypub-http-fetcher.ts b/server/lib/job-queue/handlers/activitypub-http-fetcher.ts
index 0182c5169..524aadc27 100644
--- a/server/lib/job-queue/handlers/activitypub-http-fetcher.ts
+++ b/server/lib/job-queue/handlers/activitypub-http-fetcher.ts
@@ -5,22 +5,15 @@ import { processActivities } from '../../activitypub/process'
 import { addVideoComments } from '../../activitypub/video-comments'
 import { crawlCollectionPage } from '../../activitypub/crawl'
 import { VideoModel } from '../../../models/video/video'
-import { addVideoShares, createRates } from '../../activitypub'
+import { addVideoShares } from '../../activitypub/share'
+import { createRates } from '../../activitypub/video-rates'
 import { createAccountPlaylists } from '../../activitypub/playlist'
 import { AccountModel } from '../../../models/account/account'
 import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
 import { VideoShareModel } from '../../../models/video/video-share'
 import { VideoCommentModel } from '../../../models/video/video-comment'
 import { MAccountDefault, MVideoFullLight } from '../../../typings/models'
-
-type FetchType = 'activity' | 'video-likes' | 'video-dislikes' | 'video-shares' | 'video-comments' | 'account-playlists'
-
-export type ActivitypubHttpFetcherPayload = {
-  uri: string
-  type: FetchType
-  videoId?: number
-  accountId?: number
-}
+import { ActivitypubHttpFetcherPayload, FetchType } from '@shared/models'
 
 async function processActivityPubHttpFetcher (job: Bull.Job) {
   logger.info('Processing ActivityPub fetcher in job %d.', job.id)
diff --git a/server/lib/job-queue/handlers/activitypub-http-unicast.ts b/server/lib/job-queue/handlers/activitypub-http-unicast.ts
index 6b71e2891..b65eeb677 100644
--- a/server/lib/job-queue/handlers/activitypub-http-unicast.ts
+++ b/server/lib/job-queue/handlers/activitypub-http-unicast.ts
@@ -4,14 +4,7 @@ import { doRequest } from '../../../helpers/requests'
 import { buildGlobalHeaders, buildSignedRequestOptions, computeBody } from './utils/activitypub-http-utils'
 import { JOB_REQUEST_TIMEOUT } from '../../../initializers/constants'
 import { ActorFollowScoreCache } from '../../files-cache'
-import { ContextType } from '@server/helpers/activitypub'
-
-export type ActivitypubHttpUnicastPayload = {
-  uri: string
-  signatureActorId?: number
-  body: any
-  contextType?: ContextType
-}
+import { ActivitypubHttpUnicastPayload } from '@shared/models'
 
 async function processActivityPubHttpUnicast (job: Bull.Job) {
   logger.info('Processing ActivityPub unicast in job %d.', job.id)
diff --git a/server/lib/job-queue/handlers/activitypub-refresher.ts b/server/lib/job-queue/handlers/activitypub-refresher.ts
index 4d6c38cfa..666e56868 100644
--- a/server/lib/job-queue/handlers/activitypub-refresher.ts
+++ b/server/lib/job-queue/handlers/activitypub-refresher.ts
@@ -1,14 +1,12 @@
 import * as Bull from 'bull'
 import { logger } from '../../../helpers/logger'
 import { fetchVideoByUrl } from '../../../helpers/video'
-import { refreshActorIfNeeded, refreshVideoIfNeeded, refreshVideoPlaylistIfNeeded } from '../../activitypub'
+import { refreshActorIfNeeded } from '../../activitypub/actor'
+import { refreshVideoIfNeeded } from '../../activitypub/videos'
 import { ActorModel } from '../../../models/activitypub/actor'
 import { VideoPlaylistModel } from '../../../models/video/video-playlist'
-
-export type RefreshPayload = {
-  type: 'video' | 'video-playlist' | 'actor'
-  url: string
-}
+import { RefreshPayload } from '@shared/models'
+import { refreshVideoPlaylistIfNeeded } from '@server/lib/activitypub/playlist'
 
 async function refreshAPObject (job: Bull.Job) {
   const payload = job.data as RefreshPayload
diff --git a/server/lib/job-queue/handlers/email.ts b/server/lib/job-queue/handlers/email.ts
index 62701222c..3157731e2 100644
--- a/server/lib/job-queue/handlers/email.ts
+++ b/server/lib/job-queue/handlers/email.ts
@@ -1,8 +1,7 @@
 import * as Bull from 'bull'
 import { logger } from '../../../helpers/logger'
-import { Emailer, SendEmailOptions } from '../../emailer'
-
-export type EmailPayload = SendEmailOptions
+import { Emailer } from '../../emailer'
+import { EmailPayload } from '@shared/models'
 
 async function processEmail (job: Bull.Job) {
   const payload = job.data as EmailPayload
diff --git a/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts b/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts
index 258ffabee..437ea06fc 100644
--- a/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts
+++ b/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts
@@ -1,9 +1,9 @@
 import { buildSignedActivity, ContextType } from '../../../../helpers/activitypub'
-import { getServerActor } from '../../../../helpers/utils'
 import { ActorModel } from '../../../../models/activitypub/actor'
-import { sha256 } from '../../../../helpers/core-utils'
 import { ACTIVITY_PUB, HTTP_SIGNATURE } from '../../../../initializers/constants'
 import { MActor } from '../../../../typings/models'
+import { getServerActor } from '@server/models/application/application'
+import { buildDigest } from '@server/helpers/peertube-crypto'
 
 type Payload = { body: any, contextType?: ContextType, signatureActorId?: number }
 
@@ -48,14 +48,7 @@ function buildGlobalHeaders (body: any) {
   }
 }
 
-function buildDigest (body: any) {
-  const rawBody = typeof body === 'string' ? body : JSON.stringify(body)
-
-  return 'SHA-256=' + sha256(rawBody, 'base64')
-}
-
 export {
-  buildDigest,
   buildGlobalHeaders,
   computeBody,
   buildSignedRequestOptions
diff --git a/server/lib/job-queue/handlers/video-file-import.ts b/server/lib/job-queue/handlers/video-file-import.ts
index be9e7d181..ae11f1de3 100644
--- a/server/lib/job-queue/handlers/video-file-import.ts
+++ b/server/lib/job-queue/handlers/video-file-import.ts
@@ -9,11 +9,7 @@ import { extname } from 'path'
 import { MVideoFile, MVideoWithFile } from '@server/typings/models'
 import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
 import { getVideoFilePath } from '@server/lib/video-paths'
-
-export type VideoFileImportPayload = {
-  videoUUID: string
-  filePath: string
-}
+import { VideoFileImportPayload } from '@shared/models'
 
 async function processVideoFileImport (job: Bull.Job) {
   const payload = job.data as VideoFileImportPayload
diff --git a/server/lib/job-queue/handlers/video-import.ts b/server/lib/job-queue/handlers/video-import.ts
index 6cdae5b03..ad549c6fc 100644
--- a/server/lib/job-queue/handlers/video-import.ts
+++ b/server/lib/job-queue/handlers/video-import.ts
@@ -7,8 +7,8 @@ import { getDurationFromVideoFile, getVideoFileFPS, getVideoFileResolution } fro
 import { extname } from 'path'
 import { VideoFileModel } from '../../../models/video/video-file'
 import { VIDEO_IMPORT_TIMEOUT } from '../../../initializers/constants'
-import { VideoState } from '../../../../shared'
-import { federateVideoIfNeeded } from '../../activitypub'
+import { VideoImportPayload, VideoImportTorrentPayload, VideoImportYoutubeDLPayload, VideoState } from '../../../../shared'
+import { federateVideoIfNeeded } from '../../activitypub/videos'
 import { VideoModel } from '../../../models/video/video'
 import { createTorrentAndSetInfoHash, downloadWebTorrentVideo } from '../../../helpers/webtorrent'
 import { getSecureTorrentName } from '../../../helpers/utils'
@@ -21,24 +21,7 @@ import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type'
 import { MThumbnail } from '../../../typings/models/video/thumbnail'
 import { MVideoImportDefault, MVideoImportDefaultFiles, MVideoImportVideo } from '@server/typings/models/video/video-import'
 import { getVideoFilePath } from '@server/lib/video-paths'
-import { addOptimizeOrMergeAudioJob } from '@server/lib/videos'
-
-type VideoImportYoutubeDLPayload = {
-  type: 'youtube-dl'
-  videoImportId: number
-
-  generateThumbnail: boolean
-  generatePreview: boolean
-
-  fileExt?: string
-}
-
-type VideoImportTorrentPayload = {
-  type: 'magnet-uri' | 'torrent-file'
-  videoImportId: number
-}
-
-export type VideoImportPayload = VideoImportYoutubeDLPayload | VideoImportTorrentPayload
+import { addOptimizeOrMergeAudioJob } from '@server/helpers/video'
 
 async function processVideoImport (job: Bull.Job) {
   const payload = job.data as VideoImportPayload
diff --git a/server/lib/job-queue/handlers/video-redundancy.ts b/server/lib/job-queue/handlers/video-redundancy.ts
index 319d7090e..6296dab05 100644
--- a/server/lib/job-queue/handlers/video-redundancy.ts
+++ b/server/lib/job-queue/handlers/video-redundancy.ts
@@ -1,10 +1,7 @@
 import * as Bull from 'bull'
 import { logger } from '../../../helpers/logger'
 import { VideosRedundancyScheduler } from '@server/lib/schedulers/videos-redundancy-scheduler'
-
-export type VideoRedundancyPayload = {
-  videoId: number
-}
+import { VideoRedundancyPayload } from '@shared/models'
 
 async function processVideoRedundancy (job: Bull.Job) {
   const payload = job.data as VideoRedundancyPayload
diff --git a/server/lib/job-queue/handlers/video-transcoding.ts b/server/lib/job-queue/handlers/video-transcoding.ts
index c020057c9..46add57d4 100644
--- a/server/lib/job-queue/handlers/video-transcoding.ts
+++ b/server/lib/job-queue/handlers/video-transcoding.ts
@@ -1,9 +1,14 @@
 import * as Bull from 'bull'
-import { VideoResolution } from '../../../../shared'
+import {
+  MergeAudioTranscodingPayload,
+  NewResolutionTranscodingPayload,
+  OptimizeTranscodingPayload,
+  VideoTranscodingPayload
+} from '../../../../shared'
 import { logger } from '../../../helpers/logger'
 import { VideoModel } from '../../../models/video/video'
 import { JobQueue } from '../job-queue'
-import { federateVideoIfNeeded } from '../../activitypub'
+import { federateVideoIfNeeded } from '../../activitypub/videos'
 import { retryTransactionWrapper } from '../../../helpers/database-utils'
 import { sequelizeTypescript } from '../../../initializers'
 import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils'
@@ -12,39 +17,6 @@ import { Notifier } from '../../notifier'
 import { CONFIG } from '../../../initializers/config'
 import { MVideoFullLight, MVideoUUID, MVideoWithFile } from '@server/typings/models'
 
-interface BaseTranscodingPayload {
-  videoUUID: string
-  isNewVideo?: boolean
-}
-
-interface HLSTranscodingPayload extends BaseTranscodingPayload {
-  type: 'hls'
-  isPortraitMode?: boolean
-  resolution: VideoResolution
-  copyCodecs: boolean
-}
-
-interface NewResolutionTranscodingPayload extends BaseTranscodingPayload {
-  type: 'new-resolution'
-  isPortraitMode?: boolean
-  resolution: VideoResolution
-}
-
-interface MergeAudioTranscodingPayload extends BaseTranscodingPayload {
-  type: 'merge-audio'
-  resolution: VideoResolution
-}
-
-interface OptimizeTranscodingPayload extends BaseTranscodingPayload {
-  type: 'optimize'
-}
-
-export type VideoTranscodingPayload =
-  HLSTranscodingPayload
-  | NewResolutionTranscodingPayload
-  | OptimizeTranscodingPayload
-  | MergeAudioTranscodingPayload
-
 async function processVideoTranscoding (job: Bull.Job) {
   const payload = job.data as VideoTranscodingPayload
   logger.info('Processing video file in job %d.', job.id)
diff --git a/server/lib/job-queue/handlers/video-views.ts b/server/lib/job-queue/handlers/video-views.ts
index 2258cd029..7211df237 100644
--- a/server/lib/job-queue/handlers/video-views.ts
+++ b/server/lib/job-queue/handlers/video-views.ts
@@ -3,7 +3,7 @@ import { logger } from '../../../helpers/logger'
 import { VideoModel } from '../../../models/video/video'
 import { VideoViewModel } from '../../../models/video/video-views'
 import { isTestInstance } from '../../../helpers/core-utils'
-import { federateVideoIfNeeded } from '../../activitypub'
+import { federateVideoIfNeeded } from '../../activitypub/videos'
 
 async function processVideosViews () {
   const lastHour = new Date()
diff --git a/server/lib/job-queue/job-queue.ts b/server/lib/job-queue/job-queue.ts
index 14acace7d..d8d64caaf 100644
--- a/server/lib/job-queue/job-queue.ts
+++ b/server/lib/job-queue/job-queue.ts
@@ -1,19 +1,25 @@
 import * as Bull from 'bull'
-import { JobState, JobType } from '../../../shared/models'
+import {
+  ActivitypubFollowPayload,
+  ActivitypubHttpBroadcastPayload,
+  ActivitypubHttpFetcherPayload, ActivitypubHttpUnicastPayload, EmailPayload,
+  JobState,
+  JobType, RefreshPayload, VideoFileImportPayload, VideoImportPayload, VideoRedundancyPayload, VideoTranscodingPayload
+} from '../../../shared/models'
 import { logger } from '../../helpers/logger'
 import { Redis } from '../redis'
 import { JOB_ATTEMPTS, JOB_COMPLETED_LIFETIME, JOB_CONCURRENCY, JOB_TTL, REPEAT_JOBS, WEBSERVER } from '../../initializers/constants'
-import { ActivitypubHttpBroadcastPayload, processActivityPubHttpBroadcast } from './handlers/activitypub-http-broadcast'
-import { ActivitypubHttpFetcherPayload, processActivityPubHttpFetcher } from './handlers/activitypub-http-fetcher'
-import { ActivitypubHttpUnicastPayload, processActivityPubHttpUnicast } from './handlers/activitypub-http-unicast'
-import { EmailPayload, processEmail } from './handlers/email'
-import { processVideoTranscoding, VideoTranscodingPayload } from './handlers/video-transcoding'
-import { ActivitypubFollowPayload, processActivityPubFollow } from './handlers/activitypub-follow'
-import { processVideoImport, VideoImportPayload } from './handlers/video-import'
+import { processActivityPubHttpBroadcast } from './handlers/activitypub-http-broadcast'
+import { processActivityPubHttpFetcher } from './handlers/activitypub-http-fetcher'
+import { processActivityPubHttpUnicast } from './handlers/activitypub-http-unicast'
+import { processEmail } from './handlers/email'
+import { processVideoTranscoding} from './handlers/video-transcoding'
+import { processActivityPubFollow } from './handlers/activitypub-follow'
+import { processVideoImport} from './handlers/video-import'
 import { processVideosViews } from './handlers/video-views'
-import { refreshAPObject, RefreshPayload } from './handlers/activitypub-refresher'
-import { processVideoFileImport, VideoFileImportPayload } from './handlers/video-file-import'
-import { processVideoRedundancy, VideoRedundancyPayload } from '@server/lib/job-queue/handlers/video-redundancy'
+import { refreshAPObject} from './handlers/activitypub-refresher'
+import { processVideoFileImport} from './handlers/video-file-import'
+import { processVideoRedundancy} from '@server/lib/job-queue/handlers/video-redundancy'
 
 type CreateJobArgument =
   { type: 'activitypub-http-broadcast', payload: ActivitypubHttpBroadcastPayload } |
diff --git a/server/lib/notifier.ts b/server/lib/notifier.ts
index 63197eee1..710c2d30f 100644
--- a/server/lib/notifier.ts
+++ b/server/lib/notifier.ts
@@ -26,7 +26,7 @@ import {
 import { MAccountDefault, MActorFollowFull } from '../typings/models'
 import { MVideoImportVideo } from '@server/typings/models/video/video-import'
 import { ServerBlocklistModel } from '@server/models/server/server-blocklist'
-import { getServerActor } from '@server/helpers/utils'
+import { getServerActor } from '@server/models/application/application'
 
 class Notifier {
 
diff --git a/server/lib/redundancy.ts b/server/lib/redundancy.ts
index aa0e37478..361b401a5 100644
--- a/server/lib/redundancy.ts
+++ b/server/lib/redundancy.ts
@@ -1,12 +1,12 @@
 import { VideoRedundancyModel } from '../models/redundancy/video-redundancy'
 import { sendUndoCacheFile } from './activitypub/send'
 import { Transaction } from 'sequelize'
-import { getServerActor } from '../helpers/utils'
 import { MActorSignature, MVideoRedundancyVideo } from '@server/typings/models'
 import { CONFIG } from '@server/initializers/config'
 import { logger } from '@server/helpers/logger'
 import { ActorFollowModel } from '@server/models/activitypub/actor-follow'
 import { Activity } from '@shared/models'
+import { getServerActor } from '@server/models/application/application'
 
 async function removeVideoRedundancy (videoRedundancy: MVideoRedundancyVideo, t?: Transaction) {
   const serverActor = await getServerActor()
diff --git a/server/lib/schedulers/auto-follow-index-instances.ts b/server/lib/schedulers/auto-follow-index-instances.ts
index a1f5e4a91..e852c7fc6 100644
--- a/server/lib/schedulers/auto-follow-index-instances.ts
+++ b/server/lib/schedulers/auto-follow-index-instances.ts
@@ -6,7 +6,7 @@ import { chunk } from 'lodash'
 import { doRequest } from '@server/helpers/requests'
 import { ActorFollowModel } from '@server/models/activitypub/actor-follow'
 import { JobQueue } from '@server/lib/job-queue'
-import { getServerActor } from '@server/helpers/utils'
+import { getServerActor } from '@server/models/application/application'
 
 export class AutoFollowIndexInstances extends AbstractScheduler {
 
diff --git a/server/lib/schedulers/update-videos-scheduler.ts b/server/lib/schedulers/update-videos-scheduler.ts
index 956780a77..d32c1c068 100644
--- a/server/lib/schedulers/update-videos-scheduler.ts
+++ b/server/lib/schedulers/update-videos-scheduler.ts
@@ -2,7 +2,7 @@ import { logger } from '../../helpers/logger'
 import { AbstractScheduler } from './abstract-scheduler'
 import { ScheduleVideoUpdateModel } from '../../models/video/schedule-video-update'
 import { retryTransactionWrapper } from '../../helpers/database-utils'
-import { federateVideoIfNeeded } from '../activitypub'
+import { federateVideoIfNeeded } from '../activitypub/videos'
 import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants'
 import { Notifier } from '../notifier'
 import { sequelizeTypescript } from '../../initializers/database'
diff --git a/server/lib/schedulers/videos-redundancy-scheduler.ts b/server/lib/schedulers/videos-redundancy-scheduler.ts
index e33a4133a..8da9d52b5 100644
--- a/server/lib/schedulers/videos-redundancy-scheduler.ts
+++ b/server/lib/schedulers/videos-redundancy-scheduler.ts
@@ -6,11 +6,10 @@ import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy'
 import { downloadWebTorrentVideo, generateMagnetUri } from '../../helpers/webtorrent'
 import { join } from 'path'
 import { move } from 'fs-extra'
-import { getServerActor } from '../../helpers/utils'
 import { sendCreateCacheFile, sendUpdateCacheFile } from '../activitypub/send'
 import { getVideoCacheFileActivityPubUrl, getVideoCacheStreamingPlaylistActivityPubUrl } from '../activitypub/url'
 import { removeVideoRedundancy } from '../redundancy'
-import { getOrCreateVideoAndAccountAndChannel } from '../activitypub'
+import { getOrCreateVideoAndAccountAndChannel } from '../activitypub/videos'
 import { downloadPlaylistSegments } from '../hls'
 import { CONFIG } from '../../initializers/config'
 import {
@@ -26,6 +25,7 @@ import {
 } from '@server/typings/models'
 import { getVideoFilename } from '../video-paths'
 import { VideoModel } from '@server/models/video/video'
+import { getServerActor } from '@server/models/application/application'
 
 type CandidateToDuplicate = {
   redundancy: VideosRedundancyStrategy
diff --git a/server/lib/video-comment.ts b/server/lib/video-comment.ts
index fe83d23e7..516c912a9 100644
--- a/server/lib/video-comment.ts
+++ b/server/lib/video-comment.ts
@@ -2,7 +2,7 @@ import * as Sequelize from 'sequelize'
 import { ResultList } from '../../shared/models'
 import { VideoCommentThreadTree } from '../../shared/models/videos/video-comment.model'
 import { VideoCommentModel } from '../models/video/video-comment'
-import { getVideoCommentActivityPubUrl } from './activitypub'
+import { getVideoCommentActivityPubUrl } from './activitypub/url'
 import { sendCreateVideoComment } from './activitypub/send'
 import { MAccountDefault, MComment, MCommentOwnerVideoReply, MVideoFullLight } from '../typings/models'
 
diff --git a/server/lib/video-paths.ts b/server/lib/video-paths.ts
index fe0a004e4..05aaca8af 100644
--- a/server/lib/video-paths.ts
+++ b/server/lib/video-paths.ts
@@ -1,8 +1,8 @@
 import { isStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoUUID } from '@server/typings/models'
-import { extractVideo } from './videos'
 import { join } from 'path'
 import { CONFIG } from '@server/initializers/config'
 import { HLS_REDUNDANCY_DIRECTORY, HLS_STREAMING_PLAYLIST_DIRECTORY } from '@server/initializers/constants'
+import { extractVideo } from '@server/helpers/video'
 
 // ################## Video file name ##################
 
diff --git a/server/lib/video-playlist.ts b/server/lib/video-playlist.ts
index 29b70cfda..75fbd6896 100644
--- a/server/lib/video-playlist.ts
+++ b/server/lib/video-playlist.ts
@@ -1,7 +1,7 @@
 import * as Sequelize from 'sequelize'
 import { VideoPlaylistModel } from '../models/video/video-playlist'
 import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model'
-import { getVideoPlaylistActivityPubUrl } from './activitypub'
+import { getVideoPlaylistActivityPubUrl } from './activitypub/url'
 import { VideoPlaylistType } from '../../shared/models/videos/playlist/video-playlist-type.model'
 import { MAccount } from '../typings/models'
 import { MVideoPlaylistOwner } from '../typings/models/video/video-playlist'
diff --git a/server/lib/videos.ts b/server/lib/videos.ts
deleted file mode 100644
index 96bdd42e9..000000000
--- a/server/lib/videos.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { isStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoFile } from '@server/typings/models'
-import { VideoTranscodingPayload } from '@server/lib/job-queue/handlers/video-transcoding'
-import { DEFAULT_AUDIO_RESOLUTION } from '@server/initializers/constants'
-import { JobQueue } from '@server/lib/job-queue'
-
-function extractVideo (videoOrPlaylist: MVideo | MStreamingPlaylistVideo) {
-  return isStreamingPlaylist(videoOrPlaylist)
-    ? videoOrPlaylist.Video
-    : videoOrPlaylist
-}
-
-function addOptimizeOrMergeAudioJob (video: MVideo, videoFile: MVideoFile) {
-  let dataInput: VideoTranscodingPayload
-
-  if (videoFile.isAudio()) {
-    dataInput = {
-      type: 'merge-audio' as 'merge-audio',
-      resolution: DEFAULT_AUDIO_RESOLUTION,
-      videoUUID: video.uuid,
-      isNewVideo: true
-    }
-  } else {
-    dataInput = {
-      type: 'optimize' as 'optimize',
-      videoUUID: video.uuid,
-      isNewVideo: true
-    }
-  }
-
-  return JobQueue.Instance.createJobWithPromise({ type: 'video-transcoding', payload: dataInput })
-}
-
-export {
-  addOptimizeOrMergeAudioJob,
-  extractVideo
-}
diff --git a/server/middlewares/activitypub.ts b/server/middlewares/activitypub.ts
index 45899818e..580606a68 100644
--- a/server/middlewares/activitypub.ts
+++ b/server/middlewares/activitypub.ts
@@ -3,7 +3,7 @@ import { ActivityDelete, ActivityPubSignature } from '../../shared'
 import { logger } from '../helpers/logger'
 import { isHTTPSignatureVerified, isJsonLDSignatureVerified, parseHTTPSignature } from '../helpers/peertube-crypto'
 import { ACCEPT_HEADERS, ACTIVITY_PUB, HTTP_SIGNATURE } from '../initializers/constants'
-import { getOrCreateActorAndServerAndModel } from '../lib/activitypub'
+import { getOrCreateActorAndServerAndModel } from '../lib/activitypub/actor'
 import { loadActorUrlOrGetFromWebfinger } from '../helpers/webfinger'
 import { isActorDeleteActivityValid } from '@server/helpers/custom-validators/activitypub/actor'
 import { getAPId } from '@server/helpers/activitypub'
diff --git a/server/middlewares/validators/activitypub/activity.ts b/server/middlewares/validators/activitypub/activity.ts
index 7582f65e7..7350be5d5 100644
--- a/server/middlewares/validators/activitypub/activity.ts
+++ b/server/middlewares/validators/activitypub/activity.ts
@@ -1,7 +1,7 @@
 import * as express from 'express'
 import { isRootActivityValid } from '../../../helpers/custom-validators/activitypub/activity'
 import { logger } from '../../../helpers/logger'
-import { getServerActor } from '../../../helpers/utils'
+import { getServerActor } from '@server/models/application/application'
 
 async function activityPubValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
   logger.debug('Checking activity pub parameters')
diff --git a/server/middlewares/validators/blocklist.ts b/server/middlewares/validators/blocklist.ts
index b2183437c..c00a7e4df 100644
--- a/server/middlewares/validators/blocklist.ts
+++ b/server/middlewares/validators/blocklist.ts
@@ -6,9 +6,9 @@ import { AccountBlocklistModel } from '../../models/account/account-blocklist'
 import { isHostValid } from '../../helpers/custom-validators/servers'
 import { ServerBlocklistModel } from '../../models/server/server-blocklist'
 import { ServerModel } from '../../models/server/server'
-import { getServerActor } from '../../helpers/utils'
 import { WEBSERVER } from '../../initializers/constants'
 import { doesAccountNameWithHostExist } from '../../helpers/middlewares'
+import { getServerActor } from '@server/models/application/application'
 
 const blockAccountValidator = [
   body('accountName').exists().withMessage('Should have an account name with host'),
diff --git a/server/middlewares/validators/follows.ts b/server/middlewares/validators/follows.ts
index a98d32d86..7808135f7 100644
--- a/server/middlewares/validators/follows.ts
+++ b/server/middlewares/validators/follows.ts
@@ -3,7 +3,6 @@ import { body, param, query } from 'express-validator'
 import { isTestInstance } from '../../helpers/core-utils'
 import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers'
 import { logger } from '../../helpers/logger'
-import { getServerActor } from '../../helpers/utils'
 import { SERVER_ACTOR_NAME, WEBSERVER } from '../../initializers/constants'
 import { ActorFollowModel } from '../../models/activitypub/actor-follow'
 import { areValidationErrors } from './utils'
@@ -12,6 +11,7 @@ import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger'
 import { isActorTypeValid, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor'
 import { MActorFollowActorsDefault } from '@server/typings/models'
 import { isFollowStateValid } from '@server/helpers/custom-validators/follows'
+import { getServerActor } from '@server/models/application/application'
 
 const listFollowsValidator = [
   query('state')
diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts
index 3a7869354..867c05fc1 100644
--- a/server/middlewares/validators/videos/videos.ts
+++ b/server/middlewares/validators/videos/videos.ts
@@ -38,7 +38,6 @@ import { checkUserCanTerminateOwnershipChange, doesChangeVideoOwnershipExist } f
 import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/video-change-ownership-accept.model'
 import { AccountModel } from '../../../models/account/account'
 import { isNSFWQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search'
-import { getServerActor } from '../../../helpers/utils'
 import { CONFIG } from '../../../initializers/config'
 import { isLocalVideoAccepted } from '../../../lib/moderation'
 import { Hooks } from '../../../lib/plugins/hooks'
@@ -50,6 +49,7 @@ import {
 } from '../../../helpers/middlewares'
 import { MVideoFullLight } from '@server/typings/models'
 import { getVideoWithAttributes } from '../../../helpers/video'
+import { getServerActor } from '@server/models/application/application'
 
 const videosAddValidator = getCommonVideoEditAttributes().concat([
   body('videofile')
diff --git a/server/models/activitypub/actor-follow.ts b/server/models/activitypub/actor-follow.ts
index 5a8e450a5..85a371026 100644
--- a/server/models/activitypub/actor-follow.ts
+++ b/server/models/activitypub/actor-follow.ts
@@ -20,7 +20,6 @@ import {
 import { FollowState } from '../../../shared/models/actors'
 import { ActorFollow } from '../../../shared/models/actors/follow.model'
 import { logger } from '../../helpers/logger'
-import { getServerActor } from '../../helpers/utils'
 import { ACTOR_FOLLOW_SCORE, FOLLOW_STATES, SERVER_ACTOR_NAME } from '../../initializers/constants'
 import { ServerModel } from '../server/server'
 import { createSafeIn, getFollowsSort, getSort } from '../utils'
@@ -37,6 +36,7 @@ import {
 } from '@server/typings/models'
 import { ActivityPubActorType } from '@shared/models'
 import { VideoModel } from '@server/models/video/video'
+import { getServerActor } from '@server/models/application/application'
 
 @Table({
   tableName: 'actorFollow',
diff --git a/server/models/application/application.ts b/server/models/application/application.ts
index 81320b9af..3bba2c70e 100644
--- a/server/models/application/application.ts
+++ b/server/models/application/application.ts
@@ -1,5 +1,16 @@
 import { AllowNull, Column, Default, DefaultScope, HasOne, IsInt, Model, Table } from 'sequelize-typescript'
 import { AccountModel } from '../account/account'
+import * as memoizee from 'memoizee'
+
+export const getServerActor = memoizee(async function () {
+  const application = await ApplicationModel.load()
+  if (!application) throw Error('Could not load Application from database.')
+
+  const actor = application.Account.Actor
+  actor.Account = application.Account
+
+  return actor
+}, { promise: true })
 
 @DefaultScope(() => ({
   include: [
diff --git a/server/models/redundancy/video-redundancy.ts b/server/models/redundancy/video-redundancy.ts
index 857b9eca6..6021408bf 100644
--- a/server/models/redundancy/video-redundancy.ts
+++ b/server/models/redundancy/video-redundancy.ts
@@ -17,7 +17,6 @@ import { getSort, getVideoSort, parseAggregateResult, throwIfNotValid } from '..
 import { isActivityPubUrlValid, isUrlValid } from '../../helpers/custom-validators/activitypub/misc'
 import { CONSTRAINTS_FIELDS, MIMETYPES } from '../../initializers/constants'
 import { VideoFileModel } from '../video/video-file'
-import { getServerActor } from '../../helpers/utils'
 import { VideoModel } from '../video/video'
 import { VideoRedundancyStrategy, VideoRedundancyStrategyWithManual } from '../../../shared/models/redundancy'
 import { logger } from '../../helpers/logger'
@@ -37,6 +36,7 @@ import {
   StreamingPlaylistRedundancyInformation,
   VideoRedundancy
 } from '@shared/models/redundancy/video-redundancy.model'
+import { getServerActor } from '@server/models/application/application'
 
 export enum ScopeNames {
   WITH_VIDEO = 'WITH_VIDEO'
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts
index 7cdff8c2c..b7ed6240e 100644
--- a/server/models/video/video-comment.ts
+++ b/server/models/video/video-comment.ts
@@ -9,7 +9,6 @@ import { ActorModel } from '../activitypub/actor'
 import { buildBlockedAccountSQL, buildLocalAccountIdsIn, getCommentSort, throwIfNotValid } from '../utils'
 import { VideoModel } from './video'
 import { VideoChannelModel } from './video-channel'
-import { getServerActor } from '../../helpers/utils'
 import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor'
 import { regexpCapture } from '../../helpers/regexp'
 import { uniq } from 'lodash'
@@ -28,6 +27,7 @@ import {
 } from '../../typings/models/video'
 import { MUserAccountId } from '@server/typings/models'
 import { VideoPrivacy } from '@shared/models'
+import { getServerActor } from '@server/models/application/application'
 
 enum ScopeNames {
   WITH_ACCOUNT = 'WITH_ACCOUNT',
diff --git a/server/models/video/video-format-utils.ts b/server/models/video/video-format-utils.ts
index 365c9581e..0d3c5a8ac 100644
--- a/server/models/video/video-format-utils.ts
+++ b/server/models/video/video-format-utils.ts
@@ -8,7 +8,7 @@ import {
   getVideoDislikesActivityPubUrl,
   getVideoLikesActivityPubUrl,
   getVideoSharesActivityPubUrl
-} from '../../lib/activitypub'
+} from '../../lib/activitypub/url'
 import { isArray } from '../../helpers/custom-validators/misc'
 import { VideoStreamingPlaylist } from '../../../shared/models/videos/video-streaming-playlist.model'
 import {
@@ -23,7 +23,7 @@ import {
 import { MVideoFileRedundanciesOpt } from '../../typings/models/video/video-file'
 import { VideoFile } from '@shared/models/videos/video-file.model'
 import { generateMagnetUri } from '@server/helpers/webtorrent'
-import { extractVideo } from '@server/lib/videos'
+import { extractVideo } from '@server/helpers/video'
 
 export type VideoFormattingJSONOptions = {
   completeDescription?: boolean
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index ccb9d64ca..96b96e5ac 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -43,7 +43,6 @@ import {
 } from '../../helpers/custom-validators/videos'
 import { getVideoFileResolution } from '../../helpers/ffmpeg-utils'
 import { logger } from '../../helpers/logger'
-import { getServerActor } from '../../helpers/utils'
 import {
   ACTIVITY_PUB,
   API_VERSION,
@@ -126,6 +125,7 @@ import { getHLSDirectory, getTorrentFileName, getTorrentFilePath, getVideoFilena
 import { ModelCache } from '@server/models/model-cache'
 import { buildListQuery, BuildVideosQueryOptions, wrapForAPIResults } from './video-query-builder'
 import { buildNSFWFilter } from '@server/helpers/express-utils'
+import { getServerActor } from '@server/models/application/application'
 
 export enum ScopeNames {
   AVAILABLE_FOR_LIST_IDS = 'AVAILABLE_FOR_LIST_IDS',
diff --git a/server/tests/api/activitypub/security.ts b/server/tests/api/activitypub/security.ts
index 7e58bf065..ac4bc7c6a 100644
--- a/server/tests/api/activitypub/security.ts
+++ b/server/tests/api/activitypub/security.ts
@@ -4,10 +4,11 @@ import 'mocha'
 
 import { cleanupTests, closeAllSequelize, flushAndRunMultipleServers, ServerInfo, setActorField } from '../../../../shared/extra-utils'
 import { HTTP_SIGNATURE } from '../../../initializers/constants'
-import { buildDigest, buildGlobalHeaders } from '../../../lib/job-queue/handlers/utils/activitypub-http-utils'
+import { buildGlobalHeaders } from '../../../lib/job-queue/handlers/utils/activitypub-http-utils'
 import * as chai from 'chai'
 import { activityPubContextify, buildSignedActivity } from '../../../helpers/activitypub'
 import { makeFollowRequest, makePOSTAPRequest } from '../../../../shared/extra-utils/requests/activitypub'
+import { buildDigest } from '@server/helpers/peertube-crypto'
 
 const expect = chai.expect
 
diff --git a/shared/models/server/emailer.model.ts b/shared/models/server/emailer.model.ts
new file mode 100644
index 000000000..2d8feda81
--- /dev/null
+++ b/shared/models/server/emailer.model.ts
@@ -0,0 +1,8 @@
+export type SendEmailOptions = {
+  to: string[]
+  subject: string
+  text: string
+
+  fromDisplayName?: string
+  replyTo?: string
+}
diff --git a/shared/models/server/index.ts b/shared/models/server/index.ts
index bf61ab270..b0afb2c66 100644
--- a/shared/models/server/index.ts
+++ b/shared/models/server/index.ts
@@ -2,6 +2,7 @@ export * from './about.model'
 export * from './contact-form.model'
 export * from './custom-config.model'
 export * from './debug.model'
+export * from './emailer.model'
 export * from './job.model'
 export * from './server-config.model'
 export * from './server-stats.model'
diff --git a/shared/models/server/job.model.ts b/shared/models/server/job.model.ts
index cf29d20d4..694361276 100644
--- a/shared/models/server/job.model.ts
+++ b/shared/models/server/job.model.ts
@@ -1,3 +1,7 @@
+import { ContextType } from '@server/helpers/activitypub'
+import { SendEmailOptions } from './emailer.model'
+import { VideoResolution } from '@shared/models'
+
 export type JobState = 'active' | 'completed' | 'failed' | 'waiting' | 'delayed'
 
 export type JobType =
@@ -23,3 +27,99 @@ export interface Job {
   finishedOn: Date | string
   processedOn: Date | string
 }
+
+export type ActivitypubHttpBroadcastPayload = {
+  uris: string[]
+  signatureActorId?: number
+  body: any
+  contextType?: ContextType
+}
+
+export type ActivitypubFollowPayload = {
+  followerActorId: number
+  name: string
+  host: string
+  isAutoFollow?: boolean
+  assertIsChannel?: boolean
+}
+
+export type FetchType = 'activity' | 'video-likes' | 'video-dislikes' | 'video-shares' | 'video-comments' | 'account-playlists'
+export type ActivitypubHttpFetcherPayload = {
+  uri: string
+  type: FetchType
+  videoId?: number
+  accountId?: number
+}
+
+export type ActivitypubHttpUnicastPayload = {
+  uri: string
+  signatureActorId?: number
+  body: any
+  contextType?: ContextType
+}
+
+export type RefreshPayload = {
+  type: 'video' | 'video-playlist' | 'actor'
+  url: string
+}
+
+export type EmailPayload = SendEmailOptions
+
+export type VideoFileImportPayload = {
+  videoUUID: string
+  filePath: string
+}
+
+export type VideoImportYoutubeDLPayload = {
+  type: 'youtube-dl'
+  videoImportId: number
+
+  generateThumbnail: boolean
+  generatePreview: boolean
+
+  fileExt?: string
+}
+export type VideoImportTorrentPayload = {
+  type: 'magnet-uri' | 'torrent-file'
+  videoImportId: number
+}
+export type VideoImportPayload = VideoImportYoutubeDLPayload | VideoImportTorrentPayload
+
+export type VideoRedundancyPayload = {
+  videoId: number
+}
+
+// Video transcoding payloads
+
+interface BaseTranscodingPayload {
+  videoUUID: string
+  isNewVideo?: boolean
+}
+
+interface HLSTranscodingPayload extends BaseTranscodingPayload {
+  type: 'hls'
+  isPortraitMode?: boolean
+  resolution: VideoResolution
+  copyCodecs: boolean
+}
+
+export interface NewResolutionTranscodingPayload extends BaseTranscodingPayload {
+  type: 'new-resolution'
+  isPortraitMode?: boolean
+  resolution: VideoResolution
+}
+
+export interface MergeAudioTranscodingPayload extends BaseTranscodingPayload {
+  type: 'merge-audio'
+  resolution: VideoResolution
+}
+
+export interface OptimizeTranscodingPayload extends BaseTranscodingPayload {
+  type: 'optimize'
+}
+
+export type VideoTranscodingPayload =
+  HLSTranscodingPayload
+  | NewResolutionTranscodingPayload
+  | OptimizeTranscodingPayload
+  | MergeAudioTranscodingPayload