Delete invalid or deleted remote videos
This commit is contained in:
parent
f107470e50
commit
04b8c3fba6
|
@ -387,6 +387,11 @@ async function updateVideo (req: express.Request, res: express.Response) {
|
||||||
function getVideo (req: express.Request, res: express.Response) {
|
function getVideo (req: express.Request, res: express.Response) {
|
||||||
const videoInstance = res.locals.video
|
const videoInstance = res.locals.video
|
||||||
|
|
||||||
|
if (videoInstance.isOutdated()) {
|
||||||
|
JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', videoUrl: videoInstance.url } })
|
||||||
|
.catch(err => logger.error('Cannot create AP refresher job for video %s.', videoInstance.url, { err }))
|
||||||
|
}
|
||||||
|
|
||||||
return res.json(videoInstance.toFormattedDetailsJSON())
|
return res.json(videoInstance.toFormattedDetailsJSON())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -429,7 +434,7 @@ async function getVideoDescription (req: express.Request, res: express.Response)
|
||||||
return res.json({ description })
|
return res.json({ description })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
|
async function listVideos (req: express.Request, res: express.Response) {
|
||||||
const resultList = await VideoModel.listForApi({
|
const resultList = await VideoModel.listForApi({
|
||||||
start: req.query.start,
|
start: req.query.start,
|
||||||
count: req.query.count,
|
count: req.query.count,
|
||||||
|
|
|
@ -102,7 +102,8 @@ const JOB_ATTEMPTS: { [ id in JobType ]: number } = {
|
||||||
'video-file': 1,
|
'video-file': 1,
|
||||||
'video-import': 1,
|
'video-import': 1,
|
||||||
'email': 5,
|
'email': 5,
|
||||||
'videos-views': 1
|
'videos-views': 1,
|
||||||
|
'activitypub-refresher': 1
|
||||||
}
|
}
|
||||||
const JOB_CONCURRENCY: { [ id in JobType ]: number } = {
|
const JOB_CONCURRENCY: { [ id in JobType ]: number } = {
|
||||||
'activitypub-http-broadcast': 1,
|
'activitypub-http-broadcast': 1,
|
||||||
|
@ -113,7 +114,8 @@ const JOB_CONCURRENCY: { [ id in JobType ]: number } = {
|
||||||
'video-file': 1,
|
'video-file': 1,
|
||||||
'video-import': 1,
|
'video-import': 1,
|
||||||
'email': 5,
|
'email': 5,
|
||||||
'videos-views': 1
|
'videos-views': 1,
|
||||||
|
'activitypub-refresher': 1
|
||||||
}
|
}
|
||||||
const JOB_TTL: { [ id in JobType ]: number } = {
|
const JOB_TTL: { [ id in JobType ]: number } = {
|
||||||
'activitypub-http-broadcast': 60000 * 10, // 10 minutes
|
'activitypub-http-broadcast': 60000 * 10, // 10 minutes
|
||||||
|
@ -124,11 +126,12 @@ const JOB_TTL: { [ id in JobType ]: number } = {
|
||||||
'video-file': 1000 * 3600 * 48, // 2 days, transcoding could be long
|
'video-file': 1000 * 3600 * 48, // 2 days, transcoding could be long
|
||||||
'video-import': 1000 * 3600 * 2, // hours
|
'video-import': 1000 * 3600 * 2, // hours
|
||||||
'email': 60000 * 10, // 10 minutes
|
'email': 60000 * 10, // 10 minutes
|
||||||
'videos-views': undefined // Unlimited
|
'videos-views': undefined, // Unlimited
|
||||||
|
'activitypub-refresher': 60000 * 10 // 10 minutes
|
||||||
}
|
}
|
||||||
const REPEAT_JOBS: { [ id: string ]: EveryRepeatOptions | CronRepeatOptions } = {
|
const REPEAT_JOBS: { [ id: string ]: EveryRepeatOptions | CronRepeatOptions } = {
|
||||||
'videos-views': {
|
'videos-views': {
|
||||||
cron: '1 * * * *' // At 1 minutes past the hour
|
cron: '1 * * * *' // At 1 minute past the hour
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -543,7 +546,7 @@ const HTTP_SIGNATURE = {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const PRIVATE_RSA_KEY_SIZE = 2048
|
let PRIVATE_RSA_KEY_SIZE = 2048
|
||||||
|
|
||||||
// Password encryption
|
// Password encryption
|
||||||
const BCRYPT_SALT_SIZE = 10
|
const BCRYPT_SALT_SIZE = 10
|
||||||
|
@ -647,6 +650,8 @@ const TRACKER_RATE_LIMITS = {
|
||||||
|
|
||||||
// Special constants for a test instance
|
// Special constants for a test instance
|
||||||
if (isTestInstance() === true) {
|
if (isTestInstance() === true) {
|
||||||
|
PRIVATE_RSA_KEY_SIZE = 1024
|
||||||
|
|
||||||
ACTOR_FOLLOW_SCORE.BASE = 20
|
ACTOR_FOLLOW_SCORE.BASE = 20
|
||||||
|
|
||||||
REMOTE_SCHEME.HTTP = 'http'
|
REMOTE_SCHEME.HTTP = 'http'
|
||||||
|
|
|
@ -59,7 +59,6 @@ async function processUpdateVideo (actor: ActorModel, activity: ActivityUpdate)
|
||||||
videoObject,
|
videoObject,
|
||||||
account: actor.Account,
|
account: actor.Account,
|
||||||
channel: channelActor.VideoChannel,
|
channel: channelActor.VideoChannel,
|
||||||
updateViews: true,
|
|
||||||
overrideTo: activity.to
|
overrideTo: activity.to
|
||||||
}
|
}
|
||||||
return updateVideoFromAP(updateOptions)
|
return updateVideoFromAP(updateOptions)
|
||||||
|
|
|
@ -117,7 +117,7 @@ type SyncParam = {
|
||||||
shares: boolean
|
shares: boolean
|
||||||
comments: boolean
|
comments: boolean
|
||||||
thumbnail: boolean
|
thumbnail: boolean
|
||||||
refreshVideo: boolean
|
refreshVideo?: boolean
|
||||||
}
|
}
|
||||||
async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: VideoTorrentObject, syncParam: SyncParam) {
|
async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: VideoTorrentObject, syncParam: SyncParam) {
|
||||||
logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid)
|
logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid)
|
||||||
|
@ -158,13 +158,11 @@ async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: Vid
|
||||||
async function getOrCreateVideoAndAccountAndChannel (options: {
|
async function getOrCreateVideoAndAccountAndChannel (options: {
|
||||||
videoObject: VideoTorrentObject | string,
|
videoObject: VideoTorrentObject | string,
|
||||||
syncParam?: SyncParam,
|
syncParam?: SyncParam,
|
||||||
fetchType?: VideoFetchByUrlType,
|
fetchType?: VideoFetchByUrlType
|
||||||
refreshViews?: boolean
|
|
||||||
}) {
|
}) {
|
||||||
// Default params
|
// Default params
|
||||||
const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false }
|
const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false }
|
||||||
const fetchType = options.fetchType || 'all'
|
const fetchType = options.fetchType || 'all'
|
||||||
const refreshViews = options.refreshViews || false
|
|
||||||
|
|
||||||
// Get video url
|
// Get video url
|
||||||
const videoUrl = getAPUrl(options.videoObject)
|
const videoUrl = getAPUrl(options.videoObject)
|
||||||
|
@ -174,11 +172,11 @@ async function getOrCreateVideoAndAccountAndChannel (options: {
|
||||||
const refreshOptions = {
|
const refreshOptions = {
|
||||||
video: videoFromDatabase,
|
video: videoFromDatabase,
|
||||||
fetchedType: fetchType,
|
fetchedType: fetchType,
|
||||||
syncParam,
|
syncParam
|
||||||
refreshViews
|
|
||||||
}
|
}
|
||||||
const p = refreshVideoIfNeeded(refreshOptions)
|
|
||||||
if (syncParam.refreshVideo === true) videoFromDatabase = await p
|
if (syncParam.refreshVideo === true) videoFromDatabase = await refreshVideoIfNeeded(refreshOptions)
|
||||||
|
else await JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', videoUrl: videoFromDatabase.url } })
|
||||||
|
|
||||||
return { video: videoFromDatabase }
|
return { video: videoFromDatabase }
|
||||||
}
|
}
|
||||||
|
@ -199,7 +197,6 @@ async function updateVideoFromAP (options: {
|
||||||
videoObject: VideoTorrentObject,
|
videoObject: VideoTorrentObject,
|
||||||
account: AccountModel,
|
account: AccountModel,
|
||||||
channel: VideoChannelModel,
|
channel: VideoChannelModel,
|
||||||
updateViews: boolean,
|
|
||||||
overrideTo?: string[]
|
overrideTo?: string[]
|
||||||
}) {
|
}) {
|
||||||
logger.debug('Updating remote video "%s".', options.videoObject.uuid)
|
logger.debug('Updating remote video "%s".', options.videoObject.uuid)
|
||||||
|
@ -238,8 +235,8 @@ async function updateVideoFromAP (options: {
|
||||||
options.video.set('publishedAt', videoData.publishedAt)
|
options.video.set('publishedAt', videoData.publishedAt)
|
||||||
options.video.set('privacy', videoData.privacy)
|
options.video.set('privacy', videoData.privacy)
|
||||||
options.video.set('channelId', videoData.channelId)
|
options.video.set('channelId', videoData.channelId)
|
||||||
|
options.video.set('views', videoData.views)
|
||||||
|
|
||||||
if (options.updateViews === true) options.video.set('views', videoData.views)
|
|
||||||
await options.video.save(sequelizeOptions)
|
await options.video.save(sequelizeOptions)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -297,8 +294,58 @@ async function updateVideoFromAP (options: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function refreshVideoIfNeeded (options: {
|
||||||
|
video: VideoModel,
|
||||||
|
fetchedType: VideoFetchByUrlType,
|
||||||
|
syncParam: SyncParam
|
||||||
|
}): Promise<VideoModel> {
|
||||||
|
if (!options.video.isOutdated()) return options.video
|
||||||
|
|
||||||
|
// We need more attributes if the argument video was fetched with not enough joints
|
||||||
|
const video = options.fetchedType === 'all' ? options.video : await VideoModel.loadByUrlAndPopulateAccount(options.video.url)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { response, videoObject } = await fetchRemoteVideo(video.url)
|
||||||
|
if (response.statusCode === 404) {
|
||||||
|
logger.info('Cannot refresh remote video %s: video does not exist anymore. Deleting it.', video.url)
|
||||||
|
|
||||||
|
// Video does not exist anymore
|
||||||
|
await video.destroy()
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoObject === undefined) {
|
||||||
|
logger.warn('Cannot refresh remote video %s: invalid body.', video.url)
|
||||||
|
|
||||||
|
await video.setAsRefreshed()
|
||||||
|
return video
|
||||||
|
}
|
||||||
|
|
||||||
|
const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject)
|
||||||
|
const account = await AccountModel.load(channelActor.VideoChannel.accountId)
|
||||||
|
|
||||||
|
const updateOptions = {
|
||||||
|
video,
|
||||||
|
videoObject,
|
||||||
|
account,
|
||||||
|
channel: channelActor.VideoChannel
|
||||||
|
}
|
||||||
|
await retryTransactionWrapper(updateVideoFromAP, updateOptions)
|
||||||
|
await syncVideoExternalAttributes(video, videoObject, options.syncParam)
|
||||||
|
|
||||||
|
return video
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn('Cannot refresh video %s.', options.video.url, { err })
|
||||||
|
|
||||||
|
// Don't refresh in loop
|
||||||
|
await video.setAsRefreshed()
|
||||||
|
return video
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
updateVideoFromAP,
|
updateVideoFromAP,
|
||||||
|
refreshVideoIfNeeded,
|
||||||
federateVideoIfNeeded,
|
federateVideoIfNeeded,
|
||||||
fetchRemoteVideo,
|
fetchRemoteVideo,
|
||||||
getOrCreateVideoAndAccountAndChannel,
|
getOrCreateVideoAndAccountAndChannel,
|
||||||
|
@ -362,52 +409,6 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor
|
||||||
return videoCreated
|
return videoCreated
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshVideoIfNeeded (options: {
|
|
||||||
video: VideoModel,
|
|
||||||
fetchedType: VideoFetchByUrlType,
|
|
||||||
syncParam: SyncParam,
|
|
||||||
refreshViews: boolean
|
|
||||||
}): Promise<VideoModel> {
|
|
||||||
if (!options.video.isOutdated()) return options.video
|
|
||||||
|
|
||||||
// We need more attributes if the argument video was fetched with not enough joints
|
|
||||||
const video = options.fetchedType === 'all' ? options.video : await VideoModel.loadByUrlAndPopulateAccount(options.video.url)
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { response, videoObject } = await fetchRemoteVideo(video.url)
|
|
||||||
if (response.statusCode === 404) {
|
|
||||||
logger.info('Cannot refresh remote video %s: video does not exist anymore. Deleting it.', video.url)
|
|
||||||
|
|
||||||
// Video does not exist anymore
|
|
||||||
await video.destroy()
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
if (videoObject === undefined) {
|
|
||||||
logger.warn('Cannot refresh remote video %s: invalid body.', video.url)
|
|
||||||
return video
|
|
||||||
}
|
|
||||||
|
|
||||||
const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject)
|
|
||||||
const account = await AccountModel.load(channelActor.VideoChannel.accountId)
|
|
||||||
|
|
||||||
const updateOptions = {
|
|
||||||
video,
|
|
||||||
videoObject,
|
|
||||||
account,
|
|
||||||
channel: channelActor.VideoChannel,
|
|
||||||
updateViews: options.refreshViews
|
|
||||||
}
|
|
||||||
await retryTransactionWrapper(updateVideoFromAP, updateOptions)
|
|
||||||
await syncVideoExternalAttributes(video, videoObject, options.syncParam)
|
|
||||||
|
|
||||||
return video
|
|
||||||
} catch (err) {
|
|
||||||
logger.warn('Cannot refresh video %s.', options.video.url, { err })
|
|
||||||
return video
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function videoActivityObjectToDBAttributes (
|
async function videoActivityObjectToDBAttributes (
|
||||||
videoChannel: VideoChannelModel,
|
videoChannel: VideoChannelModel,
|
||||||
videoObject: VideoTorrentObject,
|
videoObject: VideoTorrentObject,
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import * as Bull from 'bull'
|
||||||
|
import { logger } from '../../../helpers/logger'
|
||||||
|
import { fetchVideoByUrl } from '../../../helpers/video'
|
||||||
|
import { refreshVideoIfNeeded } from '../../activitypub'
|
||||||
|
|
||||||
|
export type RefreshPayload = {
|
||||||
|
videoUrl: string
|
||||||
|
type: 'video'
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshAPObject (job: Bull.Job) {
|
||||||
|
const payload = job.data as RefreshPayload
|
||||||
|
logger.info('Processing AP refresher in job %d.', job.id)
|
||||||
|
|
||||||
|
if (payload.type === 'video') return refreshAPVideo(payload.videoUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export {
|
||||||
|
refreshAPObject
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async function refreshAPVideo (videoUrl: string) {
|
||||||
|
const fetchType = 'all' as 'all'
|
||||||
|
const syncParam = { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true }
|
||||||
|
|
||||||
|
const videoFromDatabase = await fetchVideoByUrl(videoUrl, fetchType)
|
||||||
|
if (videoFromDatabase) {
|
||||||
|
const refreshOptions = {
|
||||||
|
video: videoFromDatabase,
|
||||||
|
fetchedType: fetchType,
|
||||||
|
syncParam
|
||||||
|
}
|
||||||
|
|
||||||
|
await refreshVideoIfNeeded(refreshOptions)
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import { processVideoFile, processVideoFileImport, VideoFileImportPayload, Video
|
||||||
import { ActivitypubFollowPayload, processActivityPubFollow } from './handlers/activitypub-follow'
|
import { ActivitypubFollowPayload, processActivityPubFollow } from './handlers/activitypub-follow'
|
||||||
import { processVideoImport, VideoImportPayload } from './handlers/video-import'
|
import { processVideoImport, VideoImportPayload } from './handlers/video-import'
|
||||||
import { processVideosViews } from './handlers/video-views'
|
import { processVideosViews } from './handlers/video-views'
|
||||||
|
import { refreshAPObject, RefreshPayload } from './handlers/activitypub-refresher'
|
||||||
|
|
||||||
type CreateJobArgument =
|
type CreateJobArgument =
|
||||||
{ type: 'activitypub-http-broadcast', payload: ActivitypubHttpBroadcastPayload } |
|
{ type: 'activitypub-http-broadcast', payload: ActivitypubHttpBroadcastPayload } |
|
||||||
|
@ -21,6 +22,7 @@ type CreateJobArgument =
|
||||||
{ type: 'video-file', payload: VideoFilePayload } |
|
{ type: 'video-file', payload: VideoFilePayload } |
|
||||||
{ type: 'email', payload: EmailPayload } |
|
{ type: 'email', payload: EmailPayload } |
|
||||||
{ type: 'video-import', payload: VideoImportPayload } |
|
{ type: 'video-import', payload: VideoImportPayload } |
|
||||||
|
{ type: 'activitypub-refresher', payload: RefreshPayload } |
|
||||||
{ type: 'videos-views', payload: {} }
|
{ type: 'videos-views', payload: {} }
|
||||||
|
|
||||||
const handlers: { [ id in JobType ]: (job: Bull.Job) => Promise<any>} = {
|
const handlers: { [ id in JobType ]: (job: Bull.Job) => Promise<any>} = {
|
||||||
|
@ -32,7 +34,8 @@ const handlers: { [ id in JobType ]: (job: Bull.Job) => Promise<any>} = {
|
||||||
'video-file': processVideoFile,
|
'video-file': processVideoFile,
|
||||||
'email': processEmail,
|
'email': processEmail,
|
||||||
'video-import': processVideoImport,
|
'video-import': processVideoImport,
|
||||||
'videos-views': processVideosViews
|
'videos-views': processVideosViews,
|
||||||
|
'activitypub-refresher': refreshAPObject
|
||||||
}
|
}
|
||||||
|
|
||||||
const jobTypes: JobType[] = [
|
const jobTypes: JobType[] = [
|
||||||
|
@ -44,7 +47,8 @@ const jobTypes: JobType[] = [
|
||||||
'video-file',
|
'video-file',
|
||||||
'video-file-import',
|
'video-file-import',
|
||||||
'video-import',
|
'video-import',
|
||||||
'videos-views'
|
'videos-views',
|
||||||
|
'activitypub-refresher'
|
||||||
]
|
]
|
||||||
|
|
||||||
class JobQueue {
|
class JobQueue {
|
||||||
|
|
|
@ -1561,6 +1561,12 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
(now - updatedAtTime) > ACTIVITY_PUB.VIDEO_REFRESH_INTERVAL
|
(now - updatedAtTime) > ACTIVITY_PUB.VIDEO_REFRESH_INTERVAL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setAsRefreshed () {
|
||||||
|
this.changed('updatedAt', true)
|
||||||
|
|
||||||
|
return this.save()
|
||||||
|
}
|
||||||
|
|
||||||
getBaseUrls () {
|
getBaseUrls () {
|
||||||
let baseUrlHttp
|
let baseUrlHttp
|
||||||
let baseUrlWs
|
let baseUrlWs
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import './client'
|
import './client'
|
||||||
import './fetch'
|
import './fetch'
|
||||||
import './helpers'
|
import './helpers'
|
||||||
|
import './refresher'
|
||||||
import './security'
|
import './security'
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
/* tslint:disable:no-unused-expression */
|
||||||
|
|
||||||
|
import 'mocha'
|
||||||
|
import { doubleFollow, getVideo, reRunServer } from '../../utils'
|
||||||
|
import { flushAndRunMultipleServers, killallServers, ServerInfo, setAccessTokensToServers, uploadVideo, wait } from '../../utils/index'
|
||||||
|
import { waitJobs } from '../../utils/server/jobs'
|
||||||
|
import { setVideoField } from '../../utils/miscs/sql'
|
||||||
|
|
||||||
|
describe('Test AP refresher', function () {
|
||||||
|
let servers: ServerInfo[] = []
|
||||||
|
let videoUUID1: string
|
||||||
|
let videoUUID2: string
|
||||||
|
let videoUUID3: string
|
||||||
|
|
||||||
|
before(async function () {
|
||||||
|
this.timeout(30000)
|
||||||
|
|
||||||
|
servers = await flushAndRunMultipleServers(2)
|
||||||
|
|
||||||
|
// Get the access tokens
|
||||||
|
await setAccessTokensToServers(servers)
|
||||||
|
|
||||||
|
{
|
||||||
|
const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video1' })
|
||||||
|
videoUUID1 = res.body.video.uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video2' })
|
||||||
|
videoUUID2 = res.body.video.uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video3' })
|
||||||
|
videoUUID3 = res.body.video.uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
await doubleFollow(servers[0], servers[1])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should remove a deleted remote video', async function () {
|
||||||
|
this.timeout(60000)
|
||||||
|
|
||||||
|
await wait(10000)
|
||||||
|
|
||||||
|
// Change UUID so the remote server returns a 404
|
||||||
|
await setVideoField(2, videoUUID1, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b174f')
|
||||||
|
|
||||||
|
await getVideo(servers[0].url, videoUUID1)
|
||||||
|
await getVideo(servers[0].url, videoUUID2)
|
||||||
|
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
await getVideo(servers[0].url, videoUUID1, 404)
|
||||||
|
await getVideo(servers[0].url, videoUUID2, 200)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should not update a remote video if the remote instance is down', async function () {
|
||||||
|
this.timeout(60000)
|
||||||
|
|
||||||
|
killallServers([ servers[1] ])
|
||||||
|
|
||||||
|
await setVideoField(2, videoUUID3, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b174e')
|
||||||
|
|
||||||
|
// Video will need a refresh
|
||||||
|
await wait(10000)
|
||||||
|
|
||||||
|
await getVideo(servers[0].url, videoUUID3)
|
||||||
|
// The refresh should fail
|
||||||
|
await waitJobs([ servers[0] ])
|
||||||
|
|
||||||
|
await reRunServer(servers[1])
|
||||||
|
|
||||||
|
// Should not refresh the video, even if the last refresh failed (to avoir a loop on dead instances)
|
||||||
|
await getVideo(servers[0].url, videoUUID3)
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
await getVideo(servers[0].url, videoUUID3, 200)
|
||||||
|
})
|
||||||
|
|
||||||
|
after(async function () {
|
||||||
|
killallServers(servers)
|
||||||
|
})
|
||||||
|
})
|
|
@ -8,7 +8,8 @@ export type JobType = 'activitypub-http-unicast' |
|
||||||
'video-file' |
|
'video-file' |
|
||||||
'email' |
|
'email' |
|
||||||
'video-import' |
|
'video-import' |
|
||||||
'videos-views'
|
'videos-views' |
|
||||||
|
'activitypub-refresher'
|
||||||
|
|
||||||
export interface Job {
|
export interface Job {
|
||||||
id: number
|
id: number
|
||||||
|
|
Loading…
Reference in New Issue