Ensure channel is owned by the account
This commit is contained in:
parent
0ee00337f3
commit
fd6b6b5931
16 changed files with 144 additions and 111 deletions
|
@ -72,6 +72,7 @@ export type ActivitypubHttpFetcherPayload = {
|
|||
uri: string
|
||||
type: FetchType
|
||||
videoId?: number
|
||||
accountId?: number
|
||||
}
|
||||
|
||||
export type ActivitypubHttpUnicastPayload = {
|
||||
|
@ -129,19 +130,18 @@ export type VideoRedundancyPayload = {
|
|||
videoId: number
|
||||
}
|
||||
|
||||
export type ManageVideoTorrentPayload =
|
||||
{
|
||||
action: 'create'
|
||||
videoId: number
|
||||
videoFileId: number
|
||||
} | {
|
||||
action: 'update-metadata'
|
||||
export type ManageVideoTorrentPayload = {
|
||||
action: 'create'
|
||||
videoId: number
|
||||
videoFileId: number
|
||||
} | {
|
||||
action: 'update-metadata'
|
||||
|
||||
videoId?: number
|
||||
streamingPlaylistId?: number
|
||||
videoId?: number
|
||||
streamingPlaylistId?: number
|
||||
|
||||
videoFileId: number
|
||||
}
|
||||
videoFileId: number
|
||||
}
|
||||
|
||||
// Video transcoding payloads
|
||||
|
||||
|
@ -182,7 +182,7 @@ export interface OptimizeTranscodingPayload extends BaseTranscodingPayload {
|
|||
}
|
||||
|
||||
export type VideoTranscodingPayload =
|
||||
HLSTranscodingPayload
|
||||
| HLSTranscodingPayload
|
||||
| NewWebVideoResolutionTranscodingPayload
|
||||
| OptimizeTranscodingPayload
|
||||
| MergeAudioTranscodingPayload
|
||||
|
@ -255,10 +255,10 @@ export type VideoStudioTaskWatermarkPayload = {
|
|||
}
|
||||
|
||||
export type VideoStudioTaskPayload =
|
||||
VideoStudioTaskCutPayload |
|
||||
VideoStudioTaskIntroPayload |
|
||||
VideoStudioTaskOutroPayload |
|
||||
VideoStudioTaskWatermarkPayload
|
||||
| VideoStudioTaskCutPayload
|
||||
| VideoStudioTaskIntroPayload
|
||||
| VideoStudioTaskOutroPayload
|
||||
| VideoStudioTaskWatermarkPayload
|
||||
|
||||
export interface VideoStudioEditionPayload {
|
||||
videoUUID: string
|
||||
|
@ -280,11 +280,10 @@ export interface AfterVideoChannelImportPayload {
|
|||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export type NotifyPayload =
|
||||
{
|
||||
action: 'new-video'
|
||||
videoUUID: string
|
||||
}
|
||||
export type NotifyPayload = {
|
||||
action: 'new-video'
|
||||
videoUUID: string
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ describe('Test video playlists API validator', function () {
|
|||
let userAccessToken: string
|
||||
|
||||
let playlist: VideoPlaylistCreateResult
|
||||
let userPlaylist: VideoPlaylistCreateResult
|
||||
let privatePlaylistUUID: string
|
||||
|
||||
let watchLaterPlaylistId: number
|
||||
|
@ -45,6 +46,8 @@ describe('Test video playlists API validator', function () {
|
|||
await setDefaultVideoChannel([ server ])
|
||||
|
||||
userAccessToken = await server.users.generateUserAndToken('user1')
|
||||
const user = await server.users.getMyInfo({ token: userAccessToken })
|
||||
|
||||
videoId = (await server.videos.quickUpload({ name: 'video 1' })).id
|
||||
|
||||
command = server.playlists
|
||||
|
@ -70,6 +73,17 @@ describe('Test video playlists API validator', function () {
|
|||
})
|
||||
}
|
||||
|
||||
{
|
||||
userPlaylist = await command.create({
|
||||
token: userAccessToken,
|
||||
attributes: {
|
||||
displayName: 'user playlist',
|
||||
privacy: VideoPlaylistPrivacy.PUBLIC,
|
||||
videoChannelId: user.videoChannels[0].id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
const created = await command.create({
|
||||
attributes: {
|
||||
|
@ -246,7 +260,7 @@ describe('Test video playlists API validator', function () {
|
|||
})
|
||||
|
||||
it('Should fail with an unknown video channel id', async function () {
|
||||
const params = getBase({ videoChannelId: 42 }, { expectedStatus: HttpStatusCode.NOT_FOUND_404 })
|
||||
const params = getBase({ videoChannelId: 42 }, { expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
|
||||
|
||||
await command.create(params)
|
||||
await command.update(getUpdate(params, playlist.shortUUID))
|
||||
|
@ -292,6 +306,13 @@ describe('Test video playlists API validator', function () {
|
|||
))
|
||||
})
|
||||
|
||||
it('Should fail to set a playlist to a channel owned by another user', async function () {
|
||||
const params = getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
|
||||
|
||||
await command.create(params)
|
||||
await command.update(getUpdate(params, userPlaylist.shortUUID))
|
||||
})
|
||||
|
||||
it('Should fail to update the watch later playlist', async function () {
|
||||
await command.update(getUpdate(
|
||||
getBase({}, { expectedStatus: HttpStatusCode.BAD_REQUEST_400 }),
|
||||
|
|
|
@ -117,7 +117,6 @@ describe('Test video playlists', function () {
|
|||
})
|
||||
|
||||
describe('Check playlists filters and privacies', function () {
|
||||
|
||||
it('Should list video playlist privacies', async function () {
|
||||
const privacies = await commands[0].getPrivacies()
|
||||
|
||||
|
@ -169,7 +168,6 @@ describe('Test video playlists', function () {
|
|||
|
||||
let playlist: VideoPlaylist = null
|
||||
for (const body of [ bodyList, bodyChannel ]) {
|
||||
|
||||
expect(body.total).to.equal(1)
|
||||
expect(body.data).to.have.lengthOf(1)
|
||||
|
||||
|
@ -218,7 +216,6 @@ describe('Test video playlists', function () {
|
|||
})
|
||||
|
||||
describe('Create and federate playlists', function () {
|
||||
|
||||
it('Should create a playlist on server 1 and have the playlist on server 2 and 3', async function () {
|
||||
this.timeout(30000)
|
||||
|
||||
|
@ -345,7 +342,6 @@ describe('Test video playlists', function () {
|
|||
})
|
||||
|
||||
describe('List playlists', function () {
|
||||
|
||||
it('Should correctly list the playlists', async function () {
|
||||
this.timeout(30000)
|
||||
|
||||
|
@ -495,7 +491,6 @@ describe('Test video playlists', function () {
|
|||
})
|
||||
|
||||
describe('Update playlists', function () {
|
||||
|
||||
it('Should update a playlist', async function () {
|
||||
this.timeout(30000)
|
||||
|
||||
|
@ -535,7 +530,6 @@ describe('Test video playlists', function () {
|
|||
})
|
||||
|
||||
describe('Element timestamps', function () {
|
||||
|
||||
it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () {
|
||||
this.timeout(120000)
|
||||
|
||||
|
@ -659,12 +653,14 @@ describe('Test video playlists', function () {
|
|||
group1 = [ servers[0] ]
|
||||
group2 = [ servers[1], servers[2] ]
|
||||
|
||||
const myInfo = await servers[0].users.getMyInfo({ token: userTokenServer1 })
|
||||
|
||||
const playlist = await commands[0].create({
|
||||
token: userTokenServer1,
|
||||
attributes: {
|
||||
displayName: 'playlist 56',
|
||||
privacy: VideoPlaylistPrivacy.PUBLIC,
|
||||
videoChannelId: servers[0].store.channel.id
|
||||
videoChannelId: myInfo.videoChannels[0].id
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -820,7 +816,6 @@ describe('Test video playlists', function () {
|
|||
})
|
||||
|
||||
describe('Managing playlist elements', function () {
|
||||
|
||||
it('Should reorder the playlist', async function () {
|
||||
this.timeout(30000)
|
||||
|
||||
|
@ -1094,7 +1089,6 @@ describe('Test video playlists', function () {
|
|||
})
|
||||
|
||||
describe('Playlist deletion', function () {
|
||||
|
||||
it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
|
||||
this.timeout(30000)
|
||||
|
||||
|
|
8
server/core/lib/activitypub/actors/check-actor.ts
Normal file
8
server/core/lib/activitypub/actors/check-actor.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { MActorHostOnly } from '@server/types/models/index.js'
|
||||
|
||||
export function haveActorsSameRemoteHost (base: MActorHostOnly, other: MActorHostOnly) {
|
||||
if (!base.serverId || !other.serverId) return false
|
||||
if (base.serverId !== other.serverId) return false
|
||||
|
||||
return true
|
||||
}
|
|
@ -143,7 +143,7 @@ async function scheduleOutboxFetchIfNeeded (actor: MActor, created: boolean, ref
|
|||
async function schedulePlaylistFetchIfNeeded (actor: MActorAccountId, created: boolean, accountPlaylistsUrl: string) {
|
||||
// We created a new account: fetch the playlists
|
||||
if (created === true && actor.Account && accountPlaylistsUrl) {
|
||||
const payload = { uri: accountPlaylistsUrl, type: 'account-playlists' as 'account-playlists' }
|
||||
const payload = { uri: accountPlaylistsUrl, type: 'account-playlists' as 'account-playlists', accountId: actor.Account.id }
|
||||
await JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export * from './check-actor.js'
|
||||
export * from './get.js'
|
||||
export * from './image.js'
|
||||
export * from './keys.js'
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { HttpStatusCode, PlaylistObject } from '@peertube/peertube-models'
|
||||
import { AttributesOnly } from '@peertube/peertube-typescript-utils'
|
||||
import { isArray } from '@server/helpers/custom-validators/misc.js'
|
||||
import { retryTransactionWrapper } from '@server/helpers/database-utils.js'
|
||||
import { logger, loggerTagsFactory } from '@server/helpers/logger.js'
|
||||
|
@ -10,11 +9,18 @@ import { updateRemotePlaylistMiniatureFromUrl } from '@server/lib/thumbnail.js'
|
|||
import { VideoPlaylistElementModel } from '@server/models/video/video-playlist-element.js'
|
||||
import { VideoPlaylistModel } from '@server/models/video/video-playlist.js'
|
||||
import { FilteredModelAttributes } from '@server/types/index.js'
|
||||
import { MThumbnail, MVideoPlaylist, MVideoPlaylistFull, MVideoPlaylistVideosLength } from '@server/types/models/index.js'
|
||||
import {
|
||||
MAccountHost,
|
||||
MThumbnail,
|
||||
MVideoPlaylist,
|
||||
MVideoPlaylistFull,
|
||||
MVideoPlaylistVideosLength
|
||||
} from '@server/types/models/index.js'
|
||||
import Bluebird from 'bluebird'
|
||||
import { getAPId } from '../activity.js'
|
||||
import { getOrCreateAPActor } from '../actors/index.js'
|
||||
import { crawlCollectionPage } from '../crawl.js'
|
||||
import { checkUrlsSameHost } from '../url.js'
|
||||
import { getOrCreateAPVideo } from '../videos/index.js'
|
||||
import {
|
||||
fetchRemotePlaylistElement,
|
||||
|
@ -22,11 +28,17 @@ import {
|
|||
playlistElementObjectToDBAttributes,
|
||||
playlistObjectToDBAttributes
|
||||
} from './shared/index.js'
|
||||
import { isActivityPubUrlValid } from '@server/helpers/custom-validators/activitypub/misc.js'
|
||||
|
||||
const lTags = loggerTagsFactory('ap', 'video-playlist')
|
||||
|
||||
async function createAccountPlaylists (playlistUrls: string[]) {
|
||||
export async function createAccountPlaylists (playlistUrls: string[], account: MAccountHost) {
|
||||
await Bluebird.map(playlistUrls, async playlistUrl => {
|
||||
if (!checkUrlsSameHost(playlistUrl, account.Actor.url)) {
|
||||
logger.warn(`Playlist ${playlistUrl} is not on the same host as owner account ${account.Actor.url}`, lTags(playlistUrl))
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const exists = await VideoPlaylistModel.doesPlaylistExist(playlistUrl)
|
||||
if (exists === true) return
|
||||
|
@ -37,17 +49,23 @@ async function createAccountPlaylists (playlistUrls: string[]) {
|
|||
throw new Error(`Cannot refresh remote playlist ${playlistUrl}: invalid body.`)
|
||||
}
|
||||
|
||||
return createOrUpdateVideoPlaylist(playlistObject)
|
||||
return createOrUpdateVideoPlaylist({ playlistObject })
|
||||
} catch (err) {
|
||||
logger.warn(`Cannot create or update playlist ${playlistUrl}`, { err, ...lTags(playlistUrl) })
|
||||
}
|
||||
}, { concurrency: CRAWL_REQUEST_CONCURRENCY })
|
||||
}
|
||||
|
||||
async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, to?: string[]) {
|
||||
export async function createOrUpdateVideoPlaylist (options: {
|
||||
playlistObject: PlaylistObject
|
||||
to?: string[]
|
||||
}) {
|
||||
const { playlistObject, to } = options
|
||||
const playlistAttributes = playlistObjectToDBAttributes(playlistObject, to || playlistObject.to)
|
||||
|
||||
await setVideoChannel(playlistObject, playlistAttributes)
|
||||
const channel = await getRemotePlaylistChannel(playlistObject)
|
||||
playlistAttributes.videoChannelId = channel.id
|
||||
playlistAttributes.ownerAccountId = channel.accountId
|
||||
|
||||
const [ upsertPlaylist ] = await VideoPlaylistModel.upsert<MVideoPlaylistVideosLength>(playlistAttributes, { returning: true })
|
||||
|
||||
|
@ -64,31 +82,29 @@ async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, to?:
|
|||
return playlist
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
createAccountPlaylists,
|
||||
createOrUpdateVideoPlaylist
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function setVideoChannel (playlistObject: PlaylistObject, playlistAttributes: AttributesOnly<VideoPlaylistModel>) {
|
||||
export async function getRemotePlaylistChannel (playlistObject: PlaylistObject) {
|
||||
if (!isArray(playlistObject.attributedTo) || playlistObject.attributedTo.length !== 1) {
|
||||
throw new Error('Not attributed to for playlist object ' + getAPId(playlistObject))
|
||||
}
|
||||
|
||||
const actor = await getOrCreateAPActor(getAPId(playlistObject.attributedTo[0]), 'all')
|
||||
|
||||
if (!actor.VideoChannel) {
|
||||
logger.warn('Playlist "attributedTo" %s is not a video channel.', playlistObject.id, { playlistObject, ...lTags(playlistObject.id) })
|
||||
return
|
||||
const channelUrl = getAPId(playlistObject.attributedTo[0])
|
||||
if (!checkUrlsSameHost(channelUrl, playlistObject.id)) {
|
||||
throw new Error(`Playlist ${playlistObject.id} and "attributedTo" channel ${channelUrl} are not on the same host`)
|
||||
}
|
||||
|
||||
playlistAttributes.videoChannelId = actor.VideoChannel.id
|
||||
playlistAttributes.ownerAccountId = actor.VideoChannel.Account.id
|
||||
const actor = await getOrCreateAPActor(channelUrl, 'all')
|
||||
|
||||
if (!actor.VideoChannel) {
|
||||
throw new Error(`Playlist ${playlistObject.id} "attributedTo" is not a video channel.`)
|
||||
}
|
||||
|
||||
return actor.VideoChannel
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Private
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function fetchElementUrls (playlistObject: PlaylistObject) {
|
||||
let accItems: string[] = []
|
||||
await crawlCollectionPage<string>(playlistObject.id, items => {
|
||||
|
@ -97,7 +113,7 @@ async function fetchElementUrls (playlistObject: PlaylistObject) {
|
|||
return Promise.resolve()
|
||||
})
|
||||
|
||||
return accItems
|
||||
return accItems.filter(i => isActivityPubUrlValid(i))
|
||||
}
|
||||
|
||||
async function updatePlaylistThumbnail (playlistObject: PlaylistObject, playlist: MVideoPlaylistFull) {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { APObjectId } from '@peertube/peertube-models'
|
||||
import { VideoPlaylistModel } from '@server/models/video/video-playlist.js'
|
||||
import { MVideoPlaylistFullSummary } from '@server/types/models/index.js'
|
||||
import { APObjectId } from '@peertube/peertube-models'
|
||||
import { getAPId } from '../activity.js'
|
||||
import { createOrUpdateVideoPlaylist } from './create-update.js'
|
||||
import { scheduleRefreshIfNeeded } from './refresh.js'
|
||||
import { fetchRemoteVideoPlaylist } from './shared/index.js'
|
||||
|
||||
async function getOrCreateAPVideoPlaylist (playlistObjectArg: APObjectId): Promise<MVideoPlaylistFullSummary> {
|
||||
export async function getOrCreateAPVideoPlaylist (playlistObjectArg: APObjectId): Promise<MVideoPlaylistFullSummary> {
|
||||
const playlistUrl = getAPId(playlistObjectArg)
|
||||
|
||||
const playlistFromDatabase = await VideoPlaylistModel.loadByUrlWithAccountAndChannelSummary(playlistUrl)
|
||||
|
@ -23,13 +23,7 @@ async function getOrCreateAPVideoPlaylist (playlistObjectArg: APObjectId): Promi
|
|||
// playlistUrl is just an alias/redirection, so process object id instead
|
||||
if (playlistObject.id !== playlistUrl) return getOrCreateAPVideoPlaylist(playlistObject)
|
||||
|
||||
const playlistCreated = await createOrUpdateVideoPlaylist(playlistObject)
|
||||
const playlistCreated = await createOrUpdateVideoPlaylist({ playlistObject })
|
||||
|
||||
return playlistCreated
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
getOrCreateAPVideoPlaylist
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { HttpStatusCode } from '@peertube/peertube-models'
|
||||
import { logger, loggerTagsFactory } from '@server/helpers/logger.js'
|
||||
import { PeerTubeRequestError } from '@server/helpers/requests.js'
|
||||
import { JobQueue } from '@server/lib/job-queue/index.js'
|
||||
import { MVideoPlaylist, MVideoPlaylistOwner } from '@server/types/models/index.js'
|
||||
import { HttpStatusCode } from '@peertube/peertube-models'
|
||||
import { MVideoPlaylist, MVideoPlaylistOwnerDefault } from '@server/types/models/index.js'
|
||||
import { createOrUpdateVideoPlaylist } from './create-update.js'
|
||||
import { fetchRemoteVideoPlaylist } from './shared/index.js'
|
||||
|
||||
|
@ -12,7 +12,7 @@ function scheduleRefreshIfNeeded (playlist: MVideoPlaylist) {
|
|||
JobQueue.Instance.createJobAsync({ type: 'activitypub-refresher', payload: { type: 'video-playlist', url: playlist.url } })
|
||||
}
|
||||
|
||||
async function refreshVideoPlaylistIfNeeded (videoPlaylist: MVideoPlaylistOwner): Promise<MVideoPlaylistOwner> {
|
||||
async function refreshVideoPlaylistIfNeeded (videoPlaylist: MVideoPlaylistOwnerDefault): Promise<MVideoPlaylistOwnerDefault> {
|
||||
if (!videoPlaylist.isOutdated()) return videoPlaylist
|
||||
|
||||
const lTags = loggerTagsFactory('ap', 'video-playlist', 'refresh', videoPlaylist.uuid, videoPlaylist.url)
|
||||
|
@ -29,7 +29,7 @@ async function refreshVideoPlaylistIfNeeded (videoPlaylist: MVideoPlaylistOwner)
|
|||
return videoPlaylist
|
||||
}
|
||||
|
||||
await createOrUpdateVideoPlaylist(playlistObject)
|
||||
await createOrUpdateVideoPlaylist({ playlistObject })
|
||||
|
||||
return videoPlaylist
|
||||
} catch (err) {
|
||||
|
@ -50,6 +50,6 @@ async function refreshVideoPlaylistIfNeeded (videoPlaylist: MVideoPlaylistOwner)
|
|||
}
|
||||
|
||||
export {
|
||||
scheduleRefreshIfNeeded,
|
||||
refreshVideoPlaylistIfNeeded
|
||||
refreshVideoPlaylistIfNeeded,
|
||||
scheduleRefreshIfNeeded
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@ export function playlistObjectToDBAttributes (playlistObject: PlaylistObject, to
|
|||
privacy,
|
||||
url: playlistObject.id,
|
||||
uuid: playlistObject.uuid,
|
||||
ownerAccountId: null,
|
||||
videoChannelId: null,
|
||||
ownerAccountId: null,
|
||||
createdAt: new Date(playlistObject.published),
|
||||
updatedAt: new Date(playlistObject.updated)
|
||||
} as AttributesOnly<VideoPlaylistModel>
|
||||
|
|
|
@ -184,8 +184,7 @@ async function processCreatePlaylist (
|
|||
byActor: MActorSignature
|
||||
) {
|
||||
const byAccount = byActor.Account
|
||||
|
||||
if (!byAccount) throw new Error('Cannot create video playlist with the non account actor ' + byActor.url)
|
||||
|
||||
await createOrUpdateVideoPlaylist(playlistObject, activity.to)
|
||||
await createOrUpdateVideoPlaylist({ playlistObject, to: activity.to })
|
||||
}
|
||||
|
|
|
@ -127,5 +127,5 @@ async function processUpdatePlaylist (
|
|||
const byAccount = byActor.Account
|
||||
if (!byAccount) throw new Error('Cannot update video playlist with the non account actor ' + byActor.url)
|
||||
|
||||
await createOrUpdateVideoPlaylist(playlistObject, activity.to)
|
||||
await createOrUpdateVideoPlaylist({ playlistObject, to: activity.to })
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { Transaction } from 'sequelize'
|
||||
import { VideoObject, VideoPrivacy } from '@peertube/peertube-models'
|
||||
import { resetSequelizeInstance, runInReadCommittedTransaction } from '@server/helpers/database-utils.js'
|
||||
import { logger, loggerTagsFactory, LoggerTagsFn } from '@server/helpers/logger.js'
|
||||
|
@ -8,12 +7,14 @@ import { Hooks } from '@server/lib/plugins/hooks.js'
|
|||
import { autoBlacklistVideoIfNeeded } from '@server/lib/video-blacklist.js'
|
||||
import { VideoLiveModel } from '@server/models/video/video-live.js'
|
||||
import {
|
||||
MActor,
|
||||
MActorHost,
|
||||
MChannelAccountLight,
|
||||
MChannelId,
|
||||
MVideoAccountLightBlacklistAllFiles,
|
||||
MVideoFullLight
|
||||
} from '@server/types/models/index.js'
|
||||
import { Transaction } from 'sequelize'
|
||||
import { haveActorsSameRemoteHost } from '../actors/check-actor.js'
|
||||
import { APVideoAbstractBuilder, getVideoAttributesFromObject, updateVideoRates } from './shared/index.js'
|
||||
|
||||
export class APVideoUpdater extends APVideoAbstractBuilder {
|
||||
|
@ -40,7 +41,8 @@ export class APVideoUpdater extends APVideoAbstractBuilder {
|
|||
|
||||
async update (overrideTo?: string[]) {
|
||||
logger.debug(
|
||||
'Updating remote video "%s".', this.videoObject.uuid,
|
||||
'Updating remote video "%s".',
|
||||
this.videoObject.uuid,
|
||||
{ videoObject: this.videoObject, ...this.lTags() }
|
||||
)
|
||||
|
||||
|
@ -111,13 +113,9 @@ export class APVideoUpdater extends APVideoAbstractBuilder {
|
|||
}
|
||||
|
||||
// Check we can update the channel: we trust the remote server
|
||||
private checkChannelUpdateOrThrow (newChannelActor: MActor) {
|
||||
if (!this.oldVideoChannel.Actor.serverId || !newChannelActor.serverId) {
|
||||
throw new Error('Cannot check old channel/new channel validity because `serverId` is null')
|
||||
}
|
||||
|
||||
if (this.oldVideoChannel.Actor.serverId !== newChannelActor.serverId) {
|
||||
throw new Error(`New channel ${newChannelActor.url} is not on the same server than new channel ${this.oldVideoChannel.Actor.url}`)
|
||||
private checkChannelUpdateOrThrow (newChannelActor: MActorHost) {
|
||||
if (haveActorsSameRemoteHost(this.oldVideoChannel.Actor, newChannelActor) !== true) {
|
||||
throw new Error(`Actor ${this.oldVideoChannel.Actor.url} is not on the same host as ${newChannelActor.url}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,12 +4,13 @@ import { logger } from '../../../helpers/logger.js'
|
|||
import { VideoModel } from '../../../models/video/video.js'
|
||||
import { VideoCommentModel } from '../../../models/video/video-comment.js'
|
||||
import { VideoShareModel } from '../../../models/video/video-share.js'
|
||||
import { MVideoFullLight } from '../../../types/models/index.js'
|
||||
import { MAccountDefault, MVideoFullLight } from '../../../types/models/index.js'
|
||||
import { crawlCollectionPage } from '../../activitypub/crawl.js'
|
||||
import { createAccountPlaylists } from '../../activitypub/playlists/index.js'
|
||||
import { processActivities } from '../../activitypub/process/index.js'
|
||||
import { addVideoShares } from '../../activitypub/share.js'
|
||||
import { addVideoComments } from '../../activitypub/video-comments.js'
|
||||
import { AccountModel } from '@server/models/account/account.js'
|
||||
|
||||
async function processActivityPubHttpFetcher (job: Job) {
|
||||
logger.info('Processing ActivityPub fetcher in job %s.', job.id)
|
||||
|
@ -19,14 +20,17 @@ async function processActivityPubHttpFetcher (job: Job) {
|
|||
let video: MVideoFullLight
|
||||
if (payload.videoId) video = await VideoModel.loadFull(payload.videoId)
|
||||
|
||||
const fetcherType: { [ id in FetchType ]: (items: any[]) => Promise<any> } = {
|
||||
let account: MAccountDefault
|
||||
if (payload.accountId) account = await AccountModel.load(payload.accountId)
|
||||
|
||||
const fetcherType: { [id in FetchType]: (items: any[]) => Promise<any> } = {
|
||||
'activity': items => processActivities(items, { outboxUrl: payload.uri, fromFetch: true }),
|
||||
'video-shares': items => addVideoShares(items, video),
|
||||
'video-comments': items => addVideoComments(items),
|
||||
'account-playlists': items => createAccountPlaylists(items)
|
||||
'account-playlists': items => createAccountPlaylists(items, account)
|
||||
}
|
||||
|
||||
const cleanerType: { [ id in FetchType ]?: (crawlStartDate: Date) => Promise<any> } = {
|
||||
const cleanerType: { [id in FetchType]?: (crawlStartDate: Date) => Promise<any> } = {
|
||||
'video-shares': crawlStartDate => VideoShareModel.cleanOldSharesOf(video.id, crawlStartDate),
|
||||
'video-comments': crawlStartDate => VideoCommentModel.cleanOldCommentsOf(video.id, crawlStartDate)
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ export async function doesVideoChannelOfAccountExist (channelId: number, user: M
|
|||
const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId)
|
||||
|
||||
if (videoChannel === null) {
|
||||
res.fail({ message: 'Unknown video "video channel" for this instance.' })
|
||||
res.fail({ message: `Unknown ${channelId} on this instance.` })
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -94,9 +94,7 @@ export async function doesVideoChannelOfAccountExist (channelId: number, user: M
|
|||
}
|
||||
|
||||
if (videoChannel.Account.id !== user.Account.id) {
|
||||
res.fail({
|
||||
message: 'Unknown video "video channel" for this account.'
|
||||
})
|
||||
res.fail({ message: `Unknown channel ${channelId} for this account.` })
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import express from 'express'
|
||||
import { body, param, query, ValidationChain } from 'express-validator'
|
||||
import { forceNumber } from '@peertube/peertube-core-utils'
|
||||
import {
|
||||
HttpStatusCode,
|
||||
|
@ -12,6 +10,8 @@ import {
|
|||
} from '@peertube/peertube-models'
|
||||
import { ExpressPromiseHandler } from '@server/types/express-handler.js'
|
||||
import { MUserAccountId } from '@server/types/models/index.js'
|
||||
import express from 'express'
|
||||
import { body, param, query, ValidationChain } from 'express-validator'
|
||||
import {
|
||||
isArrayOf,
|
||||
isIdOrUUIDValid,
|
||||
|
@ -37,7 +37,7 @@ import { MVideoPlaylist } from '../../../types/models/video/video-playlist.js'
|
|||
import { authenticatePromise } from '../../auth.js'
|
||||
import {
|
||||
areValidationErrors,
|
||||
doesVideoChannelIdExist,
|
||||
doesVideoChannelOfAccountExist,
|
||||
doesVideoExist,
|
||||
doesVideoPlaylistExist,
|
||||
isValidPlaylistIdParam,
|
||||
|
@ -52,7 +52,9 @@ const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([
|
|||
if (areValidationErrors(req, res)) return cleanUpReqFiles(req)
|
||||
|
||||
const body: VideoPlaylistCreate = req.body
|
||||
if (body.videoChannelId && !await doesVideoChannelIdExist(body.videoChannelId, res)) return cleanUpReqFiles(req)
|
||||
if (body.videoChannelId && !await doesVideoChannelOfAccountExist(body.videoChannelId, res.locals.oauth.token.User, res)) {
|
||||
return cleanUpReqFiles(req)
|
||||
}
|
||||
|
||||
if (
|
||||
!body.videoChannelId &&
|
||||
|
@ -88,7 +90,8 @@ const videoPlaylistsUpdateValidator = getCommonPlaylistEditAttributes().concat([
|
|||
const body: VideoPlaylistUpdate = req.body
|
||||
|
||||
const newPrivacy = body.privacy || videoPlaylist.privacy
|
||||
if (newPrivacy === VideoPlaylistPrivacy.PUBLIC &&
|
||||
if (
|
||||
newPrivacy === VideoPlaylistPrivacy.PUBLIC &&
|
||||
(
|
||||
(!videoPlaylist.videoChannelId && !body.videoChannelId) ||
|
||||
body.videoChannelId === null
|
||||
|
@ -105,7 +108,9 @@ const videoPlaylistsUpdateValidator = getCommonPlaylistEditAttributes().concat([
|
|||
return res.fail({ message: 'Cannot update a watch later playlist.' })
|
||||
}
|
||||
|
||||
if (body.videoChannelId && !await doesVideoChannelIdExist(body.videoChannelId, res)) return cleanUpReqFiles(req)
|
||||
if (body.videoChannelId && !await doesVideoChannelOfAccountExist(body.videoChannelId, res.locals.oauth.token.User, res)) {
|
||||
return cleanUpReqFiles(req)
|
||||
}
|
||||
|
||||
return next()
|
||||
}
|
||||
|
@ -350,21 +355,17 @@ const doVideosInPlaylistExistValidator = [
|
|||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
commonVideoPlaylistFiltersValidator,
|
||||
doVideosInPlaylistExistValidator,
|
||||
videoPlaylistElementAPGetValidator,
|
||||
videoPlaylistsAddValidator,
|
||||
videoPlaylistsUpdateValidator,
|
||||
videoPlaylistsAddVideoValidator,
|
||||
videoPlaylistsDeleteValidator,
|
||||
videoPlaylistsGetValidator,
|
||||
videoPlaylistsSearchValidator,
|
||||
|
||||
videoPlaylistsAddVideoValidator,
|
||||
videoPlaylistsUpdateOrRemoveVideoValidator,
|
||||
videoPlaylistsReorderVideosValidator,
|
||||
|
||||
videoPlaylistElementAPGetValidator,
|
||||
|
||||
commonVideoPlaylistFiltersValidator,
|
||||
|
||||
doVideosInPlaylistExistValidator
|
||||
videoPlaylistsSearchValidator,
|
||||
videoPlaylistsUpdateOrRemoveVideoValidator,
|
||||
videoPlaylistsUpdateValidator
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
@ -375,7 +376,7 @@ function getCommonPlaylistEditAttributes () {
|
|||
.custom((value, { req }) => isVideoImageValid(req.files, 'thumbnailfile'))
|
||||
.withMessage(
|
||||
'This thumbnail file is not supported or too large. Please, make sure it is of the following type: ' +
|
||||
CONSTRAINTS_FIELDS.VIDEO_PLAYLISTS.IMAGE.EXTNAME.join(', ')
|
||||
CONSTRAINTS_FIELDS.VIDEO_PLAYLISTS.IMAGE.EXTNAME.join(', ')
|
||||
),
|
||||
|
||||
body('description')
|
||||
|
|
Loading…
Add table
Reference in a new issue