diff options
19 files changed, 214 insertions, 49 deletions
diff --git a/client/package.json b/client/package.json index 4b86fb947..a026d61c1 100644 --- a/client/package.json +++ b/client/package.json | |||
@@ -96,7 +96,7 @@ | |||
96 | "expect-webdriverio": "^3.4.0", | 96 | "expect-webdriverio": "^3.4.0", |
97 | "focus-visible": "^5.0.2", | 97 | "focus-visible": "^5.0.2", |
98 | "geckodriver": "^3.2.0", | 98 | "geckodriver": "^3.2.0", |
99 | "hls.js": "1.2.7", | 99 | "hls.js": "1.2.9", |
100 | "html-loader": "^4.1.0", | 100 | "html-loader": "^4.1.0", |
101 | "html-webpack-plugin": "^5.3.1", | 101 | "html-webpack-plugin": "^5.3.1", |
102 | "https-browserify": "^1.0.0", | 102 | "https-browserify": "^1.0.0", |
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html b/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html index 43f1438e0..174f5d29c 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html +++ b/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html | |||
@@ -44,9 +44,13 @@ | |||
44 | 44 | ||
45 | <div class="peertube-select-container"> | 45 | <div class="peertube-select-container"> |
46 | <select id="trendingVideosAlgorithmsDefault" formControlName="default" class="form-control"> | 46 | <select id="trendingVideosAlgorithmsDefault" formControlName="default" class="form-control"> |
47 | <option i18n value="publishedAt">Recently added videos</option> | ||
48 | <option i18n value="originallyPublishedAt">Original publication date</option> | ||
49 | <option i18n value="name">Name</option> | ||
47 | <option i18n value="hot">Hot videos</option> | 50 | <option i18n value="hot">Hot videos</option> |
48 | <option i18n value="most-viewed">Most viewed videos</option> | 51 | <option i18n value="most-viewed">Recent views</option> |
49 | <option i18n value="most-liked">Most liked videos</option> | 52 | <option i18n value="most-liked">Most liked videos</option> |
53 | <option i18n value="views">Global views</option> | ||
50 | </select> | 54 | </select> |
51 | </div> | 55 | </div> |
52 | 56 | ||
diff --git a/client/src/app/+videos/video-list/videos-list-common-page.component.ts b/client/src/app/+videos/video-list/videos-list-common-page.component.ts index c8fa8ef30..bafe30fd7 100644 --- a/client/src/app/+videos/video-list/videos-list-common-page.component.ts +++ b/client/src/app/+videos/video-list/videos-list-common-page.component.ts | |||
@@ -177,6 +177,9 @@ export class VideosListCommonPageComponent implements OnInit, OnDestroy, Disable | |||
177 | case 'best': | 177 | case 'best': |
178 | return '-hot' | 178 | return '-hot' |
179 | 179 | ||
180 | case 'name': | ||
181 | return 'name' | ||
182 | |||
180 | default: | 183 | default: |
181 | return '-' + algorithm as VideoSortField | 184 | return '-' + algorithm as VideoSortField |
182 | } | 185 | } |
diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts index 56310c4e9..710c9dc87 100644 --- a/client/src/assets/player/peertube-player-manager.ts +++ b/client/src/assets/player/peertube-player-manager.ts | |||
@@ -11,6 +11,7 @@ import './shared/control-bar/p2p-info-button' | |||
11 | import './shared/control-bar/peertube-link-button' | 11 | import './shared/control-bar/peertube-link-button' |
12 | import './shared/control-bar/peertube-load-progress-bar' | 12 | import './shared/control-bar/peertube-load-progress-bar' |
13 | import './shared/control-bar/theater-button' | 13 | import './shared/control-bar/theater-button' |
14 | import './shared/control-bar/peertube-live-display' | ||
14 | import './shared/settings/resolution-menu-button' | 15 | import './shared/settings/resolution-menu-button' |
15 | import './shared/settings/resolution-menu-item' | 16 | import './shared/settings/resolution-menu-item' |
16 | import './shared/settings/settings-dialog' | 17 | import './shared/settings/settings-dialog' |
diff --git a/client/src/assets/player/shared/control-bar/index.ts b/client/src/assets/player/shared/control-bar/index.ts index db5b8938d..e71e90713 100644 --- a/client/src/assets/player/shared/control-bar/index.ts +++ b/client/src/assets/player/shared/control-bar/index.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | export * from './next-previous-video-button' | 1 | export * from './next-previous-video-button' |
2 | export * from './p2p-info-button' | 2 | export * from './p2p-info-button' |
3 | export * from './peertube-link-button' | 3 | export * from './peertube-link-button' |
4 | export * from './peertube-live-display' | ||
4 | export * from './peertube-load-progress-bar' | 5 | export * from './peertube-load-progress-bar' |
5 | export * from './theater-button' | 6 | export * from './theater-button' |
diff --git a/client/src/assets/player/shared/control-bar/peertube-live-display.ts b/client/src/assets/player/shared/control-bar/peertube-live-display.ts new file mode 100644 index 000000000..cc5927473 --- /dev/null +++ b/client/src/assets/player/shared/control-bar/peertube-live-display.ts | |||
@@ -0,0 +1,92 @@ | |||
1 | import videojs from 'video.js' | ||
2 | import { PeerTubeLinkButtonOptions } from '../../types' | ||
3 | |||
4 | const ClickableComponent = videojs.getComponent('ClickableComponent') | ||
5 | |||
6 | class PeerTubeLiveDisplay extends ClickableComponent { | ||
7 | private interval: any | ||
8 | |||
9 | private contentEl_: any | ||
10 | |||
11 | constructor (player: videojs.Player, options?: PeerTubeLinkButtonOptions) { | ||
12 | super(player, options as any) | ||
13 | |||
14 | this.interval = this.setInterval(() => this.updateClass(), 1000) | ||
15 | |||
16 | this.show() | ||
17 | this.updateSync(true) | ||
18 | } | ||
19 | |||
20 | dispose () { | ||
21 | if (this.interval) { | ||
22 | this.clearInterval(this.interval) | ||
23 | this.interval = undefined | ||
24 | } | ||
25 | |||
26 | this.contentEl_ = null | ||
27 | |||
28 | super.dispose() | ||
29 | } | ||
30 | |||
31 | createEl () { | ||
32 | const el = super.createEl('div', { | ||
33 | className: 'vjs-live-control vjs-control' | ||
34 | }) | ||
35 | |||
36 | this.contentEl_ = videojs.dom.createEl('div', { | ||
37 | className: 'vjs-live-display' | ||
38 | }, { | ||
39 | 'aria-live': 'off' | ||
40 | }) | ||
41 | |||
42 | this.contentEl_.appendChild(videojs.dom.createEl('span', { | ||
43 | className: 'vjs-control-text', | ||
44 | textContent: `${this.localize('Stream Type')}\u00a0` | ||
45 | })) | ||
46 | |||
47 | this.contentEl_.appendChild(document.createTextNode(this.localize('LIVE'))) | ||
48 | |||
49 | el.appendChild(this.contentEl_) | ||
50 | return el | ||
51 | } | ||
52 | |||
53 | handleClick () { | ||
54 | const hlsjs = this.getHLSJS() | ||
55 | if (!hlsjs) return | ||
56 | |||
57 | this.player().currentTime(hlsjs.liveSyncPosition) | ||
58 | this.updateSync(true) | ||
59 | } | ||
60 | |||
61 | private updateClass () { | ||
62 | const hlsjs = this.getHLSJS() | ||
63 | if (!hlsjs) return | ||
64 | |||
65 | // Not loaded yet | ||
66 | if (this.player().currentTime() === 0) return | ||
67 | |||
68 | const isSync = Math.abs(this.player().currentTime() - hlsjs.liveSyncPosition) < 10 | ||
69 | this.updateSync(isSync) | ||
70 | } | ||
71 | |||
72 | private updateSync (isSync: boolean) { | ||
73 | if (isSync) { | ||
74 | this.addClass('synced-with-live-edge') | ||
75 | this.removeAttribute('title') | ||
76 | this.disable() | ||
77 | } else { | ||
78 | this.removeClass('synced-with-live-edge') | ||
79 | this.setAttribute('title', this.localize('Go back to the live')) | ||
80 | this.enable() | ||
81 | } | ||
82 | } | ||
83 | |||
84 | private getHLSJS () { | ||
85 | const p2pMediaLoader = this.player()?.p2pMediaLoader | ||
86 | if (!p2pMediaLoader) return undefined | ||
87 | |||
88 | return p2pMediaLoader().getHLSJS() | ||
89 | } | ||
90 | } | ||
91 | |||
92 | videojs.registerComponent('PeerTubeLiveDisplay', PeerTubeLiveDisplay) | ||
diff --git a/client/src/assets/player/shared/manager-options/control-bar-options-builder.ts b/client/src/assets/player/shared/manager-options/control-bar-options-builder.ts index 27f366732..df1e8eabe 100644 --- a/client/src/assets/player/shared/manager-options/control-bar-options-builder.ts +++ b/client/src/assets/player/shared/manager-options/control-bar-options-builder.ts | |||
@@ -30,10 +30,7 @@ export class ControlBarOptionsBuilder { | |||
30 | } | 30 | } |
31 | 31 | ||
32 | Object.assign(children, { | 32 | Object.assign(children, { |
33 | currentTimeDisplay: {}, | 33 | ...this.getTimeControls(), |
34 | timeDivider: {}, | ||
35 | durationDisplay: {}, | ||
36 | liveDisplay: {}, | ||
37 | 34 | ||
38 | flexibleWidthSpacer: {}, | 35 | flexibleWidthSpacer: {}, |
39 | 36 | ||
@@ -90,7 +87,23 @@ export class ControlBarOptionsBuilder { | |||
90 | } | 87 | } |
91 | } | 88 | } |
92 | 89 | ||
90 | private getTimeControls () { | ||
91 | if (this.options.isLive) { | ||
92 | return { | ||
93 | peerTubeLiveDisplay: {} | ||
94 | } | ||
95 | } | ||
96 | |||
97 | return { | ||
98 | currentTimeDisplay: {}, | ||
99 | timeDivider: {}, | ||
100 | durationDisplay: {} | ||
101 | } | ||
102 | } | ||
103 | |||
93 | private getProgressControl () { | 104 | private getProgressControl () { |
105 | if (this.options.isLive) return {} | ||
106 | |||
94 | const loadProgressBar = this.mode === 'webtorrent' | 107 | const loadProgressBar = this.mode === 'webtorrent' |
95 | ? 'peerTubeLoadProgressBar' | 108 | ? 'peerTubeLoadProgressBar' |
96 | : 'loadProgressBar' | 109 | : 'loadProgressBar' |
diff --git a/client/src/sass/player/control-bar.scss b/client/src/sass/player/control-bar.scss index 0082378e4..96b3adf66 100644 --- a/client/src/sass/player/control-bar.scss +++ b/client/src/sass/player/control-bar.scss | |||
@@ -153,8 +153,25 @@ | |||
153 | } | 153 | } |
154 | 154 | ||
155 | .vjs-live-control { | 155 | .vjs-live-control { |
156 | line-height: $control-bar-height; | 156 | padding: 5px 7px; |
157 | min-width: 4em; | 157 | border-radius: 3px; |
158 | height: fit-content; | ||
159 | margin: auto 10px; | ||
160 | font-weight: bold; | ||
161 | max-width: fit-content; | ||
162 | opacity: 1 !important; | ||
163 | line-height: normal; | ||
164 | position: relative; | ||
165 | top: -1px; | ||
166 | |||
167 | &.synced-with-live-edge { | ||
168 | background: #d7281c; | ||
169 | } | ||
170 | |||
171 | &:not(.synced-with-live-edge) { | ||
172 | cursor: pointer; | ||
173 | background: #80807f; | ||
174 | } | ||
158 | } | 175 | } |
159 | 176 | ||
160 | .vjs-peertube { | 177 | .vjs-peertube { |
diff --git a/client/yarn.lock b/client/yarn.lock index eac68d35e..633f90fa8 100644 --- a/client/yarn.lock +++ b/client/yarn.lock | |||
@@ -6136,10 +6136,10 @@ he@1.2.0, he@^1.2.0: | |||
6136 | resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" | 6136 | resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" |
6137 | integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== | 6137 | integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== |
6138 | 6138 | ||
6139 | hls.js@1.2.7: | 6139 | hls.js@1.2.9: |
6140 | version "1.2.7" | 6140 | version "1.2.9" |
6141 | resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-1.2.7.tgz#f421e258b10aa797cffb5ab2c3786675ead617f5" | 6141 | resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-1.2.9.tgz#2f25e42ec4c2ea8c88ab23c0f854f39062d45ac9" |
6142 | integrity sha512-mD4Po7Q5TPNIYX6G8sDD+RS/xfrWjMjrtp+xPw3Thw8Tq557Vn0wdXIX/Zii28F9ncUMMQPZsGkoCWFna9CZCw== | 6142 | integrity sha512-SPjm8ix0xe6cYzwDvdVGh2QvQPDkCYrGWpZu6bRaKNNVyEGWM9uF0pooh/Lqj/g8QBQgPFEx1vHzW8SyMY9rqg== |
6143 | 6143 | ||
6144 | hosted-git-info@^2.1.4: | 6144 | hosted-git-info@^2.1.4: |
6145 | version "2.8.9" | 6145 | version "2.8.9" |
diff --git a/scripts/i18n/create-custom-files.ts b/scripts/i18n/create-custom-files.ts index bcd7fe2a2..3b5045954 100755 --- a/scripts/i18n/create-custom-files.ts +++ b/scripts/i18n/create-custom-files.ts | |||
@@ -41,6 +41,7 @@ const playerKeys = { | |||
41 | 'Volume': 'Volume', | 41 | 'Volume': 'Volume', |
42 | 'Codecs': 'Codecs', | 42 | 'Codecs': 'Codecs', |
43 | 'Color': 'Color', | 43 | 'Color': 'Color', |
44 | 'Go back to the live': 'Go back to the live', | ||
44 | 'Connection Speed': 'Connection Speed', | 45 | 'Connection Speed': 'Connection Speed', |
45 | 'Network Activity': 'Network Activity', | 46 | 'Network Activity': 'Network Activity', |
46 | 'Total Transfered': 'Total Transfered', | 47 | 'Total Transfered': 'Total Transfered', |
diff --git a/server/controllers/api/videos/token.ts b/server/controllers/api/videos/token.ts index 009b6dfb6..22387c3e8 100644 --- a/server/controllers/api/videos/token.ts +++ b/server/controllers/api/videos/token.ts | |||
@@ -22,7 +22,7 @@ export { | |||
22 | function generateToken (req: express.Request, res: express.Response) { | 22 | function generateToken (req: express.Request, res: express.Response) { |
23 | const video = res.locals.onlyVideo | 23 | const video = res.locals.onlyVideo |
24 | 24 | ||
25 | const { token, expires } = VideoTokensManager.Instance.create(video.uuid) | 25 | const { token, expires } = VideoTokensManager.Instance.create({ videoUUID: video.uuid, user: res.locals.oauth.token.User }) |
26 | 26 | ||
27 | return res.json({ | 27 | return res.json({ |
28 | files: { | 28 | files: { |
diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts index 772fe734d..ef810a842 100644 --- a/server/controllers/feeds.ts +++ b/server/controllers/feeds.ts | |||
@@ -285,8 +285,8 @@ function addVideosToFeed (feed: Feed, videos: VideoModel[]) { | |||
285 | content: toSafeHtml(video.description), | 285 | content: toSafeHtml(video.description), |
286 | author: [ | 286 | author: [ |
287 | { | 287 | { |
288 | name: video.VideoChannel.Account.getDisplayName(), | 288 | name: video.VideoChannel.getDisplayName(), |
289 | link: video.VideoChannel.Account.Actor.url | 289 | link: video.VideoChannel.Actor.url |
290 | } | 290 | } |
291 | ], | 291 | ], |
292 | date: video.publishedAt, | 292 | date: video.publishedAt, |
diff --git a/server/lib/plugins/plugin-helpers-builder.ts b/server/lib/plugins/plugin-helpers-builder.ts index 7b1def6e3..e75c0b994 100644 --- a/server/lib/plugins/plugin-helpers-builder.ts +++ b/server/lib/plugins/plugin-helpers-builder.ts | |||
@@ -245,7 +245,7 @@ function buildUserHelpers () { | |||
245 | }, | 245 | }, |
246 | 246 | ||
247 | getAuthUser: (res: express.Response) => { | 247 | getAuthUser: (res: express.Response) => { |
248 | const user = res.locals.oauth?.token?.User | 248 | const user = res.locals.oauth?.token?.User || res.locals.videoFileToken?.user |
249 | if (!user) return undefined | 249 | if (!user) return undefined |
250 | 250 | ||
251 | return UserModel.loadByIdFull(user.id) | 251 | return UserModel.loadByIdFull(user.id) |
diff --git a/server/lib/video-tokens-manager.ts b/server/lib/video-tokens-manager.ts index c43085d16..17aa29cdd 100644 --- a/server/lib/video-tokens-manager.ts +++ b/server/lib/video-tokens-manager.ts | |||
@@ -1,5 +1,7 @@ | |||
1 | import LRUCache from 'lru-cache' | 1 | import LRUCache from 'lru-cache' |
2 | import { LRU_CACHE } from '@server/initializers/constants' | 2 | import { LRU_CACHE } from '@server/initializers/constants' |
3 | import { MUserAccountUrl } from '@server/types/models' | ||
4 | import { pick } from '@shared/core-utils' | ||
3 | import { buildUUID } from '@shared/extra-utils' | 5 | import { buildUUID } from '@shared/extra-utils' |
4 | 6 | ||
5 | // --------------------------------------------------------------------------- | 7 | // --------------------------------------------------------------------------- |
@@ -10,19 +12,22 @@ class VideoTokensManager { | |||
10 | 12 | ||
11 | private static instance: VideoTokensManager | 13 | private static instance: VideoTokensManager |
12 | 14 | ||
13 | private readonly lruCache = new LRUCache<string, string>({ | 15 | private readonly lruCache = new LRUCache<string, { videoUUID: string, user: MUserAccountUrl }>({ |
14 | max: LRU_CACHE.VIDEO_TOKENS.MAX_SIZE, | 16 | max: LRU_CACHE.VIDEO_TOKENS.MAX_SIZE, |
15 | ttl: LRU_CACHE.VIDEO_TOKENS.TTL | 17 | ttl: LRU_CACHE.VIDEO_TOKENS.TTL |
16 | }) | 18 | }) |
17 | 19 | ||
18 | private constructor () {} | 20 | private constructor () {} |
19 | 21 | ||
20 | create (videoUUID: string) { | 22 | create (options: { |
23 | user: MUserAccountUrl | ||
24 | videoUUID: string | ||
25 | }) { | ||
21 | const token = buildUUID() | 26 | const token = buildUUID() |
22 | 27 | ||
23 | const expires = new Date(new Date().getTime() + LRU_CACHE.VIDEO_TOKENS.TTL) | 28 | const expires = new Date(new Date().getTime() + LRU_CACHE.VIDEO_TOKENS.TTL) |
24 | 29 | ||
25 | this.lruCache.set(token, videoUUID) | 30 | this.lruCache.set(token, pick(options, [ 'user', 'videoUUID' ])) |
26 | 31 | ||
27 | return { token, expires } | 32 | return { token, expires } |
28 | } | 33 | } |
@@ -34,7 +39,16 @@ class VideoTokensManager { | |||
34 | const value = this.lruCache.get(options.token) | 39 | const value = this.lruCache.get(options.token) |
35 | if (!value) return false | 40 | if (!value) return false |
36 | 41 | ||
37 | return value === options.videoUUID | 42 | return value.videoUUID === options.videoUUID |
43 | } | ||
44 | |||
45 | getUserFromToken (options: { | ||
46 | token: string | ||
47 | }) { | ||
48 | const value = this.lruCache.get(options.token) | ||
49 | if (!value) return undefined | ||
50 | |||
51 | return value.user | ||
38 | } | 52 | } |
39 | 53 | ||
40 | static get Instance () { | 54 | static get Instance () { |
diff --git a/server/middlewares/validators/shared/videos.ts b/server/middlewares/validators/shared/videos.ts index ebbfc0a0a..0033a32ff 100644 --- a/server/middlewares/validators/shared/videos.ts +++ b/server/middlewares/validators/shared/videos.ts | |||
@@ -180,18 +180,16 @@ async function checkCanAccessVideoStaticFiles (options: { | |||
180 | return checkCanSeeVideo(options) | 180 | return checkCanSeeVideo(options) |
181 | } | 181 | } |
182 | 182 | ||
183 | if (!video.hasPrivateStaticPath()) return true | ||
184 | |||
185 | const videoFileToken = req.query.videoFileToken | 183 | const videoFileToken = req.query.videoFileToken |
186 | if (!videoFileToken) { | 184 | if (videoFileToken && VideoTokensManager.Instance.hasToken({ token: videoFileToken, videoUUID: video.uuid })) { |
187 | res.sendStatus(HttpStatusCode.FORBIDDEN_403) | 185 | const user = VideoTokensManager.Instance.getUserFromToken({ token: videoFileToken }) |
188 | return false | ||
189 | } | ||
190 | 186 | ||
191 | if (VideoTokensManager.Instance.hasToken({ token: videoFileToken, videoUUID: video.uuid })) { | 187 | res.locals.videoFileToken = { user } |
192 | return true | 188 | return true |
193 | } | 189 | } |
194 | 190 | ||
191 | if (!video.hasPrivateStaticPath()) return true | ||
192 | |||
195 | res.sendStatus(HttpStatusCode.FORBIDDEN_403) | 193 | res.sendStatus(HttpStatusCode.FORBIDDEN_403) |
196 | return false | 194 | return false |
197 | } | 195 | } |
diff --git a/server/tests/feeds/feeds.ts b/server/tests/feeds/feeds.ts index 906dab1a3..7345f728a 100644 --- a/server/tests/feeds/feeds.ts +++ b/server/tests/feeds/feeds.ts | |||
@@ -189,7 +189,7 @@ describe('Test syndication feeds', () => { | |||
189 | const jsonObj = JSON.parse(json) | 189 | const jsonObj = JSON.parse(json) |
190 | expect(jsonObj.items.length).to.be.equal(1) | 190 | expect(jsonObj.items.length).to.be.equal(1) |
191 | expect(jsonObj.items[0].title).to.equal('my super name for server 1') | 191 | expect(jsonObj.items[0].title).to.equal('my super name for server 1') |
192 | expect(jsonObj.items[0].author.name).to.equal('root') | 192 | expect(jsonObj.items[0].author.name).to.equal('Main root channel') |
193 | } | 193 | } |
194 | 194 | ||
195 | { | 195 | { |
@@ -197,7 +197,7 @@ describe('Test syndication feeds', () => { | |||
197 | const jsonObj = JSON.parse(json) | 197 | const jsonObj = JSON.parse(json) |
198 | expect(jsonObj.items.length).to.be.equal(1) | 198 | expect(jsonObj.items.length).to.be.equal(1) |
199 | expect(jsonObj.items[0].title).to.equal('user video') | 199 | expect(jsonObj.items[0].title).to.equal('user video') |
200 | expect(jsonObj.items[0].author.name).to.equal('john') | 200 | expect(jsonObj.items[0].author.name).to.equal('Main john channel') |
201 | } | 201 | } |
202 | 202 | ||
203 | for (const server of servers) { | 203 | for (const server of servers) { |
@@ -223,7 +223,7 @@ describe('Test syndication feeds', () => { | |||
223 | const jsonObj = JSON.parse(json) | 223 | const jsonObj = JSON.parse(json) |
224 | expect(jsonObj.items.length).to.be.equal(1) | 224 | expect(jsonObj.items.length).to.be.equal(1) |
225 | expect(jsonObj.items[0].title).to.equal('my super name for server 1') | 225 | expect(jsonObj.items[0].title).to.equal('my super name for server 1') |
226 | expect(jsonObj.items[0].author.name).to.equal('root') | 226 | expect(jsonObj.items[0].author.name).to.equal('Main root channel') |
227 | } | 227 | } |
228 | 228 | ||
229 | { | 229 | { |
@@ -231,7 +231,7 @@ describe('Test syndication feeds', () => { | |||
231 | const jsonObj = JSON.parse(json) | 231 | const jsonObj = JSON.parse(json) |
232 | expect(jsonObj.items.length).to.be.equal(1) | 232 | expect(jsonObj.items.length).to.be.equal(1) |
233 | expect(jsonObj.items[0].title).to.equal('user video') | 233 | expect(jsonObj.items[0].title).to.equal('user video') |
234 | expect(jsonObj.items[0].author.name).to.equal('john') | 234 | expect(jsonObj.items[0].author.name).to.equal('Main john channel') |
235 | } | 235 | } |
236 | 236 | ||
237 | for (const server of servers) { | 237 | for (const server of servers) { |
diff --git a/server/tests/fixtures/peertube-plugin-test/main.js b/server/tests/fixtures/peertube-plugin-test/main.js index 19dccf26e..19ba9f278 100644 --- a/server/tests/fixtures/peertube-plugin-test/main.js +++ b/server/tests/fixtures/peertube-plugin-test/main.js | |||
@@ -250,7 +250,10 @@ async function register ({ registerHook, registerSetting, settingsManager, stora | |||
250 | 250 | ||
251 | registerHook({ | 251 | registerHook({ |
252 | target: 'filter:api.download.video.allowed.result', | 252 | target: 'filter:api.download.video.allowed.result', |
253 | handler: (result, params) => { | 253 | handler: async (result, params) => { |
254 | const loggedInUser = await peertubeHelpers.user.getAuthUser(params.res) | ||
255 | if (loggedInUser) return { allowed: true } | ||
256 | |||
254 | if (params && !params.streamingPlaylist && params.video.name.includes('bad file')) { | 257 | if (params && !params.streamingPlaylist && params.video.name.includes('bad file')) { |
255 | return { allowed: false, errorMessage: 'Cao Cao' } | 258 | return { allowed: false, errorMessage: 'Cao Cao' } |
256 | } | 259 | } |
diff --git a/server/tests/plugins/filter-hooks.ts b/server/tests/plugins/filter-hooks.ts index 083fd43ca..6724b3bf8 100644 --- a/server/tests/plugins/filter-hooks.ts +++ b/server/tests/plugins/filter-hooks.ts | |||
@@ -430,6 +430,7 @@ describe('Test plugin filter hooks', function () { | |||
430 | 430 | ||
431 | describe('Download hooks', function () { | 431 | describe('Download hooks', function () { |
432 | const downloadVideos: VideoDetails[] = [] | 432 | const downloadVideos: VideoDetails[] = [] |
433 | let downloadVideo2Token: string | ||
433 | 434 | ||
434 | before(async function () { | 435 | before(async function () { |
435 | this.timeout(120000) | 436 | this.timeout(120000) |
@@ -459,6 +460,8 @@ describe('Test plugin filter hooks', function () { | |||
459 | for (const uuid of uuids) { | 460 | for (const uuid of uuids) { |
460 | downloadVideos.push(await servers[0].videos.get({ id: uuid })) | 461 | downloadVideos.push(await servers[0].videos.get({ id: uuid })) |
461 | } | 462 | } |
463 | |||
464 | downloadVideo2Token = await servers[0].videoToken.getVideoFileToken({ videoId: downloadVideos[2].uuid }) | ||
462 | }) | 465 | }) |
463 | 466 | ||
464 | it('Should run filter:api.download.torrent.allowed.result', async function () { | 467 | it('Should run filter:api.download.torrent.allowed.result', async function () { |
@@ -471,32 +474,42 @@ describe('Test plugin filter hooks', function () { | |||
471 | 474 | ||
472 | it('Should run filter:api.download.video.allowed.result', async function () { | 475 | it('Should run filter:api.download.video.allowed.result', async function () { |
473 | { | 476 | { |
474 | const res = await makeRawRequest({ url: downloadVideos[1].files[0].fileDownloadUrl, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) | 477 | const refused = downloadVideos[1].files[0].fileDownloadUrl |
478 | const allowed = [ | ||
479 | downloadVideos[0].files[0].fileDownloadUrl, | ||
480 | downloadVideos[2].files[0].fileDownloadUrl | ||
481 | ] | ||
482 | |||
483 | const res = await makeRawRequest({ url: refused, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) | ||
475 | expect(res.body.error).to.equal('Cao Cao') | 484 | expect(res.body.error).to.equal('Cao Cao') |
476 | 485 | ||
477 | await makeRawRequest({ url: downloadVideos[0].files[0].fileDownloadUrl, expectedStatus: HttpStatusCode.OK_200 }) | 486 | for (const url of allowed) { |
478 | await makeRawRequest({ url: downloadVideos[2].files[0].fileDownloadUrl, expectedStatus: HttpStatusCode.OK_200 }) | 487 | await makeRawRequest({ url, expectedStatus: HttpStatusCode.OK_200 }) |
488 | await makeRawRequest({ url, expectedStatus: HttpStatusCode.OK_200 }) | ||
489 | } | ||
479 | } | 490 | } |
480 | 491 | ||
481 | { | 492 | { |
482 | const res = await makeRawRequest({ | 493 | const refused = downloadVideos[2].streamingPlaylists[0].files[0].fileDownloadUrl |
483 | url: downloadVideos[2].streamingPlaylists[0].files[0].fileDownloadUrl, | ||
484 | expectedStatus: HttpStatusCode.FORBIDDEN_403 | ||
485 | }) | ||
486 | 494 | ||
487 | expect(res.body.error).to.equal('Sun Jian') | 495 | const allowed = [ |
496 | downloadVideos[2].files[0].fileDownloadUrl, | ||
497 | downloadVideos[0].streamingPlaylists[0].files[0].fileDownloadUrl, | ||
498 | downloadVideos[1].streamingPlaylists[0].files[0].fileDownloadUrl | ||
499 | ] | ||
488 | 500 | ||
489 | await makeRawRequest({ url: downloadVideos[2].files[0].fileDownloadUrl, expectedStatus: HttpStatusCode.OK_200 }) | 501 | // Only streaming playlist is refuse |
502 | const res = await makeRawRequest({ url: refused, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) | ||
503 | expect(res.body.error).to.equal('Sun Jian') | ||
490 | 504 | ||
491 | await makeRawRequest({ | 505 | // But not we there is a user in res |
492 | url: downloadVideos[0].streamingPlaylists[0].files[0].fileDownloadUrl, | 506 | await makeRawRequest({ url: refused, token: servers[0].accessToken, expectedStatus: HttpStatusCode.OK_200 }) |
493 | expectedStatus: HttpStatusCode.OK_200 | 507 | await makeRawRequest({ url: refused, query: { videoFileToken: downloadVideo2Token }, expectedStatus: HttpStatusCode.OK_200 }) |
494 | }) | ||
495 | 508 | ||
496 | await makeRawRequest({ | 509 | // Other files work |
497 | url: downloadVideos[1].streamingPlaylists[0].files[0].fileDownloadUrl, | 510 | for (const url of allowed) { |
498 | expectedStatus: HttpStatusCode.OK_200 | 511 | await makeRawRequest({ url, expectedStatus: HttpStatusCode.OK_200 }) |
499 | }) | 512 | } |
500 | } | 513 | } |
501 | }) | 514 | }) |
502 | }) | 515 | }) |
diff --git a/server/types/express.d.ts b/server/types/express.d.ts index 3738ffc47..99244d2a0 100644 --- a/server/types/express.d.ts +++ b/server/types/express.d.ts | |||
@@ -10,6 +10,7 @@ import { | |||
10 | MChannelBannerAccountDefault, | 10 | MChannelBannerAccountDefault, |
11 | MChannelSyncChannel, | 11 | MChannelSyncChannel, |
12 | MStreamingPlaylist, | 12 | MStreamingPlaylist, |
13 | MUserAccountUrl, | ||
13 | MVideoChangeOwnershipFull, | 14 | MVideoChangeOwnershipFull, |
14 | MVideoFile, | 15 | MVideoFile, |
15 | MVideoFormattableDetails, | 16 | MVideoFormattableDetails, |
@@ -187,6 +188,10 @@ declare module 'express' { | |||
187 | actor: MActorAccountChannelId | 188 | actor: MActorAccountChannelId |
188 | } | 189 | } |
189 | 190 | ||
191 | videoFileToken?: { | ||
192 | user: MUserAccountUrl | ||
193 | } | ||
194 | |||
190 | authenticated?: boolean | 195 | authenticated?: boolean |
191 | 196 | ||
192 | registeredPlugin?: RegisteredPlugin | 197 | registeredPlugin?: RegisteredPlugin |