mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-10-03 17:59:37 +02:00
Add channel collaborators feature
This commit is contained in:
parent
94e55dfc6c
commit
135d5c7363
185 changed files with 5457 additions and 2631 deletions
|
@ -31,6 +31,7 @@ import {
|
|||
BlacklistCommand,
|
||||
CaptionsCommand,
|
||||
ChangeOwnershipCommand,
|
||||
ChannelCollaboratorsCommand,
|
||||
ChannelSyncsCommand,
|
||||
ChannelsCommand,
|
||||
ChaptersCommand,
|
||||
|
@ -82,6 +83,7 @@ export class PeerTubeServer {
|
|||
|
||||
parallel?: boolean
|
||||
internalServerNumber: number
|
||||
adminEmail: string
|
||||
|
||||
serverNumber?: number
|
||||
customConfigFile?: string
|
||||
|
@ -170,6 +172,8 @@ export class PeerTubeServer {
|
|||
watchedWordsLists?: WatchedWordsCommand
|
||||
autoTags?: AutomaticTagsCommand
|
||||
|
||||
channelCollaborators?: ChannelCollaboratorsCommand
|
||||
|
||||
constructor (options: { serverNumber: number } | { url: string }) {
|
||||
if ((options as any).url) {
|
||||
this.setUrl((options as any).url)
|
||||
|
@ -188,6 +192,7 @@ export class PeerTubeServer {
|
|||
}
|
||||
}
|
||||
|
||||
this.adminEmail = this.buildEmail()
|
||||
this.assignCommands()
|
||||
}
|
||||
|
||||
|
@ -406,7 +411,7 @@ export class PeerTubeServer {
|
|||
well_known: this.getDirectoryPath('well-known') + '/'
|
||||
},
|
||||
admin: {
|
||||
email: `admin${this.internalServerNumber}@example.com`
|
||||
email: this.buildEmail()
|
||||
},
|
||||
live: {
|
||||
rtmp: {
|
||||
|
@ -477,5 +482,11 @@ export class PeerTubeServer {
|
|||
|
||||
this.watchedWordsLists = new WatchedWordsCommand(this)
|
||||
this.autoTags = new AutomaticTagsCommand(this)
|
||||
|
||||
this.channelCollaborators = new ChannelCollaboratorsCommand(this)
|
||||
}
|
||||
|
||||
private buildEmail () {
|
||||
return `admin${this.internalServerNumber}@example.com`
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,8 @@ import {
|
|||
UserUpdate,
|
||||
UserUpdateMe,
|
||||
UserVideoQuota,
|
||||
UserVideoRate
|
||||
UserVideoRate,
|
||||
VideoChannel
|
||||
} from '@peertube/peertube-models'
|
||||
import { unwrapBody } from '../requests/index.js'
|
||||
import { AbstractCommand, OverrideCommandOptions } from '../shared/index.js'
|
||||
|
@ -282,6 +283,20 @@ export class UsersCommand extends AbstractCommand {
|
|||
})
|
||||
}
|
||||
|
||||
listMyChannels (options: OverrideCommandOptions = {}) {
|
||||
const path = '/api/v1/users/me/video-channels'
|
||||
|
||||
return this.getRequestBody<ResultList<VideoChannel>>({
|
||||
...options,
|
||||
|
||||
path,
|
||||
implicitToken: true,
|
||||
defaultExpectedStatus: HttpStatusCode.OK_200
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
deleteMe (options: OverrideCommandOptions = {}) {
|
||||
const path = '/api/v1/users/me'
|
||||
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
import { HttpStatusCode, ResultList, VideoChannelCollaborator } from '@peertube/peertube-models'
|
||||
import { unwrapBody } from '../requests/index.js'
|
||||
import { AbstractCommand, OverrideCommandOptions } from '../shared/index.js'
|
||||
|
||||
export class ChannelCollaboratorsCommand extends AbstractCommand {
|
||||
list (
|
||||
options: OverrideCommandOptions & {
|
||||
channel: string
|
||||
}
|
||||
) {
|
||||
const { channel } = options
|
||||
const path = '/api/v1/video-channels/' + channel + '/collaborators'
|
||||
|
||||
return this.getRequestBody<ResultList<VideoChannelCollaborator>>({
|
||||
...options,
|
||||
|
||||
path,
|
||||
implicitToken: true,
|
||||
defaultExpectedStatus: HttpStatusCode.OK_200
|
||||
})
|
||||
}
|
||||
|
||||
async invite (
|
||||
options: OverrideCommandOptions & {
|
||||
channel: string
|
||||
target: string
|
||||
}
|
||||
) {
|
||||
const { channel, target } = options
|
||||
const path = '/api/v1/video-channels/' + channel + '/collaborators/invite'
|
||||
|
||||
const body = await unwrapBody<{ collaborator: VideoChannelCollaborator }>(this.postBodyRequest({
|
||||
...options,
|
||||
|
||||
path,
|
||||
fields: {
|
||||
accountHandle: target
|
||||
},
|
||||
implicitToken: true,
|
||||
defaultExpectedStatus: HttpStatusCode.OK_200
|
||||
}))
|
||||
|
||||
return body.collaborator
|
||||
}
|
||||
|
||||
accept (
|
||||
options: OverrideCommandOptions & {
|
||||
channel: string
|
||||
id: number
|
||||
}
|
||||
) {
|
||||
const { id, channel } = options
|
||||
const path = '/api/v1/video-channels/' + channel + '/collaborators/' + id + '/accept'
|
||||
|
||||
return this.postBodyRequest({
|
||||
...options,
|
||||
|
||||
path,
|
||||
implicitToken: true,
|
||||
defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
|
||||
})
|
||||
}
|
||||
|
||||
reject (
|
||||
options: OverrideCommandOptions & {
|
||||
channel: string
|
||||
id: number
|
||||
}
|
||||
) {
|
||||
const { id, channel } = options
|
||||
const path = '/api/v1/video-channels/' + channel + '/collaborators/' + id + '/reject'
|
||||
|
||||
return this.postBodyRequest({
|
||||
...options,
|
||||
|
||||
path,
|
||||
implicitToken: true,
|
||||
defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
|
||||
})
|
||||
}
|
||||
|
||||
remove (
|
||||
options: OverrideCommandOptions & {
|
||||
channel: string
|
||||
id: number
|
||||
}
|
||||
) {
|
||||
const { id, channel } = options
|
||||
const path = '/api/v1/video-channels/' + channel + '/collaborators/' + id
|
||||
|
||||
return this.deleteRequest({
|
||||
...options,
|
||||
|
||||
path,
|
||||
implicitToken: true,
|
||||
defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
|
||||
})
|
||||
}
|
||||
}
|
|
@ -39,6 +39,7 @@ export class ChannelsCommand extends AbstractCommand {
|
|||
sort?: string
|
||||
withStats?: boolean
|
||||
search?: string
|
||||
includeCollaborations?: boolean
|
||||
}
|
||||
) {
|
||||
const { accountName, sort = 'createdAt' } = options
|
||||
|
@ -48,7 +49,7 @@ export class ChannelsCommand extends AbstractCommand {
|
|||
...options,
|
||||
|
||||
path,
|
||||
query: { sort, ...pick(options, [ 'start', 'count', 'withStats', 'search' ]) },
|
||||
query: { sort, ...pick(options, [ 'start', 'count', 'withStats', 'search', 'includeCollaborations' ]) },
|
||||
implicitToken: false,
|
||||
defaultExpectedStatus: HttpStatusCode.OK_200
|
||||
})
|
||||
|
|
|
@ -23,7 +23,6 @@ type ListForAdminOrAccountCommonOptions = {
|
|||
}
|
||||
|
||||
export class CommentsCommand extends AbstractCommand {
|
||||
|
||||
private lastVideoId: number | string
|
||||
private lastThreadId: number
|
||||
private lastReplyId: number
|
||||
|
@ -51,6 +50,7 @@ export class CommentsCommand extends AbstractCommand {
|
|||
|
||||
listCommentsOnMyVideos (options: OverrideCommandOptions & ListForAdminOrAccountCommonOptions & {
|
||||
isHeldForReview?: boolean
|
||||
includeCollaborations?: boolean
|
||||
} = {}) {
|
||||
const path = '/api/v1/users/me/videos/comments'
|
||||
|
||||
|
@ -61,7 +61,7 @@ export class CommentsCommand extends AbstractCommand {
|
|||
query: {
|
||||
...this.buildListForAdminOrAccountQuery(options),
|
||||
|
||||
isHeldForReview: options.isHeldForReview
|
||||
...pick(options, [ 'isHeldForReview', 'includeCollaborations' ])
|
||||
},
|
||||
implicitToken: true,
|
||||
defaultExpectedStatus: HttpStatusCode.OK_200
|
||||
|
@ -78,13 +78,15 @@ export class CommentsCommand extends AbstractCommand {
|
|||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
listThreads (options: OverrideCommandOptions & {
|
||||
videoId: number | string
|
||||
videoPassword?: string
|
||||
start?: number
|
||||
count?: number
|
||||
sort?: string
|
||||
}) {
|
||||
listThreads (
|
||||
options: OverrideCommandOptions & {
|
||||
videoId: number | string
|
||||
videoPassword?: string
|
||||
start?: number
|
||||
count?: number
|
||||
sort?: string
|
||||
}
|
||||
) {
|
||||
const { start, count, sort, videoId, videoPassword } = options
|
||||
const path = '/api/v1/videos/' + videoId + '/comment-threads'
|
||||
|
||||
|
@ -99,10 +101,12 @@ export class CommentsCommand extends AbstractCommand {
|
|||
})
|
||||
}
|
||||
|
||||
getThread (options: OverrideCommandOptions & {
|
||||
videoId: number | string
|
||||
threadId: number
|
||||
}) {
|
||||
getThread (
|
||||
options: OverrideCommandOptions & {
|
||||
videoId: number | string
|
||||
threadId: number
|
||||
}
|
||||
) {
|
||||
const { videoId, threadId } = options
|
||||
const path = '/api/v1/videos/' + videoId + '/comment-threads/' + threadId
|
||||
|
||||
|
@ -115,21 +119,25 @@ export class CommentsCommand extends AbstractCommand {
|
|||
})
|
||||
}
|
||||
|
||||
async getThreadOf (options: OverrideCommandOptions & {
|
||||
videoId: number | string
|
||||
text: string
|
||||
}) {
|
||||
async getThreadOf (
|
||||
options: OverrideCommandOptions & {
|
||||
videoId: number | string
|
||||
text: string
|
||||
}
|
||||
) {
|
||||
const { videoId, text } = options
|
||||
const threadId = await this.findCommentId({ videoId, text })
|
||||
|
||||
return this.getThread({ ...options, videoId, threadId })
|
||||
}
|
||||
|
||||
async createThread (options: OverrideCommandOptions & {
|
||||
videoId: number | string
|
||||
text: string
|
||||
videoPassword?: string
|
||||
}) {
|
||||
async createThread (
|
||||
options: OverrideCommandOptions & {
|
||||
videoId: number | string
|
||||
text: string
|
||||
videoPassword?: string
|
||||
}
|
||||
) {
|
||||
const { videoId, text, videoPassword } = options
|
||||
const path = '/api/v1/videos/' + videoId + '/comment-threads'
|
||||
|
||||
|
@ -149,12 +157,14 @@ export class CommentsCommand extends AbstractCommand {
|
|||
return body.comment
|
||||
}
|
||||
|
||||
async addReply (options: OverrideCommandOptions & {
|
||||
videoId: number | string
|
||||
toCommentId: number
|
||||
text: string
|
||||
videoPassword?: string
|
||||
}) {
|
||||
async addReply (
|
||||
options: OverrideCommandOptions & {
|
||||
videoId: number | string
|
||||
toCommentId: number
|
||||
text: string
|
||||
videoPassword?: string
|
||||
}
|
||||
) {
|
||||
const { videoId, toCommentId, text, videoPassword } = options
|
||||
const path = '/api/v1/videos/' + videoId + '/comments/' + toCommentId
|
||||
|
||||
|
@ -173,22 +183,28 @@ export class CommentsCommand extends AbstractCommand {
|
|||
return body.comment
|
||||
}
|
||||
|
||||
async addReplyToLastReply (options: OverrideCommandOptions & {
|
||||
text: string
|
||||
}) {
|
||||
async addReplyToLastReply (
|
||||
options: OverrideCommandOptions & {
|
||||
text: string
|
||||
}
|
||||
) {
|
||||
return this.addReply({ ...options, videoId: this.lastVideoId, toCommentId: this.lastReplyId })
|
||||
}
|
||||
|
||||
async addReplyToLastThread (options: OverrideCommandOptions & {
|
||||
text: string
|
||||
}) {
|
||||
async addReplyToLastThread (
|
||||
options: OverrideCommandOptions & {
|
||||
text: string
|
||||
}
|
||||
) {
|
||||
return this.addReply({ ...options, videoId: this.lastVideoId, toCommentId: this.lastThreadId })
|
||||
}
|
||||
|
||||
async findCommentId (options: OverrideCommandOptions & {
|
||||
videoId: number | string
|
||||
text: string
|
||||
}) {
|
||||
async findCommentId (
|
||||
options: OverrideCommandOptions & {
|
||||
videoId: number | string
|
||||
text: string
|
||||
}
|
||||
) {
|
||||
const { videoId, text } = options
|
||||
const { data } = await this.listForAdmin({ videoId, count: 25, sort: '-createdAt' })
|
||||
|
||||
|
@ -197,10 +213,12 @@ export class CommentsCommand extends AbstractCommand {
|
|||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
delete (options: OverrideCommandOptions & {
|
||||
videoId: number | string
|
||||
commentId: number
|
||||
}) {
|
||||
delete (
|
||||
options: OverrideCommandOptions & {
|
||||
videoId: number | string
|
||||
commentId: number
|
||||
}
|
||||
) {
|
||||
const { videoId, commentId } = options
|
||||
const path = '/api/v1/videos/' + videoId + '/comments/' + commentId
|
||||
|
||||
|
@ -213,9 +231,11 @@ export class CommentsCommand extends AbstractCommand {
|
|||
})
|
||||
}
|
||||
|
||||
async deleteAllComments (options: OverrideCommandOptions & {
|
||||
videoUUID: string
|
||||
}) {
|
||||
async deleteAllComments (
|
||||
options: OverrideCommandOptions & {
|
||||
videoUUID: string
|
||||
}
|
||||
) {
|
||||
const { data } = await this.listForAdmin({ ...options, start: 0, count: 20 })
|
||||
|
||||
for (const comment of data) {
|
||||
|
@ -227,10 +247,12 @@ export class CommentsCommand extends AbstractCommand {
|
|||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
approve (options: OverrideCommandOptions & {
|
||||
videoId: number | string
|
||||
commentId: number
|
||||
}) {
|
||||
approve (
|
||||
options: OverrideCommandOptions & {
|
||||
videoId: number | string
|
||||
commentId: number
|
||||
}
|
||||
) {
|
||||
const { videoId, commentId } = options
|
||||
const path = '/api/v1/videos/' + videoId + '/comments/' + commentId + '/approve'
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
export * from './blacklist-command.js'
|
||||
export * from './captions-command.js'
|
||||
export * from './channel-collaborators-command.js'
|
||||
export * from './change-ownership-command.js'
|
||||
export * from './channels.js'
|
||||
export * from './channels-command.js'
|
||||
|
|
|
@ -71,10 +71,11 @@ export class PlaylistsCommand extends AbstractCommand {
|
|||
sort?: string
|
||||
search?: string
|
||||
playlistType?: VideoPlaylistType_Type
|
||||
includeCollaborations?: boolean
|
||||
}
|
||||
) {
|
||||
const path = '/api/v1/accounts/' + options.handle + '/video-playlists'
|
||||
const query = pick(options, [ 'start', 'count', 'sort', 'search', 'playlistType' ])
|
||||
const query = pick(options, [ 'start', 'count', 'sort', 'search', 'playlistType', 'includeCollaborations' ])
|
||||
|
||||
return this.getRequestBody<ResultList<VideoPlaylist>>({
|
||||
...options,
|
||||
|
|
|
@ -229,14 +229,18 @@ export class VideosCommand extends AbstractCommand {
|
|||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
listMyVideos (options: OverrideCommandOptions & VideosCommonQuery & { channelId?: number, channelNameOneOf?: string[] } = {}) {
|
||||
listMyVideos (options: OverrideCommandOptions & VideosCommonQuery & {
|
||||
channelId?: number
|
||||
channelNameOneOf?: string[]
|
||||
includeCollaborations?: boolean
|
||||
} = {}) {
|
||||
const path = '/api/v1/users/me/videos'
|
||||
|
||||
return this.getRequestBody<ResultList<Video>>({
|
||||
...options,
|
||||
|
||||
path,
|
||||
query: { ...this.buildListQuery(options), ...pick(options, [ 'channelId', 'channelNameOneOf' ]) },
|
||||
query: { ...this.buildListQuery(options), ...pick(options, [ 'channelId', 'channelNameOneOf', 'includeCollaborations' ]) },
|
||||
implicitToken: true,
|
||||
defaultExpectedStatus: HttpStatusCode.OK_200
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue