diff options
author | Wicklow <123956049+wickloww@users.noreply.github.com> | 2023-06-29 07:48:55 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-06-29 09:48:55 +0200 |
commit | 40346ead2b0b7afa475aef057d3673b6c7574b7a (patch) | |
tree | 24ffdc23c3a9d987334842e0d400b5bd44500cf7 /shared/server-commands | |
parent | ae22c59f14d0d553f60b281948b6c232c2aca178 (diff) | |
download | PeerTube-40346ead2b0b7afa475aef057d3673b6c7574b7a.tar.gz PeerTube-40346ead2b0b7afa475aef057d3673b6c7574b7a.tar.zst PeerTube-40346ead2b0b7afa475aef057d3673b6c7574b7a.zip |
Feature/password protected videos (#5836)
* Add server endpoints
* Refactoring test suites
* Update server and add openapi documentation
* fix compliation and tests
* upload/import password protected video on client
* add server error code
* Add video password to update resolver
* add custom message when sharing pw protected video
* improve confirm component
* Add new alert in component
* Add ability to watch protected video on client
* Cannot have password protected replay privacy
* Add migration
* Add tests
* update after review
* Update check params tests
* Add live videos test
* Add more filter test
* Update static file privacy test
* Update object storage tests
* Add test on feeds
* Add missing word
* Fix tests
* Fix tests on live videos
* add embed support on password protected videos
* fix style
* Correcting data leaks
* Unable to add password protected privacy on replay
* Updated code based on review comments
* fix validator and command
* Updated code based on review comments
Diffstat (limited to 'shared/server-commands')
-rw-r--r-- | shared/server-commands/requests/requests.ts | 4 | ||||
-rw-r--r-- | shared/server-commands/server/server.ts | 3 | ||||
-rw-r--r-- | shared/server-commands/shared/abstract-command.ts | 18 | ||||
-rw-r--r-- | shared/server-commands/videos/captions-command.ts | 4 | ||||
-rw-r--r-- | shared/server-commands/videos/comments-command.ts | 12 | ||||
-rw-r--r-- | shared/server-commands/videos/index.ts | 1 | ||||
-rw-r--r-- | shared/server-commands/videos/live-command.ts | 12 | ||||
-rw-r--r-- | shared/server-commands/videos/video-passwords-command.ts | 55 | ||||
-rw-r--r-- | shared/server-commands/videos/video-token-command.ts | 5 | ||||
-rw-r--r-- | shared/server-commands/videos/videos-command.ts | 23 |
10 files changed, 123 insertions, 14 deletions
diff --git a/shared/server-commands/requests/requests.ts b/shared/server-commands/requests/requests.ts index e3f1817f1..8227017eb 100644 --- a/shared/server-commands/requests/requests.ts +++ b/shared/server-commands/requests/requests.ts | |||
@@ -29,6 +29,7 @@ function makeRawRequest (options: { | |||
29 | range?: string | 29 | range?: string |
30 | query?: { [ id: string ]: string } | 30 | query?: { [ id: string ]: string } |
31 | method?: 'GET' | 'POST' | 31 | method?: 'GET' | 'POST' |
32 | headers?: { [ name: string ]: string } | ||
32 | }) { | 33 | }) { |
33 | const { host, protocol, pathname } = new URL(options.url) | 34 | const { host, protocol, pathname } = new URL(options.url) |
34 | 35 | ||
@@ -37,7 +38,7 @@ function makeRawRequest (options: { | |||
37 | path: pathname, | 38 | path: pathname, |
38 | contentType: undefined, | 39 | contentType: undefined, |
39 | 40 | ||
40 | ...pick(options, [ 'expectedStatus', 'range', 'token', 'query' ]) | 41 | ...pick(options, [ 'expectedStatus', 'range', 'token', 'query', 'headers' ]) |
41 | } | 42 | } |
42 | 43 | ||
43 | if (options.method === 'POST') { | 44 | if (options.method === 'POST') { |
@@ -132,6 +133,7 @@ function makePutBodyRequest (options: { | |||
132 | token?: string | 133 | token?: string |
133 | fields: { [ fieldName: string ]: any } | 134 | fields: { [ fieldName: string ]: any } |
134 | expectedStatus?: HttpStatusCode | 135 | expectedStatus?: HttpStatusCode |
136 | headers?: { [name: string]: string } | ||
135 | }) { | 137 | }) { |
136 | const req = request(options.url).put(options.path) | 138 | const req = request(options.url).put(options.path) |
137 | .send(options.fields) | 139 | .send(options.fields) |
diff --git a/shared/server-commands/server/server.ts b/shared/server-commands/server/server.ts index 70f7a3ee2..0911e22b0 100644 --- a/shared/server-commands/server/server.ts +++ b/shared/server-commands/server/server.ts | |||
@@ -32,6 +32,7 @@ import { | |||
32 | HistoryCommand, | 32 | HistoryCommand, |
33 | ImportsCommand, | 33 | ImportsCommand, |
34 | LiveCommand, | 34 | LiveCommand, |
35 | VideoPasswordsCommand, | ||
35 | PlaylistsCommand, | 36 | PlaylistsCommand, |
36 | ServicesCommand, | 37 | ServicesCommand, |
37 | StreamingPlaylistsCommand, | 38 | StreamingPlaylistsCommand, |
@@ -146,6 +147,7 @@ export class PeerTubeServer { | |||
146 | twoFactor?: TwoFactorCommand | 147 | twoFactor?: TwoFactorCommand |
147 | videoToken?: VideoTokenCommand | 148 | videoToken?: VideoTokenCommand |
148 | registrations?: RegistrationsCommand | 149 | registrations?: RegistrationsCommand |
150 | videoPasswords?: VideoPasswordsCommand | ||
149 | 151 | ||
150 | runners?: RunnersCommand | 152 | runners?: RunnersCommand |
151 | runnerRegistrationTokens?: RunnerRegistrationTokensCommand | 153 | runnerRegistrationTokens?: RunnerRegistrationTokensCommand |
@@ -437,5 +439,6 @@ export class PeerTubeServer { | |||
437 | this.runners = new RunnersCommand(this) | 439 | this.runners = new RunnersCommand(this) |
438 | this.runnerRegistrationTokens = new RunnerRegistrationTokensCommand(this) | 440 | this.runnerRegistrationTokens = new RunnerRegistrationTokensCommand(this) |
439 | this.runnerJobs = new RunnerJobsCommand(this) | 441 | this.runnerJobs = new RunnerJobsCommand(this) |
442 | this.videoPasswords = new VideoPasswordsCommand(this) | ||
440 | } | 443 | } |
441 | } | 444 | } |
diff --git a/shared/server-commands/shared/abstract-command.ts b/shared/server-commands/shared/abstract-command.ts index ca4ffada9..463acc26b 100644 --- a/shared/server-commands/shared/abstract-command.ts +++ b/shared/server-commands/shared/abstract-command.ts | |||
@@ -101,25 +101,29 @@ abstract class AbstractCommand { | |||
101 | 101 | ||
102 | protected putBodyRequest (options: InternalCommonCommandOptions & { | 102 | protected putBodyRequest (options: InternalCommonCommandOptions & { |
103 | fields?: { [ fieldName: string ]: any } | 103 | fields?: { [ fieldName: string ]: any } |
104 | headers?: { [name: string]: string } | ||
104 | }) { | 105 | }) { |
105 | const { fields } = options | 106 | const { fields, headers } = options |
106 | 107 | ||
107 | return makePutBodyRequest({ | 108 | return makePutBodyRequest({ |
108 | ...this.buildCommonRequestOptions(options), | 109 | ...this.buildCommonRequestOptions(options), |
109 | 110 | ||
110 | fields | 111 | fields, |
112 | headers | ||
111 | }) | 113 | }) |
112 | } | 114 | } |
113 | 115 | ||
114 | protected postBodyRequest (options: InternalCommonCommandOptions & { | 116 | protected postBodyRequest (options: InternalCommonCommandOptions & { |
115 | fields?: { [ fieldName: string ]: any } | 117 | fields?: { [ fieldName: string ]: any } |
118 | headers?: { [name: string]: string } | ||
116 | }) { | 119 | }) { |
117 | const { fields } = options | 120 | const { fields, headers } = options |
118 | 121 | ||
119 | return makePostBodyRequest({ | 122 | return makePostBodyRequest({ |
120 | ...this.buildCommonRequestOptions(options), | 123 | ...this.buildCommonRequestOptions(options), |
121 | 124 | ||
122 | fields | 125 | fields, |
126 | headers | ||
123 | }) | 127 | }) |
124 | } | 128 | } |
125 | 129 | ||
@@ -206,6 +210,12 @@ abstract class AbstractCommand { | |||
206 | 210 | ||
207 | return expectedStatus !== undefined ? expectedStatus : defaultExpectedStatus | 211 | return expectedStatus !== undefined ? expectedStatus : defaultExpectedStatus |
208 | } | 212 | } |
213 | |||
214 | protected buildVideoPasswordHeader (videoPassword: string) { | ||
215 | return videoPassword !== undefined && videoPassword !== null | ||
216 | ? { 'x-peertube-video-password': videoPassword } | ||
217 | : undefined | ||
218 | } | ||
209 | } | 219 | } |
210 | 220 | ||
211 | export { | 221 | export { |
diff --git a/shared/server-commands/videos/captions-command.ts b/shared/server-commands/videos/captions-command.ts index 62bf9c5e6..a26fcb57d 100644 --- a/shared/server-commands/videos/captions-command.ts +++ b/shared/server-commands/videos/captions-command.ts | |||
@@ -34,14 +34,16 @@ export class CaptionsCommand extends AbstractCommand { | |||
34 | 34 | ||
35 | list (options: OverrideCommandOptions & { | 35 | list (options: OverrideCommandOptions & { |
36 | videoId: string | number | 36 | videoId: string | number |
37 | videoPassword?: string | ||
37 | }) { | 38 | }) { |
38 | const { videoId } = options | 39 | const { videoId, videoPassword } = options |
39 | const path = '/api/v1/videos/' + videoId + '/captions' | 40 | const path = '/api/v1/videos/' + videoId + '/captions' |
40 | 41 | ||
41 | return this.getRequestBody<ResultList<VideoCaption>>({ | 42 | return this.getRequestBody<ResultList<VideoCaption>>({ |
42 | ...options, | 43 | ...options, |
43 | 44 | ||
44 | path, | 45 | path, |
46 | headers: this.buildVideoPasswordHeader(videoPassword), | ||
45 | implicitToken: false, | 47 | implicitToken: false, |
46 | defaultExpectedStatus: HttpStatusCode.OK_200 | 48 | defaultExpectedStatus: HttpStatusCode.OK_200 |
47 | }) | 49 | }) |
diff --git a/shared/server-commands/videos/comments-command.ts b/shared/server-commands/videos/comments-command.ts index 154ec0c24..0dab1b66a 100644 --- a/shared/server-commands/videos/comments-command.ts +++ b/shared/server-commands/videos/comments-command.ts | |||
@@ -36,11 +36,12 @@ export class CommentsCommand extends AbstractCommand { | |||
36 | 36 | ||
37 | listThreads (options: OverrideCommandOptions & { | 37 | listThreads (options: OverrideCommandOptions & { |
38 | videoId: number | string | 38 | videoId: number | string |
39 | videoPassword?: string | ||
39 | start?: number | 40 | start?: number |
40 | count?: number | 41 | count?: number |
41 | sort?: string | 42 | sort?: string |
42 | }) { | 43 | }) { |
43 | const { start, count, sort, videoId } = options | 44 | const { start, count, sort, videoId, videoPassword } = options |
44 | const path = '/api/v1/videos/' + videoId + '/comment-threads' | 45 | const path = '/api/v1/videos/' + videoId + '/comment-threads' |
45 | 46 | ||
46 | return this.getRequestBody<VideoCommentThreads>({ | 47 | return this.getRequestBody<VideoCommentThreads>({ |
@@ -48,6 +49,7 @@ export class CommentsCommand extends AbstractCommand { | |||
48 | 49 | ||
49 | path, | 50 | path, |
50 | query: { start, count, sort }, | 51 | query: { start, count, sort }, |
52 | headers: this.buildVideoPasswordHeader(videoPassword), | ||
51 | implicitToken: false, | 53 | implicitToken: false, |
52 | defaultExpectedStatus: HttpStatusCode.OK_200 | 54 | defaultExpectedStatus: HttpStatusCode.OK_200 |
53 | }) | 55 | }) |
@@ -72,8 +74,9 @@ export class CommentsCommand extends AbstractCommand { | |||
72 | async createThread (options: OverrideCommandOptions & { | 74 | async createThread (options: OverrideCommandOptions & { |
73 | videoId: number | string | 75 | videoId: number | string |
74 | text: string | 76 | text: string |
77 | videoPassword?: string | ||
75 | }) { | 78 | }) { |
76 | const { videoId, text } = options | 79 | const { videoId, text, videoPassword } = options |
77 | const path = '/api/v1/videos/' + videoId + '/comment-threads' | 80 | const path = '/api/v1/videos/' + videoId + '/comment-threads' |
78 | 81 | ||
79 | const body = await unwrapBody<{ comment: VideoComment }>(this.postBodyRequest({ | 82 | const body = await unwrapBody<{ comment: VideoComment }>(this.postBodyRequest({ |
@@ -81,6 +84,7 @@ export class CommentsCommand extends AbstractCommand { | |||
81 | 84 | ||
82 | path, | 85 | path, |
83 | fields: { text }, | 86 | fields: { text }, |
87 | headers: this.buildVideoPasswordHeader(videoPassword), | ||
84 | implicitToken: true, | 88 | implicitToken: true, |
85 | defaultExpectedStatus: HttpStatusCode.OK_200 | 89 | defaultExpectedStatus: HttpStatusCode.OK_200 |
86 | })) | 90 | })) |
@@ -95,8 +99,9 @@ export class CommentsCommand extends AbstractCommand { | |||
95 | videoId: number | string | 99 | videoId: number | string |
96 | toCommentId: number | 100 | toCommentId: number |
97 | text: string | 101 | text: string |
102 | videoPassword?: string | ||
98 | }) { | 103 | }) { |
99 | const { videoId, toCommentId, text } = options | 104 | const { videoId, toCommentId, text, videoPassword } = options |
100 | const path = '/api/v1/videos/' + videoId + '/comments/' + toCommentId | 105 | const path = '/api/v1/videos/' + videoId + '/comments/' + toCommentId |
101 | 106 | ||
102 | const body = await unwrapBody<{ comment: VideoComment }>(this.postBodyRequest({ | 107 | const body = await unwrapBody<{ comment: VideoComment }>(this.postBodyRequest({ |
@@ -104,6 +109,7 @@ export class CommentsCommand extends AbstractCommand { | |||
104 | 109 | ||
105 | path, | 110 | path, |
106 | fields: { text }, | 111 | fields: { text }, |
112 | headers: this.buildVideoPasswordHeader(videoPassword), | ||
107 | implicitToken: true, | 113 | implicitToken: true, |
108 | defaultExpectedStatus: HttpStatusCode.OK_200 | 114 | defaultExpectedStatus: HttpStatusCode.OK_200 |
109 | })) | 115 | })) |
diff --git a/shared/server-commands/videos/index.ts b/shared/server-commands/videos/index.ts index c17f6ef20..da36b5b6b 100644 --- a/shared/server-commands/videos/index.ts +++ b/shared/server-commands/videos/index.ts | |||
@@ -17,3 +17,4 @@ export * from './video-studio-command' | |||
17 | export * from './video-token-command' | 17 | export * from './video-token-command' |
18 | export * from './views-command' | 18 | export * from './views-command' |
19 | export * from './videos-command' | 19 | export * from './videos-command' |
20 | export * from './video-passwords-command' | ||
diff --git a/shared/server-commands/videos/live-command.ts b/shared/server-commands/videos/live-command.ts index 44d625970..6006d9fe9 100644 --- a/shared/server-commands/videos/live-command.ts +++ b/shared/server-commands/videos/live-command.ts | |||
@@ -120,8 +120,13 @@ export class LiveCommand extends AbstractCommand { | |||
120 | saveReplay: boolean | 120 | saveReplay: boolean |
121 | permanentLive: boolean | 121 | permanentLive: boolean |
122 | privacy?: VideoPrivacy | 122 | privacy?: VideoPrivacy |
123 | videoPasswords?: string[] | ||
123 | }) { | 124 | }) { |
124 | const { saveReplay, permanentLive, privacy = VideoPrivacy.PUBLIC } = options | 125 | const { saveReplay, permanentLive, privacy = VideoPrivacy.PUBLIC, videoPasswords } = options |
126 | |||
127 | const replaySettings = privacy === VideoPrivacy.PASSWORD_PROTECTED | ||
128 | ? { privacy: VideoPrivacy.PRIVATE } | ||
129 | : { privacy } | ||
125 | 130 | ||
126 | const { uuid } = await this.create({ | 131 | const { uuid } = await this.create({ |
127 | ...options, | 132 | ...options, |
@@ -130,9 +135,10 @@ export class LiveCommand extends AbstractCommand { | |||
130 | name: 'live', | 135 | name: 'live', |
131 | permanentLive, | 136 | permanentLive, |
132 | saveReplay, | 137 | saveReplay, |
133 | replaySettings: { privacy }, | 138 | replaySettings, |
134 | channelId: this.server.store.channel.id, | 139 | channelId: this.server.store.channel.id, |
135 | privacy | 140 | privacy, |
141 | videoPasswords | ||
136 | } | 142 | } |
137 | }) | 143 | }) |
138 | 144 | ||
diff --git a/shared/server-commands/videos/video-passwords-command.ts b/shared/server-commands/videos/video-passwords-command.ts new file mode 100644 index 000000000..bf10335b4 --- /dev/null +++ b/shared/server-commands/videos/video-passwords-command.ts | |||
@@ -0,0 +1,55 @@ | |||
1 | import { HttpStatusCode, ResultList, VideoPassword } from '@shared/models' | ||
2 | import { AbstractCommand, OverrideCommandOptions } from '../shared' | ||
3 | export class VideoPasswordsCommand extends AbstractCommand { | ||
4 | |||
5 | list (options: OverrideCommandOptions & { | ||
6 | videoId: number | string | ||
7 | start?: number | ||
8 | count?: number | ||
9 | sort?: string | ||
10 | }) { | ||
11 | const { start, count, sort, videoId } = options | ||
12 | const path = '/api/v1/videos/' + videoId + '/passwords' | ||
13 | |||
14 | return this.getRequestBody<ResultList<VideoPassword>>({ | ||
15 | ...options, | ||
16 | |||
17 | path, | ||
18 | query: { start, count, sort }, | ||
19 | implicitToken: true, | ||
20 | defaultExpectedStatus: HttpStatusCode.OK_200 | ||
21 | }) | ||
22 | } | ||
23 | |||
24 | updateAll (options: OverrideCommandOptions & { | ||
25 | videoId: number | string | ||
26 | passwords: string[] | ||
27 | }) { | ||
28 | const { videoId, passwords } = options | ||
29 | const path = `/api/v1/videos/${videoId}/passwords` | ||
30 | |||
31 | return this.putBodyRequest({ | ||
32 | ...options, | ||
33 | path, | ||
34 | fields: { passwords }, | ||
35 | implicitToken: true, | ||
36 | defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 | ||
37 | }) | ||
38 | } | ||
39 | |||
40 | remove (options: OverrideCommandOptions & { | ||
41 | id: number | ||
42 | videoId: number | string | ||
43 | }) { | ||
44 | const { id, videoId } = options | ||
45 | const path = `/api/v1/videos/${videoId}/passwords/${id}` | ||
46 | |||
47 | return this.deleteRequest({ | ||
48 | ...options, | ||
49 | |||
50 | path, | ||
51 | implicitToken: true, | ||
52 | defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 | ||
53 | }) | ||
54 | } | ||
55 | } | ||
diff --git a/shared/server-commands/videos/video-token-command.ts b/shared/server-commands/videos/video-token-command.ts index 0531bee65..c4ed29a8c 100644 --- a/shared/server-commands/videos/video-token-command.ts +++ b/shared/server-commands/videos/video-token-command.ts | |||
@@ -8,12 +8,14 @@ export class VideoTokenCommand extends AbstractCommand { | |||
8 | 8 | ||
9 | create (options: OverrideCommandOptions & { | 9 | create (options: OverrideCommandOptions & { |
10 | videoId: number | string | 10 | videoId: number | string |
11 | videoPassword?: string | ||
11 | }) { | 12 | }) { |
12 | const { videoId } = options | 13 | const { videoId, videoPassword } = options |
13 | const path = '/api/v1/videos/' + videoId + '/token' | 14 | const path = '/api/v1/videos/' + videoId + '/token' |
14 | 15 | ||
15 | return unwrapBody<VideoToken>(this.postBodyRequest({ | 16 | return unwrapBody<VideoToken>(this.postBodyRequest({ |
16 | ...options, | 17 | ...options, |
18 | headers: this.buildVideoPasswordHeader(videoPassword), | ||
17 | 19 | ||
18 | path, | 20 | path, |
19 | implicitToken: true, | 21 | implicitToken: true, |
@@ -23,6 +25,7 @@ export class VideoTokenCommand extends AbstractCommand { | |||
23 | 25 | ||
24 | async getVideoFileToken (options: OverrideCommandOptions & { | 26 | async getVideoFileToken (options: OverrideCommandOptions & { |
25 | videoId: number | string | 27 | videoId: number | string |
28 | videoPassword?: string | ||
26 | }) { | 29 | }) { |
27 | const { files } = await this.create(options) | 30 | const { files } = await this.create(options) |
28 | 31 | ||
diff --git a/shared/server-commands/videos/videos-command.ts b/shared/server-commands/videos/videos-command.ts index b5df9c325..93ca623e1 100644 --- a/shared/server-commands/videos/videos-command.ts +++ b/shared/server-commands/videos/videos-command.ts | |||
@@ -111,8 +111,9 @@ export class VideosCommand extends AbstractCommand { | |||
111 | rate (options: OverrideCommandOptions & { | 111 | rate (options: OverrideCommandOptions & { |
112 | id: number | string | 112 | id: number | string |
113 | rating: UserVideoRateType | 113 | rating: UserVideoRateType |
114 | videoPassword?: string | ||
114 | }) { | 115 | }) { |
115 | const { id, rating } = options | 116 | const { id, rating, videoPassword } = options |
116 | const path = '/api/v1/videos/' + id + '/rate' | 117 | const path = '/api/v1/videos/' + id + '/rate' |
117 | 118 | ||
118 | return this.putBodyRequest({ | 119 | return this.putBodyRequest({ |
@@ -120,6 +121,7 @@ export class VideosCommand extends AbstractCommand { | |||
120 | 121 | ||
121 | path, | 122 | path, |
122 | fields: { rating }, | 123 | fields: { rating }, |
124 | headers: this.buildVideoPasswordHeader(videoPassword), | ||
123 | implicitToken: true, | 125 | implicitToken: true, |
124 | defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 | 126 | defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 |
125 | }) | 127 | }) |
@@ -151,6 +153,23 @@ export class VideosCommand extends AbstractCommand { | |||
151 | }) | 153 | }) |
152 | } | 154 | } |
153 | 155 | ||
156 | getWithPassword (options: OverrideCommandOptions & { | ||
157 | id: number | string | ||
158 | password?: string | ||
159 | }) { | ||
160 | const path = '/api/v1/videos/' + options.id | ||
161 | |||
162 | return this.getRequestBody<VideoDetails>({ | ||
163 | ...options, | ||
164 | headers:{ | ||
165 | 'x-peertube-video-password': options.password | ||
166 | }, | ||
167 | path, | ||
168 | implicitToken: false, | ||
169 | defaultExpectedStatus: HttpStatusCode.OK_200 | ||
170 | }) | ||
171 | } | ||
172 | |||
154 | getSource (options: OverrideCommandOptions & { | 173 | getSource (options: OverrideCommandOptions & { |
155 | id: number | string | 174 | id: number | string |
156 | }) { | 175 | }) { |
@@ -608,11 +627,13 @@ export class VideosCommand extends AbstractCommand { | |||
608 | nsfw?: boolean | 627 | nsfw?: boolean |
609 | privacy?: VideoPrivacy | 628 | privacy?: VideoPrivacy |
610 | fixture?: string | 629 | fixture?: string |
630 | videoPasswords?: string[] | ||
611 | }) { | 631 | }) { |
612 | const attributes: VideoEdit = { name: options.name } | 632 | const attributes: VideoEdit = { name: options.name } |
613 | if (options.nsfw) attributes.nsfw = options.nsfw | 633 | if (options.nsfw) attributes.nsfw = options.nsfw |
614 | if (options.privacy) attributes.privacy = options.privacy | 634 | if (options.privacy) attributes.privacy = options.privacy |
615 | if (options.fixture) attributes.fixture = options.fixture | 635 | if (options.fixture) attributes.fixture = options.fixture |
636 | if (options.videoPasswords) attributes.videoPasswords = options.videoPasswords | ||
616 | 637 | ||
617 | return this.upload({ ...options, attributes }) | 638 | return this.upload({ ...options, attributes }) |
618 | } | 639 | } |