diff options
127 files changed, 1514 insertions, 989 deletions
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index b3847b8d7..b88027042 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md | |||
@@ -9,8 +9,6 @@ Interested in contributing? Awesome! | |||
9 | * [Write documentation](#write-documentation) | 9 | * [Write documentation](#write-documentation) |
10 | * [Develop](#develop) | 10 | * [Develop](#develop) |
11 | * [Improve the website](#improve-the-website) | 11 | * [Improve the website](#improve-the-website) |
12 | * [Troubleshooting](#troubleshooting) | ||
13 | * [Tutorials](#tutorials) | ||
14 | 12 | ||
15 | ## Translate | 13 | ## Translate |
16 | 14 | ||
@@ -101,7 +99,7 @@ You can get a complete PeerTube development setup with Gitpod, a free one-click | |||
101 | 99 | ||
102 | ### Server side | 100 | ### Server side |
103 | 101 | ||
104 | You can find a documentation of the server code/architecture [here](/support/doc/development/server/code.md). | 102 | You can find a documentation of the server code/architecture [here](https://docs.joinpeertube.org/#/contribute-architecture?id=server-code). |
105 | 103 | ||
106 | To develop on the server-side: | 104 | To develop on the server-side: |
107 | 105 | ||
@@ -116,7 +114,7 @@ restart. | |||
116 | ### Client side | 114 | ### Client side |
117 | 115 | ||
118 | You can find a documentation of the server code/architecture | 116 | You can find a documentation of the server code/architecture |
119 | [here](/support/doc/development/client/code.md). | 117 | [here](https://docs.joinpeertube.org/#/contribute-architecture?id=client-code). |
120 | 118 | ||
121 | 119 | ||
122 | To develop on the client side: | 120 | To develop on the client side: |
@@ -193,11 +191,3 @@ $ npm run mocha -- --exit --require ts-node/register/type-check --bail server/te | |||
193 | 191 | ||
194 | Instance configurations are in `config/test-{1,2,3,4,5,6}.yaml`. | 192 | Instance configurations are in `config/test-{1,2,3,4,5,6}.yaml`. |
195 | Note that only instance 2 has transcoding enabled. | 193 | Note that only instance 2 has transcoding enabled. |
196 | |||
197 | ### Troubleshooting | ||
198 | |||
199 | Please check out the issues and [list of common errors](https://docs.joinpeertube.org/lang/en/devdocs/troubleshooting.html). | ||
200 | |||
201 | ### Tutorials | ||
202 | |||
203 | Please check out the related section in the [development documentation](https://docs.joinpeertube.org/lang/en/devdocs/index.html#tutorials). Contribute tutorials at [framagit.org/framasoft/peertube/documentation](https://framagit.org/framasoft/peertube/documentation). | ||
diff --git a/.travis.yml b/.travis.yml index 5fa41fb43..8b3ec94d9 100644 --- a/.travis.yml +++ b/.travis.yml | |||
@@ -29,8 +29,8 @@ install: | |||
29 | - CC=gcc-4.9 CXX=g++-4.9 yarn install | 29 | - CC=gcc-4.9 CXX=g++-4.9 yarn install |
30 | 30 | ||
31 | before_script: | 31 | before_script: |
32 | - wget --no-check-certificate "https://download.cpy.re/ffmpeg/ffmpeg-release-4.0.2-64bit-static.tar.xz" | 32 | - wget --no-check-certificate "https://download.cpy.re/ffmpeg/ffmpeg-release-4.0.3-64bit-static.tar.xz" |
33 | - tar xf ffmpeg-release-4.0.2-64bit-static.tar.xz | 33 | - tar xf ffmpeg-release-4.0.3-64bit-static.tar.xz |
34 | - mkdir -p $HOME/bin | 34 | - mkdir -p $HOME/bin |
35 | - cp ffmpeg-*/{ffmpeg,ffprobe} $HOME/bin | 35 | - cp ffmpeg-*/{ffmpeg,ffprobe} $HOME/bin |
36 | - export PATH=$HOME/bin:$PATH | 36 | - export PATH=$HOME/bin:$PATH |
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md deleted file mode 100644 index f3254d2d6..000000000 --- a/ARCHITECTURE.md +++ /dev/null | |||
@@ -1,45 +0,0 @@ | |||
1 | # Architecture | ||
2 | |||
3 | ## Vocabulary | ||
4 | |||
5 | - **Fediverse:** several servers following one another, several users | ||
6 | following each other. Designates federated communities in general. | ||
7 | - **Vidiverse:** same as Fediverse, but federating videos specifically. | ||
8 | - **Instance:** a server which runs PeerTube in the fediverse. | ||
9 | - **Origin instance:** the instance on which the video was uploaded and which | ||
10 | is seeding (through the WebSeed protocol) the video. | ||
11 | - **Cache instance:** an instance that decided to make available a WebSeed | ||
12 | of its own for a video originating from another instance. It sends a `ptCache` | ||
13 | activity to notify the origin instance, which will then update its list of | ||
14 | WebSeeds for the video. | ||
15 | - **Following:** the action of a PeerTube instance which will follow another | ||
16 | instance (subscribe to its videos). | ||
17 | |||
18 | ## Base | ||
19 | |||
20 | ### Communications | ||
21 | * All the communication between the instances are signed with [Linked Data | ||
22 | Signatures](https://w3c-dvcg.github.io/ld-signatures/) with the private key | ||
23 | of the account that authored the action. | ||
24 | * We use the [ActivityPub](https://www.w3.org/TR/activitypub/) protocol (only | ||
25 | server-server for now). Object models could be found in | ||
26 | [shared/models/activitypub | ||
27 | directory](/shared/models/activitypub). | ||
28 | * All the requests are retried several times if they fail. | ||
29 | |||
30 | ### Instance | ||
31 | * An instance has a websocket tracker which is responsible for all videos | ||
32 | uploaded by its users. | ||
33 | * An instance has an administrator that can follow other instances. | ||
34 | * An instance can be configured to follow back automatically. | ||
35 | * An instance can blacklist other instances (only used in "follow back" | ||
36 | mode). | ||
37 | * An instance cannot choose which other instances follow it, but it can | ||
38 | decide to **reject all** followers. | ||
39 | * After having uploaded a video, the instance seeds it (WebSeed protocol). | ||
40 | * If a user wants to watch a video, they ask its instance the magnet URI and | ||
41 | the frontend adds the torrent (with WebTorrent), creates the HTML5 video | ||
42 | player and streams the file into it. | ||
43 | * A user watching a video seeds it too (BitTorrent). Thus another user who is | ||
44 | watching the same video can get the data from the origin server and other | ||
45 | users watching it. | ||
diff --git a/CHANGELOG.md b/CHANGELOG.md index 62b1b057a..172509269 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md | |||
@@ -224,8 +224,8 @@ and update your [.env](https://github.com/Chocobozzz/PeerTube/blob/develop/suppo | |||
224 | 224 | ||
225 | ### Maintenance | 225 | ### Maintenance |
226 | 226 | ||
227 | * Improve REST API documentation: https://docs.joinpeertube.org/api.html ([@rigelk](https://github.com/rigelk)) | 227 | * Improve REST API documentation ([@rigelk](https://github.com/rigelk)) |
228 | * Add basic ActivityPub documentation: https://docs.joinpeertube.org/lang/en/devdocs/federation.html ([@rigelk](https://github.com/rigelk)) | 228 | * Add basic ActivityPub documentation ([@rigelk](https://github.com/rigelk)) |
229 | * Add CLI option to run PeerTube without client ([@rigelk](https://github.com/rigelk)) | 229 | * Add CLI option to run PeerTube without client ([@rigelk](https://github.com/rigelk)) |
230 | * Add manpage to peertube CLI ([@rigelk](https://github.com/rigelk)) | 230 | * Add manpage to peertube CLI ([@rigelk](https://github.com/rigelk)) |
231 | * Make backups of files in optimize-old-videos script ([@Nutomic](https://github.com/nutomic)) | 231 | * Make backups of files in optimize-old-videos script ([@Nutomic](https://github.com/nutomic)) |
@@ -310,8 +310,8 @@ and update your [.env](https://github.com/Chocobozzz/PeerTube/blob/develop/suppo | |||
310 | 310 | ||
311 | ### Maintenance | 311 | ### Maintenance |
312 | 312 | ||
313 | * Improve REST API documentation: https://docs.joinpeertube.org/api.html ([@rigelk](https://github.com/rigelk)) | 313 | * Improve REST API documentation ([@rigelk](https://github.com/rigelk)) |
314 | * Add basic ActivityPub documentation: https://docs.joinpeertube.org/lang/en/devdocs/federation.html ([@rigelk](https://github.com/rigelk)) | 314 | * Add basic ActivityPub documentation ([@rigelk](https://github.com/rigelk)) |
315 | * Add CLI option to run PeerTube without client ([@rigelk](https://github.com/rigelk)) | 315 | * Add CLI option to run PeerTube without client ([@rigelk](https://github.com/rigelk)) |
316 | * Add manpage to peertube CLI ([@rigelk](https://github.com/rigelk)) | 316 | * Add manpage to peertube CLI ([@rigelk](https://github.com/rigelk)) |
317 | * Make backups of files in optimize-old-videos script ([@Nutomic](https://github.com/nutomic)) | 317 | * Make backups of files in optimize-old-videos script ([@Nutomic](https://github.com/nutomic)) |
@@ -525,7 +525,7 @@ This release could contain bugs. Don't expect a stable v1.1.0 until December :) | |||
525 | 525 | ||
526 | ### Features | 526 | ### Features |
527 | 527 | ||
528 | * Video redundancy system (experimental, see [the doc](https://docs.joinpeertube.org/lang/en/devdocs/architecture.html#redundancy-between-instances)) | 528 | * Video redundancy system (experimental) |
529 | * Add peertube script (see [the doc](/support/doc/tools.md#cli-wrapper)) ([@rigelk](https://github.com/rigelk)) | 529 | * Add peertube script (see [the doc](/support/doc/tools.md#cli-wrapper)) ([@rigelk](https://github.com/rigelk)) |
530 | * Improve download modal ([@rigelk](https://github.com/rigelk)) | 530 | * Improve download modal ([@rigelk](https://github.com/rigelk)) |
531 | * Add redirect after login ([@BO41](https://github.com/BO41)) | 531 | * Add redirect after login ([@BO41](https://github.com/BO41)) |
@@ -58,7 +58,7 @@ is named "Framatube". | |||
58 | 58 | ||
59 | Yes, the origin server always seeds videos uploaded on it thanks to | 59 | Yes, the origin server always seeds videos uploaded on it thanks to |
60 | [Webseed](http://www.bittorrent.org/beps/bep_0019.html). | 60 | [Webseed](http://www.bittorrent.org/beps/bep_0019.html). |
61 | It can also be helped by other servers using [redundancy](https://docs.joinpeertube.org/lang/en/devdocs/architecture.html#redundancy-between-instances). | 61 | It can also be helped by other servers using [redundancy](https://docs.joinpeertube.org/#/contribute-architecture?id=redundancy-between-instances). |
62 | 62 | ||
63 | 63 | ||
64 | ## What is WebSeed? | 64 | ## What is WebSeed? |
@@ -115,7 +115,7 @@ Be it as a user or an instance administrator, you can decide what your experienc | |||
115 | 115 | ||
116 | <h3 align="right">Communities that help each other</h3> | 116 | <h3 align="right">Communities that help each other</h3> |
117 | <p align="right"> | 117 | <p align="right"> |
118 | In addition to visitors using WebTorrent to share the load among them, instances can help each other by caching one another's videos. This way even small instances have a way to show content to a wider audience, as they will be shouldered by friend instances (more about that in our <a href="https://docs.joinpeertube.org/lang/en/devdocs/architecture.html#redundancy-between-instances">redundancy guide</a>). | 118 | In addition to visitors using WebTorrent to share the load among them, instances can help each other by caching one another's videos. This way even small instances have a way to show content to a wider audience, as they will be shouldered by friend instances (more about that in our <a href="https://docs.joinpeertube.org/#/contribute-architecture?id=redundancy-between-instances">redundancy guide</a>). |
119 | </p> | 119 | </p> |
120 | <p align="right"> | 120 | <p align="right"> |
121 | Content creators can get help from their viewers in the simplest way possible: a support button showing a message linking to their donation accounts or really anything else. No more pay-per-view and advertisements that hurt visitors and <strike>incentivize</strike> alter creativity (more about that in our <a href="./FAQ.md">FAQ</a>). | 121 | Content creators can get help from their viewers in the simplest way possible: a support button showing a message linking to their donation accounts or really anything else. No more pay-per-view and advertisements that hurt visitors and <strike>incentivize</strike> alter creativity (more about that in our <a href="./FAQ.md">FAQ</a>). |
@@ -151,9 +151,9 @@ Feel free to reach out if you have any questions or ideas! :speech_balloon: | |||
151 | * **yarn >= 1.x** | 151 | * **yarn >= 1.x** |
152 | * **FFmpeg >= 3.x** | 152 | * **FFmpeg >= 3.x** |
153 | 153 | ||
154 | See the [production guide](/support/doc/production.md), which is the recommended way. | 154 | See the [production guide](/support/doc/production.md), which is the recommended way to install or upgrade PeerTube. |
155 | 155 | ||
156 | See the [community packages](https://docs.joinpeertube.org/lang/en/docs/install.html), which cover various platforms (including [YunoHost](https://install-app.yunohost.org/?app=peertube) and [Docker](/support/doc/docker.md)). | 156 | See the [community packages](https://docs.joinpeertube.org/#/install-unofficial), which cover various platforms (including [YunoHost](https://install-app.yunohost.org/?app=peertube) and [Docker](/support/doc/docker.md)). |
157 | 157 | ||
158 | :book: Documentation | 158 | :book: Documentation |
159 | ---------------------------------------------------------------- | 159 | ---------------------------------------------------------------- |
@@ -162,13 +162,13 @@ If you have a question, please try to find the answer in the [FAQ](/FAQ.md) firs | |||
162 | 162 | ||
163 | ### User documentation | 163 | ### User documentation |
164 | 164 | ||
165 | See the [user documentation](https://docs.joinpeertube.org/lang/en/userdocs/). | 165 | See the [user documentation](https://docs.joinpeertube.org/#/use-setup-account). |
166 | 166 | ||
167 | ### Admin documentation | 167 | ### Admin documentation |
168 | 168 | ||
169 | See [how to create your own instance](#package-create-your-own-instance). | 169 | See [how to create your own instance](#package-create-your-own-instance). |
170 | 170 | ||
171 | See the more general [admin documentation](https://docs.joinpeertube.org/lang/en/docs/). | 171 | See the more general [admin documentation](https://docs.joinpeertube.org/#/admin-following-instances). |
172 | 172 | ||
173 | #### Tools | 173 | #### Tools |
174 | 174 | ||
@@ -178,13 +178,13 @@ See the more general [admin documentation](https://docs.joinpeertube.org/lang/en | |||
178 | 178 | ||
179 | ### Technical documentation | 179 | ### Technical documentation |
180 | 180 | ||
181 | See the [architecture blueprint](https://docs.joinpeertube.org/lang/en/devdocs/architecture.html) for a more detailed explanation of the architectural choices. | 181 | See the [architecture blueprint](https://docs.joinpeertube.org/#/contribute-architecture) for a more detailed explanation of the architectural choices. |
182 | 182 | ||
183 | See our REST API documentation: | 183 | See our REST API documentation: |
184 | * OpenAPI 3.0.0 schema: [/support/doc/api/openapi.yaml](/support/doc/api/openapi.yaml) | 184 | * OpenAPI 3.0.0 schema: [/support/doc/api/openapi.yaml](/support/doc/api/openapi.yaml) |
185 | * Spec explorer: [docs.joinpeertube.org/api.html](http://docs.joinpeertube.org/api.html) | 185 | * Spec explorer: [docs.joinpeertube.org/#/api-rest-reference.html](https://docs.joinpeertube.org/#/api-rest-reference.html) |
186 | 186 | ||
187 | See our [ActivityPub documentation](https://docs.joinpeertube.org/lang/en/devdocs/federation.html). | 187 | See our [ActivityPub documentation](https://docs.joinpeertube.org/#/api-activitypub). |
188 | 188 | ||
189 | :heart: Supports of our crowdfunding | 189 | :heart: Supports of our crowdfunding |
190 | ---------------------------------------------------------------- | 190 | ---------------------------------------------------------------- |
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html index 637484622..44fc6dc26 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html | |||
@@ -287,6 +287,14 @@ | |||
287 | </div> | 287 | </div> |
288 | 288 | ||
289 | <div class="form-group"> | 289 | <div class="form-group"> |
290 | <my-peertube-checkbox | ||
291 | inputName="transcodingAllowAudioFiles" formControlName="allowAudioFiles" | ||
292 | i18n-labelText labelText="Allow audio files upload" | ||
293 | i18n-helpHtml helpHtml="Allow your users to upload audio files that will be merged with the preview file on upload" | ||
294 | ></my-peertube-checkbox> | ||
295 | </div> | ||
296 | |||
297 | <div class="form-group"> | ||
290 | <label i18n for="transcodingThreads">Transcoding threads</label> | 298 | <label i18n for="transcodingThreads">Transcoding threads</label> |
291 | <div class="peertube-select-container"> | 299 | <div class="peertube-select-container"> |
292 | <select id="transcodingThreads" formControlName="threads"> | 300 | <select id="transcodingThreads" formControlName="threads"> |
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts index e64750713..c238a6c81 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts | |||
@@ -116,6 +116,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { | |||
116 | enabled: null, | 116 | enabled: null, |
117 | threads: this.customConfigValidatorsService.TRANSCODING_THREADS, | 117 | threads: this.customConfigValidatorsService.TRANSCODING_THREADS, |
118 | allowAdditionalExtensions: null, | 118 | allowAdditionalExtensions: null, |
119 | allowAudioFiles: null, | ||
119 | resolutions: {} | 120 | resolutions: {} |
120 | }, | 121 | }, |
121 | autoBlacklist: { | 122 | autoBlacklist: { |
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-edit.component.html b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-edit.component.html index 303fc46f7..82321459f 100644 --- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-edit.component.html +++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-edit.component.html | |||
@@ -57,10 +57,12 @@ | |||
57 | </div> | 57 | </div> |
58 | 58 | ||
59 | <div class="form-group"> | 59 | <div class="form-group"> |
60 | <my-image-upload | 60 | <label i18n>Playlist thumbnail</label> |
61 | i18n-inputLabel inputLabel="Upload thumbnail" inputName="thumbnailfile" formControlName="thumbnailfile" | 61 | |
62 | previewWidth="200px" previewHeight="110px" | 62 | <my-preview-upload |
63 | ></my-image-upload> | 63 | i18n-inputLabel inputLabel="Edit" inputName="thumbnailfile" formControlName="thumbnailfile" |
64 | previewWidth="223px" previewHeight="122px" | ||
65 | ></my-preview-upload> | ||
64 | </div> | 66 | </div> |
65 | </div> | 67 | </div> |
66 | </div> | 68 | </div> |
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-edit.ts b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-edit.ts index fbfb4c8f7..81dd9a75f 100644 --- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-edit.ts +++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-edit.ts | |||
@@ -1,6 +1,4 @@ | |||
1 | import { FormReactive } from '@app/shared' | 1 | import { FormReactive } from '@app/shared' |
2 | import { VideoChannel } from '@app/shared/video-channel/video-channel.model' | ||
3 | import { ServerService } from '@app/core' | ||
4 | import { VideoPlaylist } from '@shared/models/videos/playlist/video-playlist.model' | 2 | import { VideoPlaylist } from '@shared/models/videos/playlist/video-playlist.model' |
5 | 3 | ||
6 | export abstract class MyAccountVideoPlaylistEdit extends FormReactive { | 4 | export abstract class MyAccountVideoPlaylistEdit extends FormReactive { |
diff --git a/client/src/app/+my-account/my-account-videos/my-account-videos.component.html b/client/src/app/+my-account/my-account-videos/my-account-videos.component.html index 84d464800..2854093c4 100644 --- a/client/src/app/+my-account/my-account-videos/my-account-videos.component.html +++ b/client/src/app/+my-account/my-account-videos/my-account-videos.component.html | |||
@@ -20,7 +20,7 @@ | |||
20 | <my-edit-button [routerLink]="[ '/videos', 'update', video.uuid ]"></my-edit-button> | 20 | <my-edit-button [routerLink]="[ '/videos', 'update', video.uuid ]"></my-edit-button> |
21 | 21 | ||
22 | <my-button i18n-label label="Change ownership" | 22 | <my-button i18n-label label="Change ownership" |
23 | className="action-button-change-ownership" | 23 | className="action-button-change-ownership grey-button" |
24 | icon="im-with-her" | 24 | icon="im-with-her" |
25 | (click)="changeOwnership($event, video)" | 25 | (click)="changeOwnership($event, video)" |
26 | ></my-button> | 26 | ></my-button> |
diff --git a/client/src/app/shared/buttons/button.component.scss b/client/src/app/shared/buttons/button.component.scss index 04199a2a9..99d7f51c1 100644 --- a/client/src/app/shared/buttons/button.component.scss +++ b/client/src/app/shared/buttons/button.component.scss | |||
@@ -5,16 +5,9 @@ | |||
5 | @include peertube-button-link; | 5 | @include peertube-button-link; |
6 | @include button-with-icon(21px, 0, -2px); | 6 | @include button-with-icon(21px, 0, -2px); |
7 | 7 | ||
8 | font-weight: $font-semibold; | 8 | // FIXME: Firefox does not apply global .orange-button icon color |
9 | color: $grey-foreground-color; | 9 | &.orange-button { |
10 | background-color: $grey-background-color; | 10 | @include apply-svg-color(#fff) |
11 | |||
12 | &:hover { | ||
13 | background-color: $grey-background-hover-color; | ||
14 | } | ||
15 | |||
16 | my-global-icon { | ||
17 | @include apply-svg-color($grey-foreground-color); | ||
18 | } | 11 | } |
19 | } | 12 | } |
20 | 13 | ||
diff --git a/client/src/app/shared/buttons/button.component.ts b/client/src/app/shared/buttons/button.component.ts index c2b69d31a..cf334e8d5 100644 --- a/client/src/app/shared/buttons/button.component.ts +++ b/client/src/app/shared/buttons/button.component.ts | |||
@@ -9,7 +9,7 @@ import { GlobalIconName } from '@app/shared/images/global-icon.component' | |||
9 | 9 | ||
10 | export class ButtonComponent { | 10 | export class ButtonComponent { |
11 | @Input() label = '' | 11 | @Input() label = '' |
12 | @Input() className: string = undefined | 12 | @Input() className = 'grey-button' |
13 | @Input() icon: GlobalIconName = undefined | 13 | @Input() icon: GlobalIconName = undefined |
14 | @Input() title: string = undefined | 14 | @Input() title: string = undefined |
15 | 15 | ||
diff --git a/client/src/app/shared/buttons/delete-button.component.html b/client/src/app/shared/buttons/delete-button.component.html index b4acb9d32..25196fbd5 100644 --- a/client/src/app/shared/buttons/delete-button.component.html +++ b/client/src/app/shared/buttons/delete-button.component.html | |||
@@ -1,4 +1,4 @@ | |||
1 | <span class="action-button action-button-delete" [title]="title" role="button"> | 1 | <span class="action-button action-button-delete grey-button" [title]="title" role="button"> |
2 | <my-global-icon iconName="delete"></my-global-icon> | 2 | <my-global-icon iconName="delete"></my-global-icon> |
3 | 3 | ||
4 | <span class="button-label" *ngIf="label">{{ label }}</span> | 4 | <span class="button-label" *ngIf="label">{{ label }}</span> |
diff --git a/client/src/app/shared/buttons/edit-button.component.html b/client/src/app/shared/buttons/edit-button.component.html index da3addbae..3d7cd4780 100644 --- a/client/src/app/shared/buttons/edit-button.component.html +++ b/client/src/app/shared/buttons/edit-button.component.html | |||
@@ -1,4 +1,4 @@ | |||
1 | <a class="action-button action-button-edit" [routerLink]="routerLink" i18n-title title="Edit"> | 1 | <a class="action-button action-button-edit grey-button" [routerLink]="routerLink" i18n-title title="Edit"> |
2 | <my-global-icon iconName="edit"></my-global-icon> | 2 | <my-global-icon iconName="edit"></my-global-icon> |
3 | 3 | ||
4 | <span class="button-label" *ngIf="label">{{ label }}</span> | 4 | <span class="button-label" *ngIf="label">{{ label }}</span> |
diff --git a/client/src/app/shared/forms/reactive-file.component.html b/client/src/app/shared/forms/reactive-file.component.html index 7d691059d..f6bf5f9ae 100644 --- a/client/src/app/shared/forms/reactive-file.component.html +++ b/client/src/app/shared/forms/reactive-file.component.html | |||
@@ -1,6 +1,9 @@ | |||
1 | <div class="root"> | 1 | <div class="root"> |
2 | <div class="button-file"> | 2 | <div class="button-file" [ngClass]="{ 'with-icon': !!icon }"> |
3 | <my-global-icon *ngIf="icon" [iconName]="icon"></my-global-icon> | ||
4 | |||
3 | <span>{{ inputLabel }}</span> | 5 | <span>{{ inputLabel }}</span> |
6 | |||
4 | <input | 7 | <input |
5 | type="file" | 8 | type="file" |
6 | [name]="inputName" [id]="inputName" [accept]="extensions" | 9 | [name]="inputName" [id]="inputName" [accept]="extensions" |
@@ -8,7 +11,5 @@ | |||
8 | /> | 11 | /> |
9 | </div> | 12 | </div> |
10 | 13 | ||
11 | <div i18n class="file-constraints">(extensions: {{ allowedExtensionsMessage }}, max size: {{ maxFileSize | bytes }})</div> | ||
12 | |||
13 | <div class="filename" *ngIf="displayFilename === true && filename">{{ filename }}</div> | 14 | <div class="filename" *ngIf="displayFilename === true && filename">{{ filename }}</div> |
14 | </div> | 15 | </div> |
diff --git a/client/src/app/shared/forms/reactive-file.component.scss b/client/src/app/shared/forms/reactive-file.component.scss index d89844264..84c23c1d6 100644 --- a/client/src/app/shared/forms/reactive-file.component.scss +++ b/client/src/app/shared/forms/reactive-file.component.scss | |||
@@ -8,13 +8,11 @@ | |||
8 | 8 | ||
9 | .button-file { | 9 | .button-file { |
10 | @include peertube-button-file(auto); | 10 | @include peertube-button-file(auto); |
11 | @include grey-button; | ||
11 | 12 | ||
12 | min-width: 190px; | 13 | &.with-icon { |
13 | } | 14 | @include button-with-icon; |
14 | 15 | } | |
15 | .file-constraints { | ||
16 | margin-left: 5px; | ||
17 | font-size: 13px; | ||
18 | } | 16 | } |
19 | 17 | ||
20 | .filename { | 18 | .filename { |
diff --git a/client/src/app/shared/forms/reactive-file.component.ts b/client/src/app/shared/forms/reactive-file.component.ts index f60c38e8d..b7a821d4f 100644 --- a/client/src/app/shared/forms/reactive-file.component.ts +++ b/client/src/app/shared/forms/reactive-file.component.ts | |||
@@ -2,6 +2,7 @@ import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@ang | |||
2 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' | 2 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' |
3 | import { Notifier } from '@app/core' | 3 | import { Notifier } from '@app/core' |
4 | import { I18n } from '@ngx-translate/i18n-polyfill' | 4 | import { I18n } from '@ngx-translate/i18n-polyfill' |
5 | import { GlobalIconName } from '@app/shared/images/global-icon.component' | ||
5 | 6 | ||
6 | @Component({ | 7 | @Component({ |
7 | selector: 'my-reactive-file', | 8 | selector: 'my-reactive-file', |
@@ -21,6 +22,7 @@ export class ReactiveFileComponent implements OnInit, ControlValueAccessor { | |||
21 | @Input() extensions: string[] = [] | 22 | @Input() extensions: string[] = [] |
22 | @Input() maxFileSize: number | 23 | @Input() maxFileSize: number |
23 | @Input() displayFilename = false | 24 | @Input() displayFilename = false |
25 | @Input() icon: GlobalIconName | ||
24 | 26 | ||
25 | @Output() fileChanged = new EventEmitter<Blob>() | 27 | @Output() fileChanged = new EventEmitter<Blob>() |
26 | 28 | ||
diff --git a/client/src/app/shared/images/image-upload.component.html b/client/src/app/shared/images/image-upload.component.html deleted file mode 100644 index c09c862c4..000000000 --- a/client/src/app/shared/images/image-upload.component.html +++ /dev/null | |||
@@ -1,9 +0,0 @@ | |||
1 | <div class="root"> | ||
2 | <my-reactive-file | ||
3 | [inputName]="inputName" [inputLabel]="inputLabel" [extensions]="videoImageExtensions" [maxFileSize]="maxVideoImageSize" | ||
4 | (fileChanged)="onFileChanged($event)" | ||
5 | ></my-reactive-file> | ||
6 | |||
7 | <img *ngIf="imageSrc" [ngStyle]="{ width: previewWidth, height: previewHeight }" [src]="imageSrc" class="preview" /> | ||
8 | <div *ngIf="!imageSrc" [ngStyle]="{ width: previewWidth, height: previewHeight }" class="preview no-image"></div> | ||
9 | </div> | ||
diff --git a/client/src/app/shared/images/image-upload.component.scss b/client/src/app/shared/images/image-upload.component.scss deleted file mode 100644 index b63963bca..000000000 --- a/client/src/app/shared/images/image-upload.component.scss +++ /dev/null | |||
@@ -1,18 +0,0 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | .root { | ||
5 | height: auto; | ||
6 | display: flex; | ||
7 | align-items: center; | ||
8 | |||
9 | .preview { | ||
10 | border: 2px solid grey; | ||
11 | border-radius: 4px; | ||
12 | margin-left: 50px; | ||
13 | |||
14 | &.no-image { | ||
15 | background-color: #ececec; | ||
16 | } | ||
17 | } | ||
18 | } | ||
diff --git a/client/src/app/shared/images/preview-upload.component.html b/client/src/app/shared/images/preview-upload.component.html new file mode 100644 index 000000000..5e1d5211b --- /dev/null +++ b/client/src/app/shared/images/preview-upload.component.html | |||
@@ -0,0 +1,13 @@ | |||
1 | <div class="root"> | ||
2 | <div class="preview-container"> | ||
3 | <my-reactive-file | ||
4 | [inputName]="inputName" [inputLabel]="inputLabel" [extensions]="videoImageExtensions" [maxFileSize]="maxVideoImageSize" | ||
5 | icon="edit" (fileChanged)="onFileChanged($event)" | ||
6 | ></my-reactive-file> | ||
7 | |||
8 | <img *ngIf="imageSrc" [ngStyle]="{ width: previewWidth, height: previewHeight }" [src]="imageSrc" class="preview" /> | ||
9 | <div *ngIf="!imageSrc" [ngStyle]="{ width: previewWidth, height: previewHeight }" class="preview no-image"></div> | ||
10 | </div> | ||
11 | |||
12 | <div i18n class="file-constraints">(extensions: {{ allowedExtensionsMessage }}, max size: {{ maxVideoImageSize | bytes }})</div> | ||
13 | </div> | ||
diff --git a/client/src/app/shared/images/preview-upload.component.scss b/client/src/app/shared/images/preview-upload.component.scss new file mode 100644 index 000000000..257060239 --- /dev/null +++ b/client/src/app/shared/images/preview-upload.component.scss | |||
@@ -0,0 +1,27 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | .root { | ||
5 | height: auto; | ||
6 | display: flex; | ||
7 | flex-direction: column; | ||
8 | |||
9 | .preview-container { | ||
10 | position: relative; | ||
11 | |||
12 | my-reactive-file { | ||
13 | position: absolute; | ||
14 | bottom: 10px; | ||
15 | left: 10px; | ||
16 | } | ||
17 | |||
18 | .preview { | ||
19 | border: 2px solid grey; | ||
20 | border-radius: 4px; | ||
21 | |||
22 | &.no-image { | ||
23 | background-color: #ececec; | ||
24 | } | ||
25 | } | ||
26 | } | ||
27 | } | ||
diff --git a/client/src/app/shared/images/image-upload.component.ts b/client/src/app/shared/images/preview-upload.component.ts index 2da1592ff..44b78866e 100644 --- a/client/src/app/shared/images/image-upload.component.ts +++ b/client/src/app/shared/images/preview-upload.component.ts | |||
@@ -1,27 +1,28 @@ | |||
1 | import { Component, forwardRef, Input } from '@angular/core' | 1 | import { Component, forwardRef, Input, OnInit } from '@angular/core' |
2 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' | 2 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' |
3 | import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser' | 3 | import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser' |
4 | import { ServerService } from '@app/core' | 4 | import { ServerService } from '@app/core' |
5 | 5 | ||
6 | @Component({ | 6 | @Component({ |
7 | selector: 'my-image-upload', | 7 | selector: 'my-preview-upload', |
8 | styleUrls: [ './image-upload.component.scss' ], | 8 | styleUrls: [ './preview-upload.component.scss' ], |
9 | templateUrl: './image-upload.component.html', | 9 | templateUrl: './preview-upload.component.html', |
10 | providers: [ | 10 | providers: [ |
11 | { | 11 | { |
12 | provide: NG_VALUE_ACCESSOR, | 12 | provide: NG_VALUE_ACCESSOR, |
13 | useExisting: forwardRef(() => ImageUploadComponent), | 13 | useExisting: forwardRef(() => PreviewUploadComponent), |
14 | multi: true | 14 | multi: true |
15 | } | 15 | } |
16 | ] | 16 | ] |
17 | }) | 17 | }) |
18 | export class ImageUploadComponent implements ControlValueAccessor { | 18 | export class PreviewUploadComponent implements OnInit, ControlValueAccessor { |
19 | @Input() inputLabel: string | 19 | @Input() inputLabel: string |
20 | @Input() inputName: string | 20 | @Input() inputName: string |
21 | @Input() previewWidth: string | 21 | @Input() previewWidth: string |
22 | @Input() previewHeight: string | 22 | @Input() previewHeight: string |
23 | 23 | ||
24 | imageSrc: SafeResourceUrl | 24 | imageSrc: SafeResourceUrl |
25 | allowedExtensionsMessage = '' | ||
25 | 26 | ||
26 | private file: File | 27 | private file: File |
27 | 28 | ||
@@ -38,6 +39,10 @@ export class ImageUploadComponent implements ControlValueAccessor { | |||
38 | return this.serverService.getConfig().video.image.size.max | 39 | return this.serverService.getConfig().video.image.size.max |
39 | } | 40 | } |
40 | 41 | ||
42 | ngOnInit () { | ||
43 | this.allowedExtensionsMessage = this.videoImageExtensions.join(', ') | ||
44 | } | ||
45 | |||
41 | onFileChanged (file: File) { | 46 | onFileChanged (file: File) { |
42 | this.file = file | 47 | this.file = file |
43 | 48 | ||
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index ded65653f..39f1a69e2 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts | |||
@@ -69,7 +69,7 @@ import { HtmlRendererService, LinkifierService, MarkdownService } from '@app/sha | |||
69 | import { ConfirmComponent } from '@app/shared/confirm/confirm.component' | 69 | import { ConfirmComponent } from '@app/shared/confirm/confirm.component' |
70 | import { SmallLoaderComponent } from '@app/shared/misc/small-loader.component' | 70 | import { SmallLoaderComponent } from '@app/shared/misc/small-loader.component' |
71 | import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service' | 71 | import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service' |
72 | import { ImageUploadComponent } from '@app/shared/images/image-upload.component' | 72 | import { PreviewUploadComponent } from '@app/shared/images/preview-upload.component' |
73 | import { GlobalIconComponent } from '@app/shared/images/global-icon.component' | 73 | import { GlobalIconComponent } from '@app/shared/images/global-icon.component' |
74 | import { VideoPlaylistMiniatureComponent } from '@app/shared/video-playlist/video-playlist-miniature.component' | 74 | import { VideoPlaylistMiniatureComponent } from '@app/shared/video-playlist/video-playlist-miniature.component' |
75 | import { VideoAddToPlaylistComponent } from '@app/shared/video-playlist/video-add-to-playlist.component' | 75 | import { VideoAddToPlaylistComponent } from '@app/shared/video-playlist/video-add-to-playlist.component' |
@@ -154,7 +154,7 @@ import { ClipboardModule } from 'ngx-clipboard' | |||
154 | ConfirmComponent, | 154 | ConfirmComponent, |
155 | 155 | ||
156 | GlobalIconComponent, | 156 | GlobalIconComponent, |
157 | ImageUploadComponent | 157 | PreviewUploadComponent |
158 | ], | 158 | ], |
159 | 159 | ||
160 | exports: [ | 160 | exports: [ |
@@ -218,7 +218,7 @@ import { ClipboardModule } from 'ngx-clipboard' | |||
218 | ConfirmComponent, | 218 | ConfirmComponent, |
219 | 219 | ||
220 | GlobalIconComponent, | 220 | GlobalIconComponent, |
221 | ImageUploadComponent, | 221 | PreviewUploadComponent, |
222 | 222 | ||
223 | NumberFormatterPipe, | 223 | NumberFormatterPipe, |
224 | ObjectLengthPipe, | 224 | ObjectLengthPipe, |
diff --git a/client/src/app/shared/video/modals/video-download.component.ts b/client/src/app/shared/video/modals/video-download.component.ts index d6d10d29e..a07560f87 100644 --- a/client/src/app/shared/video/modals/video-download.component.ts +++ b/client/src/app/shared/video/modals/video-download.component.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { Component, ElementRef, ViewChild } from '@angular/core' | 1 | import { Component, ElementRef, ViewChild } from '@angular/core' |
2 | import { VideoDetails } from '../../../shared/video/video-details.model' | 2 | import { VideoDetails } from '../../../shared/video/video-details.model' |
3 | import { NgbModal } from '@ng-bootstrap/ng-bootstrap' | 3 | import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap' |
4 | import { I18n } from '@ngx-translate/i18n-polyfill' | 4 | import { I18n } from '@ngx-translate/i18n-polyfill' |
5 | import { Notifier } from '@app/core' | 5 | import { Notifier } from '@app/core' |
6 | 6 | ||
@@ -16,6 +16,7 @@ export class VideoDownloadComponent { | |||
16 | resolutionId: number | string = -1 | 16 | resolutionId: number | string = -1 |
17 | 17 | ||
18 | video: VideoDetails | 18 | video: VideoDetails |
19 | activeModal: NgbActiveModal | ||
19 | 20 | ||
20 | constructor ( | 21 | constructor ( |
21 | private notifier: Notifier, | 22 | private notifier: Notifier, |
@@ -26,9 +27,7 @@ export class VideoDownloadComponent { | |||
26 | show (video: VideoDetails) { | 27 | show (video: VideoDetails) { |
27 | this.video = video | 28 | this.video = video |
28 | 29 | ||
29 | const m = this.modalService.open(this.modal) | 30 | this.activeModal = this.modalService.open(this.modal) |
30 | m.result.then(() => this.onClose()) | ||
31 | .catch(() => this.onClose()) | ||
32 | 31 | ||
33 | this.resolutionId = this.video.files[0].resolution.id | 32 | this.resolutionId = this.video.files[0].resolution.id |
34 | } | 33 | } |
@@ -39,6 +38,7 @@ export class VideoDownloadComponent { | |||
39 | 38 | ||
40 | download () { | 39 | download () { |
41 | window.location.assign(this.getLink()) | 40 | window.location.assign(this.getLink()) |
41 | this.activeModal.close() | ||
42 | } | 42 | } |
43 | 43 | ||
44 | getLink () { | 44 | getLink () { |
diff --git a/client/src/app/shared/video/video-edit.model.ts b/client/src/app/shared/video/video-edit.model.ts index 1f633d427..67d8e7711 100644 --- a/client/src/app/shared/video/video-edit.model.ts +++ b/client/src/app/shared/video/video-edit.model.ts | |||
@@ -85,6 +85,11 @@ export class VideoEdit implements VideoUpdate { | |||
85 | const originallyPublishedAt = new Date(values['originallyPublishedAt']) | 85 | const originallyPublishedAt = new Date(values['originallyPublishedAt']) |
86 | this.originallyPublishedAt = originallyPublishedAt.toISOString() | 86 | this.originallyPublishedAt = originallyPublishedAt.toISOString() |
87 | } | 87 | } |
88 | |||
89 | // Use the same file than the preview for the thumbnail | ||
90 | if (this.previewfile) { | ||
91 | this.thumbnailfile = this.previewfile | ||
92 | } | ||
88 | } | 93 | } |
89 | 94 | ||
90 | toFormPatch () { | 95 | toFormPatch () { |
diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.html b/client/src/app/videos/+video-edit/shared/video-edit.component.html index 99695204d..28572d611 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.component.html +++ b/client/src/app/videos/+video-edit/shared/video-edit.component.html | |||
@@ -187,18 +187,14 @@ | |||
187 | <ng-template ngbTabContent> | 187 | <ng-template ngbTabContent> |
188 | <div class="row advanced-settings"> | 188 | <div class="row advanced-settings"> |
189 | <div class="col-md-12 col-xl-8"> | 189 | <div class="col-md-12 col-xl-8"> |
190 | <div class="form-group"> | ||
191 | <my-image-upload | ||
192 | i18n-inputLabel inputLabel="Upload thumbnail" inputName="thumbnailfile" formControlName="thumbnailfile" | ||
193 | previewWidth="200px" previewHeight="110px" | ||
194 | ></my-image-upload> | ||
195 | </div> | ||
196 | 190 | ||
197 | <div class="form-group"> | 191 | <div class="form-group"> |
198 | <my-image-upload | 192 | <label i18n for="previewfile">Video preview</label> |
199 | i18n-inputLabel inputLabel="Upload preview" inputName="previewfile" formControlName="previewfile" | 193 | |
194 | <my-preview-upload | ||
195 | i18n-inputLabel inputLabel="Edit" inputName="previewfile" formControlName="previewfile" | ||
200 | previewWidth="360px" previewHeight="200px" | 196 | previewWidth="360px" previewHeight="200px" |
201 | ></my-image-upload> | 197 | ></my-preview-upload> |
202 | </div> | 198 | </div> |
203 | 199 | ||
204 | <div class="form-group"> | 200 | <div class="form-group"> |
diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.ts b/client/src/app/videos/+video-edit/shared/video-edit.component.ts index c80efd802..95d397b52 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.component.ts +++ b/client/src/app/videos/+video-edit/shared/video-edit.component.ts | |||
@@ -100,7 +100,6 @@ export class VideoEditComponent implements OnInit, OnDestroy { | |||
100 | language: this.videoValidatorsService.VIDEO_LANGUAGE, | 100 | language: this.videoValidatorsService.VIDEO_LANGUAGE, |
101 | description: this.videoValidatorsService.VIDEO_DESCRIPTION, | 101 | description: this.videoValidatorsService.VIDEO_DESCRIPTION, |
102 | tags: null, | 102 | tags: null, |
103 | thumbnailfile: null, | ||
104 | previewfile: null, | 103 | previewfile: null, |
105 | support: this.videoValidatorsService.VIDEO_SUPPORT, | 104 | support: this.videoValidatorsService.VIDEO_SUPPORT, |
106 | schedulePublicationAt: this.videoValidatorsService.VIDEO_SCHEDULE_PUBLICATION_AT, | 105 | schedulePublicationAt: this.videoValidatorsService.VIDEO_SCHEDULE_PUBLICATION_AT, |
diff --git a/client/src/app/videos/+video-edit/video-add-components/video-upload.component.html b/client/src/app/videos/+video-edit/video-add-components/video-upload.component.html index 536769d2f..3247a2bd6 100644 --- a/client/src/app/videos/+video-edit/video-add-components/video-upload.component.html +++ b/client/src/app/videos/+video-edit/video-add-components/video-upload.component.html | |||
@@ -26,6 +26,27 @@ | |||
26 | </select> | 26 | </select> |
27 | </div> | 27 | </div> |
28 | </div> | 28 | </div> |
29 | |||
30 | <ng-container *ngIf="isUploadingAudioFile"> | ||
31 | <div class="form-group audio-preview"> | ||
32 | <label i18n for="previewfileUpload">Video background image</label> | ||
33 | |||
34 | <div i18n class="audio-image-info"> | ||
35 | Image that will be merged with your audio file. | ||
36 | <br /> | ||
37 | The chosen image will be definitive and cannot be modified. | ||
38 | </div> | ||
39 | |||
40 | <my-preview-upload | ||
41 | i18n-inputLabel inputLabel="Edit" inputName="previewfileUpload" [(ngModel)]="previewfileUpload" | ||
42 | previewWidth="360px" previewHeight="200px" | ||
43 | ></my-preview-upload> | ||
44 | </div> | ||
45 | |||
46 | <div class="form-group upload-audio-button"> | ||
47 | <my-button className="orange-button" i18n-label [label]="getAudioUploadLabel()" icon="upload" (click)="uploadFirstStep(true)"></my-button> | ||
48 | </div> | ||
49 | </ng-container> | ||
29 | </div> | 50 | </div> |
30 | </div> | 51 | </div> |
31 | 52 | ||
diff --git a/client/src/app/videos/+video-edit/video-add-components/video-upload.component.scss b/client/src/app/videos/+video-edit/video-add-components/video-upload.component.scss index 8adf8f169..684342f09 100644 --- a/client/src/app/videos/+video-edit/video-add-components/video-upload.component.scss +++ b/client/src/app/videos/+video-edit/video-add-components/video-upload.component.scss | |||
@@ -1,9 +1,20 @@ | |||
1 | @import 'variables'; | 1 | @import 'variables'; |
2 | @import 'mixins'; | 2 | @import 'mixins'; |
3 | 3 | ||
4 | .first-step-block .form-group-channel { | 4 | .first-step-block { |
5 | margin-bottom: 20px; | 5 | |
6 | margin-top: 35px; | 6 | .form-group-channel { |
7 | margin-bottom: 20px; | ||
8 | margin-top: 35px; | ||
9 | } | ||
10 | |||
11 | .audio-image-info { | ||
12 | margin-bottom: 10px; | ||
13 | } | ||
14 | |||
15 | .audio-preview { | ||
16 | margin: 30px 0; | ||
17 | } | ||
7 | } | 18 | } |
8 | 19 | ||
9 | .upload-progress-cancel { | 20 | .upload-progress-cancel { |
diff --git a/client/src/app/videos/+video-edit/video-add-components/video-upload.component.ts b/client/src/app/videos/+video-edit/video-add-components/video-upload.component.ts index d6d4bad21..73de25c59 100644 --- a/client/src/app/videos/+video-edit/video-add-components/video-upload.component.ts +++ b/client/src/app/videos/+video-edit/video-add-components/video-upload.component.ts | |||
@@ -35,8 +35,10 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy | |||
35 | userVideoQuotaUsed = 0 | 35 | userVideoQuotaUsed = 0 |
36 | userVideoQuotaUsedDaily = 0 | 36 | userVideoQuotaUsedDaily = 0 |
37 | 37 | ||
38 | isUploadingAudioFile = false | ||
38 | isUploadingVideo = false | 39 | isUploadingVideo = false |
39 | isUpdatingVideo = false | 40 | isUpdatingVideo = false |
41 | |||
40 | videoUploaded = false | 42 | videoUploaded = false |
41 | videoUploadObservable: Subscription = null | 43 | videoUploadObservable: Subscription = null |
42 | videoUploadPercents = 0 | 44 | videoUploadPercents = 0 |
@@ -44,7 +46,9 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy | |||
44 | id: 0, | 46 | id: 0, |
45 | uuid: '' | 47 | uuid: '' |
46 | } | 48 | } |
49 | |||
47 | waitTranscodingEnabled = true | 50 | waitTranscodingEnabled = true |
51 | previewfileUpload: File | ||
48 | 52 | ||
49 | error: string | 53 | error: string |
50 | 54 | ||
@@ -100,6 +104,17 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy | |||
100 | } | 104 | } |
101 | } | 105 | } |
102 | 106 | ||
107 | getVideoFile () { | ||
108 | return this.videofileInput.nativeElement.files[0] | ||
109 | } | ||
110 | |||
111 | getAudioUploadLabel () { | ||
112 | const videofile = this.getVideoFile() | ||
113 | if (!videofile) return this.i18n('Upload') | ||
114 | |||
115 | return this.i18n('Upload {{videofileName}}', { videofileName: videofile.name }) | ||
116 | } | ||
117 | |||
103 | fileChange () { | 118 | fileChange () { |
104 | this.uploadFirstStep() | 119 | this.uploadFirstStep() |
105 | } | 120 | } |
@@ -114,38 +129,15 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy | |||
114 | } | 129 | } |
115 | } | 130 | } |
116 | 131 | ||
117 | uploadFirstStep () { | 132 | uploadFirstStep (clickedOnButton = false) { |
118 | const videofile = this.videofileInput.nativeElement.files[0] | 133 | const videofile = this.getVideoFile() |
119 | if (!videofile) return | 134 | if (!videofile) return |
120 | 135 | ||
121 | // Check global user quota | 136 | if (!this.checkGlobalUserQuota(videofile)) return |
122 | const bytePipes = new BytesPipe() | 137 | if (!this.checkDailyUserQuota(videofile)) return |
123 | const videoQuota = this.authService.getUser().videoQuota | ||
124 | if (videoQuota !== -1 && (this.userVideoQuotaUsed + videofile.size) > videoQuota) { | ||
125 | const msg = this.i18n( | ||
126 | 'Your video quota is exceeded with this video (video size: {{videoSize}}, used: {{videoQuotaUsed}}, quota: {{videoQuota}})', | ||
127 | { | ||
128 | videoSize: bytePipes.transform(videofile.size, 0), | ||
129 | videoQuotaUsed: bytePipes.transform(this.userVideoQuotaUsed, 0), | ||
130 | videoQuota: bytePipes.transform(videoQuota, 0) | ||
131 | } | ||
132 | ) | ||
133 | this.notifier.error(msg) | ||
134 | return | ||
135 | } | ||
136 | 138 | ||
137 | // Check daily user quota | 139 | if (clickedOnButton === false && this.isAudioFile(videofile.name)) { |
138 | const videoQuotaDaily = this.authService.getUser().videoQuotaDaily | 140 | this.isUploadingAudioFile = true |
139 | if (videoQuotaDaily !== -1 && (this.userVideoQuotaUsedDaily + videofile.size) > videoQuotaDaily) { | ||
140 | const msg = this.i18n( | ||
141 | 'Your daily video quota is exceeded with this video (video size: {{videoSize}}, used: {{quotaUsedDaily}}, quota: {{quotaDaily}})', | ||
142 | { | ||
143 | videoSize: bytePipes.transform(videofile.size, 0), | ||
144 | quotaUsedDaily: bytePipes.transform(this.userVideoQuotaUsedDaily, 0), | ||
145 | quotaDaily: bytePipes.transform(videoQuotaDaily, 0) | ||
146 | } | ||
147 | ) | ||
148 | this.notifier.error(msg) | ||
149 | return | 141 | return |
150 | } | 142 | } |
151 | 143 | ||
@@ -180,6 +172,11 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy | |||
180 | formData.append('channelId', '' + channelId) | 172 | formData.append('channelId', '' + channelId) |
181 | formData.append('videofile', videofile) | 173 | formData.append('videofile', videofile) |
182 | 174 | ||
175 | if (this.previewfileUpload) { | ||
176 | formData.append('previewfile', this.previewfileUpload) | ||
177 | formData.append('thumbnailfile', this.previewfileUpload) | ||
178 | } | ||
179 | |||
183 | this.isUploadingVideo = true | 180 | this.isUploadingVideo = true |
184 | this.firstStepDone.emit(name) | 181 | this.firstStepDone.emit(name) |
185 | 182 | ||
@@ -187,7 +184,8 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy | |||
187 | name, | 184 | name, |
188 | privacy, | 185 | privacy, |
189 | nsfw, | 186 | nsfw, |
190 | channelId | 187 | channelId, |
188 | previewfile: this.previewfileUpload | ||
191 | }) | 189 | }) |
192 | 190 | ||
193 | this.explainedVideoPrivacies = this.videoService.explainedPrivacyLabels(this.videoPrivacies) | 191 | this.explainedVideoPrivacies = this.videoService.explainedPrivacyLabels(this.videoPrivacies) |
@@ -251,4 +249,52 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy | |||
251 | } | 249 | } |
252 | ) | 250 | ) |
253 | } | 251 | } |
252 | |||
253 | private checkGlobalUserQuota (videofile: File) { | ||
254 | const bytePipes = new BytesPipe() | ||
255 | |||
256 | // Check global user quota | ||
257 | const videoQuota = this.authService.getUser().videoQuota | ||
258 | if (videoQuota !== -1 && (this.userVideoQuotaUsed + videofile.size) > videoQuota) { | ||
259 | const msg = this.i18n( | ||
260 | 'Your video quota is exceeded with this video (video size: {{videoSize}}, used: {{videoQuotaUsed}}, quota: {{videoQuota}})', | ||
261 | { | ||
262 | videoSize: bytePipes.transform(videofile.size, 0), | ||
263 | videoQuotaUsed: bytePipes.transform(this.userVideoQuotaUsed, 0), | ||
264 | videoQuota: bytePipes.transform(videoQuota, 0) | ||
265 | } | ||
266 | ) | ||
267 | this.notifier.error(msg) | ||
268 | |||
269 | return false | ||
270 | } | ||
271 | |||
272 | return true | ||
273 | } | ||
274 | |||
275 | private checkDailyUserQuota (videofile: File) { | ||
276 | const bytePipes = new BytesPipe() | ||
277 | |||
278 | // Check daily user quota | ||
279 | const videoQuotaDaily = this.authService.getUser().videoQuotaDaily | ||
280 | if (videoQuotaDaily !== -1 && (this.userVideoQuotaUsedDaily + videofile.size) > videoQuotaDaily) { | ||
281 | const msg = this.i18n( | ||
282 | 'Your daily video quota is exceeded with this video (video size: {{videoSize}}, used: {{quotaUsedDaily}}, quota: {{quotaDaily}})', | ||
283 | { | ||
284 | videoSize: bytePipes.transform(videofile.size, 0), | ||
285 | quotaUsedDaily: bytePipes.transform(this.userVideoQuotaUsedDaily, 0), | ||
286 | quotaDaily: bytePipes.transform(videoQuotaDaily, 0) | ||
287 | } | ||
288 | ) | ||
289 | this.notifier.error(msg) | ||
290 | |||
291 | return false | ||
292 | } | ||
293 | |||
294 | return true | ||
295 | } | ||
296 | |||
297 | private isAudioFile (filename: string) { | ||
298 | return filename.endsWith('.mp3') || filename.endsWith('.flac') || filename.endsWith('.ogg') | ||
299 | } | ||
254 | } | 300 | } |
diff --git a/client/src/app/videos/+video-watch/video-watch.component.ts b/client/src/app/videos/+video-watch/video-watch.component.ts index 631504eab..55109dc32 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts | |||
@@ -545,8 +545,12 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
545 | private flushPlayer () { | 545 | private flushPlayer () { |
546 | // Remove player if it exists | 546 | // Remove player if it exists |
547 | if (this.player) { | 547 | if (this.player) { |
548 | this.player.dispose() | 548 | try { |
549 | this.player = undefined | 549 | this.player.dispose() |
550 | this.player = undefined | ||
551 | } catch (err) { | ||
552 | console.error('Cannot dispose player.', err) | ||
553 | } | ||
550 | } | 554 | } |
551 | } | 555 | } |
552 | 556 | ||
diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts index 6cdd54372..31cbc7dfd 100644 --- a/client/src/assets/player/peertube-player-manager.ts +++ b/client/src/assets/player/peertube-player-manager.ts | |||
@@ -117,8 +117,17 @@ export class PeertubePlayerManager { | |||
117 | videojs(options.common.playerElement, videojsOptions, function (this: any) { | 117 | videojs(options.common.playerElement, videojsOptions, function (this: any) { |
118 | const player = this | 118 | const player = this |
119 | 119 | ||
120 | player.tech_.one('error', () => self.maybeFallbackToWebTorrent(mode, player, options)) | 120 | let alreadyFallback = false |
121 | player.one('error', () => self.maybeFallbackToWebTorrent(mode, player, options)) | 121 | |
122 | player.tech_.one('error', () => { | ||
123 | if (!alreadyFallback) self.maybeFallbackToWebTorrent(mode, player, options) | ||
124 | alreadyFallback = true | ||
125 | }) | ||
126 | |||
127 | player.one('error', () => { | ||
128 | if (!alreadyFallback) self.maybeFallbackToWebTorrent(mode, player, options) | ||
129 | alreadyFallback = true | ||
130 | }) | ||
122 | 131 | ||
123 | self.addContextMenu(mode, player, options.common.embedUrl) | 132 | self.addContextMenu(mode, player, options.common.embedUrl) |
124 | 133 | ||
diff --git a/config/default.yaml b/config/default.yaml index 37ef4366f..fcbbf17e8 100644 --- a/config/default.yaml +++ b/config/default.yaml | |||
@@ -53,6 +53,12 @@ smtp: | |||
53 | ca_file: null # Used for self signed certificates | 53 | ca_file: null # Used for self signed certificates |
54 | from_address: 'admin@example.com' | 54 | from_address: 'admin@example.com' |
55 | 55 | ||
56 | email: | ||
57 | body: | ||
58 | signature: "PeerTube" | ||
59 | object: | ||
60 | prefix: "[PeerTube]" | ||
61 | |||
56 | # From the project root directory | 62 | # From the project root directory |
57 | storage: | 63 | storage: |
58 | tmp: 'storage/tmp/' # Used to download data (imports etc), store uploaded files before processing... | 64 | tmp: 'storage/tmp/' # Used to download data (imports etc), store uploaded files before processing... |
@@ -174,6 +180,8 @@ transcoding: | |||
174 | enabled: true | 180 | enabled: true |
175 | # Allow your users to upload .mkv, .mov, .avi, .flv videos | 181 | # Allow your users to upload .mkv, .mov, .avi, .flv videos |
176 | allow_additional_extensions: true | 182 | allow_additional_extensions: true |
183 | # If a user uploads an audio file, PeerTube will create a video by merging the preview file and the audio file | ||
184 | allow_audio_files: true | ||
177 | threads: 1 | 185 | threads: 1 |
178 | resolutions: # Only created if the original video has a higher resolution, uses more storage! | 186 | resolutions: # Only created if the original video has a higher resolution, uses more storage! |
179 | 240p: false | 187 | 240p: false |
diff --git a/config/production.yaml.example b/config/production.yaml.example index f84e15670..0ab99ac45 100644 --- a/config/production.yaml.example +++ b/config/production.yaml.example | |||
@@ -188,6 +188,8 @@ transcoding: | |||
188 | enabled: true | 188 | enabled: true |
189 | # Allow your users to upload .mkv, .mov, .avi, .flv videos | 189 | # Allow your users to upload .mkv, .mov, .avi, .flv videos |
190 | allow_additional_extensions: true | 190 | allow_additional_extensions: true |
191 | # If a user uploads an audio file, PeerTube will create a video by merging the preview file and the audio file | ||
192 | allow_audio_files: true | ||
191 | threads: 1 | 193 | threads: 1 |
192 | resolutions: # Only created if the original video has a higher resolution, uses more storage! | 194 | resolutions: # Only created if the original video has a higher resolution, uses more storage! |
193 | 240p: false | 195 | 240p: false |
diff --git a/config/test-2.yaml b/config/test-2.yaml index a5515afa4..de7300366 100644 --- a/config/test-2.yaml +++ b/config/test-2.yaml | |||
@@ -31,3 +31,4 @@ signup: | |||
31 | transcoding: | 31 | transcoding: |
32 | enabled: true | 32 | enabled: true |
33 | allow_additional_extensions: true | 33 | allow_additional_extensions: true |
34 | allow_audio_files: true | ||
diff --git a/config/test.yaml b/config/test.yaml index 682530840..7dabe433c 100644 --- a/config/test.yaml +++ b/config/test.yaml | |||
@@ -55,6 +55,7 @@ signup: | |||
55 | transcoding: | 55 | transcoding: |
56 | enabled: true | 56 | enabled: true |
57 | allow_additional_extensions: false | 57 | allow_additional_extensions: false |
58 | allow_audio_files: false | ||
58 | threads: 2 | 59 | threads: 2 |
59 | resolutions: | 60 | resolutions: |
60 | 240p: true | 61 | 240p: true |
diff --git a/package.json b/package.json index 25c27c447..caacec808 100644 --- a/package.json +++ b/package.json | |||
@@ -205,6 +205,7 @@ | |||
205 | "maildev": "^1.0.0-rc3", | 205 | "maildev": "^1.0.0-rc3", |
206 | "marked-man": "^0.4.2", | 206 | "marked-man": "^0.4.2", |
207 | "mocha": "^6.0.0", | 207 | "mocha": "^6.0.0", |
208 | "mocha-parallel-tests": "^2.1.0", | ||
208 | "nodemon": "^1.18.6", | 209 | "nodemon": "^1.18.6", |
209 | "sass-lint": "^1.12.1", | 210 | "sass-lint": "^1.12.1", |
210 | "source-map-support": "^0.5.0", | 211 | "source-map-support": "^0.5.0", |
diff --git a/scripts/create-transcoding-job.ts b/scripts/create-transcoding-job.ts index 4a677eacb..2b7cb5177 100755 --- a/scripts/create-transcoding-job.ts +++ b/scripts/create-transcoding-job.ts | |||
@@ -2,6 +2,7 @@ import * as program from 'commander' | |||
2 | import { VideoModel } from '../server/models/video/video' | 2 | import { VideoModel } from '../server/models/video/video' |
3 | import { initDatabaseModels } from '../server/initializers' | 3 | import { initDatabaseModels } from '../server/initializers' |
4 | import { JobQueue } from '../server/lib/job-queue' | 4 | import { JobQueue } from '../server/lib/job-queue' |
5 | import { VideoTranscodingPayload } from '../server/lib/job-queue/handlers/video-transcoding' | ||
5 | 6 | ||
6 | program | 7 | program |
7 | .option('-v, --video [videoUUID]', 'Video UUID') | 8 | .option('-v, --video [videoUUID]', 'Video UUID') |
@@ -31,15 +32,9 @@ async function run () { | |||
31 | const video = await VideoModel.loadByUUIDWithFile(program['video']) | 32 | const video = await VideoModel.loadByUUIDWithFile(program['video']) |
32 | if (!video) throw new Error('Video not found.') | 33 | if (!video) throw new Error('Video not found.') |
33 | 34 | ||
34 | const dataInput = { | 35 | const dataInput: VideoTranscodingPayload = program.resolution !== undefined |
35 | videoUUID: video.uuid, | 36 | ? { type: 'new-resolution' as 'new-resolution', videoUUID: video.uuid, isNewVideo: false, resolution: program.resolution } |
36 | isNewVideo: false, | 37 | : { type: 'optimize' as 'optimize', videoUUID: video.uuid, isNewVideo: false } |
37 | resolution: undefined | ||
38 | } | ||
39 | |||
40 | if (program.resolution !== undefined) { | ||
41 | dataInput.resolution = program.resolution | ||
42 | } | ||
43 | 38 | ||
44 | await JobQueue.Instance.init() | 39 | await JobQueue.Instance.init() |
45 | await JobQueue.Instance.createJob({ type: 'video-transcoding', payload: dataInput }) | 40 | await JobQueue.Instance.createJob({ type: 'video-transcoding', payload: dataInput }) |
diff --git a/scripts/travis.sh b/scripts/travis.sh index 3557816c8..c38bd2cab 100755 --- a/scripts/travis.sh +++ b/scripts/travis.sh | |||
@@ -20,16 +20,16 @@ elif [ "$1" = "cli" ]; then | |||
20 | mocha --timeout 5000 --exit --require ts-node/register --bail server/tests/cli/index.ts | 20 | mocha --timeout 5000 --exit --require ts-node/register --bail server/tests/cli/index.ts |
21 | elif [ "$1" = "api-1" ]; then | 21 | elif [ "$1" = "api-1" ]; then |
22 | npm run build:server | 22 | npm run build:server |
23 | mocha --timeout 5000 --exit --require ts-node/register --bail server/tests/api/index-1.ts | 23 | sh ./server/tests/api/travis-1.sh 2 |
24 | elif [ "$1" = "api-2" ]; then | 24 | elif [ "$1" = "api-2" ]; then |
25 | npm run build:server | 25 | npm run build:server |
26 | mocha --timeout 5000 --exit --require ts-node/register --bail server/tests/api/index-2.ts | 26 | sh ./server/tests/api/travis-2.sh 2 |
27 | elif [ "$1" = "api-3" ]; then | 27 | elif [ "$1" = "api-3" ]; then |
28 | npm run build:server | 28 | npm run build:server |
29 | mocha --timeout 5000 --exit --require ts-node/register --bail server/tests/api/index-3.ts | 29 | sh ./server/tests/api/travis-3.sh 2 |
30 | elif [ "$1" = "api-4" ]; then | 30 | elif [ "$1" = "api-4" ]; then |
31 | npm run build:server | 31 | npm run build:server |
32 | mocha --timeout 5000 --exit --require ts-node/register --bail server/tests/api/index-4.ts | 32 | sh ./server/tests/api/travis-4.sh 2 |
33 | elif [ "$1" = "lint" ]; then | 33 | elif [ "$1" = "lint" ]; then |
34 | npm run tslint -- --project ./tsconfig.json -c ./tslint.json server.ts "server/**/*.ts" "shared/**/*.ts" | 34 | npm run tslint -- --project ./tsconfig.json -c ./tslint.json server.ts "server/**/*.ts" "shared/**/*.ts" |
35 | 35 | ||
diff --git a/server/assets/default-audio-background.jpg b/server/assets/default-audio-background.jpg new file mode 100644 index 000000000..a19173eac --- /dev/null +++ b/server/assets/default-audio-background.jpg | |||
Binary files differ | |||
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index 40012c03b..d9ce6a153 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts | |||
@@ -255,6 +255,7 @@ function customConfig (): CustomConfig { | |||
255 | transcoding: { | 255 | transcoding: { |
256 | enabled: CONFIG.TRANSCODING.ENABLED, | 256 | enabled: CONFIG.TRANSCODING.ENABLED, |
257 | allowAdditionalExtensions: CONFIG.TRANSCODING.ALLOW_ADDITIONAL_EXTENSIONS, | 257 | allowAdditionalExtensions: CONFIG.TRANSCODING.ALLOW_ADDITIONAL_EXTENSIONS, |
258 | allowAudioFiles: CONFIG.TRANSCODING.ALLOW_AUDIO_FILES, | ||
258 | threads: CONFIG.TRANSCODING.THREADS, | 259 | threads: CONFIG.TRANSCODING.THREADS, |
259 | resolutions: { | 260 | resolutions: { |
260 | '240p': CONFIG.TRANSCODING.RESOLUTIONS[ '240p' ], | 261 | '240p': CONFIG.TRANSCODING.RESOLUTIONS[ '240p' ], |
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 1a18a8ae8..40a2c972b 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts | |||
@@ -6,7 +6,14 @@ import { logger } from '../../../helpers/logger' | |||
6 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' | 6 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' |
7 | import { getFormattedObjects, getServerActor } from '../../../helpers/utils' | 7 | import { getFormattedObjects, getServerActor } from '../../../helpers/utils' |
8 | import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' | 8 | import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' |
9 | import { MIMETYPES, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../initializers/constants' | 9 | import { |
10 | DEFAULT_AUDIO_RESOLUTION, | ||
11 | MIMETYPES, | ||
12 | VIDEO_CATEGORIES, | ||
13 | VIDEO_LANGUAGES, | ||
14 | VIDEO_LICENCES, | ||
15 | VIDEO_PRIVACIES | ||
16 | } from '../../../initializers/constants' | ||
10 | import { | 17 | import { |
11 | changeVideoChannelShare, | 18 | changeVideoChannelShare, |
12 | federateVideoIfNeeded, | 19 | federateVideoIfNeeded, |
@@ -54,6 +61,7 @@ import { CONFIG } from '../../../initializers/config' | |||
54 | import { sequelizeTypescript } from '../../../initializers/database' | 61 | import { sequelizeTypescript } from '../../../initializers/database' |
55 | import { createVideoMiniatureFromExisting, generateVideoMiniature } from '../../../lib/thumbnail' | 62 | import { createVideoMiniatureFromExisting, generateVideoMiniature } from '../../../lib/thumbnail' |
56 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' | 63 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' |
64 | import { VideoTranscodingPayload } from '../../../lib/job-queue/handlers/video-transcoding' | ||
57 | 65 | ||
58 | const auditLogger = auditLoggerFactory('videos') | 66 | const auditLogger = auditLoggerFactory('videos') |
59 | const videosRouter = express.Router() | 67 | const videosRouter = express.Router() |
@@ -191,18 +199,19 @@ async function addVideo (req: express.Request, res: express.Response) { | |||
191 | const video = new VideoModel(videoData) | 199 | const video = new VideoModel(videoData) |
192 | video.url = getVideoActivityPubUrl(video) // We use the UUID, so set the URL after building the object | 200 | video.url = getVideoActivityPubUrl(video) // We use the UUID, so set the URL after building the object |
193 | 201 | ||
194 | // Build the file object | ||
195 | const { videoFileResolution } = await getVideoFileResolution(videoPhysicalFile.path) | ||
196 | const fps = await getVideoFileFPS(videoPhysicalFile.path) | ||
197 | |||
198 | const videoFileData = { | 202 | const videoFileData = { |
199 | extname: extname(videoPhysicalFile.filename), | 203 | extname: extname(videoPhysicalFile.filename), |
200 | resolution: videoFileResolution, | 204 | size: videoPhysicalFile.size |
201 | size: videoPhysicalFile.size, | ||
202 | fps | ||
203 | } | 205 | } |
204 | const videoFile = new VideoFileModel(videoFileData) | 206 | const videoFile = new VideoFileModel(videoFileData) |
205 | 207 | ||
208 | if (!videoFile.isAudio()) { | ||
209 | videoFile.fps = await getVideoFileFPS(videoPhysicalFile.path) | ||
210 | videoFile.resolution = (await getVideoFileResolution(videoPhysicalFile.path)).videoFileResolution | ||
211 | } else { | ||
212 | videoFile.resolution = DEFAULT_AUDIO_RESOLUTION | ||
213 | } | ||
214 | |||
206 | // Move physical file | 215 | // Move physical file |
207 | const videoDir = CONFIG.STORAGE.VIDEOS_DIR | 216 | const videoDir = CONFIG.STORAGE.VIDEOS_DIR |
208 | const destination = join(videoDir, video.getVideoFilename(videoFile)) | 217 | const destination = join(videoDir, video.getVideoFilename(videoFile)) |
@@ -279,9 +288,21 @@ async function addVideo (req: express.Request, res: express.Response) { | |||
279 | 288 | ||
280 | if (video.state === VideoState.TO_TRANSCODE) { | 289 | if (video.state === VideoState.TO_TRANSCODE) { |
281 | // Put uuid because we don't have id auto incremented for now | 290 | // Put uuid because we don't have id auto incremented for now |
282 | const dataInput = { | 291 | let dataInput: VideoTranscodingPayload |
283 | videoUUID: videoCreated.uuid, | 292 | |
284 | isNewVideo: true | 293 | if (videoFile.isAudio()) { |
294 | dataInput = { | ||
295 | type: 'merge-audio' as 'merge-audio', | ||
296 | resolution: DEFAULT_AUDIO_RESOLUTION, | ||
297 | videoUUID: videoCreated.uuid, | ||
298 | isNewVideo: true | ||
299 | } | ||
300 | } else { | ||
301 | dataInput = { | ||
302 | type: 'optimize' as 'optimize', | ||
303 | videoUUID: videoCreated.uuid, | ||
304 | isNewVideo: true | ||
305 | } | ||
285 | } | 306 | } |
286 | 307 | ||
287 | await JobQueue.Instance.createJob({ type: 'video-transcoding', payload: dataInput }) | 308 | await JobQueue.Instance.createJob({ type: 'video-transcoding', payload: dataInput }) |
diff --git a/server/controllers/static.ts b/server/controllers/static.ts index 05019fcc2..d57dba6ce 100644 --- a/server/controllers/static.ts +++ b/server/controllers/static.ts | |||
@@ -181,7 +181,7 @@ async function getVideoCaption (req: express.Request, res: express.Response) { | |||
181 | return res.sendFile(result.path, { maxAge: STATIC_MAX_AGE }) | 181 | return res.sendFile(result.path, { maxAge: STATIC_MAX_AGE }) |
182 | } | 182 | } |
183 | 183 | ||
184 | async function generateNodeinfo (req: express.Request, res: express.Response, next: express.NextFunction) { | 184 | async function generateNodeinfo (req: express.Request, res: express.Response) { |
185 | const { totalVideos } = await VideoModel.getStats() | 185 | const { totalVideos } = await VideoModel.getStats() |
186 | const { totalLocalVideoComments } = await VideoCommentModel.getStats() | 186 | const { totalLocalVideoComments } = await VideoCommentModel.getStats() |
187 | const { totalUsers } = await UserModel.getStats() | 187 | const { totalUsers } = await UserModel.getStats() |
diff --git a/server/helpers/express-utils.ts b/server/helpers/express-utils.ts index e0a1d56a5..00f3f198b 100644 --- a/server/helpers/express-utils.ts +++ b/server/helpers/express-utils.ts | |||
@@ -74,7 +74,18 @@ function createReqFiles ( | |||
74 | }, | 74 | }, |
75 | 75 | ||
76 | filename: async (req, file, cb) => { | 76 | filename: async (req, file, cb) => { |
77 | const extension = mimeTypes[ file.mimetype ] || extname(file.originalname) | 77 | let extension: string |
78 | const fileExtension = extname(file.originalname) | ||
79 | const extensionFromMimetype = mimeTypes[ file.mimetype ] | ||
80 | |||
81 | // Take the file extension if we don't understand the mime type | ||
82 | // We have the OGG/OGV exception too because firefox sends a bad mime type when sending an OGG file | ||
83 | if (fileExtension === '.ogg' || fileExtension === '.ogv' || !extensionFromMimetype) { | ||
84 | extension = fileExtension | ||
85 | } else { | ||
86 | extension = extensionFromMimetype | ||
87 | } | ||
88 | |||
78 | let randomString = '' | 89 | let randomString = '' |
79 | 90 | ||
80 | try { | 91 | try { |
diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts index 76b744de8..c180da832 100644 --- a/server/helpers/ffmpeg-utils.ts +++ b/server/helpers/ffmpeg-utils.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import * as ffmpeg from 'fluent-ffmpeg' | 1 | import * as ffmpeg from 'fluent-ffmpeg' |
2 | import { dirname, join } from 'path' | 2 | import { dirname, join } from 'path' |
3 | import { getTargetBitrate, VideoResolution } from '../../shared/models/videos' | 3 | import { getTargetBitrate, getMaxBitrate, VideoResolution } from '../../shared/models/videos' |
4 | import { FFMPEG_NICE, VIDEO_TRANSCODING_FPS } from '../initializers/constants' | 4 | import { FFMPEG_NICE, VIDEO_TRANSCODING_FPS } from '../initializers/constants' |
5 | import { processImage } from './image-utils' | 5 | import { processImage } from './image-utils' |
6 | import { logger } from './logger' | 6 | import { logger } from './logger' |
@@ -31,7 +31,7 @@ function computeResolutionsToTranscode (videoFileHeight: number) { | |||
31 | } | 31 | } |
32 | 32 | ||
33 | async function getVideoFileSize (path: string) { | 33 | async function getVideoFileSize (path: string) { |
34 | const videoStream = await getVideoFileStream(path) | 34 | const videoStream = await getVideoStreamFromFile(path) |
35 | 35 | ||
36 | return { | 36 | return { |
37 | width: videoStream.width, | 37 | width: videoStream.width, |
@@ -49,7 +49,7 @@ async function getVideoFileResolution (path: string) { | |||
49 | } | 49 | } |
50 | 50 | ||
51 | async function getVideoFileFPS (path: string) { | 51 | async function getVideoFileFPS (path: string) { |
52 | const videoStream = await getVideoFileStream(path) | 52 | const videoStream = await getVideoStreamFromFile(path) |
53 | 53 | ||
54 | for (const key of [ 'avg_frame_rate', 'r_frame_rate' ]) { | 54 | for (const key of [ 'avg_frame_rate', 'r_frame_rate' ]) { |
55 | const valuesText: string = videoStream[key] | 55 | const valuesText: string = videoStream[key] |
@@ -117,25 +117,50 @@ async function generateImageFromVideoFile (fromPath: string, folder: string, ima | |||
117 | } | 117 | } |
118 | } | 118 | } |
119 | 119 | ||
120 | type TranscodeOptions = { | 120 | type TranscodeOptionsType = 'hls' | 'quick-transcode' | 'video' | 'merge-audio' |
121 | |||
122 | interface BaseTranscodeOptions { | ||
123 | type: TranscodeOptionsType | ||
121 | inputPath: string | 124 | inputPath: string |
122 | outputPath: string | 125 | outputPath: string |
123 | resolution: VideoResolution | 126 | resolution: VideoResolution |
124 | isPortraitMode?: boolean | 127 | isPortraitMode?: boolean |
128 | } | ||
125 | 129 | ||
126 | hlsPlaylist?: { | 130 | interface HLSTranscodeOptions extends BaseTranscodeOptions { |
131 | type: 'hls' | ||
132 | hlsPlaylist: { | ||
127 | videoFilename: string | 133 | videoFilename: string |
128 | } | 134 | } |
129 | } | 135 | } |
130 | 136 | ||
137 | interface QuickTranscodeOptions extends BaseTranscodeOptions { | ||
138 | type: 'quick-transcode' | ||
139 | } | ||
140 | |||
141 | interface VideoTranscodeOptions extends BaseTranscodeOptions { | ||
142 | type: 'video' | ||
143 | } | ||
144 | |||
145 | interface MergeAudioTranscodeOptions extends BaseTranscodeOptions { | ||
146 | type: 'merge-audio' | ||
147 | audioPath: string | ||
148 | } | ||
149 | |||
150 | type TranscodeOptions = HLSTranscodeOptions | VideoTranscodeOptions | MergeAudioTranscodeOptions | QuickTranscodeOptions | ||
151 | |||
131 | function transcode (options: TranscodeOptions) { | 152 | function transcode (options: TranscodeOptions) { |
132 | return new Promise<void>(async (res, rej) => { | 153 | return new Promise<void>(async (res, rej) => { |
133 | try { | 154 | try { |
134 | let command = ffmpeg(options.inputPath, { niceness: FFMPEG_NICE.TRANSCODING }) | 155 | let command = ffmpeg(options.inputPath, { niceness: FFMPEG_NICE.TRANSCODING }) |
135 | .output(options.outputPath) | 156 | .output(options.outputPath) |
136 | 157 | ||
137 | if (options.hlsPlaylist) { | 158 | if (options.type === 'quick-transcode') { |
159 | command = await buildQuickTranscodeCommand(command) | ||
160 | } else if (options.type === 'hls') { | ||
138 | command = await buildHLSCommand(command, options) | 161 | command = await buildHLSCommand(command, options) |
162 | } else if (options.type === 'merge-audio') { | ||
163 | command = await buildAudioMergeCommand(command, options) | ||
139 | } else { | 164 | } else { |
140 | command = await buildx264Command(command, options) | 165 | command = await buildx264Command(command, options) |
141 | } | 166 | } |
@@ -151,7 +176,7 @@ function transcode (options: TranscodeOptions) { | |||
151 | return rej(err) | 176 | return rej(err) |
152 | }) | 177 | }) |
153 | .on('end', () => { | 178 | .on('end', () => { |
154 | return onTranscodingSuccess(options) | 179 | return fixHLSPlaylistIfNeeded(options) |
155 | .then(() => res()) | 180 | .then(() => res()) |
156 | .catch(err => rej(err)) | 181 | .catch(err => rej(err)) |
157 | }) | 182 | }) |
@@ -162,6 +187,30 @@ function transcode (options: TranscodeOptions) { | |||
162 | }) | 187 | }) |
163 | } | 188 | } |
164 | 189 | ||
190 | async function canDoQuickTranscode (path: string): Promise<boolean> { | ||
191 | // NOTE: This could be optimized by running ffprobe only once (but it runs fast anyway) | ||
192 | const videoStream = await getVideoStreamFromFile(path) | ||
193 | const parsedAudio = await audio.get(path) | ||
194 | const fps = await getVideoFileFPS(path) | ||
195 | const bitRate = await getVideoFileBitrate(path) | ||
196 | const resolution = await getVideoFileResolution(path) | ||
197 | |||
198 | // check video params | ||
199 | if (videoStream[ 'codec_name' ] !== 'h264') return false | ||
200 | if (fps < VIDEO_TRANSCODING_FPS.MIN || fps > VIDEO_TRANSCODING_FPS.MAX) return false | ||
201 | if (bitRate > getMaxBitrate(resolution.videoFileResolution, fps, VIDEO_TRANSCODING_FPS)) return false | ||
202 | |||
203 | // check audio params (if audio stream exists) | ||
204 | if (parsedAudio.audioStream) { | ||
205 | if (parsedAudio.audioStream[ 'codec_name' ] !== 'aac') return false | ||
206 | |||
207 | const maxAudioBitrate = audio.bitrate[ 'aac' ](parsedAudio.audioStream[ 'bit_rate' ]) | ||
208 | if (maxAudioBitrate !== -1 && parsedAudio.audioStream[ 'bit_rate' ] > maxAudioBitrate) return false | ||
209 | } | ||
210 | |||
211 | return true | ||
212 | } | ||
213 | |||
165 | // --------------------------------------------------------------------------- | 214 | // --------------------------------------------------------------------------- |
166 | 215 | ||
167 | export { | 216 | export { |
@@ -169,16 +218,19 @@ export { | |||
169 | getVideoFileResolution, | 218 | getVideoFileResolution, |
170 | getDurationFromVideoFile, | 219 | getDurationFromVideoFile, |
171 | generateImageFromVideoFile, | 220 | generateImageFromVideoFile, |
221 | TranscodeOptions, | ||
222 | TranscodeOptionsType, | ||
172 | transcode, | 223 | transcode, |
173 | getVideoFileFPS, | 224 | getVideoFileFPS, |
174 | computeResolutionsToTranscode, | 225 | computeResolutionsToTranscode, |
175 | audio, | 226 | audio, |
176 | getVideoFileBitrate | 227 | getVideoFileBitrate, |
228 | canDoQuickTranscode | ||
177 | } | 229 | } |
178 | 230 | ||
179 | // --------------------------------------------------------------------------- | 231 | // --------------------------------------------------------------------------- |
180 | 232 | ||
181 | async function buildx264Command (command: ffmpeg.FfmpegCommand, options: TranscodeOptions) { | 233 | async function buildx264Command (command: ffmpeg.FfmpegCommand, options: VideoTranscodeOptions) { |
182 | let fps = await getVideoFileFPS(options.inputPath) | 234 | let fps = await getVideoFileFPS(options.inputPath) |
183 | // On small/medium resolutions, limit FPS | 235 | // On small/medium resolutions, limit FPS |
184 | if ( | 236 | if ( |
@@ -189,7 +241,7 @@ async function buildx264Command (command: ffmpeg.FfmpegCommand, options: Transco | |||
189 | fps = VIDEO_TRANSCODING_FPS.AVERAGE | 241 | fps = VIDEO_TRANSCODING_FPS.AVERAGE |
190 | } | 242 | } |
191 | 243 | ||
192 | command = await presetH264(command, options.resolution, fps) | 244 | command = await presetH264(command, options.inputPath, options.resolution, fps) |
193 | 245 | ||
194 | if (options.resolution !== undefined) { | 246 | if (options.resolution !== undefined) { |
195 | // '?x720' or '720x?' for example | 247 | // '?x720' or '720x?' for example |
@@ -208,7 +260,29 @@ async function buildx264Command (command: ffmpeg.FfmpegCommand, options: Transco | |||
208 | return command | 260 | return command |
209 | } | 261 | } |
210 | 262 | ||
211 | async function buildHLSCommand (command: ffmpeg.FfmpegCommand, options: TranscodeOptions) { | 263 | async function buildAudioMergeCommand (command: ffmpeg.FfmpegCommand, options: MergeAudioTranscodeOptions) { |
264 | command = command.loop(undefined) | ||
265 | |||
266 | command = await presetH264VeryFast(command, options.audioPath, options.resolution) | ||
267 | |||
268 | command = command.input(options.audioPath) | ||
269 | .videoFilter('scale=trunc(iw/2)*2:trunc(ih/2)*2') // Avoid "height not divisible by 2" error | ||
270 | .outputOption('-tune stillimage') | ||
271 | .outputOption('-shortest') | ||
272 | |||
273 | return command | ||
274 | } | ||
275 | |||
276 | async function buildQuickTranscodeCommand (command: ffmpeg.FfmpegCommand) { | ||
277 | command = await presetCopy(command) | ||
278 | |||
279 | command = command.outputOption('-map_metadata -1') // strip all metadata | ||
280 | .outputOption('-movflags faststart') | ||
281 | |||
282 | return command | ||
283 | } | ||
284 | |||
285 | async function buildHLSCommand (command: ffmpeg.FfmpegCommand, options: HLSTranscodeOptions) { | ||
212 | const videoPath = getHLSVideoPath(options) | 286 | const videoPath = getHLSVideoPath(options) |
213 | 287 | ||
214 | command = await presetCopy(command) | 288 | command = await presetCopy(command) |
@@ -224,26 +298,26 @@ async function buildHLSCommand (command: ffmpeg.FfmpegCommand, options: Transcod | |||
224 | return command | 298 | return command |
225 | } | 299 | } |
226 | 300 | ||
227 | function getHLSVideoPath (options: TranscodeOptions) { | 301 | function getHLSVideoPath (options: HLSTranscodeOptions) { |
228 | return `${dirname(options.outputPath)}/${options.hlsPlaylist.videoFilename}` | 302 | return `${dirname(options.outputPath)}/${options.hlsPlaylist.videoFilename}` |
229 | } | 303 | } |
230 | 304 | ||
231 | async function onTranscodingSuccess (options: TranscodeOptions) { | 305 | async function fixHLSPlaylistIfNeeded (options: TranscodeOptions) { |
232 | if (!options.hlsPlaylist) return | 306 | if (options.type !== 'hls') return |
233 | 307 | ||
234 | // Fix wrong mapping with some ffmpeg versions | ||
235 | const fileContent = await readFile(options.outputPath) | 308 | const fileContent = await readFile(options.outputPath) |
236 | 309 | ||
237 | const videoFileName = options.hlsPlaylist.videoFilename | 310 | const videoFileName = options.hlsPlaylist.videoFilename |
238 | const videoFilePath = getHLSVideoPath(options) | 311 | const videoFilePath = getHLSVideoPath(options) |
239 | 312 | ||
313 | // Fix wrong mapping with some ffmpeg versions | ||
240 | const newContent = fileContent.toString() | 314 | const newContent = fileContent.toString() |
241 | .replace(`#EXT-X-MAP:URI="${videoFilePath}",`, `#EXT-X-MAP:URI="${videoFileName}",`) | 315 | .replace(`#EXT-X-MAP:URI="${videoFilePath}",`, `#EXT-X-MAP:URI="${videoFileName}",`) |
242 | 316 | ||
243 | await writeFile(options.outputPath, newContent) | 317 | await writeFile(options.outputPath, newContent) |
244 | } | 318 | } |
245 | 319 | ||
246 | function getVideoFileStream (path: string) { | 320 | function getVideoStreamFromFile (path: string) { |
247 | return new Promise<any>((res, rej) => { | 321 | return new Promise<any>((res, rej) => { |
248 | ffmpeg.ffprobe(path, (err, metadata) => { | 322 | ffmpeg.ffprobe(path, (err, metadata) => { |
249 | if (err) return rej(err) | 323 | if (err) return rej(err) |
@@ -263,44 +337,27 @@ function getVideoFileStream (path: string) { | |||
263 | * and quality. Superfast and ultrafast will give you better | 337 | * and quality. Superfast and ultrafast will give you better |
264 | * performance, but then quality is noticeably worse. | 338 | * performance, but then quality is noticeably worse. |
265 | */ | 339 | */ |
266 | async function presetH264VeryFast (command: ffmpeg.FfmpegCommand, resolution: VideoResolution, fps: number): Promise<ffmpeg.FfmpegCommand> { | 340 | async function presetH264VeryFast (command: ffmpeg.FfmpegCommand, input: string, resolution: VideoResolution, fps?: number) { |
267 | let localCommand = await presetH264(command, resolution, fps) | 341 | let localCommand = await presetH264(command, input, resolution, fps) |
342 | |||
268 | localCommand = localCommand.outputOption('-preset:v veryfast') | 343 | localCommand = localCommand.outputOption('-preset:v veryfast') |
269 | .outputOption([ '--aq-mode=2', '--aq-strength=1.3' ]) | 344 | |
270 | /* | 345 | /* |
271 | MAIN reference: https://slhck.info/video/2017/03/01/rate-control.html | 346 | MAIN reference: https://slhck.info/video/2017/03/01/rate-control.html |
272 | Our target situation is closer to a livestream than a stream, | 347 | Our target situation is closer to a livestream than a stream, |
273 | since we want to reduce as much a possible the encoding burden, | 348 | since we want to reduce as much a possible the encoding burden, |
274 | altough not to the point of a livestream where there is a hard | 349 | although not to the point of a livestream where there is a hard |
275 | constraint on the frames per second to be encoded. | 350 | constraint on the frames per second to be encoded. |
276 | |||
277 | why '--aq-mode=2 --aq-strength=1.3' instead of '-profile:v main'? | ||
278 | Make up for most of the loss of grain and macroblocking | ||
279 | with less computing power. | ||
280 | */ | 351 | */ |
281 | 352 | ||
282 | return localCommand | 353 | return localCommand |
283 | } | 354 | } |
284 | 355 | ||
285 | /** | 356 | /** |
286 | * A preset optimised for a stillimage audio video | ||
287 | */ | ||
288 | async function presetStillImageWithAudio ( | ||
289 | command: ffmpeg.FfmpegCommand, | ||
290 | resolution: VideoResolution, | ||
291 | fps: number | ||
292 | ): Promise<ffmpeg.FfmpegCommand> { | ||
293 | let localCommand = await presetH264VeryFast(command, resolution, fps) | ||
294 | localCommand = localCommand.outputOption('-tune stillimage') | ||
295 | |||
296 | return localCommand | ||
297 | } | ||
298 | |||
299 | /** | ||
300 | * A toolbox to play with audio | 357 | * A toolbox to play with audio |
301 | */ | 358 | */ |
302 | namespace audio { | 359 | namespace audio { |
303 | export const get = (option: ffmpeg.FfmpegCommand | string) => { | 360 | export const get = (option: string) => { |
304 | // without position, ffprobe considers the last input only | 361 | // without position, ffprobe considers the last input only |
305 | // we make it consider the first input only | 362 | // we make it consider the first input only |
306 | // if you pass a file path to pos, then ffprobe acts on that file directly | 363 | // if you pass a file path to pos, then ffprobe acts on that file directly |
@@ -322,11 +379,7 @@ namespace audio { | |||
322 | return res({ absolutePath: data.format.filename }) | 379 | return res({ absolutePath: data.format.filename }) |
323 | } | 380 | } |
324 | 381 | ||
325 | if (typeof option === 'string') { | 382 | return ffmpeg.ffprobe(option, parseFfprobe) |
326 | return ffmpeg.ffprobe(option, parseFfprobe) | ||
327 | } | ||
328 | |||
329 | return option.ffprobe(parseFfprobe) | ||
330 | }) | 383 | }) |
331 | } | 384 | } |
332 | 385 | ||
@@ -368,7 +421,7 @@ namespace audio { | |||
368 | * As for the audio, quality '5' is the highest and ensures 96-112kbps/channel | 421 | * As for the audio, quality '5' is the highest and ensures 96-112kbps/channel |
369 | * See https://trac.ffmpeg.org/wiki/Encode/AAC#fdk_vbr | 422 | * See https://trac.ffmpeg.org/wiki/Encode/AAC#fdk_vbr |
370 | */ | 423 | */ |
371 | async function presetH264 (command: ffmpeg.FfmpegCommand, resolution: VideoResolution, fps: number): Promise<ffmpeg.FfmpegCommand> { | 424 | async function presetH264 (command: ffmpeg.FfmpegCommand, input: string, resolution: VideoResolution, fps?: number) { |
372 | let localCommand = command | 425 | let localCommand = command |
373 | .format('mp4') | 426 | .format('mp4') |
374 | .videoCodec('libx264') | 427 | .videoCodec('libx264') |
@@ -379,7 +432,7 @@ async function presetH264 (command: ffmpeg.FfmpegCommand, resolution: VideoResol | |||
379 | .outputOption('-map_metadata -1') // strip all metadata | 432 | .outputOption('-map_metadata -1') // strip all metadata |
380 | .outputOption('-movflags faststart') | 433 | .outputOption('-movflags faststart') |
381 | 434 | ||
382 | const parsedAudio = await audio.get(localCommand) | 435 | const parsedAudio = await audio.get(input) |
383 | 436 | ||
384 | if (!parsedAudio.audioStream) { | 437 | if (!parsedAudio.audioStream) { |
385 | localCommand = localCommand.noAudio() | 438 | localCommand = localCommand.noAudio() |
@@ -388,28 +441,30 @@ async function presetH264 (command: ffmpeg.FfmpegCommand, resolution: VideoResol | |||
388 | .audioCodec('libfdk_aac') | 441 | .audioCodec('libfdk_aac') |
389 | .audioQuality(5) | 442 | .audioQuality(5) |
390 | } else { | 443 | } else { |
391 | // we try to reduce the ceiling bitrate by making rough correspondances of bitrates | 444 | // we try to reduce the ceiling bitrate by making rough matches of bitrates |
392 | // of course this is far from perfect, but it might save some space in the end | 445 | // of course this is far from perfect, but it might save some space in the end |
446 | localCommand = localCommand.audioCodec('aac') | ||
447 | |||
393 | const audioCodecName = parsedAudio.audioStream[ 'codec_name' ] | 448 | const audioCodecName = parsedAudio.audioStream[ 'codec_name' ] |
394 | let bitrate: number | ||
395 | if (audio.bitrate[ audioCodecName ]) { | ||
396 | localCommand = localCommand.audioCodec('aac') | ||
397 | 449 | ||
398 | bitrate = audio.bitrate[ audioCodecName ](parsedAudio.audioStream[ 'bit_rate' ]) | 450 | if (audio.bitrate[ audioCodecName ]) { |
451 | const bitrate = audio.bitrate[ audioCodecName ](parsedAudio.audioStream[ 'bit_rate' ]) | ||
399 | if (bitrate !== undefined && bitrate !== -1) localCommand = localCommand.audioBitrate(bitrate) | 452 | if (bitrate !== undefined && bitrate !== -1) localCommand = localCommand.audioBitrate(bitrate) |
400 | } | 453 | } |
401 | } | 454 | } |
402 | 455 | ||
403 | // Constrained Encoding (VBV) | 456 | if (fps) { |
404 | // https://slhck.info/video/2017/03/01/rate-control.html | 457 | // Constrained Encoding (VBV) |
405 | // https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate | 458 | // https://slhck.info/video/2017/03/01/rate-control.html |
406 | const targetBitrate = getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS) | 459 | // https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate |
407 | localCommand = localCommand.outputOptions([`-maxrate ${ targetBitrate }`, `-bufsize ${ targetBitrate * 2 }`]) | 460 | const targetBitrate = getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS) |
408 | 461 | localCommand = localCommand.outputOptions([ `-maxrate ${targetBitrate}`, `-bufsize ${targetBitrate * 2}` ]) | |
409 | // Keyframe interval of 2 seconds for faster seeking and resolution switching. | 462 | |
410 | // https://streaminglearningcenter.com/blogs/whats-the-right-keyframe-interval.html | 463 | // Keyframe interval of 2 seconds for faster seeking and resolution switching. |
411 | // https://superuser.com/a/908325 | 464 | // https://streaminglearningcenter.com/blogs/whats-the-right-keyframe-interval.html |
412 | localCommand = localCommand.outputOption(`-g ${ fps * 2 }`) | 465 | // https://superuser.com/a/908325 |
466 | localCommand = localCommand.outputOption(`-g ${fps * 2}`) | ||
467 | } | ||
413 | 468 | ||
414 | return localCommand | 469 | return localCommand |
415 | } | 470 | } |
diff --git a/server/initializers/config.ts b/server/initializers/config.ts index 4f77e144d..2be300a57 100644 --- a/server/initializers/config.ts +++ b/server/initializers/config.ts | |||
@@ -44,6 +44,14 @@ const CONFIG = { | |||
44 | CA_FILE: config.get<string>('smtp.ca_file'), | 44 | CA_FILE: config.get<string>('smtp.ca_file'), |
45 | FROM_ADDRESS: config.get<string>('smtp.from_address') | 45 | FROM_ADDRESS: config.get<string>('smtp.from_address') |
46 | }, | 46 | }, |
47 | EMAIL: { | ||
48 | BODY: { | ||
49 | SIGNATURE: config.get<string>('email.body.signature') | ||
50 | }, | ||
51 | OBJECT: { | ||
52 | PREFIX: config.get<string>('email.object.prefix') + ' ' | ||
53 | } | ||
54 | }, | ||
47 | STORAGE: { | 55 | STORAGE: { |
48 | TMP_DIR: buildPath(config.get<string>('storage.tmp')), | 56 | TMP_DIR: buildPath(config.get<string>('storage.tmp')), |
49 | AVATARS_DIR: buildPath(config.get<string>('storage.avatars')), | 57 | AVATARS_DIR: buildPath(config.get<string>('storage.avatars')), |
@@ -140,6 +148,7 @@ const CONFIG = { | |||
140 | TRANSCODING: { | 148 | TRANSCODING: { |
141 | get ENABLED () { return config.get<boolean>('transcoding.enabled') }, | 149 | get ENABLED () { return config.get<boolean>('transcoding.enabled') }, |
142 | get ALLOW_ADDITIONAL_EXTENSIONS () { return config.get<boolean>('transcoding.allow_additional_extensions') }, | 150 | get ALLOW_ADDITIONAL_EXTENSIONS () { return config.get<boolean>('transcoding.allow_additional_extensions') }, |
151 | get ALLOW_AUDIO_FILES () { return config.get<boolean>('transcoding.allow_audio_files') }, | ||
143 | get THREADS () { return config.get<number>('transcoding.threads') }, | 152 | get THREADS () { return config.get<number>('transcoding.threads') }, |
144 | RESOLUTIONS: { | 153 | RESOLUTIONS: { |
145 | get '240p' () { return config.get<boolean>('transcoding.resolutions.240p') }, | 154 | get '240p' () { return config.get<boolean>('transcoding.resolutions.240p') }, |
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index b5f8fc0bc..ec040b80e 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -1,10 +1,10 @@ | |||
1 | import { join } from 'path' | 1 | import { join } from 'path' |
2 | import { JobType, VideoRateType, VideoState } from '../../shared/models' | 2 | import { JobType, VideoRateType, VideoResolution, VideoState } from '../../shared/models' |
3 | import { ActivityPubActorType } from '../../shared/models/activitypub' | 3 | import { ActivityPubActorType } from '../../shared/models/activitypub' |
4 | import { FollowState } from '../../shared/models/actors' | 4 | import { FollowState } from '../../shared/models/actors' |
5 | import { VideoAbuseState, VideoImportState, VideoPrivacy, VideoTranscodingFPS } from '../../shared/models/videos' | 5 | import { VideoAbuseState, VideoImportState, VideoPrivacy, VideoTranscodingFPS } from '../../shared/models/videos' |
6 | // Do not use barrels, remain constants as independent as possible | 6 | // Do not use barrels, remain constants as independent as possible |
7 | import { isTestInstance, sanitizeHost, sanitizeUrl } from '../helpers/core-utils' | 7 | import { isTestInstance, sanitizeHost, sanitizeUrl, root } from '../helpers/core-utils' |
8 | import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type' | 8 | import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type' |
9 | import { invert } from 'lodash' | 9 | import { invert } from 'lodash' |
10 | import { CronRepeatOptions, EveryRepeatOptions } from 'bull' | 10 | import { CronRepeatOptions, EveryRepeatOptions } from 'bull' |
@@ -228,7 +228,7 @@ let CONSTRAINTS_FIELDS = { | |||
228 | max: 2 * 1024 * 1024 // 2MB | 228 | max: 2 * 1024 * 1024 // 2MB |
229 | } | 229 | } |
230 | }, | 230 | }, |
231 | EXTNAME: buildVideosExtname(), | 231 | EXTNAME: [] as string[], |
232 | INFO_HASH: { min: 40, max: 40 }, // Length, info hash is 20 bytes length but we represent it in hexadecimal so 20 * 2 | 232 | INFO_HASH: { min: 40, max: 40 }, // Length, info hash is 20 bytes length but we represent it in hexadecimal so 20 * 2 |
233 | DURATION: { min: 0 }, // Number | 233 | DURATION: { min: 0 }, // Number |
234 | TAGS: { min: 0, max: 5 }, // Number of total tags | 234 | TAGS: { min: 0, max: 5 }, // Number of total tags |
@@ -300,6 +300,8 @@ const VIDEO_TRANSCODING_FPS: VideoTranscodingFPS = { | |||
300 | KEEP_ORIGIN_FPS_RESOLUTION_MIN: 720 // We keep the original FPS on high resolutions (720 minimum) | 300 | KEEP_ORIGIN_FPS_RESOLUTION_MIN: 720 // We keep the original FPS on high resolutions (720 minimum) |
301 | } | 301 | } |
302 | 302 | ||
303 | const DEFAULT_AUDIO_RESOLUTION = VideoResolution.H_480P | ||
304 | |||
303 | const VIDEO_RATE_TYPES: { [ id: string ]: VideoRateType } = { | 305 | const VIDEO_RATE_TYPES: { [ id: string ]: VideoRateType } = { |
304 | LIKE: 'like', | 306 | LIKE: 'like', |
305 | DISLIKE: 'dislike' | 307 | DISLIKE: 'dislike' |
@@ -380,8 +382,18 @@ const VIDEO_PLAYLIST_TYPES = { | |||
380 | } | 382 | } |
381 | 383 | ||
382 | const MIMETYPES = { | 384 | const MIMETYPES = { |
385 | AUDIO: { | ||
386 | MIMETYPE_EXT: { | ||
387 | 'audio/mpeg': '.mp3', | ||
388 | 'audio/mp3': '.mp3', | ||
389 | 'application/ogg': '.ogg', | ||
390 | 'audio/ogg': '.ogg', | ||
391 | 'audio/flac': '.flac' | ||
392 | }, | ||
393 | EXT_MIMETYPE: null as { [ id: string ]: string } | ||
394 | }, | ||
383 | VIDEO: { | 395 | VIDEO: { |
384 | MIMETYPE_EXT: buildVideoMimetypeExt(), | 396 | MIMETYPE_EXT: null as { [ id: string ]: string }, |
385 | EXT_MIMETYPE: null as { [ id: string ]: string } | 397 | EXT_MIMETYPE: null as { [ id: string ]: string } |
386 | }, | 398 | }, |
387 | IMAGE: { | 399 | IMAGE: { |
@@ -403,7 +415,7 @@ const MIMETYPES = { | |||
403 | } | 415 | } |
404 | } | 416 | } |
405 | } | 417 | } |
406 | MIMETYPES.VIDEO.EXT_MIMETYPE = invert(MIMETYPES.VIDEO.MIMETYPE_EXT) | 418 | MIMETYPES.AUDIO.EXT_MIMETYPE = invert(MIMETYPES.AUDIO.MIMETYPE_EXT) |
407 | 419 | ||
408 | // --------------------------------------------------------------------------- | 420 | // --------------------------------------------------------------------------- |
409 | 421 | ||
@@ -429,7 +441,7 @@ const ACTIVITY_PUB = { | |||
429 | COLLECTION_ITEMS_PER_PAGE: 10, | 441 | COLLECTION_ITEMS_PER_PAGE: 10, |
430 | FETCH_PAGE_LIMIT: 100, | 442 | FETCH_PAGE_LIMIT: 100, |
431 | URL_MIME_TYPES: { | 443 | URL_MIME_TYPES: { |
432 | VIDEO: Object.keys(MIMETYPES.VIDEO.MIMETYPE_EXT), | 444 | VIDEO: [] as string[], |
433 | TORRENT: [ 'application/x-bittorrent' ], | 445 | TORRENT: [ 'application/x-bittorrent' ], |
434 | MAGNET: [ 'application/x-bittorrent;x-scheme-handler/magnet' ] | 446 | MAGNET: [ 'application/x-bittorrent;x-scheme-handler/magnet' ] |
435 | }, | 447 | }, |
@@ -497,8 +509,8 @@ const THUMBNAILS_SIZE = { | |||
497 | height: 122 | 509 | height: 122 |
498 | } | 510 | } |
499 | const PREVIEWS_SIZE = { | 511 | const PREVIEWS_SIZE = { |
500 | width: 560, | 512 | width: 850, |
501 | height: 315 | 513 | height: 480 |
502 | } | 514 | } |
503 | const AVATARS_SIZE = { | 515 | const AVATARS_SIZE = { |
504 | width: 120, | 516 | width: 120, |
@@ -543,6 +555,10 @@ const REDUNDANCY = { | |||
543 | 555 | ||
544 | const ACCEPT_HEADERS = [ 'html', 'application/json' ].concat(ACTIVITY_PUB.POTENTIAL_ACCEPT_HEADERS) | 556 | const ACCEPT_HEADERS = [ 'html', 'application/json' ].concat(ACTIVITY_PUB.POTENTIAL_ACCEPT_HEADERS) |
545 | 557 | ||
558 | const ASSETS_PATH = { | ||
559 | DEFAULT_AUDIO_BACKGROUND: join(root(), 'server', 'assets', 'default-audio-background.jpg') | ||
560 | } | ||
561 | |||
546 | // --------------------------------------------------------------------------- | 562 | // --------------------------------------------------------------------------- |
547 | 563 | ||
548 | const CUSTOM_HTML_TAG_COMMENTS = { | 564 | const CUSTOM_HTML_TAG_COMMENTS = { |
@@ -612,6 +628,7 @@ if (isTestInstance() === true) { | |||
612 | } | 628 | } |
613 | 629 | ||
614 | updateWebserverUrls() | 630 | updateWebserverUrls() |
631 | updateWebserverConfig() | ||
615 | 632 | ||
616 | registerConfigChangedHandler(() => { | 633 | registerConfigChangedHandler(() => { |
617 | updateWebserverUrls() | 634 | updateWebserverUrls() |
@@ -681,12 +698,14 @@ export { | |||
681 | RATES_LIMIT, | 698 | RATES_LIMIT, |
682 | MIMETYPES, | 699 | MIMETYPES, |
683 | CRAWL_REQUEST_CONCURRENCY, | 700 | CRAWL_REQUEST_CONCURRENCY, |
701 | DEFAULT_AUDIO_RESOLUTION, | ||
684 | JOB_COMPLETED_LIFETIME, | 702 | JOB_COMPLETED_LIFETIME, |
685 | HTTP_SIGNATURE, | 703 | HTTP_SIGNATURE, |
686 | VIDEO_IMPORT_STATES, | 704 | VIDEO_IMPORT_STATES, |
687 | VIDEO_VIEW_LIFETIME, | 705 | VIDEO_VIEW_LIFETIME, |
688 | CONTACT_FORM_LIFETIME, | 706 | CONTACT_FORM_LIFETIME, |
689 | VIDEO_PLAYLIST_PRIVACIES, | 707 | VIDEO_PLAYLIST_PRIVACIES, |
708 | ASSETS_PATH, | ||
690 | loadLanguages, | 709 | loadLanguages, |
691 | buildLanguages | 710 | buildLanguages |
692 | } | 711 | } |
@@ -700,15 +719,21 @@ function buildVideoMimetypeExt () { | |||
700 | 'video/mp4': '.mp4' | 719 | 'video/mp4': '.mp4' |
701 | } | 720 | } |
702 | 721 | ||
703 | if (CONFIG.TRANSCODING.ENABLED && CONFIG.TRANSCODING.ALLOW_ADDITIONAL_EXTENSIONS) { | 722 | if (CONFIG.TRANSCODING.ENABLED) { |
704 | Object.assign(data, { | 723 | if (CONFIG.TRANSCODING.ALLOW_ADDITIONAL_EXTENSIONS) { |
705 | 'video/quicktime': '.mov', | 724 | Object.assign(data, { |
706 | 'video/x-msvideo': '.avi', | 725 | 'video/quicktime': '.mov', |
707 | 'video/x-flv': '.flv', | 726 | 'video/x-msvideo': '.avi', |
708 | 'video/x-matroska': '.mkv', | 727 | 'video/x-flv': '.flv', |
709 | 'application/octet-stream': '.mkv', | 728 | 'video/x-matroska': '.mkv', |
710 | 'video/avi': '.avi' | 729 | 'application/octet-stream': '.mkv', |
711 | }) | 730 | 'video/avi': '.avi' |
731 | }) | ||
732 | } | ||
733 | |||
734 | if (CONFIG.TRANSCODING.ALLOW_AUDIO_FILES) { | ||
735 | Object.assign(data, MIMETYPES.AUDIO.MIMETYPE_EXT) | ||
736 | } | ||
712 | } | 737 | } |
713 | 738 | ||
714 | return data | 739 | return data |
@@ -724,16 +749,15 @@ function updateWebserverUrls () { | |||
724 | } | 749 | } |
725 | 750 | ||
726 | function updateWebserverConfig () { | 751 | function updateWebserverConfig () { |
727 | CONSTRAINTS_FIELDS.VIDEOS.EXTNAME = buildVideosExtname() | ||
728 | |||
729 | MIMETYPES.VIDEO.MIMETYPE_EXT = buildVideoMimetypeExt() | 752 | MIMETYPES.VIDEO.MIMETYPE_EXT = buildVideoMimetypeExt() |
730 | MIMETYPES.VIDEO.EXT_MIMETYPE = invert(MIMETYPES.VIDEO.MIMETYPE_EXT) | 753 | MIMETYPES.VIDEO.EXT_MIMETYPE = invert(MIMETYPES.VIDEO.MIMETYPE_EXT) |
754 | ACTIVITY_PUB.URL_MIME_TYPES.VIDEO = Object.keys(MIMETYPES.VIDEO.MIMETYPE_EXT) | ||
755 | |||
756 | CONSTRAINTS_FIELDS.VIDEOS.EXTNAME = buildVideosExtname() | ||
731 | } | 757 | } |
732 | 758 | ||
733 | function buildVideosExtname () { | 759 | function buildVideosExtname () { |
734 | return CONFIG.TRANSCODING.ENABLED && CONFIG.TRANSCODING.ALLOW_ADDITIONAL_EXTENSIONS | 760 | return Object.keys(MIMETYPES.VIDEO.EXT_MIMETYPE) |
735 | ? [ '.mp4', '.ogv', '.webm', '.mkv', '.mov', '.avi', '.flv' ] | ||
736 | : [ '.mp4', '.ogv', '.webm' ] | ||
737 | } | 761 | } |
738 | 762 | ||
739 | function loadLanguages () { | 763 | function loadLanguages () { |
diff --git a/server/initializers/installer.ts b/server/initializers/installer.ts index 127449577..33970f0fa 100644 --- a/server/initializers/installer.ts +++ b/server/initializers/installer.ts | |||
@@ -128,6 +128,8 @@ async function createOAuthAdminIfNotExist () { | |||
128 | 128 | ||
129 | // Our password is weak so do not validate it | 129 | // Our password is weak so do not validate it |
130 | validatePassword = false | 130 | validatePassword = false |
131 | } else if (process.env.PT_INITIAL_ROOT_PASSWORD) { | ||
132 | password = process.env.PT_INITIAL_ROOT_PASSWORD | ||
131 | } else { | 133 | } else { |
132 | password = passwordGenerator(16, true) | 134 | password = passwordGenerator(16, true) |
133 | } | 135 | } |
diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts index 8c06e9751..c4a5a5853 100644 --- a/server/lib/emailer.ts +++ b/server/lib/emailer.ts | |||
@@ -100,11 +100,11 @@ class Emailer { | |||
100 | `You can view it on ${videoUrl} ` + | 100 | `You can view it on ${videoUrl} ` + |
101 | `\n\n` + | 101 | `\n\n` + |
102 | `Cheers,\n` + | 102 | `Cheers,\n` + |
103 | `PeerTube.` | 103 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
104 | 104 | ||
105 | const emailPayload: EmailPayload = { | 105 | const emailPayload: EmailPayload = { |
106 | to, | 106 | to, |
107 | subject: channelName + ' just published a new video', | 107 | subject: CONFIG.EMAIL.OBJECT.PREFIX + channelName + ' just published a new video', |
108 | text | 108 | text |
109 | } | 109 | } |
110 | 110 | ||
@@ -119,11 +119,11 @@ class Emailer { | |||
119 | `Your ${followType} ${followingName} has a new subscriber: ${followerName}` + | 119 | `Your ${followType} ${followingName} has a new subscriber: ${followerName}` + |
120 | `\n\n` + | 120 | `\n\n` + |
121 | `Cheers,\n` + | 121 | `Cheers,\n` + |
122 | `PeerTube.` | 122 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
123 | 123 | ||
124 | const emailPayload: EmailPayload = { | 124 | const emailPayload: EmailPayload = { |
125 | to, | 125 | to, |
126 | subject: 'New follower on your channel ' + followingName, | 126 | subject: CONFIG.EMAIL.OBJECT.PREFIX + 'New follower on your channel ' + followingName, |
127 | text | 127 | text |
128 | } | 128 | } |
129 | 129 | ||
@@ -137,11 +137,11 @@ class Emailer { | |||
137 | `Your instance has a new follower: ${actorFollow.ActorFollower.url}${awaitingApproval}` + | 137 | `Your instance has a new follower: ${actorFollow.ActorFollower.url}${awaitingApproval}` + |
138 | `\n\n` + | 138 | `\n\n` + |
139 | `Cheers,\n` + | 139 | `Cheers,\n` + |
140 | `PeerTube.` | 140 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
141 | 141 | ||
142 | const emailPayload: EmailPayload = { | 142 | const emailPayload: EmailPayload = { |
143 | to, | 143 | to, |
144 | subject: 'New instance follower', | 144 | subject: CONFIG.EMAIL.OBJECT.PREFIX + 'New instance follower', |
145 | text | 145 | text |
146 | } | 146 | } |
147 | 147 | ||
@@ -157,11 +157,11 @@ class Emailer { | |||
157 | `You can view it on ${videoUrl} ` + | 157 | `You can view it on ${videoUrl} ` + |
158 | `\n\n` + | 158 | `\n\n` + |
159 | `Cheers,\n` + | 159 | `Cheers,\n` + |
160 | `PeerTube.` | 160 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
161 | 161 | ||
162 | const emailPayload: EmailPayload = { | 162 | const emailPayload: EmailPayload = { |
163 | to, | 163 | to, |
164 | subject: `Your video ${video.name} is published`, | 164 | subject: CONFIG.EMAIL.OBJECT.PREFIX + `Your video ${video.name} is published`, |
165 | text | 165 | text |
166 | } | 166 | } |
167 | 167 | ||
@@ -177,11 +177,11 @@ class Emailer { | |||
177 | `You can view the imported video on ${videoUrl} ` + | 177 | `You can view the imported video on ${videoUrl} ` + |
178 | `\n\n` + | 178 | `\n\n` + |
179 | `Cheers,\n` + | 179 | `Cheers,\n` + |
180 | `PeerTube.` | 180 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
181 | 181 | ||
182 | const emailPayload: EmailPayload = { | 182 | const emailPayload: EmailPayload = { |
183 | to, | 183 | to, |
184 | subject: `Your video import ${videoImport.getTargetIdentifier()} is finished`, | 184 | subject: CONFIG.EMAIL.OBJECT.PREFIX + `Your video import ${videoImport.getTargetIdentifier()} is finished`, |
185 | text | 185 | text |
186 | } | 186 | } |
187 | 187 | ||
@@ -197,11 +197,11 @@ class Emailer { | |||
197 | `See your videos import dashboard for more information: ${importUrl}` + | 197 | `See your videos import dashboard for more information: ${importUrl}` + |
198 | `\n\n` + | 198 | `\n\n` + |
199 | `Cheers,\n` + | 199 | `Cheers,\n` + |
200 | `PeerTube.` | 200 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
201 | 201 | ||
202 | const emailPayload: EmailPayload = { | 202 | const emailPayload: EmailPayload = { |
203 | to, | 203 | to, |
204 | subject: `Your video import ${videoImport.getTargetIdentifier()} encountered an error`, | 204 | subject: CONFIG.EMAIL.OBJECT.PREFIX + `Your video import ${videoImport.getTargetIdentifier()} encountered an error`, |
205 | text | 205 | text |
206 | } | 206 | } |
207 | 207 | ||
@@ -219,11 +219,11 @@ class Emailer { | |||
219 | `You can view it on ${commentUrl} ` + | 219 | `You can view it on ${commentUrl} ` + |
220 | `\n\n` + | 220 | `\n\n` + |
221 | `Cheers,\n` + | 221 | `Cheers,\n` + |
222 | `PeerTube.` | 222 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
223 | 223 | ||
224 | const emailPayload: EmailPayload = { | 224 | const emailPayload: EmailPayload = { |
225 | to, | 225 | to, |
226 | subject: 'New comment on your video ' + video.name, | 226 | subject: CONFIG.EMAIL.OBJECT.PREFIX + 'New comment on your video ' + video.name, |
227 | text | 227 | text |
228 | } | 228 | } |
229 | 229 | ||
@@ -241,11 +241,11 @@ class Emailer { | |||
241 | `You can view the comment on ${commentUrl} ` + | 241 | `You can view the comment on ${commentUrl} ` + |
242 | `\n\n` + | 242 | `\n\n` + |
243 | `Cheers,\n` + | 243 | `Cheers,\n` + |
244 | `PeerTube.` | 244 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
245 | 245 | ||
246 | const emailPayload: EmailPayload = { | 246 | const emailPayload: EmailPayload = { |
247 | to, | 247 | to, |
248 | subject: 'Mention on video ' + video.name, | 248 | subject: CONFIG.EMAIL.OBJECT.PREFIX + 'Mention on video ' + video.name, |
249 | text | 249 | text |
250 | } | 250 | } |
251 | 251 | ||
@@ -258,11 +258,11 @@ class Emailer { | |||
258 | const text = `Hi,\n\n` + | 258 | const text = `Hi,\n\n` + |
259 | `${WEBSERVER.HOST} received an abuse for the following video ${videoUrl}\n\n` + | 259 | `${WEBSERVER.HOST} received an abuse for the following video ${videoUrl}\n\n` + |
260 | `Cheers,\n` + | 260 | `Cheers,\n` + |
261 | `PeerTube.` | 261 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
262 | 262 | ||
263 | const emailPayload: EmailPayload = { | 263 | const emailPayload: EmailPayload = { |
264 | to, | 264 | to, |
265 | subject: '[PeerTube] Received a video abuse', | 265 | subject: CONFIG.EMAIL.OBJECT.PREFIX + 'Received a video abuse', |
266 | text | 266 | text |
267 | } | 267 | } |
268 | 268 | ||
@@ -281,11 +281,11 @@ class Emailer { | |||
281 | `A full list of auto-blacklisted videos can be reviewed here: ${VIDEO_AUTO_BLACKLIST_URL}` + | 281 | `A full list of auto-blacklisted videos can be reviewed here: ${VIDEO_AUTO_BLACKLIST_URL}` + |
282 | `\n\n` + | 282 | `\n\n` + |
283 | `Cheers,\n` + | 283 | `Cheers,\n` + |
284 | `PeerTube.` | 284 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
285 | 285 | ||
286 | const emailPayload: EmailPayload = { | 286 | const emailPayload: EmailPayload = { |
287 | to, | 287 | to, |
288 | subject: '[PeerTube] An auto-blacklisted video is awaiting review', | 288 | subject: CONFIG.EMAIL.OBJECT.PREFIX + 'An auto-blacklisted video is awaiting review', |
289 | text | 289 | text |
290 | } | 290 | } |
291 | 291 | ||
@@ -296,11 +296,11 @@ class Emailer { | |||
296 | const text = `Hi,\n\n` + | 296 | const text = `Hi,\n\n` + |
297 | `User ${user.username} just registered on ${WEBSERVER.HOST} PeerTube instance.\n\n` + | 297 | `User ${user.username} just registered on ${WEBSERVER.HOST} PeerTube instance.\n\n` + |
298 | `Cheers,\n` + | 298 | `Cheers,\n` + |
299 | `PeerTube.` | 299 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
300 | 300 | ||
301 | const emailPayload: EmailPayload = { | 301 | const emailPayload: EmailPayload = { |
302 | to, | 302 | to, |
303 | subject: '[PeerTube] New user registration on ' + WEBSERVER.HOST, | 303 | subject: CONFIG.EMAIL.OBJECT.PREFIX + 'New user registration on ' + WEBSERVER.HOST, |
304 | text | 304 | text |
305 | } | 305 | } |
306 | 306 | ||
@@ -318,11 +318,11 @@ class Emailer { | |||
318 | blockedString + | 318 | blockedString + |
319 | '\n\n' + | 319 | '\n\n' + |
320 | 'Cheers,\n' + | 320 | 'Cheers,\n' + |
321 | `PeerTube.` | 321 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
322 | 322 | ||
323 | const emailPayload: EmailPayload = { | 323 | const emailPayload: EmailPayload = { |
324 | to, | 324 | to, |
325 | subject: `[PeerTube] Video ${videoName} blacklisted`, | 325 | subject: CONFIG.EMAIL.OBJECT.PREFIX + `Video ${videoName} blacklisted`, |
326 | text | 326 | text |
327 | } | 327 | } |
328 | 328 | ||
@@ -336,11 +336,11 @@ class Emailer { | |||
336 | `Your video ${video.name} (${videoUrl}) on ${WEBSERVER.HOST} has been unblacklisted.` + | 336 | `Your video ${video.name} (${videoUrl}) on ${WEBSERVER.HOST} has been unblacklisted.` + |
337 | '\n\n' + | 337 | '\n\n' + |
338 | 'Cheers,\n' + | 338 | 'Cheers,\n' + |
339 | `PeerTube.` | 339 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
340 | 340 | ||
341 | const emailPayload: EmailPayload = { | 341 | const emailPayload: EmailPayload = { |
342 | to, | 342 | to, |
343 | subject: `[PeerTube] Video ${video.name} unblacklisted`, | 343 | subject: CONFIG.EMAIL.OBJECT.PREFIX + `Video ${video.name} unblacklisted`, |
344 | text | 344 | text |
345 | } | 345 | } |
346 | 346 | ||
@@ -353,11 +353,11 @@ class Emailer { | |||
353 | `Please follow this link to reset it: ${resetPasswordUrl}\n\n` + | 353 | `Please follow this link to reset it: ${resetPasswordUrl}\n\n` + |
354 | `If you are not the person who initiated this request, please ignore this email.\n\n` + | 354 | `If you are not the person who initiated this request, please ignore this email.\n\n` + |
355 | `Cheers,\n` + | 355 | `Cheers,\n` + |
356 | `PeerTube.` | 356 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
357 | 357 | ||
358 | const emailPayload: EmailPayload = { | 358 | const emailPayload: EmailPayload = { |
359 | to: [ to ], | 359 | to: [ to ], |
360 | subject: 'Reset your PeerTube password', | 360 | subject: CONFIG.EMAIL.OBJECT.PREFIX + 'Reset your password', |
361 | text | 361 | text |
362 | } | 362 | } |
363 | 363 | ||
@@ -370,11 +370,11 @@ class Emailer { | |||
370 | `Please follow this link to verify this email belongs to you: ${verifyEmailUrl}\n\n` + | 370 | `Please follow this link to verify this email belongs to you: ${verifyEmailUrl}\n\n` + |
371 | `If you are not the person who initiated this request, please ignore this email.\n\n` + | 371 | `If you are not the person who initiated this request, please ignore this email.\n\n` + |
372 | `Cheers,\n` + | 372 | `Cheers,\n` + |
373 | `PeerTube.` | 373 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
374 | 374 | ||
375 | const emailPayload: EmailPayload = { | 375 | const emailPayload: EmailPayload = { |
376 | to: [ to ], | 376 | to: [ to ], |
377 | subject: 'Verify your PeerTube email', | 377 | subject: CONFIG.EMAIL.OBJECT.PREFIX + 'Verify your email', |
378 | text | 378 | text |
379 | } | 379 | } |
380 | 380 | ||
@@ -390,12 +390,12 @@ class Emailer { | |||
390 | blockedString + | 390 | blockedString + |
391 | '\n\n' + | 391 | '\n\n' + |
392 | 'Cheers,\n' + | 392 | 'Cheers,\n' + |
393 | `PeerTube.` | 393 | `${CONFIG.EMAIL.BODY.SIGNATURE}` |
394 | 394 | ||
395 | const to = user.email | 395 | const to = user.email |
396 | const emailPayload: EmailPayload = { | 396 | const emailPayload: EmailPayload = { |
397 | to: [ to ], | 397 | to: [ to ], |
398 | subject: '[PeerTube] Account ' + blockedWord, | 398 | subject: CONFIG.EMAIL.OBJECT.PREFIX + 'Account ' + blockedWord, |
399 | text | 399 | text |
400 | } | 400 | } |
401 | 401 | ||
@@ -415,7 +415,7 @@ class Emailer { | |||
415 | fromDisplayName: fromEmail, | 415 | fromDisplayName: fromEmail, |
416 | replyTo: fromEmail, | 416 | replyTo: fromEmail, |
417 | to: [ CONFIG.ADMIN.EMAIL ], | 417 | to: [ CONFIG.ADMIN.EMAIL ], |
418 | subject: '[PeerTube] Contact form submitted', | 418 | subject: CONFIG.EMAIL.OBJECT.PREFIX + 'Contact form submitted', |
419 | text | 419 | text |
420 | } | 420 | } |
421 | 421 | ||
diff --git a/server/lib/files-cache/videos-preview-cache.ts b/server/lib/files-cache/videos-preview-cache.ts index 14be7f24a..a68619d07 100644 --- a/server/lib/files-cache/videos-preview-cache.ts +++ b/server/lib/files-cache/videos-preview-cache.ts | |||
@@ -21,7 +21,7 @@ class VideosPreviewCache extends AbstractVideoStaticFileCache <string> { | |||
21 | const video = await VideoModel.loadByUUIDWithFile(videoUUID) | 21 | const video = await VideoModel.loadByUUIDWithFile(videoUUID) |
22 | if (!video) return undefined | 22 | if (!video) return undefined |
23 | 23 | ||
24 | if (video.isOwned()) return { isOwned: true, path: join(CONFIG.STORAGE.PREVIEWS_DIR, video.getPreview().filename) } | 24 | if (video.isOwned()) return { isOwned: true, path: video.getPreview().getPath() } |
25 | 25 | ||
26 | return this.loadRemoteFile(videoUUID) | 26 | return this.loadRemoteFile(videoUUID) |
27 | } | 27 | } |
diff --git a/server/lib/job-queue/handlers/video-file-import.ts b/server/lib/job-queue/handlers/video-file-import.ts index 921d9a083..8cacb0ef3 100644 --- a/server/lib/job-queue/handlers/video-file-import.ts +++ b/server/lib/job-queue/handlers/video-file-import.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import * as Bull from 'bull' | 1 | import * as Bull from 'bull' |
2 | import { logger } from '../../../helpers/logger' | 2 | import { logger } from '../../../helpers/logger' |
3 | import { VideoModel } from '../../../models/video/video' | 3 | import { VideoModel } from '../../../models/video/video' |
4 | import { publishVideoIfNeeded } from './video-transcoding' | 4 | import { publishNewResolutionIfNeeded } from './video-transcoding' |
5 | import { getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils' | 5 | import { getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils' |
6 | import { copy, stat } from 'fs-extra' | 6 | import { copy, stat } from 'fs-extra' |
7 | import { VideoFileModel } from '../../../models/video/video-file' | 7 | import { VideoFileModel } from '../../../models/video/video-file' |
@@ -25,7 +25,7 @@ async function processVideoFileImport (job: Bull.Job) { | |||
25 | 25 | ||
26 | await updateVideoFile(video, payload.filePath) | 26 | await updateVideoFile(video, payload.filePath) |
27 | 27 | ||
28 | await publishVideoIfNeeded(video) | 28 | await publishNewResolutionIfNeeded(video) |
29 | return video | 29 | return video |
30 | } | 30 | } |
31 | 31 | ||
diff --git a/server/lib/job-queue/handlers/video-import.ts b/server/lib/job-queue/handlers/video-import.ts index 1650916a6..50e159245 100644 --- a/server/lib/job-queue/handlers/video-import.ts +++ b/server/lib/job-queue/handlers/video-import.ts | |||
@@ -209,6 +209,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide | |||
209 | if (videoImportUpdated.Video.state === VideoState.TO_TRANSCODE) { | 209 | if (videoImportUpdated.Video.state === VideoState.TO_TRANSCODE) { |
210 | // Put uuid because we don't have id auto incremented for now | 210 | // Put uuid because we don't have id auto incremented for now |
211 | const dataInput = { | 211 | const dataInput = { |
212 | type: 'optimize' as 'optimize', | ||
212 | videoUUID: videoImportUpdated.Video.uuid, | 213 | videoUUID: videoImportUpdated.Video.uuid, |
213 | isNewVideo: true | 214 | isNewVideo: true |
214 | } | 215 | } |
diff --git a/server/lib/job-queue/handlers/video-transcoding.ts b/server/lib/job-queue/handlers/video-transcoding.ts index 48cac517e..e9b84ecd6 100644 --- a/server/lib/job-queue/handlers/video-transcoding.ts +++ b/server/lib/job-queue/handlers/video-transcoding.ts | |||
@@ -8,18 +8,39 @@ import { retryTransactionWrapper } from '../../../helpers/database-utils' | |||
8 | import { sequelizeTypescript } from '../../../initializers' | 8 | import { sequelizeTypescript } from '../../../initializers' |
9 | import * as Bluebird from 'bluebird' | 9 | import * as Bluebird from 'bluebird' |
10 | import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils' | 10 | import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils' |
11 | import { generateHlsPlaylist, optimizeVideofile, transcodeOriginalVideofile } from '../../video-transcoding' | 11 | import { generateHlsPlaylist, optimizeVideofile, transcodeOriginalVideofile, mergeAudioVideofile } from '../../video-transcoding' |
12 | import { Notifier } from '../../notifier' | 12 | import { Notifier } from '../../notifier' |
13 | import { CONFIG } from '../../../initializers/config' | 13 | import { CONFIG } from '../../../initializers/config' |
14 | 14 | ||
15 | export type VideoTranscodingPayload = { | 15 | interface BaseTranscodingPayload { |
16 | videoUUID: string | 16 | videoUUID: string |
17 | resolution?: VideoResolution | ||
18 | isNewVideo?: boolean | 17 | isNewVideo?: boolean |
18 | } | ||
19 | |||
20 | interface HLSTranscodingPayload extends BaseTranscodingPayload { | ||
21 | type: 'hls' | ||
22 | isPortraitMode?: boolean | ||
23 | resolution: VideoResolution | ||
24 | } | ||
25 | |||
26 | interface NewResolutionTranscodingPayload extends BaseTranscodingPayload { | ||
27 | type: 'new-resolution' | ||
19 | isPortraitMode?: boolean | 28 | isPortraitMode?: boolean |
20 | generateHlsPlaylist?: boolean | 29 | resolution: VideoResolution |
30 | } | ||
31 | |||
32 | interface MergeAudioTranscodingPayload extends BaseTranscodingPayload { | ||
33 | type: 'merge-audio' | ||
34 | resolution: VideoResolution | ||
35 | } | ||
36 | |||
37 | interface OptimizeTranscodingPayload extends BaseTranscodingPayload { | ||
38 | type: 'optimize' | ||
21 | } | 39 | } |
22 | 40 | ||
41 | export type VideoTranscodingPayload = HLSTranscodingPayload | NewResolutionTranscodingPayload | ||
42 | | OptimizeTranscodingPayload | MergeAudioTranscodingPayload | ||
43 | |||
23 | async function processVideoTranscoding (job: Bull.Job) { | 44 | async function processVideoTranscoding (job: Bull.Job) { |
24 | const payload = job.data as VideoTranscodingPayload | 45 | const payload = job.data as VideoTranscodingPayload |
25 | logger.info('Processing video file in job %d.', job.id) | 46 | logger.info('Processing video file in job %d.', job.id) |
@@ -31,14 +52,18 @@ async function processVideoTranscoding (job: Bull.Job) { | |||
31 | return undefined | 52 | return undefined |
32 | } | 53 | } |
33 | 54 | ||
34 | if (payload.generateHlsPlaylist) { | 55 | if (payload.type === 'hls') { |
35 | await generateHlsPlaylist(video, payload.resolution, payload.isPortraitMode || false) | 56 | await generateHlsPlaylist(video, payload.resolution, payload.isPortraitMode || false) |
36 | 57 | ||
37 | await retryTransactionWrapper(onHlsPlaylistGenerationSuccess, video) | 58 | await retryTransactionWrapper(onHlsPlaylistGenerationSuccess, video) |
38 | } else if (payload.resolution) { // Transcoding in other resolution | 59 | } else if (payload.type === 'new-resolution') { |
39 | await transcodeOriginalVideofile(video, payload.resolution, payload.isPortraitMode || false) | 60 | await transcodeOriginalVideofile(video, payload.resolution, payload.isPortraitMode || false) |
40 | 61 | ||
41 | await retryTransactionWrapper(publishVideoIfNeeded, video, payload) | 62 | await retryTransactionWrapper(publishNewResolutionIfNeeded, video, payload) |
63 | } else if (payload.type === 'merge-audio') { | ||
64 | await mergeAudioVideofile(video, payload.resolution) | ||
65 | |||
66 | await retryTransactionWrapper(publishNewResolutionIfNeeded, video, payload) | ||
42 | } else { | 67 | } else { |
43 | await optimizeVideofile(video) | 68 | await optimizeVideofile(video) |
44 | 69 | ||
@@ -62,7 +87,7 @@ async function onHlsPlaylistGenerationSuccess (video: VideoModel) { | |||
62 | }) | 87 | }) |
63 | } | 88 | } |
64 | 89 | ||
65 | async function publishVideoIfNeeded (video: VideoModel, payload?: VideoTranscodingPayload) { | 90 | async function publishNewResolutionIfNeeded (video: VideoModel, payload?: NewResolutionTranscodingPayload | MergeAudioTranscodingPayload) { |
66 | const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => { | 91 | const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => { |
67 | // Maybe the video changed in database, refresh it | 92 | // Maybe the video changed in database, refresh it |
68 | let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t) | 93 | let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t) |
@@ -94,7 +119,7 @@ async function publishVideoIfNeeded (video: VideoModel, payload?: VideoTranscodi | |||
94 | await createHlsJobIfEnabled(payload) | 119 | await createHlsJobIfEnabled(payload) |
95 | } | 120 | } |
96 | 121 | ||
97 | async function onVideoFileOptimizerSuccess (videoArg: VideoModel, payload: VideoTranscodingPayload) { | 122 | async function onVideoFileOptimizerSuccess (videoArg: VideoModel, payload: OptimizeTranscodingPayload) { |
98 | if (videoArg === undefined) return undefined | 123 | if (videoArg === undefined) return undefined |
99 | 124 | ||
100 | // Outside the transaction (IO on disk) | 125 | // Outside the transaction (IO on disk) |
@@ -120,6 +145,7 @@ async function onVideoFileOptimizerSuccess (videoArg: VideoModel, payload: Video | |||
120 | 145 | ||
121 | for (const resolution of resolutionsEnabled) { | 146 | for (const resolution of resolutionsEnabled) { |
122 | const dataInput = { | 147 | const dataInput = { |
148 | type: 'new-resolution' as 'new-resolution', | ||
123 | videoUUID: videoDatabase.uuid, | 149 | videoUUID: videoDatabase.uuid, |
124 | resolution | 150 | resolution |
125 | } | 151 | } |
@@ -149,27 +175,27 @@ async function onVideoFileOptimizerSuccess (videoArg: VideoModel, payload: Video | |||
149 | if (payload.isNewVideo) Notifier.Instance.notifyOnNewVideo(videoDatabase) | 175 | if (payload.isNewVideo) Notifier.Instance.notifyOnNewVideo(videoDatabase) |
150 | if (videoPublished) Notifier.Instance.notifyOnVideoPublishedAfterTranscoding(videoDatabase) | 176 | if (videoPublished) Notifier.Instance.notifyOnVideoPublishedAfterTranscoding(videoDatabase) |
151 | 177 | ||
152 | await createHlsJobIfEnabled(Object.assign({}, payload, { resolution: videoDatabase.getOriginalFile().resolution })) | 178 | const hlsPayload = Object.assign({}, payload, { resolution: videoDatabase.getOriginalFile().resolution }) |
179 | await createHlsJobIfEnabled(hlsPayload) | ||
153 | } | 180 | } |
154 | 181 | ||
155 | // --------------------------------------------------------------------------- | 182 | // --------------------------------------------------------------------------- |
156 | 183 | ||
157 | export { | 184 | export { |
158 | processVideoTranscoding, | 185 | processVideoTranscoding, |
159 | publishVideoIfNeeded | 186 | publishNewResolutionIfNeeded |
160 | } | 187 | } |
161 | 188 | ||
162 | // --------------------------------------------------------------------------- | 189 | // --------------------------------------------------------------------------- |
163 | 190 | ||
164 | function createHlsJobIfEnabled (payload?: VideoTranscodingPayload) { | 191 | function createHlsJobIfEnabled (payload?: { videoUUID: string, resolution: number, isPortraitMode?: boolean }) { |
165 | // Generate HLS playlist? | 192 | // Generate HLS playlist? |
166 | if (payload && CONFIG.TRANSCODING.HLS.ENABLED) { | 193 | if (payload && CONFIG.TRANSCODING.HLS.ENABLED) { |
167 | const hlsTranscodingPayload = { | 194 | const hlsTranscodingPayload = { |
195 | type: 'hls' as 'hls', | ||
168 | videoUUID: payload.videoUUID, | 196 | videoUUID: payload.videoUUID, |
169 | resolution: payload.resolution, | 197 | resolution: payload.resolution, |
170 | isPortraitMode: payload.isPortraitMode, | 198 | isPortraitMode: payload.isPortraitMode |
171 | |||
172 | generateHlsPlaylist: true | ||
173 | } | 199 | } |
174 | 200 | ||
175 | return JobQueue.Instance.createJob({ type: 'video-transcoding', payload: hlsTranscodingPayload }) | 201 | return JobQueue.Instance.createJob({ type: 'video-transcoding', payload: hlsTranscodingPayload }) |
diff --git a/server/lib/thumbnail.ts b/server/lib/thumbnail.ts index 950b14c3b..18bdcded4 100644 --- a/server/lib/thumbnail.ts +++ b/server/lib/thumbnail.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { VideoFileModel } from '../models/video/video-file' | 1 | import { VideoFileModel } from '../models/video/video-file' |
2 | import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils' | 2 | import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils' |
3 | import { CONFIG } from '../initializers/config' | 3 | import { CONFIG } from '../initializers/config' |
4 | import { PREVIEWS_SIZE, THUMBNAILS_SIZE } from '../initializers/constants' | 4 | import { PREVIEWS_SIZE, THUMBNAILS_SIZE, ASSETS_PATH } from '../initializers/constants' |
5 | import { VideoModel } from '../models/video/video' | 5 | import { VideoModel } from '../models/video/video' |
6 | import { ThumbnailModel } from '../models/video/thumbnail' | 6 | import { ThumbnailModel } from '../models/video/thumbnail' |
7 | import { ThumbnailType } from '../../shared/models/videos/thumbnail.type' | 7 | import { ThumbnailType } from '../../shared/models/videos/thumbnail.type' |
@@ -45,8 +45,10 @@ function createVideoMiniatureFromExisting (inputPath: string, video: VideoModel, | |||
45 | function generateVideoMiniature (video: VideoModel, videoFile: VideoFileModel, type: ThumbnailType) { | 45 | function generateVideoMiniature (video: VideoModel, videoFile: VideoFileModel, type: ThumbnailType) { |
46 | const input = video.getVideoFilePath(videoFile) | 46 | const input = video.getVideoFilePath(videoFile) |
47 | 47 | ||
48 | const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromVideo(video, type) | 48 | const { filename, basePath, height, width, existingThumbnail, outputPath } = buildMetadataFromVideo(video, type) |
49 | const thumbnailCreator = () => generateImageFromVideoFile(input, basePath, filename, { height, width }) | 49 | const thumbnailCreator = videoFile.isAudio() |
50 | ? () => processImage(ASSETS_PATH.DEFAULT_AUDIO_BACKGROUND, outputPath, { width, height }, true) | ||
51 | : () => generateImageFromVideoFile(input, basePath, filename, { height, width }) | ||
50 | 52 | ||
51 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail }) | 53 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail }) |
52 | } | 54 | } |
diff --git a/server/lib/video-transcoding.ts b/server/lib/video-transcoding.ts index 0fe0ff12a..8d786e0ef 100644 --- a/server/lib/video-transcoding.ts +++ b/server/lib/video-transcoding.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION, WEBSERVER } from '../initializers/constants' | 1 | import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION, WEBSERVER } from '../initializers/constants' |
2 | import { join } from 'path' | 2 | import { join } from 'path' |
3 | import { getVideoFileFPS, transcode } from '../helpers/ffmpeg-utils' | 3 | import { canDoQuickTranscode, getVideoFileFPS, transcode, TranscodeOptions, TranscodeOptionsType } from '../helpers/ffmpeg-utils' |
4 | import { ensureDir, move, remove, stat } from 'fs-extra' | 4 | import { ensureDir, move, remove, stat } from 'fs-extra' |
5 | import { logger } from '../helpers/logger' | 5 | import { logger } from '../helpers/logger' |
6 | import { VideoResolution } from '../../shared/models/videos' | 6 | import { VideoResolution } from '../../shared/models/videos' |
@@ -11,15 +11,24 @@ import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-pla | |||
11 | import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type' | 11 | import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type' |
12 | import { CONFIG } from '../initializers/config' | 12 | import { CONFIG } from '../initializers/config' |
13 | 13 | ||
14 | /** | ||
15 | * Optimize the original video file and replace it. The resolution is not changed. | ||
16 | */ | ||
14 | async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFileModel) { | 17 | async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFileModel) { |
15 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR | 18 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR |
19 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR | ||
16 | const newExtname = '.mp4' | 20 | const newExtname = '.mp4' |
17 | 21 | ||
18 | const inputVideoFile = inputVideoFileArg ? inputVideoFileArg : video.getOriginalFile() | 22 | const inputVideoFile = inputVideoFileArg ? inputVideoFileArg : video.getOriginalFile() |
19 | const videoInputPath = join(videosDirectory, video.getVideoFilename(inputVideoFile)) | 23 | const videoInputPath = join(videosDirectory, video.getVideoFilename(inputVideoFile)) |
20 | const videoTranscodedPath = join(videosDirectory, video.id + '-transcoded' + newExtname) | 24 | const videoTranscodedPath = join(transcodeDirectory, video.id + '-transcoded' + newExtname) |
21 | 25 | ||
22 | const transcodeOptions = { | 26 | const transcodeType: TranscodeOptionsType = await canDoQuickTranscode(videoInputPath) |
27 | ? 'quick-transcode' | ||
28 | : 'video' | ||
29 | |||
30 | const transcodeOptions: TranscodeOptions = { | ||
31 | type: transcodeType as any, // FIXME: typing issue | ||
23 | inputPath: videoInputPath, | 32 | inputPath: videoInputPath, |
24 | outputPath: videoTranscodedPath, | 33 | outputPath: videoTranscodedPath, |
25 | resolution: inputVideoFile.resolution | 34 | resolution: inputVideoFile.resolution |
@@ -32,18 +41,11 @@ async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFi | |||
32 | await remove(videoInputPath) | 41 | await remove(videoInputPath) |
33 | 42 | ||
34 | // Important to do this before getVideoFilename() to take in account the new file extension | 43 | // Important to do this before getVideoFilename() to take in account the new file extension |
35 | inputVideoFile.set('extname', newExtname) | 44 | inputVideoFile.extname = newExtname |
36 | 45 | ||
37 | const videoOutputPath = video.getVideoFilePath(inputVideoFile) | 46 | const videoOutputPath = video.getVideoFilePath(inputVideoFile) |
38 | await move(videoTranscodedPath, videoOutputPath) | ||
39 | const stats = await stat(videoOutputPath) | ||
40 | const fps = await getVideoFileFPS(videoOutputPath) | ||
41 | 47 | ||
42 | inputVideoFile.set('size', stats.size) | 48 | await onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath) |
43 | inputVideoFile.set('fps', fps) | ||
44 | |||
45 | await video.createTorrentAndSetInfoHash(inputVideoFile) | ||
46 | await inputVideoFile.save() | ||
47 | } catch (err) { | 49 | } catch (err) { |
48 | // Auto destruction... | 50 | // Auto destruction... |
49 | video.destroy().catch(err => logger.error('Cannot destruct video after transcoding failure.', { err })) | 51 | video.destroy().catch(err => logger.error('Cannot destruct video after transcoding failure.', { err })) |
@@ -52,8 +54,12 @@ async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFi | |||
52 | } | 54 | } |
53 | } | 55 | } |
54 | 56 | ||
57 | /** | ||
58 | * Transcode the original video file to a lower resolution. | ||
59 | */ | ||
55 | async function transcodeOriginalVideofile (video: VideoModel, resolution: VideoResolution, isPortrait: boolean) { | 60 | async function transcodeOriginalVideofile (video: VideoModel, resolution: VideoResolution, isPortrait: boolean) { |
56 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR | 61 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR |
62 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR | ||
57 | const extname = '.mp4' | 63 | const extname = '.mp4' |
58 | 64 | ||
59 | // We are sure it's x264 in mp4 because optimizeOriginalVideofile was already executed | 65 | // We are sure it's x264 in mp4 because optimizeOriginalVideofile was already executed |
@@ -66,27 +72,49 @@ async function transcodeOriginalVideofile (video: VideoModel, resolution: VideoR | |||
66 | videoId: video.id | 72 | videoId: video.id |
67 | }) | 73 | }) |
68 | const videoOutputPath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename(newVideoFile)) | 74 | const videoOutputPath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename(newVideoFile)) |
75 | const videoTranscodedPath = join(transcodeDirectory, video.getVideoFilename(newVideoFile)) | ||
69 | 76 | ||
70 | const transcodeOptions = { | 77 | const transcodeOptions = { |
78 | type: 'video' as 'video', | ||
71 | inputPath: videoInputPath, | 79 | inputPath: videoInputPath, |
72 | outputPath: videoOutputPath, | 80 | outputPath: videoTranscodedPath, |
73 | resolution, | 81 | resolution, |
74 | isPortraitMode: isPortrait | 82 | isPortraitMode: isPortrait |
75 | } | 83 | } |
76 | 84 | ||
77 | await transcode(transcodeOptions) | 85 | await transcode(transcodeOptions) |
78 | 86 | ||
79 | const stats = await stat(videoOutputPath) | 87 | return onVideoFileTranscoding(video, newVideoFile, videoTranscodedPath, videoOutputPath) |
80 | const fps = await getVideoFileFPS(videoOutputPath) | 88 | } |
89 | |||
90 | async function mergeAudioVideofile (video: VideoModel, resolution: VideoResolution) { | ||
91 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR | ||
92 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR | ||
93 | const newExtname = '.mp4' | ||
94 | |||
95 | const inputVideoFile = video.getOriginalFile() | ||
81 | 96 | ||
82 | newVideoFile.set('size', stats.size) | 97 | const audioInputPath = join(videosDirectory, video.getVideoFilename(video.getOriginalFile())) |
83 | newVideoFile.set('fps', fps) | 98 | const videoTranscodedPath = join(transcodeDirectory, video.id + '-transcoded' + newExtname) |
84 | 99 | ||
85 | await video.createTorrentAndSetInfoHash(newVideoFile) | 100 | const transcodeOptions = { |
101 | type: 'merge-audio' as 'merge-audio', | ||
102 | inputPath: video.getPreview().getPath(), | ||
103 | outputPath: videoTranscodedPath, | ||
104 | audioPath: audioInputPath, | ||
105 | resolution | ||
106 | } | ||
107 | |||
108 | await transcode(transcodeOptions) | ||
86 | 109 | ||
87 | await newVideoFile.save() | 110 | await remove(audioInputPath) |
88 | 111 | ||
89 | video.VideoFiles.push(newVideoFile) | 112 | // Important to do this before getVideoFilename() to take in account the new file extension |
113 | inputVideoFile.extname = newExtname | ||
114 | |||
115 | const videoOutputPath = video.getVideoFilePath(inputVideoFile) | ||
116 | |||
117 | return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath) | ||
90 | } | 118 | } |
91 | 119 | ||
92 | async function generateHlsPlaylist (video: VideoModel, resolution: VideoResolution, isPortraitMode: boolean) { | 120 | async function generateHlsPlaylist (video: VideoModel, resolution: VideoResolution, isPortraitMode: boolean) { |
@@ -97,6 +125,7 @@ async function generateHlsPlaylist (video: VideoModel, resolution: VideoResoluti | |||
97 | const outputPath = join(baseHlsDirectory, VideoStreamingPlaylistModel.getHlsPlaylistFilename(resolution)) | 125 | const outputPath = join(baseHlsDirectory, VideoStreamingPlaylistModel.getHlsPlaylistFilename(resolution)) |
98 | 126 | ||
99 | const transcodeOptions = { | 127 | const transcodeOptions = { |
128 | type: 'hls' as 'hls', | ||
100 | inputPath: videoInputPath, | 129 | inputPath: videoInputPath, |
101 | outputPath, | 130 | outputPath, |
102 | resolution, | 131 | resolution, |
@@ -125,8 +154,34 @@ async function generateHlsPlaylist (video: VideoModel, resolution: VideoResoluti | |||
125 | }) | 154 | }) |
126 | } | 155 | } |
127 | 156 | ||
157 | // --------------------------------------------------------------------------- | ||
158 | |||
128 | export { | 159 | export { |
129 | generateHlsPlaylist, | 160 | generateHlsPlaylist, |
130 | optimizeVideofile, | 161 | optimizeVideofile, |
131 | transcodeOriginalVideofile | 162 | transcodeOriginalVideofile, |
163 | mergeAudioVideofile | ||
164 | } | ||
165 | |||
166 | // --------------------------------------------------------------------------- | ||
167 | |||
168 | async function onVideoFileTranscoding (video: VideoModel, videoFile: VideoFileModel, transcodingPath: string, outputPath: string) { | ||
169 | const stats = await stat(transcodingPath) | ||
170 | const fps = await getVideoFileFPS(transcodingPath) | ||
171 | |||
172 | await move(transcodingPath, outputPath) | ||
173 | |||
174 | videoFile.set('size', stats.size) | ||
175 | videoFile.set('fps', fps) | ||
176 | |||
177 | await video.createTorrentAndSetInfoHash(videoFile) | ||
178 | |||
179 | const updatedVideoFile = await videoFile.save() | ||
180 | |||
181 | // Add it if this is a new created file | ||
182 | if (video.VideoFiles.some(f => f.id === videoFile.id) === false) { | ||
183 | video.VideoFiles.push(updatedVideoFile) | ||
184 | } | ||
185 | |||
186 | return video | ||
132 | } | 187 | } |
diff --git a/server/models/video/thumbnail.ts b/server/models/video/thumbnail.ts index 206e9a3d6..8faf0adba 100644 --- a/server/models/video/thumbnail.ts +++ b/server/models/video/thumbnail.ts | |||
@@ -107,10 +107,12 @@ export class ThumbnailModel extends Model<ThumbnailModel> { | |||
107 | return WEBSERVER.URL + staticPath + this.filename | 107 | return WEBSERVER.URL + staticPath + this.filename |
108 | } | 108 | } |
109 | 109 | ||
110 | removeThumbnail () { | 110 | getPath () { |
111 | const directory = ThumbnailModel.types[this.type].directory | 111 | const directory = ThumbnailModel.types[this.type].directory |
112 | const thumbnailPath = join(directory, this.filename) | 112 | return join(directory, this.filename) |
113 | } | ||
113 | 114 | ||
114 | return remove(thumbnailPath) | 115 | removeThumbnail () { |
116 | return remove(this.getPath()) | ||
115 | } | 117 | } |
116 | } | 118 | } |
diff --git a/server/models/video/video-file.ts b/server/models/video/video-file.ts index 2203a7aba..05c490759 100644 --- a/server/models/video/video-file.ts +++ b/server/models/video/video-file.ts | |||
@@ -24,6 +24,7 @@ import { VideoModel } from './video' | |||
24 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' | 24 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' |
25 | import { VideoStreamingPlaylistModel } from './video-streaming-playlist' | 25 | import { VideoStreamingPlaylistModel } from './video-streaming-playlist' |
26 | import { FindOptions, QueryTypes, Transaction } from 'sequelize' | 26 | import { FindOptions, QueryTypes, Transaction } from 'sequelize' |
27 | import { MIMETYPES } from '../../initializers/constants' | ||
27 | 28 | ||
28 | @Table({ | 29 | @Table({ |
29 | tableName: 'videoFile', | 30 | tableName: 'videoFile', |
@@ -161,6 +162,10 @@ export class VideoFileModel extends Model<VideoFileModel> { | |||
161 | })) | 162 | })) |
162 | } | 163 | } |
163 | 164 | ||
165 | isAudio () { | ||
166 | return !!MIMETYPES.AUDIO.EXT_MIMETYPE[this.extname] | ||
167 | } | ||
168 | |||
164 | hasSameUniqueKeysThan (other: VideoFileModel) { | 169 | hasSameUniqueKeysThan (other: VideoFileModel) { |
165 | return this.fps === other.fps && | 170 | return this.fps === other.fps && |
166 | this.resolution === other.resolution && | 171 | this.resolution === other.resolution && |
diff --git a/server/tests/api/activitypub/client.ts b/server/tests/api/activitypub/client.ts index edf588c16..34c6be49b 100644 --- a/server/tests/api/activitypub/client.ts +++ b/server/tests/api/activitypub/client.ts | |||
@@ -3,6 +3,7 @@ | |||
3 | import * as chai from 'chai' | 3 | import * as chai from 'chai' |
4 | import 'mocha' | 4 | import 'mocha' |
5 | import { | 5 | import { |
6 | cleanupTests, | ||
6 | doubleFollow, | 7 | doubleFollow, |
7 | flushAndRunMultipleServers, | 8 | flushAndRunMultipleServers, |
8 | flushTests, | 9 | flushTests, |
@@ -39,7 +40,7 @@ describe('Test activitypub', function () { | |||
39 | const object = res.body | 40 | const object = res.body |
40 | 41 | ||
41 | expect(object.type).to.equal('Person') | 42 | expect(object.type).to.equal('Person') |
42 | expect(object.id).to.equal('http://localhost:9001/accounts/root') | 43 | expect(object.id).to.equal('http://localhost:' + servers[0].port + '/accounts/root') |
43 | expect(object.name).to.equal('root') | 44 | expect(object.name).to.equal('root') |
44 | expect(object.preferredUsername).to.equal('root') | 45 | expect(object.preferredUsername).to.equal('root') |
45 | }) | 46 | }) |
@@ -49,17 +50,17 @@ describe('Test activitypub', function () { | |||
49 | const object = res.body | 50 | const object = res.body |
50 | 51 | ||
51 | expect(object.type).to.equal('Video') | 52 | expect(object.type).to.equal('Video') |
52 | expect(object.id).to.equal('http://localhost:9001/videos/watch/' + videoUUID) | 53 | expect(object.id).to.equal('http://localhost:' + servers[0].port + '/videos/watch/' + videoUUID) |
53 | expect(object.name).to.equal('video') | 54 | expect(object.name).to.equal('video') |
54 | }) | 55 | }) |
55 | 56 | ||
56 | it('Should redirect to the origin video object', async function () { | 57 | it('Should redirect to the origin video object', async function () { |
57 | const res = await makeActivityPubGetRequest(servers[1].url, '/videos/watch/' + videoUUID, 302) | 58 | const res = await makeActivityPubGetRequest(servers[1].url, '/videos/watch/' + videoUUID, 302) |
58 | 59 | ||
59 | expect(res.header.location).to.equal('http://localhost:9001/videos/watch/' + videoUUID) | 60 | expect(res.header.location).to.equal('http://localhost:' + servers[0].port + '/videos/watch/' + videoUUID) |
60 | }) | 61 | }) |
61 | 62 | ||
62 | after(function () { | 63 | after(async function () { |
63 | killallServers(servers) | 64 | await cleanupTests(servers) |
64 | }) | 65 | }) |
65 | }) | 66 | }) |
diff --git a/server/tests/api/activitypub/fetch.ts b/server/tests/api/activitypub/fetch.ts index 7240bb0fb..3a1c0d321 100644 --- a/server/tests/api/activitypub/fetch.ts +++ b/server/tests/api/activitypub/fetch.ts | |||
@@ -3,6 +3,7 @@ | |||
3 | import 'mocha' | 3 | import 'mocha' |
4 | 4 | ||
5 | import { | 5 | import { |
6 | cleanupTests, | ||
6 | closeAllSequelize, | 7 | closeAllSequelize, |
7 | createUser, | 8 | createUser, |
8 | doubleFollow, | 9 | doubleFollow, |
@@ -48,8 +49,16 @@ describe('Test ActivityPub fetcher', function () { | |||
48 | const badVideoUUID = res.body.video.uuid | 49 | const badVideoUUID = res.body.video.uuid |
49 | await uploadVideo(servers[0].url, userAccessToken, { name: 'video user' }) | 50 | await uploadVideo(servers[0].url, userAccessToken, { name: 'video user' }) |
50 | 51 | ||
51 | await setActorField(1, 'http://localhost:9001/accounts/user1', 'url', 'http://localhost:9002/accounts/user1') | 52 | { |
52 | await setVideoField(1, badVideoUUID, 'url', 'http://localhost:9003/videos/watch/' + badVideoUUID) | 53 | const to = 'http://localhost:' + servers[0].port + '/accounts/user1' |
54 | const value = 'http://localhost:' + servers[1].port + '/accounts/user1' | ||
55 | await setActorField(servers[0].internalServerNumber, to, 'url', value) | ||
56 | } | ||
57 | |||
58 | { | ||
59 | const value = 'http://localhost:' + servers[2].port + '/videos/watch/' + badVideoUUID | ||
60 | await setVideoField(servers[0].internalServerNumber, badVideoUUID, 'url', value) | ||
61 | } | ||
53 | }) | 62 | }) |
54 | 63 | ||
55 | it('Should add only the video with a valid actor URL', async function () { | 64 | it('Should add only the video with a valid actor URL', async function () { |
@@ -78,7 +87,9 @@ describe('Test ActivityPub fetcher', function () { | |||
78 | }) | 87 | }) |
79 | 88 | ||
80 | after(async function () { | 89 | after(async function () { |
81 | killallServers(servers) | 90 | this.timeout(10000) |
91 | |||
92 | await cleanupTests(servers) | ||
82 | 93 | ||
83 | await closeAllSequelize(servers) | 94 | await closeAllSequelize(servers) |
84 | }) | 95 | }) |
diff --git a/server/tests/api/activitypub/refresher.ts b/server/tests/api/activitypub/refresher.ts index 9be9aa495..921ee874c 100644 --- a/server/tests/api/activitypub/refresher.ts +++ b/server/tests/api/activitypub/refresher.ts | |||
@@ -2,13 +2,14 @@ | |||
2 | 2 | ||
3 | import 'mocha' | 3 | import 'mocha' |
4 | import { | 4 | import { |
5 | cleanupTests, closeAllSequelize, | ||
5 | createVideoPlaylist, | 6 | createVideoPlaylist, |
6 | doubleFollow, | 7 | doubleFollow, |
7 | flushAndRunMultipleServers, | 8 | flushAndRunMultipleServers, |
8 | generateUserAccessToken, | 9 | generateUserAccessToken, |
9 | getVideo, | 10 | getVideo, |
10 | getVideoPlaylist, | 11 | getVideoPlaylist, |
11 | killallServers, rateVideo, | 12 | killallServers, |
12 | reRunServer, | 13 | reRunServer, |
13 | ServerInfo, | 14 | ServerInfo, |
14 | setAccessTokensToServers, | 15 | setAccessTokensToServers, |
@@ -48,26 +49,26 @@ describe('Test AP refresher', function () { | |||
48 | } | 49 | } |
49 | 50 | ||
50 | { | 51 | { |
51 | const a1 = await generateUserAccessToken(servers[1], 'user1') | 52 | const a1 = await generateUserAccessToken(servers[ 1 ], 'user1') |
52 | await uploadVideo(servers[1].url, a1, { name: 'video4' }) | 53 | await uploadVideo(servers[ 1 ].url, a1, { name: 'video4' }) |
53 | 54 | ||
54 | const a2 = await generateUserAccessToken(servers[1], 'user2') | 55 | const a2 = await generateUserAccessToken(servers[ 1 ], 'user2') |
55 | await uploadVideo(servers[1].url, a2, { name: 'video5' }) | 56 | await uploadVideo(servers[ 1 ].url, a2, { name: 'video5' }) |
56 | } | 57 | } |
57 | 58 | ||
58 | { | 59 | { |
59 | const playlistAttrs = { displayName: 'playlist1', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[1].videoChannel.id } | 60 | const playlistAttrs = { displayName: 'playlist1', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[ 1 ].videoChannel.id } |
60 | const res = await createVideoPlaylist({ url: servers[1].url, token: servers[1].accessToken, playlistAttrs }) | 61 | const res = await createVideoPlaylist({ url: servers[ 1 ].url, token: servers[ 1 ].accessToken, playlistAttrs }) |
61 | playlistUUID1 = res.body.videoPlaylist.uuid | 62 | playlistUUID1 = res.body.videoPlaylist.uuid |
62 | } | 63 | } |
63 | 64 | ||
64 | { | 65 | { |
65 | const playlistAttrs = { displayName: 'playlist2', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[1].videoChannel.id } | 66 | const playlistAttrs = { displayName: 'playlist2', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[ 1 ].videoChannel.id } |
66 | const res = await createVideoPlaylist({ url: servers[1].url, token: servers[1].accessToken, playlistAttrs }) | 67 | const res = await createVideoPlaylist({ url: servers[ 1 ].url, token: servers[ 1 ].accessToken, playlistAttrs }) |
67 | playlistUUID2 = res.body.videoPlaylist.uuid | 68 | playlistUUID2 = res.body.videoPlaylist.uuid |
68 | } | 69 | } |
69 | 70 | ||
70 | await doubleFollow(servers[0], servers[1]) | 71 | await doubleFollow(servers[ 0 ], servers[ 1 ]) |
71 | }) | 72 | }) |
72 | 73 | ||
73 | describe('Videos refresher', function () { | 74 | describe('Videos refresher', function () { |
@@ -78,7 +79,7 @@ describe('Test AP refresher', function () { | |||
78 | await wait(10000) | 79 | await wait(10000) |
79 | 80 | ||
80 | // Change UUID so the remote server returns a 404 | 81 | // Change UUID so the remote server returns a 404 |
81 | await setVideoField(2, videoUUID1, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b174f') | 82 | await setVideoField(servers[ 1 ].internalServerNumber, videoUUID1, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b174f') |
82 | 83 | ||
83 | await getVideo(servers[ 0 ].url, videoUUID1) | 84 | await getVideo(servers[ 0 ].url, videoUUID1) |
84 | await getVideo(servers[ 0 ].url, videoUUID2) | 85 | await getVideo(servers[ 0 ].url, videoUUID2) |
@@ -94,7 +95,7 @@ describe('Test AP refresher', function () { | |||
94 | 95 | ||
95 | killallServers([ servers[ 1 ] ]) | 96 | killallServers([ servers[ 1 ] ]) |
96 | 97 | ||
97 | await setVideoField(2, videoUUID3, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b174e') | 98 | await setVideoField(servers[ 1 ].internalServerNumber, videoUUID3, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b174e') |
98 | 99 | ||
99 | // Video will need a refresh | 100 | // Video will need a refresh |
100 | await wait(10000) | 101 | await wait(10000) |
@@ -121,15 +122,16 @@ describe('Test AP refresher', function () { | |||
121 | await wait(10000) | 122 | await wait(10000) |
122 | 123 | ||
123 | // Change actor name so the remote server returns a 404 | 124 | // Change actor name so the remote server returns a 404 |
124 | await setActorField(2, 'http://localhost:9002/accounts/user2', 'preferredUsername', 'toto') | 125 | const to = 'http://localhost:' + servers[ 1 ].port + '/accounts/user2' |
126 | await setActorField(servers[ 1 ].internalServerNumber, to, 'preferredUsername', 'toto') | ||
125 | 127 | ||
126 | await getAccount(servers[ 0 ].url, 'user1@localhost:9002') | 128 | await getAccount(servers[ 0 ].url, 'user1@localhost:' + servers[ 1 ].port) |
127 | await getAccount(servers[ 0 ].url, 'user2@localhost:9002') | 129 | await getAccount(servers[ 0 ].url, 'user2@localhost:' + servers[ 1 ].port) |
128 | 130 | ||
129 | await waitJobs(servers) | 131 | await waitJobs(servers) |
130 | 132 | ||
131 | await getAccount(servers[ 0 ].url, 'user1@localhost:9002', 200) | 133 | await getAccount(servers[ 0 ].url, 'user1@localhost:' + servers[ 1 ].port, 200) |
132 | await getAccount(servers[ 0 ].url, 'user2@localhost:9002', 404) | 134 | await getAccount(servers[ 0 ].url, 'user2@localhost:' + servers[ 1 ].port, 404) |
133 | }) | 135 | }) |
134 | }) | 136 | }) |
135 | 137 | ||
@@ -141,7 +143,7 @@ describe('Test AP refresher', function () { | |||
141 | await wait(10000) | 143 | await wait(10000) |
142 | 144 | ||
143 | // Change UUID so the remote server returns a 404 | 145 | // Change UUID so the remote server returns a 404 |
144 | await setPlaylistField(2, playlistUUID2, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b178e') | 146 | await setPlaylistField(servers[ 1 ].internalServerNumber, playlistUUID2, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b178e') |
145 | 147 | ||
146 | await getVideoPlaylist(servers[ 0 ].url, playlistUUID1) | 148 | await getVideoPlaylist(servers[ 0 ].url, playlistUUID1) |
147 | await getVideoPlaylist(servers[ 0 ].url, playlistUUID2) | 149 | await getVideoPlaylist(servers[ 0 ].url, playlistUUID2) |
@@ -153,7 +155,11 @@ describe('Test AP refresher', function () { | |||
153 | }) | 155 | }) |
154 | }) | 156 | }) |
155 | 157 | ||
156 | after(function () { | 158 | after(async function () { |
157 | killallServers(servers) | 159 | this.timeout(10000) |
160 | |||
161 | await cleanupTests(servers) | ||
162 | |||
163 | await closeAllSequelize(servers) | ||
158 | }) | 164 | }) |
159 | }) | 165 | }) |
diff --git a/server/tests/api/activitypub/security.ts b/server/tests/api/activitypub/security.ts index 11e6859bf..dc960c5c3 100644 --- a/server/tests/api/activitypub/security.ts +++ b/server/tests/api/activitypub/security.ts | |||
@@ -3,9 +3,9 @@ | |||
3 | import 'mocha' | 3 | import 'mocha' |
4 | 4 | ||
5 | import { | 5 | import { |
6 | cleanupTests, | ||
6 | closeAllSequelize, | 7 | closeAllSequelize, |
7 | flushAndRunMultipleServers, | 8 | flushAndRunMultipleServers, |
8 | flushTests, | ||
9 | killallServers, | 9 | killallServers, |
10 | ServerInfo, | 10 | ServerInfo, |
11 | setActorField | 11 | setActorField |
@@ -18,18 +18,26 @@ import { makeFollowRequest, makePOSTAPRequest } from '../../../../shared/extra-u | |||
18 | 18 | ||
19 | const expect = chai.expect | 19 | const expect = chai.expect |
20 | 20 | ||
21 | function setKeysOfServer2 (serverNumber: number, publicKey: string, privateKey: string) { | 21 | function setKeysOfServer (onServer: ServerInfo, ofServer: ServerInfo, publicKey: string, privateKey: string) { |
22 | return Promise.all([ | 22 | return Promise.all([ |
23 | setActorField(serverNumber, 'http://localhost:9002/accounts/peertube', 'publicKey', publicKey), | 23 | setActorField(onServer.internalServerNumber, 'http://localhost:' + ofServer.port + '/accounts/peertube', 'publicKey', publicKey), |
24 | setActorField(serverNumber, 'http://localhost:9002/accounts/peertube', 'privateKey', privateKey) | 24 | setActorField(onServer.internalServerNumber, 'http://localhost:' + ofServer.port + '/accounts/peertube', 'privateKey', privateKey) |
25 | ]) | 25 | ]) |
26 | } | 26 | } |
27 | 27 | ||
28 | function setKeysOfServer3 (serverNumber: number, publicKey: string, privateKey: string) { | 28 | function getAnnounceWithoutContext (server2: ServerInfo) { |
29 | return Promise.all([ | 29 | const json = require('./json/peertube/announce-without-context.json') |
30 | setActorField(serverNumber, 'http://localhost:9003/accounts/peertube', 'publicKey', publicKey), | 30 | const result: typeof json = {} |
31 | setActorField(serverNumber, 'http://localhost:9003/accounts/peertube', 'privateKey', privateKey) | 31 | |
32 | ]) | 32 | for (const key of Object.keys(json)) { |
33 | if (Array.isArray(json[key])) { | ||
34 | result[key] = json[key].map(v => v.replace(':9002', `:${server2.port}`)) | ||
35 | } else { | ||
36 | result[ key ] = json[ key ].replace(':9002', `:${server2.port}`) | ||
37 | } | ||
38 | } | ||
39 | |||
40 | return result | ||
33 | } | 41 | } |
34 | 42 | ||
35 | describe('Test ActivityPub security', function () { | 43 | describe('Test ActivityPub security', function () { |
@@ -38,13 +46,13 @@ describe('Test ActivityPub security', function () { | |||
38 | 46 | ||
39 | const keys = require('./json/peertube/keys.json') | 47 | const keys = require('./json/peertube/keys.json') |
40 | const invalidKeys = require('./json/peertube/invalid-keys.json') | 48 | const invalidKeys = require('./json/peertube/invalid-keys.json') |
41 | const baseHttpSignature = { | 49 | const baseHttpSignature = () => ({ |
42 | algorithm: HTTP_SIGNATURE.ALGORITHM, | 50 | algorithm: HTTP_SIGNATURE.ALGORITHM, |
43 | authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME, | 51 | authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME, |
44 | keyId: 'acct:peertube@localhost:9002', | 52 | keyId: 'acct:peertube@localhost:' + servers[1].port, |
45 | key: keys.privateKey, | 53 | key: keys.privateKey, |
46 | headers: HTTP_SIGNATURE.HEADERS_TO_SIGN | 54 | headers: HTTP_SIGNATURE.HEADERS_TO_SIGN |
47 | } | 55 | }) |
48 | 56 | ||
49 | // --------------------------------------------------------------- | 57 | // --------------------------------------------------------------- |
50 | 58 | ||
@@ -55,56 +63,56 @@ describe('Test ActivityPub security', function () { | |||
55 | 63 | ||
56 | url = servers[0].url + '/inbox' | 64 | url = servers[0].url + '/inbox' |
57 | 65 | ||
58 | await setKeysOfServer2(1, keys.publicKey, keys.privateKey) | 66 | await setKeysOfServer(servers[0], servers[1], keys.publicKey, keys.privateKey) |
59 | 67 | ||
60 | const to = { url: 'http://localhost:9001/accounts/peertube' } | 68 | const to = { url: 'http://localhost:' + servers[0].port + '/accounts/peertube' } |
61 | const by = { url: 'http://localhost:9002/accounts/peertube', privateKey: keys.privateKey } | 69 | const by = { url: 'http://localhost:' + servers[1].port + '/accounts/peertube', privateKey: keys.privateKey } |
62 | await makeFollowRequest(to, by) | 70 | await makeFollowRequest(to, by) |
63 | }) | 71 | }) |
64 | 72 | ||
65 | describe('When checking HTTP signature', function () { | 73 | describe('When checking HTTP signature', function () { |
66 | 74 | ||
67 | it('Should fail with an invalid digest', async function () { | 75 | it('Should fail with an invalid digest', async function () { |
68 | const body = activityPubContextify(require('./json/peertube/announce-without-context.json')) | 76 | const body = activityPubContextify(getAnnounceWithoutContext(servers[1])) |
69 | const headers = { | 77 | const headers = { |
70 | Digest: buildDigest({ hello: 'coucou' }) | 78 | Digest: buildDigest({ hello: 'coucou' }) |
71 | } | 79 | } |
72 | 80 | ||
73 | const { response } = await makePOSTAPRequest(url, body, baseHttpSignature, headers) | 81 | const { response } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers) |
74 | 82 | ||
75 | expect(response.statusCode).to.equal(403) | 83 | expect(response.statusCode).to.equal(403) |
76 | }) | 84 | }) |
77 | 85 | ||
78 | it('Should fail with an invalid date', async function () { | 86 | it('Should fail with an invalid date', async function () { |
79 | const body = activityPubContextify(require('./json/peertube/announce-without-context.json')) | 87 | const body = activityPubContextify(getAnnounceWithoutContext(servers[1])) |
80 | const headers = buildGlobalHeaders(body) | 88 | const headers = buildGlobalHeaders(body) |
81 | headers['date'] = 'Wed, 21 Oct 2015 07:28:00 GMT' | 89 | headers['date'] = 'Wed, 21 Oct 2015 07:28:00 GMT' |
82 | 90 | ||
83 | const { response } = await makePOSTAPRequest(url, body, baseHttpSignature, headers) | 91 | const { response } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers) |
84 | 92 | ||
85 | expect(response.statusCode).to.equal(403) | 93 | expect(response.statusCode).to.equal(403) |
86 | }) | 94 | }) |
87 | 95 | ||
88 | it('Should fail with bad keys', async function () { | 96 | it('Should fail with bad keys', async function () { |
89 | await setKeysOfServer2(1, invalidKeys.publicKey, invalidKeys.privateKey) | 97 | await setKeysOfServer(servers[0], servers[1], invalidKeys.publicKey, invalidKeys.privateKey) |
90 | await setKeysOfServer2(2, invalidKeys.publicKey, invalidKeys.privateKey) | 98 | await setKeysOfServer(servers[1], servers[1], invalidKeys.publicKey, invalidKeys.privateKey) |
91 | 99 | ||
92 | const body = activityPubContextify(require('./json/peertube/announce-without-context.json')) | 100 | const body = activityPubContextify(getAnnounceWithoutContext(servers[1])) |
93 | const headers = buildGlobalHeaders(body) | 101 | const headers = buildGlobalHeaders(body) |
94 | 102 | ||
95 | const { response } = await makePOSTAPRequest(url, body, baseHttpSignature, headers) | 103 | const { response } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers) |
96 | 104 | ||
97 | expect(response.statusCode).to.equal(403) | 105 | expect(response.statusCode).to.equal(403) |
98 | }) | 106 | }) |
99 | 107 | ||
100 | it('Should succeed with a valid HTTP signature', async function () { | 108 | it('Should succeed with a valid HTTP signature', async function () { |
101 | await setKeysOfServer2(1, keys.publicKey, keys.privateKey) | 109 | await setKeysOfServer(servers[0], servers[1], keys.publicKey, keys.privateKey) |
102 | await setKeysOfServer2(2, keys.publicKey, keys.privateKey) | 110 | await setKeysOfServer(servers[1], servers[1], keys.publicKey, keys.privateKey) |
103 | 111 | ||
104 | const body = activityPubContextify(require('./json/peertube/announce-without-context.json')) | 112 | const body = activityPubContextify(getAnnounceWithoutContext(servers[1])) |
105 | const headers = buildGlobalHeaders(body) | 113 | const headers = buildGlobalHeaders(body) |
106 | 114 | ||
107 | const { response } = await makePOSTAPRequest(url, body, baseHttpSignature, headers) | 115 | const { response } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers) |
108 | 116 | ||
109 | expect(response.statusCode).to.equal(204) | 117 | expect(response.statusCode).to.equal(204) |
110 | }) | 118 | }) |
@@ -112,28 +120,28 @@ describe('Test ActivityPub security', function () { | |||
112 | 120 | ||
113 | describe('When checking Linked Data Signature', function () { | 121 | describe('When checking Linked Data Signature', function () { |
114 | before(async () => { | 122 | before(async () => { |
115 | await setKeysOfServer3(3, keys.publicKey, keys.privateKey) | 123 | await setKeysOfServer(servers[2], servers[2], keys.publicKey, keys.privateKey) |
116 | 124 | ||
117 | const to = { url: 'http://localhost:9001/accounts/peertube' } | 125 | const to = { url: 'http://localhost:' + servers[0].port + '/accounts/peertube' } |
118 | const by = { url: 'http://localhost:9003/accounts/peertube', privateKey: keys.privateKey } | 126 | const by = { url: 'http://localhost:' + servers[2].port + '/accounts/peertube', privateKey: keys.privateKey } |
119 | await makeFollowRequest(to, by) | 127 | await makeFollowRequest(to, by) |
120 | }) | 128 | }) |
121 | 129 | ||
122 | it('Should fail with bad keys', async function () { | 130 | it('Should fail with bad keys', async function () { |
123 | this.timeout(10000) | 131 | this.timeout(10000) |
124 | 132 | ||
125 | await setKeysOfServer3(1, invalidKeys.publicKey, invalidKeys.privateKey) | 133 | await setKeysOfServer(servers[0], servers[2], invalidKeys.publicKey, invalidKeys.privateKey) |
126 | await setKeysOfServer3(3, invalidKeys.publicKey, invalidKeys.privateKey) | 134 | await setKeysOfServer(servers[2], servers[2], invalidKeys.publicKey, invalidKeys.privateKey) |
127 | 135 | ||
128 | const body = require('./json/peertube/announce-without-context.json') | 136 | const body = getAnnounceWithoutContext(servers[1]) |
129 | body.actor = 'http://localhost:9003/accounts/peertube' | 137 | body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube' |
130 | 138 | ||
131 | const signer: any = { privateKey: invalidKeys.privateKey, url: 'http://localhost:9003/accounts/peertube' } | 139 | const signer: any = { privateKey: invalidKeys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' } |
132 | const signedBody = await buildSignedActivity(signer, body) | 140 | const signedBody = await buildSignedActivity(signer, body) |
133 | 141 | ||
134 | const headers = buildGlobalHeaders(signedBody) | 142 | const headers = buildGlobalHeaders(signedBody) |
135 | 143 | ||
136 | const { response } = await makePOSTAPRequest(url, signedBody, baseHttpSignature, headers) | 144 | const { response } = await makePOSTAPRequest(url, signedBody, baseHttpSignature(), headers) |
137 | 145 | ||
138 | expect(response.statusCode).to.equal(403) | 146 | expect(response.statusCode).to.equal(403) |
139 | }) | 147 | }) |
@@ -141,20 +149,20 @@ describe('Test ActivityPub security', function () { | |||
141 | it('Should fail with an altered body', async function () { | 149 | it('Should fail with an altered body', async function () { |
142 | this.timeout(10000) | 150 | this.timeout(10000) |
143 | 151 | ||
144 | await setKeysOfServer3(1, keys.publicKey, keys.privateKey) | 152 | await setKeysOfServer(servers[0], servers[2], keys.publicKey, keys.privateKey) |
145 | await setKeysOfServer3(3, keys.publicKey, keys.privateKey) | 153 | await setKeysOfServer(servers[0], servers[2], keys.publicKey, keys.privateKey) |
146 | 154 | ||
147 | const body = require('./json/peertube/announce-without-context.json') | 155 | const body = getAnnounceWithoutContext(servers[1]) |
148 | body.actor = 'http://localhost:9003/accounts/peertube' | 156 | body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube' |
149 | 157 | ||
150 | const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:9003/accounts/peertube' } | 158 | const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' } |
151 | const signedBody = await buildSignedActivity(signer, body) | 159 | const signedBody = await buildSignedActivity(signer, body) |
152 | 160 | ||
153 | signedBody.actor = 'http://localhost:9003/account/peertube' | 161 | signedBody.actor = 'http://localhost:' + servers[2].port + '/account/peertube' |
154 | 162 | ||
155 | const headers = buildGlobalHeaders(signedBody) | 163 | const headers = buildGlobalHeaders(signedBody) |
156 | 164 | ||
157 | const { response } = await makePOSTAPRequest(url, signedBody, baseHttpSignature, headers) | 165 | const { response } = await makePOSTAPRequest(url, signedBody, baseHttpSignature(), headers) |
158 | 166 | ||
159 | expect(response.statusCode).to.equal(403) | 167 | expect(response.statusCode).to.equal(403) |
160 | }) | 168 | }) |
@@ -162,22 +170,24 @@ describe('Test ActivityPub security', function () { | |||
162 | it('Should succeed with a valid signature', async function () { | 170 | it('Should succeed with a valid signature', async function () { |
163 | this.timeout(10000) | 171 | this.timeout(10000) |
164 | 172 | ||
165 | const body = require('./json/peertube/announce-without-context.json') | 173 | const body = getAnnounceWithoutContext(servers[1]) |
166 | body.actor = 'http://localhost:9003/accounts/peertube' | 174 | body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube' |
167 | 175 | ||
168 | const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:9003/accounts/peertube' } | 176 | const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' } |
169 | const signedBody = await buildSignedActivity(signer, body) | 177 | const signedBody = await buildSignedActivity(signer, body) |
170 | 178 | ||
171 | const headers = buildGlobalHeaders(signedBody) | 179 | const headers = buildGlobalHeaders(signedBody) |
172 | 180 | ||
173 | const { response } = await makePOSTAPRequest(url, signedBody, baseHttpSignature, headers) | 181 | const { response } = await makePOSTAPRequest(url, signedBody, baseHttpSignature(), headers) |
174 | 182 | ||
175 | expect(response.statusCode).to.equal(204) | 183 | expect(response.statusCode).to.equal(204) |
176 | }) | 184 | }) |
177 | }) | 185 | }) |
178 | 186 | ||
179 | after(async function () { | 187 | after(async function () { |
180 | killallServers(servers) | 188 | this.timeout(10000) |
189 | |||
190 | await cleanupTests(servers) | ||
181 | 191 | ||
182 | await closeAllSequelize(servers) | 192 | await closeAllSequelize(servers) |
183 | }) | 193 | }) |
diff --git a/server/tests/api/check-params/config.ts b/server/tests/api/check-params/config.ts index 2a2ec606a..8155e11ab 100644 --- a/server/tests/api/check-params/config.ts +++ b/server/tests/api/check-params/config.ts | |||
@@ -59,6 +59,7 @@ describe('Test config API validators', function () { | |||
59 | transcoding: { | 59 | transcoding: { |
60 | enabled: true, | 60 | enabled: true, |
61 | allowAdditionalExtensions: true, | 61 | allowAdditionalExtensions: true, |
62 | allowAudioFiles: true, | ||
62 | threads: 1, | 63 | threads: 1, |
63 | resolutions: { | 64 | resolutions: { |
64 | '240p': false, | 65 | '240p': false, |
diff --git a/server/tests/api/index-1.ts b/server/tests/api/index-1.ts deleted file mode 100644 index 75cdd9025..000000000 --- a/server/tests/api/index-1.ts +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | import './check-params' | ||
2 | import './notifications' | ||
3 | import './search' | ||
diff --git a/server/tests/api/index-2.ts b/server/tests/api/index-2.ts deleted file mode 100644 index ed93faa91..000000000 --- a/server/tests/api/index-2.ts +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | import './server' | ||
2 | import './users' | ||
diff --git a/server/tests/api/index-3.ts b/server/tests/api/index-3.ts deleted file mode 100644 index 39823b82c..000000000 --- a/server/tests/api/index-3.ts +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | import './videos' | ||
diff --git a/server/tests/api/index-4.ts b/server/tests/api/index-4.ts deleted file mode 100644 index 7d8be2b3d..000000000 --- a/server/tests/api/index-4.ts +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | import './redundancy' | ||
2 | import './activitypub' | ||
diff --git a/server/tests/api/index.ts b/server/tests/api/index.ts index bc140f860..bac77ab2e 100644 --- a/server/tests/api/index.ts +++ b/server/tests/api/index.ts | |||
@@ -1,5 +1,9 @@ | |||
1 | // Order of the tests we want to execute | 1 | // Order of the tests we want to execute |
2 | import './index-1' | 2 | import './activitypub' |
3 | import './index-2' | 3 | import './check-params' |
4 | import './index-3' | 4 | import './notifications' |
5 | import './index-4' | 5 | import './redundancy' |
6 | import './search' | ||
7 | import './server' | ||
8 | import './users' | ||
9 | import './videos' | ||
diff --git a/server/tests/api/notifications/index.ts b/server/tests/api/notifications/index.ts index 95ac8fc51..b573f850e 100644 --- a/server/tests/api/notifications/index.ts +++ b/server/tests/api/notifications/index.ts | |||
@@ -1 +1 @@ | |||
export * from './user-notifications' | import './user-notifications' | ||
diff --git a/server/tests/api/notifications/user-notifications.ts b/server/tests/api/notifications/user-notifications.ts index f479e1785..662b64e05 100644 --- a/server/tests/api/notifications/user-notifications.ts +++ b/server/tests/api/notifications/user-notifications.ts | |||
@@ -114,11 +114,12 @@ describe('Test users notifications', function () { | |||
114 | before(async function () { | 114 | before(async function () { |
115 | this.timeout(120000) | 115 | this.timeout(120000) |
116 | 116 | ||
117 | await MockSmtpServer.Instance.collectEmails(emails) | 117 | const port = await MockSmtpServer.Instance.collectEmails(emails) |
118 | 118 | ||
119 | const overrideConfig = { | 119 | const overrideConfig = { |
120 | smtp: { | 120 | smtp: { |
121 | hostname: 'localhost' | 121 | hostname: 'localhost', |
122 | port | ||
122 | } | 123 | } |
123 | } | 124 | } |
124 | servers = await flushAndRunMultipleServers(3, overrideConfig) | 125 | servers = await flushAndRunMultipleServers(3, overrideConfig) |
@@ -194,7 +195,7 @@ describe('Test users notifications', function () { | |||
194 | it('Should send a new video notification if the user follows the local video publisher', async function () { | 195 | it('Should send a new video notification if the user follows the local video publisher', async function () { |
195 | this.timeout(15000) | 196 | this.timeout(15000) |
196 | 197 | ||
197 | await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:9001') | 198 | await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:' + servers[0].port) |
198 | await waitJobs(servers) | 199 | await waitJobs(servers) |
199 | 200 | ||
200 | const { name, uuid } = await uploadVideoByLocalAccount(servers) | 201 | const { name, uuid } = await uploadVideoByLocalAccount(servers) |
@@ -204,7 +205,7 @@ describe('Test users notifications', function () { | |||
204 | it('Should send a new video notification from a remote account', async function () { | 205 | it('Should send a new video notification from a remote account', async function () { |
205 | this.timeout(50000) // Server 2 has transcoding enabled | 206 | this.timeout(50000) // Server 2 has transcoding enabled |
206 | 207 | ||
207 | await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:9002') | 208 | await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:' + servers[1].port) |
208 | await waitJobs(servers) | 209 | await waitJobs(servers) |
209 | 210 | ||
210 | const { name, uuid } = await uploadVideoByRemoteAccount(servers) | 211 | const { name, uuid } = await uploadVideoByRemoteAccount(servers) |
@@ -578,7 +579,9 @@ describe('Test users notifications', function () { | |||
578 | const uuid = resVideo.body.video.uuid | 579 | const uuid = resVideo.body.video.uuid |
579 | 580 | ||
580 | await waitJobs(servers) | 581 | await waitJobs(servers) |
581 | const resThread = await addVideoCommentThread(servers[1].url, servers[1].accessToken, uuid, 'hello @user_1@localhost:9001 1') | 582 | |
583 | const text1 = `hello @user_1@localhost:${servers[ 0 ].port} 1` | ||
584 | const resThread = await addVideoCommentThread(servers[1].url, servers[1].accessToken, uuid, text1) | ||
582 | const server2ThreadId = resThread.body.comment.id | 585 | const server2ThreadId = resThread.body.comment.id |
583 | 586 | ||
584 | await waitJobs(servers) | 587 | await waitJobs(servers) |
@@ -588,8 +591,8 @@ describe('Test users notifications', function () { | |||
588 | const server1ThreadId = resThread2.body.data[0].id | 591 | const server1ThreadId = resThread2.body.data[0].id |
589 | await checkCommentMention(baseParams, uuid, server1ThreadId, server1ThreadId, 'super root 2 name', 'presence') | 592 | await checkCommentMention(baseParams, uuid, server1ThreadId, server1ThreadId, 'super root 2 name', 'presence') |
590 | 593 | ||
591 | const text = '@user_1@localhost:9001 hello 2 @root@localhost:9001' | 594 | const text2 = `@user_1@localhost:${servers[ 0 ].port} hello 2 @root@localhost:${servers[ 0 ].port}` |
592 | await addVideoCommentReply(servers[1].url, servers[1].accessToken, uuid, server2ThreadId, text) | 595 | await addVideoCommentReply(servers[1].url, servers[1].accessToken, uuid, server2ThreadId, text2) |
593 | 596 | ||
594 | await waitJobs(servers) | 597 | await waitJobs(servers) |
595 | 598 | ||
@@ -889,10 +892,10 @@ describe('Test users notifications', function () { | |||
889 | 892 | ||
890 | await waitJobs(servers) | 893 | await waitJobs(servers) |
891 | 894 | ||
892 | await checkNewInstanceFollower(baseParams, 'localhost:9003', 'presence') | 895 | await checkNewInstanceFollower(baseParams, 'localhost:' + servers[2].port, 'presence') |
893 | 896 | ||
894 | const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } } | 897 | const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } } |
895 | await checkNewInstanceFollower(immutableAssign(baseParams, userOverride), 'localhost:9003', 'absence') | 898 | await checkNewInstanceFollower(immutableAssign(baseParams, userOverride), 'localhost:' + servers[2].port, 'absence') |
896 | }) | 899 | }) |
897 | }) | 900 | }) |
898 | 901 | ||
@@ -933,29 +936,29 @@ describe('Test users notifications', function () { | |||
933 | it('Should notify when a local channel is following one of our channel', async function () { | 936 | it('Should notify when a local channel is following one of our channel', async function () { |
934 | this.timeout(10000) | 937 | this.timeout(10000) |
935 | 938 | ||
936 | await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:9001') | 939 | await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port) |
937 | await waitJobs(servers) | 940 | await waitJobs(servers) |
938 | 941 | ||
939 | await checkNewActorFollow(baseParams, 'channel', 'root', 'super root name', myChannelName, 'presence') | 942 | await checkNewActorFollow(baseParams, 'channel', 'root', 'super root name', myChannelName, 'presence') |
940 | 943 | ||
941 | await removeUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:9001') | 944 | await removeUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port) |
942 | }) | 945 | }) |
943 | 946 | ||
944 | it('Should notify when a remote channel is following one of our channel', async function () { | 947 | it('Should notify when a remote channel is following one of our channel', async function () { |
945 | this.timeout(10000) | 948 | this.timeout(10000) |
946 | 949 | ||
947 | await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:9001') | 950 | await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port) |
948 | await waitJobs(servers) | 951 | await waitJobs(servers) |
949 | 952 | ||
950 | await checkNewActorFollow(baseParams, 'channel', 'root', 'super root 2 name', myChannelName, 'presence') | 953 | await checkNewActorFollow(baseParams, 'channel', 'root', 'super root 2 name', myChannelName, 'presence') |
951 | 954 | ||
952 | await removeUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:9001') | 955 | await removeUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port) |
953 | }) | 956 | }) |
954 | 957 | ||
955 | it('Should notify when a local account is following one of our channel', async function () { | 958 | it('Should notify when a local account is following one of our channel', async function () { |
956 | this.timeout(10000) | 959 | this.timeout(10000) |
957 | 960 | ||
958 | await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1@localhost:9001') | 961 | await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1@localhost:' + servers[0].port) |
959 | 962 | ||
960 | await waitJobs(servers) | 963 | await waitJobs(servers) |
961 | 964 | ||
@@ -965,7 +968,7 @@ describe('Test users notifications', function () { | |||
965 | it('Should notify when a remote account is following one of our channel', async function () { | 968 | it('Should notify when a remote account is following one of our channel', async function () { |
966 | this.timeout(10000) | 969 | this.timeout(10000) |
967 | 970 | ||
968 | await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1@localhost:9001') | 971 | await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1@localhost:' + servers[0].port) |
969 | 972 | ||
970 | await waitJobs(servers) | 973 | await waitJobs(servers) |
971 | 974 | ||
@@ -1019,8 +1022,8 @@ describe('Test users notifications', function () { | |||
1019 | autoBlacklistTestsCustomConfig.transcoding.enabled = true | 1022 | autoBlacklistTestsCustomConfig.transcoding.enabled = true |
1020 | await updateCustomConfig(servers[0].url, servers[0].accessToken, autoBlacklistTestsCustomConfig) | 1023 | await updateCustomConfig(servers[0].url, servers[0].accessToken, autoBlacklistTestsCustomConfig) |
1021 | 1024 | ||
1022 | await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:9001') | 1025 | await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port) |
1023 | await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:9001') | 1026 | await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port) |
1024 | 1027 | ||
1025 | }) | 1028 | }) |
1026 | 1029 | ||
@@ -1142,8 +1145,8 @@ describe('Test users notifications', function () { | |||
1142 | after(async () => { | 1145 | after(async () => { |
1143 | await updateCustomConfig(servers[0].url, servers[0].accessToken, currentCustomConfig) | 1146 | await updateCustomConfig(servers[0].url, servers[0].accessToken, currentCustomConfig) |
1144 | 1147 | ||
1145 | await removeUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:9001') | 1148 | await removeUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port) |
1146 | await removeUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:9001') | 1149 | await removeUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port) |
1147 | }) | 1150 | }) |
1148 | }) | 1151 | }) |
1149 | 1152 | ||
diff --git a/server/tests/api/redundancy/redundancy.ts b/server/tests/api/redundancy/redundancy.ts index e31329c25..6f2c59076 100644 --- a/server/tests/api/redundancy/redundancy.ts +++ b/server/tests/api/redundancy/redundancy.ts | |||
@@ -100,7 +100,7 @@ async function check1WebSeed (videoUUID?: string) { | |||
100 | if (!videoUUID) videoUUID = video1Server2UUID | 100 | if (!videoUUID) videoUUID = video1Server2UUID |
101 | 101 | ||
102 | const webseeds = [ | 102 | const webseeds = [ |
103 | 'http://localhost:9002/static/webseed/' + videoUUID | 103 | `http://localhost:${servers[ 1 ].port}/static/webseed/${videoUUID}` |
104 | ] | 104 | ] |
105 | 105 | ||
106 | for (const server of servers) { | 106 | for (const server of servers) { |
@@ -118,8 +118,8 @@ async function check2Webseeds (videoUUID?: string) { | |||
118 | if (!videoUUID) videoUUID = video1Server2UUID | 118 | if (!videoUUID) videoUUID = video1Server2UUID |
119 | 119 | ||
120 | const webseeds = [ | 120 | const webseeds = [ |
121 | 'http://localhost:9001/static/redundancy/' + videoUUID, | 121 | `http://localhost:${servers[ 0 ].port}/static/redundancy/${videoUUID}`, |
122 | 'http://localhost:9002/static/webseed/' + videoUUID | 122 | `http://localhost:${servers[ 1 ].port}/static/webseed/${videoUUID}` |
123 | ] | 123 | ] |
124 | 124 | ||
125 | for (const server of servers) { | 125 | for (const server of servers) { |
@@ -145,7 +145,12 @@ async function check2Webseeds (videoUUID?: string) { | |||
145 | } | 145 | } |
146 | } | 146 | } |
147 | 147 | ||
148 | for (const directory of [ 'test1/redundancy', 'test2/videos' ]) { | 148 | const directories = [ |
149 | 'test' + servers[0].internalServerNumber + '/redundancy', | ||
150 | 'test' + servers[1].internalServerNumber + '/videos' | ||
151 | ] | ||
152 | |||
153 | for (const directory of directories) { | ||
149 | const files = await readdir(join(root(), directory)) | 154 | const files = await readdir(join(root(), directory)) |
150 | expect(files).to.have.length.at.least(4) | 155 | expect(files).to.have.length.at.least(4) |
151 | 156 | ||
@@ -194,7 +199,12 @@ async function check1PlaylistRedundancies (videoUUID?: string) { | |||
194 | await checkSegmentHash(baseUrlPlaylist, baseUrlSegment, videoUUID, resolution, hlsPlaylist) | 199 | await checkSegmentHash(baseUrlPlaylist, baseUrlSegment, videoUUID, resolution, hlsPlaylist) |
195 | } | 200 | } |
196 | 201 | ||
197 | for (const directory of [ 'test1/redundancy/hls', 'test2/streaming-playlists/hls' ]) { | 202 | const directories = [ |
203 | 'test' + servers[0].internalServerNumber + '/redundancy/hls', | ||
204 | 'test' + servers[1].internalServerNumber + '/streaming-playlists/hls' | ||
205 | ] | ||
206 | |||
207 | for (const directory of directories) { | ||
198 | const files = await readdir(join(root(), directory, videoUUID)) | 208 | const files = await readdir(join(root(), directory, videoUUID)) |
199 | expect(files).to.have.length.at.least(4) | 209 | expect(files).to.have.length.at.least(4) |
200 | 210 | ||
@@ -239,8 +249,8 @@ async function enableRedundancyOnServer1 () { | |||
239 | 249 | ||
240 | const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 5, '-createdAt') | 250 | const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 5, '-createdAt') |
241 | const follows: ActorFollow[] = res.body.data | 251 | const follows: ActorFollow[] = res.body.data |
242 | const server2 = follows.find(f => f.following.host === 'localhost:9002') | 252 | const server2 = follows.find(f => f.following.host === `localhost:${servers[ 1 ].port}`) |
243 | const server3 = follows.find(f => f.following.host === 'localhost:9003') | 253 | const server3 = follows.find(f => f.following.host === `localhost:${servers[ 2 ].port}`) |
244 | 254 | ||
245 | expect(server3).to.not.be.undefined | 255 | expect(server3).to.not.be.undefined |
246 | expect(server3.following.hostRedundancyAllowed).to.be.false | 256 | expect(server3.following.hostRedundancyAllowed).to.be.false |
@@ -254,8 +264,8 @@ async function disableRedundancyOnServer1 () { | |||
254 | 264 | ||
255 | const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 5, '-createdAt') | 265 | const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 5, '-createdAt') |
256 | const follows: ActorFollow[] = res.body.data | 266 | const follows: ActorFollow[] = res.body.data |
257 | const server2 = follows.find(f => f.following.host === 'localhost:9002') | 267 | const server2 = follows.find(f => f.following.host === `localhost:${servers[ 1 ].port}`) |
258 | const server3 = follows.find(f => f.following.host === 'localhost:9003') | 268 | const server3 = follows.find(f => f.following.host === `localhost:${servers[ 2 ].port}`) |
259 | 269 | ||
260 | expect(server3).to.not.be.undefined | 270 | expect(server3).to.not.be.undefined |
261 | expect(server3.following.hostRedundancyAllowed).to.be.false | 271 | expect(server3.following.hostRedundancyAllowed).to.be.false |
@@ -475,12 +485,12 @@ describe('Test videos redundancy', function () { | |||
475 | await wait(10000) | 485 | await wait(10000) |
476 | 486 | ||
477 | try { | 487 | try { |
478 | await checkContains(servers, 'http%3A%2F%2Flocalhost%3A9001') | 488 | await checkContains(servers, 'http%3A%2F%2Flocalhost%3A' + servers[0].port) |
479 | } catch { | 489 | } catch { |
480 | // Maybe a server deleted a redundancy in the scheduler | 490 | // Maybe a server deleted a redundancy in the scheduler |
481 | await wait(2000) | 491 | await wait(2000) |
482 | 492 | ||
483 | await checkContains(servers, 'http%3A%2F%2Flocalhost%3A9001') | 493 | await checkContains(servers, 'http%3A%2F%2Flocalhost%3A' + servers[0].port) |
484 | } | 494 | } |
485 | }) | 495 | }) |
486 | 496 | ||
@@ -491,7 +501,7 @@ describe('Test videos redundancy', function () { | |||
491 | 501 | ||
492 | await wait(15000) | 502 | await wait(15000) |
493 | 503 | ||
494 | await checkNotContains([ servers[1], servers[2] ], 'http%3A%2F%2Flocalhost%3A9001') | 504 | await checkNotContains([ servers[1], servers[2] ], 'http%3A%2F%2Flocalhost%3A' + servers[0].port) |
495 | }) | 505 | }) |
496 | 506 | ||
497 | after(async function () { | 507 | after(async function () { |
diff --git a/server/tests/api/search/search-activitypub-video-channels.ts b/server/tests/api/search/search-activitypub-video-channels.ts index 4d1ceb767..8a008b8c6 100644 --- a/server/tests/api/search/search-activitypub-video-channels.ts +++ b/server/tests/api/search/search-activitypub-video-channels.ts | |||
@@ -3,16 +3,17 @@ | |||
3 | import * as chai from 'chai' | 3 | import * as chai from 'chai' |
4 | import 'mocha' | 4 | import 'mocha' |
5 | import { | 5 | import { |
6 | addVideoChannel, cleanupTests, | 6 | addVideoChannel, |
7 | cleanupTests, | ||
7 | createUser, | 8 | createUser, |
8 | deleteVideoChannel, | 9 | deleteVideoChannel, |
9 | flushAndRunMultipleServers, | 10 | flushAndRunMultipleServers, |
10 | flushTests, | 11 | getVideoChannelsList, |
11 | getVideoChannelsList, getVideoChannelVideos, | 12 | getVideoChannelVideos, |
12 | killallServers, | ||
13 | ServerInfo, | 13 | ServerInfo, |
14 | setAccessTokensToServers, | 14 | setAccessTokensToServers, |
15 | updateMyUser, updateVideo, | 15 | updateMyUser, |
16 | updateVideo, | ||
16 | updateVideoChannel, | 17 | updateVideoChannel, |
17 | uploadVideo, | 18 | uploadVideo, |
18 | userLogin, | 19 | userLogin, |
@@ -24,7 +25,7 @@ import { searchVideoChannel } from '../../../../shared/extra-utils/search/video- | |||
24 | 25 | ||
25 | const expect = chai.expect | 26 | const expect = chai.expect |
26 | 27 | ||
27 | describe('Test a ActivityPub video channels search', function () { | 28 | describe('Test ActivityPub video channels search', function () { |
28 | let servers: ServerInfo[] | 29 | let servers: ServerInfo[] |
29 | let userServer2Token: string | 30 | let userServer2Token: string |
30 | let videoServer2UUID: string | 31 | let videoServer2UUID: string |
@@ -67,7 +68,7 @@ describe('Test a ActivityPub video channels search', function () { | |||
67 | 68 | ||
68 | it('Should not find a remote video channel', async function () { | 69 | it('Should not find a remote video channel', async function () { |
69 | { | 70 | { |
70 | const search = 'http://localhost:9002/video-channels/channel1_server3' | 71 | const search = 'http://localhost:' + servers[ 1 ].port + '/video-channels/channel1_server3' |
71 | const res = await searchVideoChannel(servers[ 0 ].url, search, servers[ 0 ].accessToken) | 72 | const res = await searchVideoChannel(servers[ 0 ].url, search, servers[ 0 ].accessToken) |
72 | 73 | ||
73 | expect(res.body.total).to.equal(0) | 74 | expect(res.body.total).to.equal(0) |
@@ -77,7 +78,7 @@ describe('Test a ActivityPub video channels search', function () { | |||
77 | 78 | ||
78 | { | 79 | { |
79 | // Without token | 80 | // Without token |
80 | const search = 'http://localhost:9002/video-channels/channel1_server2' | 81 | const search = 'http://localhost:' + servers[ 1 ].port + '/video-channels/channel1_server2' |
81 | const res = await searchVideoChannel(servers[0].url, search) | 82 | const res = await searchVideoChannel(servers[0].url, search) |
82 | 83 | ||
83 | expect(res.body.total).to.equal(0) | 84 | expect(res.body.total).to.equal(0) |
@@ -88,8 +89,8 @@ describe('Test a ActivityPub video channels search', function () { | |||
88 | 89 | ||
89 | it('Should search a local video channel', async function () { | 90 | it('Should search a local video channel', async function () { |
90 | const searches = [ | 91 | const searches = [ |
91 | 'http://localhost:9001/video-channels/channel1_server1', | 92 | 'http://localhost:' + servers[ 0 ].port + '/video-channels/channel1_server1', |
92 | 'channel1_server1@localhost:9001' | 93 | 'channel1_server1@localhost:' + servers[ 0 ].port |
93 | ] | 94 | ] |
94 | 95 | ||
95 | for (const search of searches) { | 96 | for (const search of searches) { |
@@ -105,8 +106,8 @@ describe('Test a ActivityPub video channels search', function () { | |||
105 | 106 | ||
106 | it('Should search a remote video channel with URL or handle', async function () { | 107 | it('Should search a remote video channel with URL or handle', async function () { |
107 | const searches = [ | 108 | const searches = [ |
108 | 'http://localhost:9002/video-channels/channel1_server2', | 109 | 'http://localhost:' + servers[ 1 ].port + '/video-channels/channel1_server2', |
109 | 'channel1_server2@localhost:9002' | 110 | 'channel1_server2@localhost:' + servers[ 1 ].port |
110 | ] | 111 | ] |
111 | 112 | ||
112 | for (const search of searches) { | 113 | for (const search of searches) { |
@@ -134,13 +135,13 @@ describe('Test a ActivityPub video channels search', function () { | |||
134 | 135 | ||
135 | await waitJobs(servers) | 136 | await waitJobs(servers) |
136 | 137 | ||
137 | const res = await getVideoChannelVideos(servers[0].url, null, 'channel1_server2@localhost:9002', 0, 5) | 138 | const res = await getVideoChannelVideos(servers[0].url, null, 'channel1_server2@localhost:' + servers[ 1 ].port, 0, 5) |
138 | expect(res.body.total).to.equal(0) | 139 | expect(res.body.total).to.equal(0) |
139 | expect(res.body.data).to.have.lengthOf(0) | 140 | expect(res.body.data).to.have.lengthOf(0) |
140 | }) | 141 | }) |
141 | 142 | ||
142 | it('Should list video channel videos of server 2 with token', async function () { | 143 | it('Should list video channel videos of server 2 with token', async function () { |
143 | const res = await getVideoChannelVideos(servers[0].url, servers[0].accessToken, 'channel1_server2@localhost:9002', 0, 5) | 144 | const res = await getVideoChannelVideos(servers[0].url, servers[0].accessToken, 'channel1_server2@localhost:' + servers[ 1 ].port, 0, 5) |
144 | 145 | ||
145 | expect(res.body.total).to.equal(1) | 146 | expect(res.body.total).to.equal(1) |
146 | expect(res.body.data[0].name).to.equal('video 1 server 2') | 147 | expect(res.body.data[0].name).to.equal('video 1 server 2') |
@@ -156,7 +157,7 @@ describe('Test a ActivityPub video channels search', function () { | |||
156 | // Expire video channel | 157 | // Expire video channel |
157 | await wait(10000) | 158 | await wait(10000) |
158 | 159 | ||
159 | const search = 'http://localhost:9002/video-channels/channel1_server2' | 160 | const search = 'http://localhost:' + servers[ 1 ].port + '/video-channels/channel1_server2' |
160 | const res = await searchVideoChannel(servers[0].url, search, servers[0].accessToken) | 161 | const res = await searchVideoChannel(servers[0].url, search, servers[0].accessToken) |
161 | expect(res.body.total).to.equal(1) | 162 | expect(res.body.total).to.equal(1) |
162 | expect(res.body.data).to.have.lengthOf(1) | 163 | expect(res.body.data).to.have.lengthOf(1) |
@@ -179,12 +180,13 @@ describe('Test a ActivityPub video channels search', function () { | |||
179 | // Expire video channel | 180 | // Expire video channel |
180 | await wait(10000) | 181 | await wait(10000) |
181 | 182 | ||
182 | const search = 'http://localhost:9002/video-channels/channel1_server2' | 183 | const search = 'http://localhost:' + servers[ 1 ].port + '/video-channels/channel1_server2' |
183 | await searchVideoChannel(servers[0].url, search, servers[0].accessToken) | 184 | await searchVideoChannel(servers[0].url, search, servers[0].accessToken) |
184 | 185 | ||
185 | await waitJobs(servers) | 186 | await waitJobs(servers) |
186 | 187 | ||
187 | const res = await getVideoChannelVideos(servers[0].url, servers[0].accessToken, 'channel1_server2@localhost:9002', 0, 5, '-createdAt') | 188 | const videoChannelName = 'channel1_server2@localhost:' + servers[ 1 ].port |
189 | const res = await getVideoChannelVideos(servers[0].url, servers[0].accessToken, videoChannelName, 0, 5, '-createdAt') | ||
188 | 190 | ||
189 | expect(res.body.total).to.equal(2) | 191 | expect(res.body.total).to.equal(2) |
190 | expect(res.body.data[0].name).to.equal('video 2 server 2') | 192 | expect(res.body.data[0].name).to.equal('video 2 server 2') |
@@ -200,7 +202,8 @@ describe('Test a ActivityPub video channels search', function () { | |||
200 | // Expire video | 202 | // Expire video |
201 | await wait(10000) | 203 | await wait(10000) |
202 | 204 | ||
203 | const res = await searchVideoChannel(servers[0].url, 'http://localhost:9002/video-channels/channel1_server2', servers[0].accessToken) | 205 | const search = 'http://localhost:' + servers[ 1 ].port + '/video-channels/channel1_server2' |
206 | const res = await searchVideoChannel(servers[0].url, search, servers[0].accessToken) | ||
204 | expect(res.body.total).to.equal(0) | 207 | expect(res.body.total).to.equal(0) |
205 | expect(res.body.data).to.have.lengthOf(0) | 208 | expect(res.body.data).to.have.lengthOf(0) |
206 | }) | 209 | }) |
diff --git a/server/tests/api/search/search-activitypub-videos.ts b/server/tests/api/search/search-activitypub-videos.ts index e039961cb..dbfefadda 100644 --- a/server/tests/api/search/search-activitypub-videos.ts +++ b/server/tests/api/search/search-activitypub-videos.ts | |||
@@ -4,25 +4,24 @@ import * as chai from 'chai' | |||
4 | import 'mocha' | 4 | import 'mocha' |
5 | import { | 5 | import { |
6 | addVideoChannel, | 6 | addVideoChannel, |
7 | cleanupTests, | ||
7 | flushAndRunMultipleServers, | 8 | flushAndRunMultipleServers, |
8 | flushTests, | ||
9 | getVideosList, | 9 | getVideosList, |
10 | killallServers, | ||
11 | removeVideo, | 10 | removeVideo, |
11 | searchVideo, | ||
12 | searchVideoWithToken, | 12 | searchVideoWithToken, |
13 | ServerInfo, | 13 | ServerInfo, |
14 | setAccessTokensToServers, | 14 | setAccessTokensToServers, |
15 | updateVideo, | 15 | updateVideo, |
16 | uploadVideo, | 16 | uploadVideo, |
17 | wait, | 17 | wait |
18 | searchVideo, cleanupTests | ||
19 | } from '../../../../shared/extra-utils' | 18 | } from '../../../../shared/extra-utils' |
20 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | 19 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' |
21 | import { Video, VideoPrivacy } from '../../../../shared/models/videos' | 20 | import { Video, VideoPrivacy } from '../../../../shared/models/videos' |
22 | 21 | ||
23 | const expect = chai.expect | 22 | const expect = chai.expect |
24 | 23 | ||
25 | describe('Test a ActivityPub videos search', function () { | 24 | describe('Test ActivityPub videos search', function () { |
26 | let servers: ServerInfo[] | 25 | let servers: ServerInfo[] |
27 | let videoServer1UUID: string | 26 | let videoServer1UUID: string |
28 | let videoServer2UUID: string | 27 | let videoServer2UUID: string |
@@ -49,7 +48,8 @@ describe('Test a ActivityPub videos search', function () { | |||
49 | 48 | ||
50 | it('Should not find a remote video', async function () { | 49 | it('Should not find a remote video', async function () { |
51 | { | 50 | { |
52 | const res = await searchVideoWithToken(servers[ 0 ].url, 'http://localhost:9002/videos/watch/43', servers[ 0 ].accessToken) | 51 | const search = 'http://localhost:' + servers[1].port + '/videos/watch/43' |
52 | const res = await searchVideoWithToken(servers[ 0 ].url, search, servers[ 0 ].accessToken) | ||
53 | 53 | ||
54 | expect(res.body.total).to.equal(0) | 54 | expect(res.body.total).to.equal(0) |
55 | expect(res.body.data).to.be.an('array') | 55 | expect(res.body.data).to.be.an('array') |
@@ -58,7 +58,8 @@ describe('Test a ActivityPub videos search', function () { | |||
58 | 58 | ||
59 | { | 59 | { |
60 | // Without token | 60 | // Without token |
61 | const res = await searchVideo(servers[0].url, 'http://localhost:9002/videos/watch/' + videoServer2UUID) | 61 | const search = 'http://localhost:' + servers[1].port + '/videos/watch/' + videoServer2UUID |
62 | const res = await searchVideo(servers[0].url, search) | ||
62 | 63 | ||
63 | expect(res.body.total).to.equal(0) | 64 | expect(res.body.total).to.equal(0) |
64 | expect(res.body.data).to.be.an('array') | 65 | expect(res.body.data).to.be.an('array') |
@@ -67,7 +68,8 @@ describe('Test a ActivityPub videos search', function () { | |||
67 | }) | 68 | }) |
68 | 69 | ||
69 | it('Should search a local video', async function () { | 70 | it('Should search a local video', async function () { |
70 | const res = await searchVideo(servers[0].url, 'http://localhost:9001/videos/watch/' + videoServer1UUID) | 71 | const search = 'http://localhost:' + servers[0].port + '/videos/watch/' + videoServer1UUID |
72 | const res = await searchVideo(servers[0].url, search) | ||
71 | 73 | ||
72 | expect(res.body.total).to.equal(1) | 74 | expect(res.body.total).to.equal(1) |
73 | expect(res.body.data).to.be.an('array') | 75 | expect(res.body.data).to.be.an('array') |
@@ -76,7 +78,8 @@ describe('Test a ActivityPub videos search', function () { | |||
76 | }) | 78 | }) |
77 | 79 | ||
78 | it('Should search a remote video', async function () { | 80 | it('Should search a remote video', async function () { |
79 | const res = await searchVideoWithToken(servers[0].url, 'http://localhost:9002/videos/watch/' + videoServer2UUID, servers[0].accessToken) | 81 | const search = 'http://localhost:' + servers[1].port + '/videos/watch/' + videoServer2UUID |
82 | const res = await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) | ||
80 | 83 | ||
81 | expect(res.body.total).to.equal(1) | 84 | expect(res.body.total).to.equal(1) |
82 | expect(res.body.data).to.be.an('array') | 85 | expect(res.body.data).to.be.an('array') |
@@ -114,12 +117,13 @@ describe('Test a ActivityPub videos search', function () { | |||
114 | await wait(10000) | 117 | await wait(10000) |
115 | 118 | ||
116 | // Will run refresh async | 119 | // Will run refresh async |
117 | await searchVideoWithToken(servers[0].url, 'http://localhost:9002/videos/watch/' + videoServer2UUID, servers[0].accessToken) | 120 | const search = 'http://localhost:' + servers[1].port + '/videos/watch/' + videoServer2UUID |
121 | await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) | ||
118 | 122 | ||
119 | // Wait refresh | 123 | // Wait refresh |
120 | await wait(5000) | 124 | await wait(5000) |
121 | 125 | ||
122 | const res = await searchVideoWithToken(servers[0].url, 'http://localhost:9002/videos/watch/' + videoServer2UUID, servers[0].accessToken) | 126 | const res = await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) |
123 | expect(res.body.total).to.equal(1) | 127 | expect(res.body.total).to.equal(1) |
124 | expect(res.body.data).to.have.lengthOf(1) | 128 | expect(res.body.data).to.have.lengthOf(1) |
125 | 129 | ||
@@ -139,12 +143,13 @@ describe('Test a ActivityPub videos search', function () { | |||
139 | await wait(10000) | 143 | await wait(10000) |
140 | 144 | ||
141 | // Will run refresh async | 145 | // Will run refresh async |
142 | await searchVideoWithToken(servers[0].url, 'http://localhost:9002/videos/watch/' + videoServer2UUID, servers[0].accessToken) | 146 | const search = 'http://localhost:' + servers[1].port + '/videos/watch/' + videoServer2UUID |
147 | await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) | ||
143 | 148 | ||
144 | // Wait refresh | 149 | // Wait refresh |
145 | await wait(5000) | 150 | await wait(5000) |
146 | 151 | ||
147 | const res = await searchVideoWithToken(servers[0].url, 'http://localhost:9002/videos/watch/' + videoServer2UUID, servers[0].accessToken) | 152 | const res = await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) |
148 | expect(res.body.total).to.equal(0) | 153 | expect(res.body.total).to.equal(0) |
149 | expect(res.body.data).to.have.lengthOf(0) | 154 | expect(res.body.data).to.have.lengthOf(0) |
150 | }) | 155 | }) |
diff --git a/server/tests/api/search/search-videos.ts b/server/tests/api/search/search-videos.ts index 1a086b33a..92cc0dc71 100644 --- a/server/tests/api/search/search-videos.ts +++ b/server/tests/api/search/search-videos.ts | |||
@@ -4,21 +4,19 @@ import * as chai from 'chai' | |||
4 | import 'mocha' | 4 | import 'mocha' |
5 | import { | 5 | import { |
6 | advancedVideosSearch, | 6 | advancedVideosSearch, |
7 | flushTests, | 7 | cleanupTests, |
8 | killallServers, | ||
9 | flushAndRunServer, | 8 | flushAndRunServer, |
9 | immutableAssign, | ||
10 | searchVideo, | 10 | searchVideo, |
11 | ServerInfo, | 11 | ServerInfo, |
12 | setAccessTokensToServers, | 12 | setAccessTokensToServers, |
13 | uploadVideo, | 13 | uploadVideo, |
14 | wait, | 14 | wait |
15 | immutableAssign, | ||
16 | cleanupTests | ||
17 | } from '../../../../shared/extra-utils' | 15 | } from '../../../../shared/extra-utils' |
18 | 16 | ||
19 | const expect = chai.expect | 17 | const expect = chai.expect |
20 | 18 | ||
21 | describe('Test a videos search', function () { | 19 | describe('Test videos search', function () { |
22 | let server: ServerInfo = null | 20 | let server: ServerInfo = null |
23 | let startDate: string | 21 | let startDate: string |
24 | 22 | ||
diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts index c0d11914b..8ea21158a 100644 --- a/server/tests/api/server/config.ts +++ b/server/tests/api/server/config.ts | |||
@@ -11,17 +11,17 @@ import { | |||
11 | getAbout, | 11 | getAbout, |
12 | getConfig, | 12 | getConfig, |
13 | getCustomConfig, | 13 | getCustomConfig, |
14 | killallServers, | 14 | killallServers, parallelTests, |
15 | registerUser, | 15 | registerUser, |
16 | reRunServer, | 16 | reRunServer, ServerInfo, |
17 | setAccessTokensToServers, | 17 | setAccessTokensToServers, |
18 | updateCustomConfig | 18 | updateCustomConfig, uploadVideo |
19 | } from '../../../../shared/extra-utils' | 19 | } from '../../../../shared/extra-utils' |
20 | import { ServerConfig } from '../../../../shared/models' | 20 | import { ServerConfig } from '../../../../shared/models' |
21 | 21 | ||
22 | const expect = chai.expect | 22 | const expect = chai.expect |
23 | 23 | ||
24 | function checkInitialConfig (data: CustomConfig) { | 24 | function checkInitialConfig (server: ServerInfo, data: CustomConfig) { |
25 | expect(data.instance.name).to.equal('PeerTube') | 25 | expect(data.instance.name).to.equal('PeerTube') |
26 | expect(data.instance.shortDescription).to.equal( | 26 | expect(data.instance.shortDescription).to.equal( |
27 | 'PeerTube, a federated (ActivityPub) video streaming platform using P2P (BitTorrent) directly in the web browser ' + | 27 | 'PeerTube, a federated (ActivityPub) video streaming platform using P2P (BitTorrent) directly in the web browser ' + |
@@ -45,13 +45,14 @@ function checkInitialConfig (data: CustomConfig) { | |||
45 | expect(data.signup.limit).to.equal(4) | 45 | expect(data.signup.limit).to.equal(4) |
46 | expect(data.signup.requiresEmailVerification).to.be.false | 46 | expect(data.signup.requiresEmailVerification).to.be.false |
47 | 47 | ||
48 | expect(data.admin.email).to.equal('admin1@example.com') | 48 | expect(data.admin.email).to.equal('admin' + server.internalServerNumber + '@example.com') |
49 | expect(data.contactForm.enabled).to.be.true | 49 | expect(data.contactForm.enabled).to.be.true |
50 | 50 | ||
51 | expect(data.user.videoQuota).to.equal(5242880) | 51 | expect(data.user.videoQuota).to.equal(5242880) |
52 | expect(data.user.videoQuotaDaily).to.equal(-1) | 52 | expect(data.user.videoQuotaDaily).to.equal(-1) |
53 | expect(data.transcoding.enabled).to.be.false | 53 | expect(data.transcoding.enabled).to.be.false |
54 | expect(data.transcoding.allowAdditionalExtensions).to.be.false | 54 | expect(data.transcoding.allowAdditionalExtensions).to.be.false |
55 | expect(data.transcoding.allowAudioFiles).to.be.false | ||
55 | expect(data.transcoding.threads).to.equal(2) | 56 | expect(data.transcoding.threads).to.equal(2) |
56 | expect(data.transcoding.resolutions['240p']).to.be.true | 57 | expect(data.transcoding.resolutions['240p']).to.be.true |
57 | expect(data.transcoding.resolutions['360p']).to.be.true | 58 | expect(data.transcoding.resolutions['360p']).to.be.true |
@@ -89,7 +90,11 @@ function checkUpdatedConfig (data: CustomConfig) { | |||
89 | expect(data.signup.limit).to.equal(5) | 90 | expect(data.signup.limit).to.equal(5) |
90 | expect(data.signup.requiresEmailVerification).to.be.false | 91 | expect(data.signup.requiresEmailVerification).to.be.false |
91 | 92 | ||
92 | expect(data.admin.email).to.equal('superadmin1@example.com') | 93 | // We override admin email in parallel tests, so skip this exception |
94 | if (parallelTests() === false) { | ||
95 | expect(data.admin.email).to.equal('superadmin1@example.com') | ||
96 | } | ||
97 | |||
93 | expect(data.contactForm.enabled).to.be.false | 98 | expect(data.contactForm.enabled).to.be.false |
94 | 99 | ||
95 | expect(data.user.videoQuota).to.equal(5242881) | 100 | expect(data.user.videoQuota).to.equal(5242881) |
@@ -98,6 +103,7 @@ function checkUpdatedConfig (data: CustomConfig) { | |||
98 | expect(data.transcoding.enabled).to.be.true | 103 | expect(data.transcoding.enabled).to.be.true |
99 | expect(data.transcoding.threads).to.equal(1) | 104 | expect(data.transcoding.threads).to.equal(1) |
100 | expect(data.transcoding.allowAdditionalExtensions).to.be.true | 105 | expect(data.transcoding.allowAdditionalExtensions).to.be.true |
106 | expect(data.transcoding.allowAudioFiles).to.be.true | ||
101 | expect(data.transcoding.resolutions['240p']).to.be.false | 107 | expect(data.transcoding.resolutions['240p']).to.be.false |
102 | expect(data.transcoding.resolutions['360p']).to.be.true | 108 | expect(data.transcoding.resolutions['360p']).to.be.true |
103 | expect(data.transcoding.resolutions['480p']).to.be.true | 109 | expect(data.transcoding.resolutions['480p']).to.be.true |
@@ -118,6 +124,7 @@ describe('Test config', function () { | |||
118 | 124 | ||
119 | before(async function () { | 125 | before(async function () { |
120 | this.timeout(30000) | 126 | this.timeout(30000) |
127 | |||
121 | server = await flushAndRunServer(1) | 128 | server = await flushAndRunServer(1) |
122 | await setAccessTokensToServers([ server ]) | 129 | await setAccessTokensToServers([ server ]) |
123 | }) | 130 | }) |
@@ -153,6 +160,9 @@ describe('Test config', function () { | |||
153 | expect(data.video.file.extensions).to.contain('.webm') | 160 | expect(data.video.file.extensions).to.contain('.webm') |
154 | expect(data.video.file.extensions).to.contain('.ogv') | 161 | expect(data.video.file.extensions).to.contain('.ogv') |
155 | 162 | ||
163 | await uploadVideo(server.url, server.accessToken, { fixture: 'video_short.mkv' }, 400) | ||
164 | await uploadVideo(server.url, server.accessToken, { fixture: 'sample.ogg' }, 400) | ||
165 | |||
156 | expect(data.contactForm.enabled).to.be.true | 166 | expect(data.contactForm.enabled).to.be.true |
157 | }) | 167 | }) |
158 | 168 | ||
@@ -160,7 +170,7 @@ describe('Test config', function () { | |||
160 | const res = await getCustomConfig(server.url, server.accessToken) | 170 | const res = await getCustomConfig(server.url, server.accessToken) |
161 | const data = res.body as CustomConfig | 171 | const data = res.body as CustomConfig |
162 | 172 | ||
163 | checkInitialConfig(data) | 173 | checkInitialConfig(server, data) |
164 | }) | 174 | }) |
165 | 175 | ||
166 | it('Should update the customized configuration', async function () { | 176 | it('Should update the customized configuration', async function () { |
@@ -210,6 +220,7 @@ describe('Test config', function () { | |||
210 | transcoding: { | 220 | transcoding: { |
211 | enabled: true, | 221 | enabled: true, |
212 | allowAdditionalExtensions: true, | 222 | allowAdditionalExtensions: true, |
223 | allowAudioFiles: true, | ||
213 | threads: 1, | 224 | threads: 1, |
214 | resolutions: { | 225 | resolutions: { |
215 | '240p': false, | 226 | '240p': false, |
@@ -264,6 +275,12 @@ describe('Test config', function () { | |||
264 | expect(data.video.file.extensions).to.contain('.ogv') | 275 | expect(data.video.file.extensions).to.contain('.ogv') |
265 | expect(data.video.file.extensions).to.contain('.flv') | 276 | expect(data.video.file.extensions).to.contain('.flv') |
266 | expect(data.video.file.extensions).to.contain('.mkv') | 277 | expect(data.video.file.extensions).to.contain('.mkv') |
278 | expect(data.video.file.extensions).to.contain('.mp3') | ||
279 | expect(data.video.file.extensions).to.contain('.ogg') | ||
280 | expect(data.video.file.extensions).to.contain('.flac') | ||
281 | |||
282 | await uploadVideo(server.url, server.accessToken, { fixture: 'video_short.mkv' }, 200) | ||
283 | await uploadVideo(server.url, server.accessToken, { fixture: 'sample.ogg' }, 200) | ||
267 | }) | 284 | }) |
268 | 285 | ||
269 | it('Should have the configuration updated after a restart', async function () { | 286 | it('Should have the configuration updated after a restart', async function () { |
@@ -297,7 +314,7 @@ describe('Test config', function () { | |||
297 | const res = await getCustomConfig(server.url, server.accessToken) | 314 | const res = await getCustomConfig(server.url, server.accessToken) |
298 | const data = res.body | 315 | const data = res.body |
299 | 316 | ||
300 | checkInitialConfig(data) | 317 | checkInitialConfig(server, data) |
301 | }) | 318 | }) |
302 | 319 | ||
303 | after(async function () { | 320 | after(async function () { |
diff --git a/server/tests/api/server/contact-form.ts b/server/tests/api/server/contact-form.ts index ba51198b3..87e55060c 100644 --- a/server/tests/api/server/contact-form.ts +++ b/server/tests/api/server/contact-form.ts | |||
@@ -24,11 +24,12 @@ describe('Test contact form', function () { | |||
24 | before(async function () { | 24 | before(async function () { |
25 | this.timeout(30000) | 25 | this.timeout(30000) |
26 | 26 | ||
27 | await MockSmtpServer.Instance.collectEmails(emails) | 27 | const port = await MockSmtpServer.Instance.collectEmails(emails) |
28 | 28 | ||
29 | const overrideConfig = { | 29 | const overrideConfig = { |
30 | smtp: { | 30 | smtp: { |
31 | hostname: 'localhost' | 31 | hostname: 'localhost', |
32 | port | ||
32 | } | 33 | } |
33 | } | 34 | } |
34 | server = await flushAndRunServer(1, overrideConfig) | 35 | server = await flushAndRunServer(1, overrideConfig) |
@@ -53,7 +54,7 @@ describe('Test contact form', function () { | |||
53 | 54 | ||
54 | expect(email['from'][0]['address']).equal('test-admin@localhost') | 55 | expect(email['from'][0]['address']).equal('test-admin@localhost') |
55 | expect(email['from'][0]['name']).equal('toto@example.com') | 56 | expect(email['from'][0]['name']).equal('toto@example.com') |
56 | expect(email['to'][0]['address']).equal('admin1@example.com') | 57 | expect(email['to'][0]['address']).equal('admin' + server.internalServerNumber + '@example.com') |
57 | expect(email['subject']).contains('Contact form') | 58 | expect(email['subject']).contains('Contact form') |
58 | expect(email['text']).contains('my super message') | 59 | expect(email['text']).contains('my super message') |
59 | }) | 60 | }) |
diff --git a/server/tests/api/server/email.ts b/server/tests/api/server/email.ts index bacdf1b1b..5929a3adb 100644 --- a/server/tests/api/server/email.ts +++ b/server/tests/api/server/email.ts | |||
@@ -7,18 +7,18 @@ import { | |||
7 | askResetPassword, | 7 | askResetPassword, |
8 | askSendVerifyEmail, | 8 | askSendVerifyEmail, |
9 | blockUser, | 9 | blockUser, |
10 | createUser, removeVideoFromBlacklist, | 10 | cleanupTests, |
11 | createUser, | ||
12 | flushAndRunServer, | ||
13 | removeVideoFromBlacklist, | ||
11 | reportVideoAbuse, | 14 | reportVideoAbuse, |
12 | resetPassword, | 15 | resetPassword, |
13 | flushAndRunServer, | 16 | ServerInfo, |
17 | setAccessTokensToServers, | ||
14 | unblockUser, | 18 | unblockUser, |
15 | uploadVideo, | 19 | uploadVideo, |
16 | userLogin, | 20 | userLogin, |
17 | verifyEmail, | 21 | verifyEmail |
18 | flushTests, | ||
19 | killallServers, | ||
20 | ServerInfo, | ||
21 | setAccessTokensToServers, cleanupTests | ||
22 | } from '../../../../shared/extra-utils' | 22 | } from '../../../../shared/extra-utils' |
23 | import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email' | 23 | import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email' |
24 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | 24 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' |
@@ -37,15 +37,17 @@ describe('Test emails', function () { | |||
37 | username: 'user_1', | 37 | username: 'user_1', |
38 | password: 'super_password' | 38 | password: 'super_password' |
39 | } | 39 | } |
40 | let emailPort: number | ||
40 | 41 | ||
41 | before(async function () { | 42 | before(async function () { |
42 | this.timeout(30000) | 43 | this.timeout(30000) |
43 | 44 | ||
44 | await MockSmtpServer.Instance.collectEmails(emails) | 45 | emailPort = await MockSmtpServer.Instance.collectEmails(emails) |
45 | 46 | ||
46 | const overrideConfig = { | 47 | const overrideConfig = { |
47 | smtp: { | 48 | smtp: { |
48 | hostname: 'localhost' | 49 | hostname: 'localhost', |
50 | port: emailPort | ||
49 | } | 51 | } |
50 | } | 52 | } |
51 | server = await flushAndRunServer(1, overrideConfig) | 53 | server = await flushAndRunServer(1, overrideConfig) |
@@ -87,7 +89,7 @@ describe('Test emails', function () { | |||
87 | 89 | ||
88 | const email = emails[0] | 90 | const email = emails[0] |
89 | 91 | ||
90 | expect(email['from'][0]['name']).equal('localhost:9001') | 92 | expect(email['from'][0]['name']).equal('localhost:' + server.port) |
91 | expect(email['from'][0]['address']).equal('test-admin@localhost') | 93 | expect(email['from'][0]['address']).equal('test-admin@localhost') |
92 | expect(email['to'][0]['address']).equal('user_1@example.com') | 94 | expect(email['to'][0]['address']).equal('user_1@example.com') |
93 | expect(email['subject']).contains('password') | 95 | expect(email['subject']).contains('password') |
@@ -132,9 +134,9 @@ describe('Test emails', function () { | |||
132 | 134 | ||
133 | const email = emails[1] | 135 | const email = emails[1] |
134 | 136 | ||
135 | expect(email['from'][0]['name']).equal('localhost:9001') | 137 | expect(email['from'][0]['name']).equal('localhost:' + server.port) |
136 | expect(email['from'][0]['address']).equal('test-admin@localhost') | 138 | expect(email['from'][0]['address']).equal('test-admin@localhost') |
137 | expect(email['to'][0]['address']).equal('admin1@example.com') | 139 | expect(email['to'][0]['address']).equal('admin' + server.internalServerNumber + '@example.com') |
138 | expect(email['subject']).contains('abuse') | 140 | expect(email['subject']).contains('abuse') |
139 | expect(email['text']).contains(videoUUID) | 141 | expect(email['text']).contains(videoUUID) |
140 | }) | 142 | }) |
@@ -153,7 +155,7 @@ describe('Test emails', function () { | |||
153 | 155 | ||
154 | const email = emails[2] | 156 | const email = emails[2] |
155 | 157 | ||
156 | expect(email['from'][0]['name']).equal('localhost:9001') | 158 | expect(email['from'][0]['name']).equal('localhost:' + server.port) |
157 | expect(email['from'][0]['address']).equal('test-admin@localhost') | 159 | expect(email['from'][0]['address']).equal('test-admin@localhost') |
158 | expect(email['to'][0]['address']).equal('user_1@example.com') | 160 | expect(email['to'][0]['address']).equal('user_1@example.com') |
159 | expect(email['subject']).contains(' blocked') | 161 | expect(email['subject']).contains(' blocked') |
@@ -171,7 +173,7 @@ describe('Test emails', function () { | |||
171 | 173 | ||
172 | const email = emails[3] | 174 | const email = emails[3] |
173 | 175 | ||
174 | expect(email['from'][0]['name']).equal('localhost:9001') | 176 | expect(email['from'][0]['name']).equal('localhost:' + server.port) |
175 | expect(email['from'][0]['address']).equal('test-admin@localhost') | 177 | expect(email['from'][0]['address']).equal('test-admin@localhost') |
176 | expect(email['to'][0]['address']).equal('user_1@example.com') | 178 | expect(email['to'][0]['address']).equal('user_1@example.com') |
177 | expect(email['subject']).contains(' unblocked') | 179 | expect(email['subject']).contains(' unblocked') |
@@ -191,7 +193,7 @@ describe('Test emails', function () { | |||
191 | 193 | ||
192 | const email = emails[4] | 194 | const email = emails[4] |
193 | 195 | ||
194 | expect(email['from'][0]['name']).equal('localhost:9001') | 196 | expect(email['from'][0]['name']).equal('localhost:' + server.port) |
195 | expect(email['from'][0]['address']).equal('test-admin@localhost') | 197 | expect(email['from'][0]['address']).equal('test-admin@localhost') |
196 | expect(email['to'][0]['address']).equal('user_1@example.com') | 198 | expect(email['to'][0]['address']).equal('user_1@example.com') |
197 | expect(email['subject']).contains(' blacklisted') | 199 | expect(email['subject']).contains(' blacklisted') |
@@ -209,7 +211,7 @@ describe('Test emails', function () { | |||
209 | 211 | ||
210 | const email = emails[5] | 212 | const email = emails[5] |
211 | 213 | ||
212 | expect(email['from'][0]['name']).equal('localhost:9001') | 214 | expect(email['from'][0]['name']).equal('localhost:' + server.port) |
213 | expect(email['from'][0]['address']).equal('test-admin@localhost') | 215 | expect(email['from'][0]['address']).equal('test-admin@localhost') |
214 | expect(email['to'][0]['address']).equal('user_1@example.com') | 216 | expect(email['to'][0]['address']).equal('user_1@example.com') |
215 | expect(email['subject']).contains(' unblacklisted') | 217 | expect(email['subject']).contains(' unblacklisted') |
@@ -229,7 +231,7 @@ describe('Test emails', function () { | |||
229 | 231 | ||
230 | const email = emails[6] | 232 | const email = emails[6] |
231 | 233 | ||
232 | expect(email['from'][0]['name']).equal('localhost:9001') | 234 | expect(email['from'][0]['name']).equal('localhost:' + server.port) |
233 | expect(email['from'][0]['address']).equal('test-admin@localhost') | 235 | expect(email['from'][0]['address']).equal('test-admin@localhost') |
234 | expect(email['to'][0]['address']).equal('user_1@example.com') | 236 | expect(email['to'][0]['address']).equal('user_1@example.com') |
235 | expect(email['subject']).contains('Verify') | 237 | expect(email['subject']).contains('Verify') |
diff --git a/server/tests/api/server/follow-constraints.ts b/server/tests/api/server/follow-constraints.ts index 4285a9e7a..ac3ff37f0 100644 --- a/server/tests/api/server/follow-constraints.ts +++ b/server/tests/api/server/follow-constraints.ts | |||
@@ -3,16 +3,16 @@ | |||
3 | import * as chai from 'chai' | 3 | import * as chai from 'chai' |
4 | import 'mocha' | 4 | import 'mocha' |
5 | import { | 5 | import { |
6 | cleanupTests, | ||
6 | doubleFollow, | 7 | doubleFollow, |
8 | flushAndRunMultipleServers, | ||
7 | getAccountVideos, | 9 | getAccountVideos, |
8 | getVideo, | 10 | getVideo, |
9 | getVideoChannelVideos, | 11 | getVideoChannelVideos, |
10 | getVideoWithToken, | 12 | getVideoWithToken, |
11 | flushAndRunMultipleServers, | ||
12 | killallServers, | ||
13 | ServerInfo, | 13 | ServerInfo, |
14 | setAccessTokensToServers, | 14 | setAccessTokensToServers, |
15 | uploadVideo, cleanupTests | 15 | uploadVideo |
16 | } from '../../../../shared/extra-utils' | 16 | } from '../../../../shared/extra-utils' |
17 | import { unfollow } from '../../../../shared/extra-utils/server/follows' | 17 | import { unfollow } from '../../../../shared/extra-utils/server/follows' |
18 | import { userLogin } from '../../../../shared/extra-utils/users/login' | 18 | import { userLogin } from '../../../../shared/extra-utils/users/login' |
@@ -66,28 +66,30 @@ describe('Test follow constraints', function () { | |||
66 | }) | 66 | }) |
67 | 67 | ||
68 | it('Should list local account videos', async function () { | 68 | it('Should list local account videos', async function () { |
69 | const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:9001', 0, 5) | 69 | const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:' + servers[0].port, 0, 5) |
70 | 70 | ||
71 | expect(res.body.total).to.equal(1) | 71 | expect(res.body.total).to.equal(1) |
72 | expect(res.body.data).to.have.lengthOf(1) | 72 | expect(res.body.data).to.have.lengthOf(1) |
73 | }) | 73 | }) |
74 | 74 | ||
75 | it('Should list remote account videos', async function () { | 75 | it('Should list remote account videos', async function () { |
76 | const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:9002', 0, 5) | 76 | const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:' + servers[1].port, 0, 5) |
77 | 77 | ||
78 | expect(res.body.total).to.equal(1) | 78 | expect(res.body.total).to.equal(1) |
79 | expect(res.body.data).to.have.lengthOf(1) | 79 | expect(res.body.data).to.have.lengthOf(1) |
80 | }) | 80 | }) |
81 | 81 | ||
82 | it('Should list local channel videos', async function () { | 82 | it('Should list local channel videos', async function () { |
83 | const res = await getVideoChannelVideos(servers[0].url, undefined, 'root_channel@localhost:9001', 0, 5) | 83 | const videoChannelName = 'root_channel@localhost:' + servers[0].port |
84 | const res = await getVideoChannelVideos(servers[0].url, undefined, videoChannelName, 0, 5) | ||
84 | 85 | ||
85 | expect(res.body.total).to.equal(1) | 86 | expect(res.body.total).to.equal(1) |
86 | expect(res.body.data).to.have.lengthOf(1) | 87 | expect(res.body.data).to.have.lengthOf(1) |
87 | }) | 88 | }) |
88 | 89 | ||
89 | it('Should list remote channel videos', async function () { | 90 | it('Should list remote channel videos', async function () { |
90 | const res = await getVideoChannelVideos(servers[0].url, undefined, 'root_channel@localhost:9002', 0, 5) | 91 | const videoChannelName = 'root_channel@localhost:' + servers[1].port |
92 | const res = await getVideoChannelVideos(servers[0].url, undefined, videoChannelName, 0, 5) | ||
91 | 93 | ||
92 | expect(res.body.total).to.equal(1) | 94 | expect(res.body.total).to.equal(1) |
93 | expect(res.body.data).to.have.lengthOf(1) | 95 | expect(res.body.data).to.have.lengthOf(1) |
@@ -104,28 +106,30 @@ describe('Test follow constraints', function () { | |||
104 | }) | 106 | }) |
105 | 107 | ||
106 | it('Should list local account videos', async function () { | 108 | it('Should list local account videos', async function () { |
107 | const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:9001', 0, 5) | 109 | const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:' + servers[0].port, 0, 5) |
108 | 110 | ||
109 | expect(res.body.total).to.equal(1) | 111 | expect(res.body.total).to.equal(1) |
110 | expect(res.body.data).to.have.lengthOf(1) | 112 | expect(res.body.data).to.have.lengthOf(1) |
111 | }) | 113 | }) |
112 | 114 | ||
113 | it('Should list remote account videos', async function () { | 115 | it('Should list remote account videos', async function () { |
114 | const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:9002', 0, 5) | 116 | const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:' + servers[1].port, 0, 5) |
115 | 117 | ||
116 | expect(res.body.total).to.equal(1) | 118 | expect(res.body.total).to.equal(1) |
117 | expect(res.body.data).to.have.lengthOf(1) | 119 | expect(res.body.data).to.have.lengthOf(1) |
118 | }) | 120 | }) |
119 | 121 | ||
120 | it('Should list local channel videos', async function () { | 122 | it('Should list local channel videos', async function () { |
121 | const res = await getVideoChannelVideos(servers[0].url, userAccessToken, 'root_channel@localhost:9001', 0, 5) | 123 | const videoChannelName = 'root_channel@localhost:' + servers[0].port |
124 | const res = await getVideoChannelVideos(servers[0].url, userAccessToken, videoChannelName, 0, 5) | ||
122 | 125 | ||
123 | expect(res.body.total).to.equal(1) | 126 | expect(res.body.total).to.equal(1) |
124 | expect(res.body.data).to.have.lengthOf(1) | 127 | expect(res.body.data).to.have.lengthOf(1) |
125 | }) | 128 | }) |
126 | 129 | ||
127 | it('Should list remote channel videos', async function () { | 130 | it('Should list remote channel videos', async function () { |
128 | const res = await getVideoChannelVideos(servers[0].url, userAccessToken, 'root_channel@localhost:9002', 0, 5) | 131 | const videoChannelName = 'root_channel@localhost:' + servers[1].port |
132 | const res = await getVideoChannelVideos(servers[0].url, userAccessToken, videoChannelName, 0, 5) | ||
129 | 133 | ||
130 | expect(res.body.total).to.equal(1) | 134 | expect(res.body.total).to.equal(1) |
131 | expect(res.body.data).to.have.lengthOf(1) | 135 | expect(res.body.data).to.have.lengthOf(1) |
@@ -152,28 +156,30 @@ describe('Test follow constraints', function () { | |||
152 | }) | 156 | }) |
153 | 157 | ||
154 | it('Should list local account videos', async function () { | 158 | it('Should list local account videos', async function () { |
155 | const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:9001', 0, 5) | 159 | const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:' + servers[0].port, 0, 5) |
156 | 160 | ||
157 | expect(res.body.total).to.equal(1) | 161 | expect(res.body.total).to.equal(1) |
158 | expect(res.body.data).to.have.lengthOf(1) | 162 | expect(res.body.data).to.have.lengthOf(1) |
159 | }) | 163 | }) |
160 | 164 | ||
161 | it('Should not list remote account videos', async function () { | 165 | it('Should not list remote account videos', async function () { |
162 | const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:9002', 0, 5) | 166 | const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:' + servers[1].port, 0, 5) |
163 | 167 | ||
164 | expect(res.body.total).to.equal(0) | 168 | expect(res.body.total).to.equal(0) |
165 | expect(res.body.data).to.have.lengthOf(0) | 169 | expect(res.body.data).to.have.lengthOf(0) |
166 | }) | 170 | }) |
167 | 171 | ||
168 | it('Should list local channel videos', async function () { | 172 | it('Should list local channel videos', async function () { |
169 | const res = await getVideoChannelVideos(servers[0].url, undefined, 'root_channel@localhost:9001', 0, 5) | 173 | const videoChannelName = 'root_channel@localhost:' + servers[0].port |
174 | const res = await getVideoChannelVideos(servers[0].url, undefined, videoChannelName, 0, 5) | ||
170 | 175 | ||
171 | expect(res.body.total).to.equal(1) | 176 | expect(res.body.total).to.equal(1) |
172 | expect(res.body.data).to.have.lengthOf(1) | 177 | expect(res.body.data).to.have.lengthOf(1) |
173 | }) | 178 | }) |
174 | 179 | ||
175 | it('Should not list remote channel videos', async function () { | 180 | it('Should not list remote channel videos', async function () { |
176 | const res = await getVideoChannelVideos(servers[0].url, undefined, 'root_channel@localhost:9002', 0, 5) | 181 | const videoChannelName = 'root_channel@localhost:' + servers[1].port |
182 | const res = await getVideoChannelVideos(servers[0].url, undefined, videoChannelName, 0, 5) | ||
177 | 183 | ||
178 | expect(res.body.total).to.equal(0) | 184 | expect(res.body.total).to.equal(0) |
179 | expect(res.body.data).to.have.lengthOf(0) | 185 | expect(res.body.data).to.have.lengthOf(0) |
@@ -190,28 +196,30 @@ describe('Test follow constraints', function () { | |||
190 | }) | 196 | }) |
191 | 197 | ||
192 | it('Should list local account videos', async function () { | 198 | it('Should list local account videos', async function () { |
193 | const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:9001', 0, 5) | 199 | const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:' + servers[0].port, 0, 5) |
194 | 200 | ||
195 | expect(res.body.total).to.equal(1) | 201 | expect(res.body.total).to.equal(1) |
196 | expect(res.body.data).to.have.lengthOf(1) | 202 | expect(res.body.data).to.have.lengthOf(1) |
197 | }) | 203 | }) |
198 | 204 | ||
199 | it('Should list remote account videos', async function () { | 205 | it('Should list remote account videos', async function () { |
200 | const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:9002', 0, 5) | 206 | const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:' + servers[1].port, 0, 5) |
201 | 207 | ||
202 | expect(res.body.total).to.equal(1) | 208 | expect(res.body.total).to.equal(1) |
203 | expect(res.body.data).to.have.lengthOf(1) | 209 | expect(res.body.data).to.have.lengthOf(1) |
204 | }) | 210 | }) |
205 | 211 | ||
206 | it('Should list local channel videos', async function () { | 212 | it('Should list local channel videos', async function () { |
207 | const res = await getVideoChannelVideos(servers[0].url, userAccessToken, 'root_channel@localhost:9001', 0, 5) | 213 | const videoChannelName = 'root_channel@localhost:' + servers[0].port |
214 | const res = await getVideoChannelVideos(servers[0].url, userAccessToken, videoChannelName, 0, 5) | ||
208 | 215 | ||
209 | expect(res.body.total).to.equal(1) | 216 | expect(res.body.total).to.equal(1) |
210 | expect(res.body.data).to.have.lengthOf(1) | 217 | expect(res.body.data).to.have.lengthOf(1) |
211 | }) | 218 | }) |
212 | 219 | ||
213 | it('Should list remote channel videos', async function () { | 220 | it('Should list remote channel videos', async function () { |
214 | const res = await getVideoChannelVideos(servers[0].url, userAccessToken, 'root_channel@localhost:9002', 0, 5) | 221 | const videoChannelName = 'root_channel@localhost:' + servers[1].port |
222 | const res = await getVideoChannelVideos(servers[0].url, userAccessToken, videoChannelName, 0, 5) | ||
215 | 223 | ||
216 | expect(res.body.total).to.equal(1) | 224 | expect(res.body.total).to.equal(1) |
217 | expect(res.body.data).to.have.lengthOf(1) | 225 | expect(res.body.data).to.have.lengthOf(1) |
diff --git a/server/tests/api/server/follows-moderation.ts b/server/tests/api/server/follows-moderation.ts index 2a3a4d5c8..a82acdb34 100644 --- a/server/tests/api/server/follows-moderation.ts +++ b/server/tests/api/server/follows-moderation.ts | |||
@@ -3,9 +3,9 @@ | |||
3 | import * as chai from 'chai' | 3 | import * as chai from 'chai' |
4 | import 'mocha' | 4 | import 'mocha' |
5 | import { | 5 | import { |
6 | acceptFollower, cleanupTests, | 6 | acceptFollower, |
7 | cleanupTests, | ||
7 | flushAndRunMultipleServers, | 8 | flushAndRunMultipleServers, |
8 | killallServers, | ||
9 | ServerInfo, | 9 | ServerInfo, |
10 | setAccessTokensToServers, | 10 | setAccessTokensToServers, |
11 | updateCustomSubConfig | 11 | updateCustomSubConfig |
@@ -14,8 +14,8 @@ import { | |||
14 | follow, | 14 | follow, |
15 | getFollowersListPaginationAndSort, | 15 | getFollowersListPaginationAndSort, |
16 | getFollowingListPaginationAndSort, | 16 | getFollowingListPaginationAndSort, |
17 | removeFollower, | 17 | rejectFollower, |
18 | rejectFollower | 18 | removeFollower |
19 | } from '../../../../shared/extra-utils/server/follows' | 19 | } from '../../../../shared/extra-utils/server/follows' |
20 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | 20 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' |
21 | import { ActorFollow } from '../../../../shared/models/actors' | 21 | import { ActorFollow } from '../../../../shared/models/actors' |
@@ -29,8 +29,8 @@ async function checkServer1And2HasFollowers (servers: ServerInfo[], state = 'acc | |||
29 | 29 | ||
30 | const follow = res.body.data[0] as ActorFollow | 30 | const follow = res.body.data[0] as ActorFollow |
31 | expect(follow.state).to.equal(state) | 31 | expect(follow.state).to.equal(state) |
32 | expect(follow.follower.url).to.equal('http://localhost:9001/accounts/peertube') | 32 | expect(follow.follower.url).to.equal('http://localhost:' + servers[0].port + '/accounts/peertube') |
33 | expect(follow.following.url).to.equal('http://localhost:9002/accounts/peertube') | 33 | expect(follow.following.url).to.equal('http://localhost:' + servers[1].port + '/accounts/peertube') |
34 | } | 34 | } |
35 | 35 | ||
36 | { | 36 | { |
@@ -39,8 +39,8 @@ async function checkServer1And2HasFollowers (servers: ServerInfo[], state = 'acc | |||
39 | 39 | ||
40 | const follow = res.body.data[0] as ActorFollow | 40 | const follow = res.body.data[0] as ActorFollow |
41 | expect(follow.state).to.equal(state) | 41 | expect(follow.state).to.equal(state) |
42 | expect(follow.follower.url).to.equal('http://localhost:9001/accounts/peertube') | 42 | expect(follow.follower.url).to.equal('http://localhost:' + servers[0].port + '/accounts/peertube') |
43 | expect(follow.following.url).to.equal('http://localhost:9002/accounts/peertube') | 43 | expect(follow.following.url).to.equal('http://localhost:' + servers[1].port + '/accounts/peertube') |
44 | } | 44 | } |
45 | } | 45 | } |
46 | 46 | ||
@@ -151,7 +151,7 @@ describe('Test follows moderation', function () { | |||
151 | }) | 151 | }) |
152 | 152 | ||
153 | it('Should accept a follower', async function () { | 153 | it('Should accept a follower', async function () { |
154 | await acceptFollower(servers[1].url, servers[1].accessToken, 'peertube@localhost:9001') | 154 | await acceptFollower(servers[1].url, servers[1].accessToken, 'peertube@localhost:' + servers[0].port) |
155 | await waitJobs(servers) | 155 | await waitJobs(servers) |
156 | 156 | ||
157 | await checkServer1And2HasFollowers(servers) | 157 | await checkServer1And2HasFollowers(servers) |
@@ -178,7 +178,7 @@ describe('Test follows moderation', function () { | |||
178 | expect(res.body.total).to.equal(1) | 178 | expect(res.body.total).to.equal(1) |
179 | } | 179 | } |
180 | 180 | ||
181 | await rejectFollower(servers[2].url, servers[2].accessToken, 'peertube@localhost:9001') | 181 | await rejectFollower(servers[2].url, servers[2].accessToken, 'peertube@localhost:' + servers[0].port) |
182 | await waitJobs(servers) | 182 | await waitJobs(servers) |
183 | 183 | ||
184 | await checkServer1And2HasFollowers(servers) | 184 | await checkServer1And2HasFollowers(servers) |
diff --git a/server/tests/api/server/follows.ts b/server/tests/api/server/follows.ts index 397093cdb..e8d6f5138 100644 --- a/server/tests/api/server/follows.ts +++ b/server/tests/api/server/follows.ts | |||
@@ -8,7 +8,6 @@ import { cleanupTests, completeVideoCheck } from '../../../../shared/extra-utils | |||
8 | import { | 8 | import { |
9 | flushAndRunMultipleServers, | 9 | flushAndRunMultipleServers, |
10 | getVideosList, | 10 | getVideosList, |
11 | killallServers, | ||
12 | ServerInfo, | 11 | ServerInfo, |
13 | setAccessTokensToServers, | 12 | setAccessTokensToServers, |
14 | uploadVideo | 13 | uploadVideo |
@@ -89,8 +88,8 @@ describe('Test follows', function () { | |||
89 | res = await getFollowingListPaginationAndSort(servers[0].url, 1, 1, 'createdAt') | 88 | res = await getFollowingListPaginationAndSort(servers[0].url, 1, 1, 'createdAt') |
90 | follows = follows.concat(res.body.data) | 89 | follows = follows.concat(res.body.data) |
91 | 90 | ||
92 | const server2Follow = follows.find(f => f.following.host === 'localhost:9002') | 91 | const server2Follow = follows.find(f => f.following.host === 'localhost:' + servers[1].port) |
93 | const server3Follow = follows.find(f => f.following.host === 'localhost:9003') | 92 | const server3Follow = follows.find(f => f.following.host === 'localhost:' + servers[2].port) |
94 | 93 | ||
95 | expect(server2Follow).to.not.be.undefined | 94 | expect(server2Follow).to.not.be.undefined |
96 | expect(server3Follow).to.not.be.undefined | 95 | expect(server3Follow).to.not.be.undefined |
@@ -100,12 +99,12 @@ describe('Test follows', function () { | |||
100 | 99 | ||
101 | it('Should search followings on server 1', async function () { | 100 | it('Should search followings on server 1', async function () { |
102 | { | 101 | { |
103 | const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 1, 'createdAt', ':9002') | 102 | const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 1, 'createdAt', ':' + servers[1].port) |
104 | const follows = res.body.data | 103 | const follows = res.body.data |
105 | 104 | ||
106 | expect(res.body.total).to.equal(1) | 105 | expect(res.body.total).to.equal(1) |
107 | expect(follows.length).to.equal(1) | 106 | expect(follows.length).to.equal(1) |
108 | expect(follows[ 0 ].following.host).to.equal('localhost:9002') | 107 | expect(follows[ 0 ].following.host).to.equal('localhost:' + servers[1].port) |
109 | } | 108 | } |
110 | 109 | ||
111 | { | 110 | { |
@@ -136,18 +135,18 @@ describe('Test follows', function () { | |||
136 | expect(res.body.total).to.equal(1) | 135 | expect(res.body.total).to.equal(1) |
137 | expect(follows).to.be.an('array') | 136 | expect(follows).to.be.an('array') |
138 | expect(follows.length).to.equal(1) | 137 | expect(follows.length).to.equal(1) |
139 | expect(follows[0].follower.host).to.equal('localhost:9001') | 138 | expect(follows[0].follower.host).to.equal('localhost:' + servers[0].port) |
140 | } | 139 | } |
141 | }) | 140 | }) |
142 | 141 | ||
143 | it('Should search followers on server 2', async function () { | 142 | it('Should search followers on server 2', async function () { |
144 | { | 143 | { |
145 | const res = await getFollowersListPaginationAndSort(servers[ 2 ].url, 0, 5, 'createdAt', '9001') | 144 | const res = await getFollowersListPaginationAndSort(servers[ 2 ].url, 0, 5, 'createdAt', servers[0].port + '') |
146 | const follows = res.body.data | 145 | const follows = res.body.data |
147 | 146 | ||
148 | expect(res.body.total).to.equal(1) | 147 | expect(res.body.total).to.equal(1) |
149 | expect(follows.length).to.equal(1) | 148 | expect(follows.length).to.equal(1) |
150 | expect(follows[ 0 ].following.host).to.equal('localhost:9003') | 149 | expect(follows[ 0 ].following.host).to.equal('localhost:' + servers[2].port) |
151 | } | 150 | } |
152 | 151 | ||
153 | { | 152 | { |
@@ -169,16 +168,16 @@ describe('Test follows', function () { | |||
169 | }) | 168 | }) |
170 | 169 | ||
171 | it('Should have the correct follows counts', async function () { | 170 | it('Should have the correct follows counts', async function () { |
172 | await expectAccountFollows(servers[0].url, 'peertube@localhost:9001', 0, 2) | 171 | await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[0].port, 0, 2) |
173 | await expectAccountFollows(servers[0].url, 'peertube@localhost:9002', 1, 0) | 172 | await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[1].port, 1, 0) |
174 | await expectAccountFollows(servers[0].url, 'peertube@localhost:9003', 1, 0) | 173 | await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[2].port, 1, 0) |
175 | 174 | ||
176 | // Server 2 and 3 does not know server 1 follow another server (there was not a refresh) | 175 | // Server 2 and 3 does not know server 1 follow another server (there was not a refresh) |
177 | await expectAccountFollows(servers[1].url, 'peertube@localhost:9001', 0, 1) | 176 | await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[0].port, 0, 1) |
178 | await expectAccountFollows(servers[1].url, 'peertube@localhost:9002', 1, 0) | 177 | await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[1].port, 1, 0) |
179 | 178 | ||
180 | await expectAccountFollows(servers[2].url, 'peertube@localhost:9001', 0, 1) | 179 | await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[0].port, 0, 1) |
181 | await expectAccountFollows(servers[2].url, 'peertube@localhost:9003', 1, 0) | 180 | await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[2].port, 1, 0) |
182 | }) | 181 | }) |
183 | 182 | ||
184 | it('Should unfollow server 3 on server 1', async function () { | 183 | it('Should unfollow server 3 on server 1', async function () { |
@@ -197,7 +196,7 @@ describe('Test follows', function () { | |||
197 | expect(follows).to.be.an('array') | 196 | expect(follows).to.be.an('array') |
198 | expect(follows.length).to.equal(1) | 197 | expect(follows.length).to.equal(1) |
199 | 198 | ||
200 | expect(follows[0].following.host).to.equal('localhost:9002') | 199 | expect(follows[0].following.host).to.equal('localhost:' + servers[1].port) |
201 | }) | 200 | }) |
202 | 201 | ||
203 | it('Should not have server 1 as follower on server 3 anymore', async function () { | 202 | it('Should not have server 1 as follower on server 3 anymore', async function () { |
@@ -210,14 +209,14 @@ describe('Test follows', function () { | |||
210 | }) | 209 | }) |
211 | 210 | ||
212 | it('Should have the correct follows counts 2', async function () { | 211 | it('Should have the correct follows counts 2', async function () { |
213 | await expectAccountFollows(servers[0].url, 'peertube@localhost:9001', 0, 1) | 212 | await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[0].port, 0, 1) |
214 | await expectAccountFollows(servers[0].url, 'peertube@localhost:9002', 1, 0) | 213 | await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[1].port, 1, 0) |
215 | 214 | ||
216 | await expectAccountFollows(servers[1].url, 'peertube@localhost:9001', 0, 1) | 215 | await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[0].port, 0, 1) |
217 | await expectAccountFollows(servers[1].url, 'peertube@localhost:9002', 1, 0) | 216 | await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[1].port, 1, 0) |
218 | 217 | ||
219 | await expectAccountFollows(servers[2].url, 'peertube@localhost:9001', 0, 0) | 218 | await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[0].port, 0, 0) |
220 | await expectAccountFollows(servers[2].url, 'peertube@localhost:9003', 0, 0) | 219 | await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[2].port, 0, 0) |
221 | }) | 220 | }) |
222 | 221 | ||
223 | it('Should upload a video on server 2 and 3 and propagate only the video of server 2', async function () { | 222 | it('Should upload a video on server 2 and 3 and propagate only the video of server 2', async function () { |
@@ -310,15 +309,15 @@ describe('Test follows', function () { | |||
310 | }) | 309 | }) |
311 | 310 | ||
312 | it('Should have the correct follows counts 3', async function () { | 311 | it('Should have the correct follows counts 3', async function () { |
313 | await expectAccountFollows(servers[0].url, 'peertube@localhost:9001', 0, 2) | 312 | await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[0].port, 0, 2) |
314 | await expectAccountFollows(servers[0].url, 'peertube@localhost:9002', 1, 0) | 313 | await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[1].port, 1, 0) |
315 | await expectAccountFollows(servers[0].url, 'peertube@localhost:9003', 1, 0) | 314 | await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[2].port, 1, 0) |
316 | 315 | ||
317 | await expectAccountFollows(servers[1].url, 'peertube@localhost:9001', 0, 1) | 316 | await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[0].port, 0, 1) |
318 | await expectAccountFollows(servers[1].url, 'peertube@localhost:9002', 1, 0) | 317 | await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[1].port, 1, 0) |
319 | 318 | ||
320 | await expectAccountFollows(servers[2].url, 'peertube@localhost:9001', 0, 2) | 319 | await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[0].port, 0, 2) |
321 | await expectAccountFollows(servers[2].url, 'peertube@localhost:9003', 1, 0) | 320 | await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[2].port, 1, 0) |
322 | }) | 321 | }) |
323 | 322 | ||
324 | it('Should have propagated videos', async function () { | 323 | it('Should have propagated videos', async function () { |
@@ -344,7 +343,7 @@ describe('Test follows', function () { | |||
344 | support: 'my super support text', | 343 | support: 'my super support text', |
345 | account: { | 344 | account: { |
346 | name: 'root', | 345 | name: 'root', |
347 | host: 'localhost:9003' | 346 | host: 'localhost:' + servers[2].port |
348 | }, | 347 | }, |
349 | isLocal, | 348 | isLocal, |
350 | commentsEnabled: true, | 349 | commentsEnabled: true, |
@@ -384,7 +383,7 @@ describe('Test follows', function () { | |||
384 | expect(comment.videoId).to.equal(video4.id) | 383 | expect(comment.videoId).to.equal(video4.id) |
385 | expect(comment.id).to.equal(comment.threadId) | 384 | expect(comment.id).to.equal(comment.threadId) |
386 | expect(comment.account.name).to.equal('root') | 385 | expect(comment.account.name).to.equal('root') |
387 | expect(comment.account.host).to.equal('localhost:9003') | 386 | expect(comment.account.host).to.equal('localhost:' + servers[2].port) |
388 | expect(comment.totalReplies).to.equal(3) | 387 | expect(comment.totalReplies).to.equal(3) |
389 | expect(dateIsValid(comment.createdAt as string)).to.be.true | 388 | expect(dateIsValid(comment.createdAt as string)).to.be.true |
390 | expect(dateIsValid(comment.updatedAt as string)).to.be.true | 389 | expect(dateIsValid(comment.updatedAt as string)).to.be.true |
diff --git a/server/tests/api/server/handle-down.ts b/server/tests/api/server/handle-down.ts index 19010dbc1..068654d8c 100644 --- a/server/tests/api/server/handle-down.ts +++ b/server/tests/api/server/handle-down.ts | |||
@@ -60,48 +60,50 @@ describe('Test handle downs', function () { | |||
60 | privacy: VideoPrivacy.UNLISTED | 60 | privacy: VideoPrivacy.UNLISTED |
61 | }) | 61 | }) |
62 | 62 | ||
63 | const checkAttributes = { | 63 | let checkAttributes: any |
64 | name: 'my super name for server 1', | 64 | let unlistedCheckAttributes: any |
65 | category: 5, | ||
66 | licence: 4, | ||
67 | language: 'ja', | ||
68 | nsfw: true, | ||
69 | description: 'my super description for server 1', | ||
70 | support: 'my super support text for server 1', | ||
71 | account: { | ||
72 | name: 'root', | ||
73 | host: 'localhost:9001' | ||
74 | }, | ||
75 | isLocal: false, | ||
76 | duration: 10, | ||
77 | tags: [ 'tag1p1', 'tag2p1' ], | ||
78 | privacy: VideoPrivacy.PUBLIC, | ||
79 | commentsEnabled: true, | ||
80 | downloadEnabled: true, | ||
81 | channel: { | ||
82 | name: 'root_channel', | ||
83 | displayName: 'Main root channel', | ||
84 | description: '', | ||
85 | isLocal: false | ||
86 | }, | ||
87 | fixture: 'video_short1.webm', | ||
88 | files: [ | ||
89 | { | ||
90 | resolution: 720, | ||
91 | size: 572456 | ||
92 | } | ||
93 | ] | ||
94 | } | ||
95 | |||
96 | const unlistedCheckAttributes = immutableAssign(checkAttributes, { | ||
97 | privacy: VideoPrivacy.UNLISTED | ||
98 | }) | ||
99 | 65 | ||
100 | before(async function () { | 66 | before(async function () { |
101 | this.timeout(30000) | 67 | this.timeout(30000) |
102 | 68 | ||
103 | servers = await flushAndRunMultipleServers(3) | 69 | servers = await flushAndRunMultipleServers(3) |
104 | 70 | ||
71 | checkAttributes = { | ||
72 | name: 'my super name for server 1', | ||
73 | category: 5, | ||
74 | licence: 4, | ||
75 | language: 'ja', | ||
76 | nsfw: true, | ||
77 | description: 'my super description for server 1', | ||
78 | support: 'my super support text for server 1', | ||
79 | account: { | ||
80 | name: 'root', | ||
81 | host: 'localhost:' + servers[0].port | ||
82 | }, | ||
83 | isLocal: false, | ||
84 | duration: 10, | ||
85 | tags: [ 'tag1p1', 'tag2p1' ], | ||
86 | privacy: VideoPrivacy.PUBLIC, | ||
87 | commentsEnabled: true, | ||
88 | downloadEnabled: true, | ||
89 | channel: { | ||
90 | name: 'root_channel', | ||
91 | displayName: 'Main root channel', | ||
92 | description: '', | ||
93 | isLocal: false | ||
94 | }, | ||
95 | fixture: 'video_short1.webm', | ||
96 | files: [ | ||
97 | { | ||
98 | resolution: 720, | ||
99 | size: 572456 | ||
100 | } | ||
101 | ] | ||
102 | } | ||
103 | unlistedCheckAttributes = immutableAssign(checkAttributes, { | ||
104 | privacy: VideoPrivacy.UNLISTED | ||
105 | }) | ||
106 | |||
105 | // Get the access tokens | 107 | // Get the access tokens |
106 | await setAccessTokensToServers(servers) | 108 | await setAccessTokensToServers(servers) |
107 | }) | 109 | }) |
@@ -172,7 +174,7 @@ describe('Test handle downs', function () { | |||
172 | const res = await getFollowersListPaginationAndSort(servers[0].url, 0, 2, 'createdAt') | 174 | const res = await getFollowersListPaginationAndSort(servers[0].url, 0, 2, 'createdAt') |
173 | expect(res.body.data).to.be.an('array') | 175 | expect(res.body.data).to.be.an('array') |
174 | expect(res.body.data).to.have.lengthOf(1) | 176 | expect(res.body.data).to.have.lengthOf(1) |
175 | expect(res.body.data[0].follower.host).to.equal('localhost:9003') | 177 | expect(res.body.data[0].follower.host).to.equal('localhost:' + servers[2].port) |
176 | }) | 178 | }) |
177 | 179 | ||
178 | it('Should not have pending/processing jobs anymore', async function () { | 180 | it('Should not have pending/processing jobs anymore', async function () { |
diff --git a/server/tests/api/server/jobs.ts b/server/tests/api/server/jobs.ts index 634654626..3ab2fe120 100644 --- a/server/tests/api/server/jobs.ts +++ b/server/tests/api/server/jobs.ts | |||
@@ -26,7 +26,7 @@ describe('Test jobs', function () { | |||
26 | }) | 26 | }) |
27 | 27 | ||
28 | it('Should create some jobs', async function () { | 28 | it('Should create some jobs', async function () { |
29 | this.timeout(30000) | 29 | this.timeout(60000) |
30 | 30 | ||
31 | await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video1' }) | 31 | await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video1' }) |
32 | await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video2' }) | 32 | await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video2' }) |
diff --git a/server/tests/api/server/logs.ts b/server/tests/api/server/logs.ts index 3644fa0d3..68f442199 100644 --- a/server/tests/api/server/logs.ts +++ b/server/tests/api/server/logs.ts | |||
@@ -45,7 +45,7 @@ describe('Test logs', function () { | |||
45 | }) | 45 | }) |
46 | 46 | ||
47 | it('Should get logs with an end date', async function () { | 47 | it('Should get logs with an end date', async function () { |
48 | this.timeout(10000) | 48 | this.timeout(20000) |
49 | 49 | ||
50 | await uploadVideo(server.url, server.accessToken, { name: 'video 3' }) | 50 | await uploadVideo(server.url, server.accessToken, { name: 'video 3' }) |
51 | await waitJobs([ server ]) | 51 | await waitJobs([ server ]) |
diff --git a/server/tests/api/travis-1.sh b/server/tests/api/travis-1.sh new file mode 100644 index 000000000..db4021b25 --- /dev/null +++ b/server/tests/api/travis-1.sh | |||
@@ -0,0 +1,10 @@ | |||
1 | #!/usr/bin/env sh | ||
2 | |||
3 | set -eu | ||
4 | |||
5 | checkParamFiles=$(find server/tests/api/check-params -type f | grep -v index.ts | xargs echo) | ||
6 | notificationsFiles=$(find server/tests/api/notifications -type f | grep -v index.ts | xargs echo) | ||
7 | searchFiles=$(find server/tests/api/search -type f | grep -v index.ts | xargs echo) | ||
8 | |||
9 | MOCHA_PARALLEL=true mocha --timeout 5000 --exit --require ts-node/register --bail \ | ||
10 | $notificationsFiles $searchFiles $checkParamFiles | ||
diff --git a/server/tests/api/travis-2.sh b/server/tests/api/travis-2.sh new file mode 100644 index 000000000..ba7a061b0 --- /dev/null +++ b/server/tests/api/travis-2.sh | |||
@@ -0,0 +1,9 @@ | |||
1 | #!/usr/bin/env sh | ||
2 | |||
3 | set -eu | ||
4 | |||
5 | serverFiles=$(find server/tests/api/server -type f | grep -v index.ts | xargs echo) | ||
6 | usersFiles=$(find server/tests/api/users -type f | grep -v index.ts | xargs echo) | ||
7 | |||
8 | MOCHA_PARALLEL=true mocha --timeout 5000 --exit --require ts-node/register --bail \ | ||
9 | $serverFiles $usersFiles | ||
diff --git a/server/tests/api/travis-3.sh b/server/tests/api/travis-3.sh new file mode 100644 index 000000000..82457222c --- /dev/null +++ b/server/tests/api/travis-3.sh | |||
@@ -0,0 +1,8 @@ | |||
1 | #!/usr/bin/env sh | ||
2 | |||
3 | set -eu | ||
4 | |||
5 | videosFiles=$(find server/tests/api/videos -type f | grep -v index.ts | xargs echo) | ||
6 | |||
7 | MOCHA_PARALLEL=true mocha --timeout 5000 --exit --require ts-node/register --bail \ | ||
8 | $videosFiles | ||
diff --git a/server/tests/api/travis-4.sh b/server/tests/api/travis-4.sh new file mode 100644 index 000000000..875986182 --- /dev/null +++ b/server/tests/api/travis-4.sh | |||
@@ -0,0 +1,9 @@ | |||
1 | #!/usr/bin/env sh | ||
2 | |||
3 | set -eu | ||
4 | |||
5 | redundancyFiles=$(find server/tests/api/redundancy -type f | grep -v index.ts | xargs echo) | ||
6 | activitypubFiles=$(find server/tests/api/activitypub -type f | grep -v index.ts | xargs echo) | ||
7 | |||
8 | MOCHA_PARALLEL=true mocha-parallel-tests --max-parallel $1 --timeout 5000 --exit --require ts-node/register --bail \ | ||
9 | $redundancyFiles $activitypubFiles | ||
diff --git a/server/tests/api/users/blocklist.ts b/server/tests/api/users/blocklist.ts index fbc57e0ef..c25e85ada 100644 --- a/server/tests/api/users/blocklist.ts +++ b/server/tests/api/users/blocklist.ts | |||
@@ -144,7 +144,7 @@ describe('Test blocklist', function () { | |||
144 | }) | 144 | }) |
145 | 145 | ||
146 | it('Should block a remote account', async function () { | 146 | it('Should block a remote account', async function () { |
147 | await addAccountToAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user2@localhost:9002') | 147 | await addAccountToAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user2@localhost:' + servers[1].port) |
148 | }) | 148 | }) |
149 | 149 | ||
150 | it('Should hide its videos', async function () { | 150 | it('Should hide its videos', async function () { |
@@ -209,7 +209,7 @@ describe('Test blocklist', function () { | |||
209 | expect(block.byAccount.name).to.equal('root') | 209 | expect(block.byAccount.name).to.equal('root') |
210 | expect(block.blockedAccount.displayName).to.equal('user2') | 210 | expect(block.blockedAccount.displayName).to.equal('user2') |
211 | expect(block.blockedAccount.name).to.equal('user2') | 211 | expect(block.blockedAccount.name).to.equal('user2') |
212 | expect(block.blockedAccount.host).to.equal('localhost:9002') | 212 | expect(block.blockedAccount.host).to.equal('localhost:' + servers[1].port) |
213 | } | 213 | } |
214 | 214 | ||
215 | { | 215 | { |
@@ -223,12 +223,12 @@ describe('Test blocklist', function () { | |||
223 | expect(block.byAccount.name).to.equal('root') | 223 | expect(block.byAccount.name).to.equal('root') |
224 | expect(block.blockedAccount.displayName).to.equal('user1') | 224 | expect(block.blockedAccount.displayName).to.equal('user1') |
225 | expect(block.blockedAccount.name).to.equal('user1') | 225 | expect(block.blockedAccount.name).to.equal('user1') |
226 | expect(block.blockedAccount.host).to.equal('localhost:9001') | 226 | expect(block.blockedAccount.host).to.equal('localhost:' + servers[0].port) |
227 | } | 227 | } |
228 | }) | 228 | }) |
229 | 229 | ||
230 | it('Should unblock the remote account', async function () { | 230 | it('Should unblock the remote account', async function () { |
231 | await removeAccountFromAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user2@localhost:9002') | 231 | await removeAccountFromAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user2@localhost:' + servers[1].port) |
232 | }) | 232 | }) |
233 | 233 | ||
234 | it('Should display its videos', async function () { | 234 | it('Should display its videos', async function () { |
@@ -260,7 +260,7 @@ describe('Test blocklist', function () { | |||
260 | }) | 260 | }) |
261 | 261 | ||
262 | it('Should block a remote server', async function () { | 262 | it('Should block a remote server', async function () { |
263 | await addServerToAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:9002') | 263 | await addServerToAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:' + servers[1].port) |
264 | }) | 264 | }) |
265 | 265 | ||
266 | it('Should hide its videos', async function () { | 266 | it('Should hide its videos', async function () { |
@@ -291,11 +291,11 @@ describe('Test blocklist', function () { | |||
291 | const block = blocks[ 0 ] | 291 | const block = blocks[ 0 ] |
292 | expect(block.byAccount.displayName).to.equal('root') | 292 | expect(block.byAccount.displayName).to.equal('root') |
293 | expect(block.byAccount.name).to.equal('root') | 293 | expect(block.byAccount.name).to.equal('root') |
294 | expect(block.blockedServer.host).to.equal('localhost:9002') | 294 | expect(block.blockedServer.host).to.equal('localhost:' + servers[1].port) |
295 | }) | 295 | }) |
296 | 296 | ||
297 | it('Should unblock the remote server', async function () { | 297 | it('Should unblock the remote server', async function () { |
298 | await removeServerFromAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:9002') | 298 | await removeServerFromAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:' + servers[1].port) |
299 | }) | 299 | }) |
300 | 300 | ||
301 | it('Should display its videos', function () { | 301 | it('Should display its videos', function () { |
@@ -324,7 +324,7 @@ describe('Test blocklist', function () { | |||
324 | }) | 324 | }) |
325 | 325 | ||
326 | it('Should block a remote account', async function () { | 326 | it('Should block a remote account', async function () { |
327 | await addAccountToServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user2@localhost:9002') | 327 | await addAccountToServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user2@localhost:' + servers[1].port) |
328 | }) | 328 | }) |
329 | 329 | ||
330 | it('Should hide its videos', async function () { | 330 | it('Should hide its videos', async function () { |
@@ -387,7 +387,7 @@ describe('Test blocklist', function () { | |||
387 | expect(block.byAccount.name).to.equal('peertube') | 387 | expect(block.byAccount.name).to.equal('peertube') |
388 | expect(block.blockedAccount.displayName).to.equal('user2') | 388 | expect(block.blockedAccount.displayName).to.equal('user2') |
389 | expect(block.blockedAccount.name).to.equal('user2') | 389 | expect(block.blockedAccount.name).to.equal('user2') |
390 | expect(block.blockedAccount.host).to.equal('localhost:9002') | 390 | expect(block.blockedAccount.host).to.equal('localhost:' + servers[1].port) |
391 | } | 391 | } |
392 | 392 | ||
393 | { | 393 | { |
@@ -401,12 +401,12 @@ describe('Test blocklist', function () { | |||
401 | expect(block.byAccount.name).to.equal('peertube') | 401 | expect(block.byAccount.name).to.equal('peertube') |
402 | expect(block.blockedAccount.displayName).to.equal('user1') | 402 | expect(block.blockedAccount.displayName).to.equal('user1') |
403 | expect(block.blockedAccount.name).to.equal('user1') | 403 | expect(block.blockedAccount.name).to.equal('user1') |
404 | expect(block.blockedAccount.host).to.equal('localhost:9001') | 404 | expect(block.blockedAccount.host).to.equal('localhost:' + servers[0].port) |
405 | } | 405 | } |
406 | }) | 406 | }) |
407 | 407 | ||
408 | it('Should unblock the remote account', async function () { | 408 | it('Should unblock the remote account', async function () { |
409 | await removeAccountFromServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user2@localhost:9002') | 409 | await removeAccountFromServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user2@localhost:' + servers[1].port) |
410 | }) | 410 | }) |
411 | 411 | ||
412 | it('Should display its videos', async function () { | 412 | it('Should display its videos', async function () { |
@@ -446,7 +446,7 @@ describe('Test blocklist', function () { | |||
446 | }) | 446 | }) |
447 | 447 | ||
448 | it('Should block a remote server', async function () { | 448 | it('Should block a remote server', async function () { |
449 | await addServerToServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:9002') | 449 | await addServerToServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:' + servers[1].port) |
450 | }) | 450 | }) |
451 | 451 | ||
452 | it('Should hide its videos', async function () { | 452 | it('Should hide its videos', async function () { |
@@ -478,11 +478,11 @@ describe('Test blocklist', function () { | |||
478 | const block = blocks[ 0 ] | 478 | const block = blocks[ 0 ] |
479 | expect(block.byAccount.displayName).to.equal('peertube') | 479 | expect(block.byAccount.displayName).to.equal('peertube') |
480 | expect(block.byAccount.name).to.equal('peertube') | 480 | expect(block.byAccount.name).to.equal('peertube') |
481 | expect(block.blockedServer.host).to.equal('localhost:9002') | 481 | expect(block.blockedServer.host).to.equal('localhost:' + servers[1].port) |
482 | }) | 482 | }) |
483 | 483 | ||
484 | it('Should unblock the remote server', async function () { | 484 | it('Should unblock the remote server', async function () { |
485 | await removeServerFromServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:9002') | 485 | await removeServerFromServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:' + servers[1].port) |
486 | }) | 486 | }) |
487 | 487 | ||
488 | it('Should list all videos', async function () { | 488 | it('Should list all videos', async function () { |
diff --git a/server/tests/api/users/user-subscriptions.ts b/server/tests/api/users/user-subscriptions.ts index 48811e647..c8a89d6be 100644 --- a/server/tests/api/users/user-subscriptions.ts +++ b/server/tests/api/users/user-subscriptions.ts | |||
@@ -71,8 +71,8 @@ describe('Test users subscriptions', function () { | |||
71 | it('User of server 1 should follow user of server 3 and root of server 1', async function () { | 71 | it('User of server 1 should follow user of server 3 and root of server 1', async function () { |
72 | this.timeout(60000) | 72 | this.timeout(60000) |
73 | 73 | ||
74 | await addUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:9003') | 74 | await addUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:' + servers[2].port) |
75 | await addUserSubscription(servers[0].url, users[0].accessToken, 'root_channel@localhost:9001') | 75 | await addUserSubscription(servers[0].url, users[0].accessToken, 'root_channel@localhost:' + servers[0].port) |
76 | 76 | ||
77 | await waitJobs(servers) | 77 | await waitJobs(servers) |
78 | 78 | ||
@@ -116,22 +116,22 @@ describe('Test users subscriptions', function () { | |||
116 | 116 | ||
117 | it('Should get subscription', async function () { | 117 | it('Should get subscription', async function () { |
118 | { | 118 | { |
119 | const res = await getUserSubscription(servers[ 0 ].url, users[ 0 ].accessToken, 'user3_channel@localhost:9003') | 119 | const res = await getUserSubscription(servers[ 0 ].url, users[ 0 ].accessToken, 'user3_channel@localhost:' + servers[2].port) |
120 | const videoChannel: VideoChannel = res.body | 120 | const videoChannel: VideoChannel = res.body |
121 | 121 | ||
122 | expect(videoChannel.name).to.equal('user3_channel') | 122 | expect(videoChannel.name).to.equal('user3_channel') |
123 | expect(videoChannel.host).to.equal('localhost:9003') | 123 | expect(videoChannel.host).to.equal('localhost:' + servers[2].port) |
124 | expect(videoChannel.displayName).to.equal('Main user3 channel') | 124 | expect(videoChannel.displayName).to.equal('Main user3 channel') |
125 | expect(videoChannel.followingCount).to.equal(0) | 125 | expect(videoChannel.followingCount).to.equal(0) |
126 | expect(videoChannel.followersCount).to.equal(1) | 126 | expect(videoChannel.followersCount).to.equal(1) |
127 | } | 127 | } |
128 | 128 | ||
129 | { | 129 | { |
130 | const res = await getUserSubscription(servers[ 0 ].url, users[ 0 ].accessToken, 'root_channel@localhost:9001') | 130 | const res = await getUserSubscription(servers[ 0 ].url, users[ 0 ].accessToken, 'root_channel@localhost:' + servers[0].port) |
131 | const videoChannel: VideoChannel = res.body | 131 | const videoChannel: VideoChannel = res.body |
132 | 132 | ||
133 | expect(videoChannel.name).to.equal('root_channel') | 133 | expect(videoChannel.name).to.equal('root_channel') |
134 | expect(videoChannel.host).to.equal('localhost:9001') | 134 | expect(videoChannel.host).to.equal('localhost:' + servers[0].port) |
135 | expect(videoChannel.displayName).to.equal('Main root channel') | 135 | expect(videoChannel.displayName).to.equal('Main root channel') |
136 | expect(videoChannel.followingCount).to.equal(0) | 136 | expect(videoChannel.followingCount).to.equal(0) |
137 | expect(videoChannel.followersCount).to.equal(1) | 137 | expect(videoChannel.followersCount).to.equal(1) |
@@ -140,19 +140,19 @@ describe('Test users subscriptions', function () { | |||
140 | 140 | ||
141 | it('Should return the existing subscriptions', async function () { | 141 | it('Should return the existing subscriptions', async function () { |
142 | const uris = [ | 142 | const uris = [ |
143 | 'user3_channel@localhost:9003', | 143 | 'user3_channel@localhost:' + servers[2].port, |
144 | 'root2_channel@localhost:9001', | 144 | 'root2_channel@localhost:' + servers[0].port, |
145 | 'root_channel@localhost:9001', | 145 | 'root_channel@localhost:' + servers[0].port, |
146 | 'user3_channel@localhost:9001' | 146 | 'user3_channel@localhost:' + servers[0].port |
147 | ] | 147 | ] |
148 | 148 | ||
149 | const res = await areSubscriptionsExist(servers[ 0 ].url, users[ 0 ].accessToken, uris) | 149 | const res = await areSubscriptionsExist(servers[ 0 ].url, users[ 0 ].accessToken, uris) |
150 | const body = res.body | 150 | const body = res.body |
151 | 151 | ||
152 | expect(body['user3_channel@localhost:9003']).to.be.true | 152 | expect(body['user3_channel@localhost:' + servers[2].port]).to.be.true |
153 | expect(body['root2_channel@localhost:9001']).to.be.false | 153 | expect(body['root2_channel@localhost:' + servers[0].port]).to.be.false |
154 | expect(body['root_channel@localhost:9001']).to.be.true | 154 | expect(body['root_channel@localhost:' + servers[0].port]).to.be.true |
155 | expect(body['user3_channel@localhost:9001']).to.be.false | 155 | expect(body['user3_channel@localhost:' + servers[0].port]).to.be.false |
156 | }) | 156 | }) |
157 | 157 | ||
158 | it('Should list subscription videos', async function () { | 158 | it('Should list subscription videos', async function () { |
@@ -291,7 +291,7 @@ describe('Test users subscriptions', function () { | |||
291 | it('Should remove user of server 3 subscription', async function () { | 291 | it('Should remove user of server 3 subscription', async function () { |
292 | this.timeout(30000) | 292 | this.timeout(30000) |
293 | 293 | ||
294 | await removeUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:9003') | 294 | await removeUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:' + servers[2].port) |
295 | 295 | ||
296 | await waitJobs(servers) | 296 | await waitJobs(servers) |
297 | }) | 297 | }) |
@@ -312,7 +312,7 @@ describe('Test users subscriptions', function () { | |||
312 | it('Should remove the root subscription and not display the videos anymore', async function () { | 312 | it('Should remove the root subscription and not display the videos anymore', async function () { |
313 | this.timeout(30000) | 313 | this.timeout(30000) |
314 | 314 | ||
315 | await removeUserSubscription(servers[0].url, users[0].accessToken, 'root_channel@localhost:9001') | 315 | await removeUserSubscription(servers[0].url, users[0].accessToken, 'root_channel@localhost:' + servers[0].port) |
316 | 316 | ||
317 | await waitJobs(servers) | 317 | await waitJobs(servers) |
318 | 318 | ||
@@ -340,7 +340,7 @@ describe('Test users subscriptions', function () { | |||
340 | it('Should follow user of server 3 again', async function () { | 340 | it('Should follow user of server 3 again', async function () { |
341 | this.timeout(60000) | 341 | this.timeout(60000) |
342 | 342 | ||
343 | await addUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:9003') | 343 | await addUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:' + servers[2].port) |
344 | 344 | ||
345 | await waitJobs(servers) | 345 | await waitJobs(servers) |
346 | 346 | ||
diff --git a/server/tests/api/users/users-multiple-servers.ts b/server/tests/api/users/users-multiple-servers.ts index 9a971adb3..988fdad3f 100644 --- a/server/tests/api/users/users-multiple-servers.ts +++ b/server/tests/api/users/users-multiple-servers.ts | |||
@@ -151,13 +151,13 @@ describe('Test users with multiple servers', function () { | |||
151 | for (const server of servers) { | 151 | for (const server of servers) { |
152 | const resAccounts = await getAccountsList(server.url, '-createdAt') | 152 | const resAccounts = await getAccountsList(server.url, '-createdAt') |
153 | 153 | ||
154 | const rootServer1List = resAccounts.body.data.find(a => a.name === 'root' && a.host === 'localhost:9001') as Account | 154 | const rootServer1List = resAccounts.body.data.find(a => a.name === 'root' && a.host === 'localhost:' + servers[0].port) as Account |
155 | expect(rootServer1List).not.to.be.undefined | 155 | expect(rootServer1List).not.to.be.undefined |
156 | 156 | ||
157 | const resAccount = await getAccount(server.url, rootServer1List.name + '@' + rootServer1List.host) | 157 | const resAccount = await getAccount(server.url, rootServer1List.name + '@' + rootServer1List.host) |
158 | const rootServer1Get = resAccount.body as Account | 158 | const rootServer1Get = resAccount.body as Account |
159 | expect(rootServer1Get.name).to.equal('root') | 159 | expect(rootServer1Get.name).to.equal('root') |
160 | expect(rootServer1Get.host).to.equal('localhost:9001') | 160 | expect(rootServer1Get.host).to.equal('localhost:' + servers[0].port) |
161 | expect(rootServer1Get.displayName).to.equal('my super display name') | 161 | expect(rootServer1Get.displayName).to.equal('my super display name') |
162 | expect(rootServer1Get.description).to.equal('my super description updated') | 162 | expect(rootServer1Get.description).to.equal('my super description updated') |
163 | 163 | ||
@@ -188,12 +188,12 @@ describe('Test users with multiple servers', function () { | |||
188 | for (const server of servers) { | 188 | for (const server of servers) { |
189 | const resAccounts = await getAccountsList(server.url, '-createdAt') | 189 | const resAccounts = await getAccountsList(server.url, '-createdAt') |
190 | 190 | ||
191 | const accountDeleted = resAccounts.body.data.find(a => a.name === 'user1' && a.host === 'localhost:9001') as Account | 191 | const accountDeleted = resAccounts.body.data.find(a => a.name === 'user1' && a.host === 'localhost:' + servers[0].port) as Account |
192 | expect(accountDeleted).not.to.be.undefined | 192 | expect(accountDeleted).not.to.be.undefined |
193 | 193 | ||
194 | const resVideoChannels = await getVideoChannelsList(server.url, 0, 10) | 194 | const resVideoChannels = await getVideoChannelsList(server.url, 0, 10) |
195 | const videoChannelDeleted = resVideoChannels.body.data.find(a => { | 195 | const videoChannelDeleted = resVideoChannels.body.data.find(a => { |
196 | return a.displayName === 'Main user1 channel' && a.host === 'localhost:9001' | 196 | return a.displayName === 'Main user1 channel' && a.host === 'localhost:' + servers[0].port |
197 | }) as VideoChannel | 197 | }) as VideoChannel |
198 | expect(videoChannelDeleted).not.to.be.undefined | 198 | expect(videoChannelDeleted).not.to.be.undefined |
199 | } | 199 | } |
@@ -205,12 +205,12 @@ describe('Test users with multiple servers', function () { | |||
205 | for (const server of servers) { | 205 | for (const server of servers) { |
206 | const resAccounts = await getAccountsList(server.url, '-createdAt') | 206 | const resAccounts = await getAccountsList(server.url, '-createdAt') |
207 | 207 | ||
208 | const accountDeleted = resAccounts.body.data.find(a => a.name === 'user1' && a.host === 'localhost:9001') as Account | 208 | const accountDeleted = resAccounts.body.data.find(a => a.name === 'user1' && a.host === 'localhost:' + servers[0].port) as Account |
209 | expect(accountDeleted).to.be.undefined | 209 | expect(accountDeleted).to.be.undefined |
210 | 210 | ||
211 | const resVideoChannels = await getVideoChannelsList(server.url, 0, 10) | 211 | const resVideoChannels = await getVideoChannelsList(server.url, 0, 10) |
212 | const videoChannelDeleted = resVideoChannels.body.data.find(a => { | 212 | const videoChannelDeleted = resVideoChannels.body.data.find(a => { |
213 | return a.name === 'Main user1 channel' && a.host === 'localhost:9001' | 213 | return a.name === 'Main user1 channel' && a.host === 'localhost:' + servers[0].port |
214 | }) as VideoChannel | 214 | }) as VideoChannel |
215 | expect(videoChannelDeleted).to.be.undefined | 215 | expect(videoChannelDeleted).to.be.undefined |
216 | } | 216 | } |
@@ -218,14 +218,14 @@ describe('Test users with multiple servers', function () { | |||
218 | 218 | ||
219 | it('Should not have actor files', async () => { | 219 | it('Should not have actor files', async () => { |
220 | for (const server of servers) { | 220 | for (const server of servers) { |
221 | await checkActorFilesWereRemoved(userAccountUUID, server.serverNumber) | 221 | await checkActorFilesWereRemoved(userAccountUUID, server.internalServerNumber) |
222 | await checkActorFilesWereRemoved(userVideoChannelUUID, server.serverNumber) | 222 | await checkActorFilesWereRemoved(userVideoChannelUUID, server.internalServerNumber) |
223 | } | 223 | } |
224 | }) | 224 | }) |
225 | 225 | ||
226 | it('Should not have video files', async () => { | 226 | it('Should not have video files', async () => { |
227 | for (const server of servers) { | 227 | for (const server of servers) { |
228 | await checkVideoFilesWereRemoved(videoUUID, server.serverNumber) | 228 | await checkVideoFilesWereRemoved(videoUUID, server.internalServerNumber) |
229 | } | 229 | } |
230 | }) | 230 | }) |
231 | 231 | ||
diff --git a/server/tests/api/users/users-verification.ts b/server/tests/api/users/users-verification.ts index 514acf2e7..3b37a26cf 100644 --- a/server/tests/api/users/users-verification.ts +++ b/server/tests/api/users/users-verification.ts | |||
@@ -30,11 +30,12 @@ describe('Test users account verification', function () { | |||
30 | before(async function () { | 30 | before(async function () { |
31 | this.timeout(30000) | 31 | this.timeout(30000) |
32 | 32 | ||
33 | await MockSmtpServer.Instance.collectEmails(emails) | 33 | const port = await MockSmtpServer.Instance.collectEmails(emails) |
34 | 34 | ||
35 | const overrideConfig = { | 35 | const overrideConfig = { |
36 | smtp: { | 36 | smtp: { |
37 | hostname: 'localhost' | 37 | hostname: 'localhost', |
38 | port | ||
38 | } | 39 | } |
39 | } | 40 | } |
40 | server = await flushAndRunServer(1, overrideConfig) | 41 | server = await flushAndRunServer(1, overrideConfig) |
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts index c8e32f3f5..c1a24b838 100644 --- a/server/tests/api/users/users.ts +++ b/server/tests/api/users/users.ts | |||
@@ -316,7 +316,7 @@ describe('Test users', function () { | |||
316 | 316 | ||
317 | const rootUser = users[ 1 ] | 317 | const rootUser = users[ 1 ] |
318 | expect(rootUser.username).to.equal('root') | 318 | expect(rootUser.username).to.equal('root') |
319 | expect(rootUser.email).to.equal('admin1@example.com') | 319 | expect(rootUser.email).to.equal('admin' + server.internalServerNumber + '@example.com') |
320 | expect(user.nsfwPolicy).to.equal('display') | 320 | expect(user.nsfwPolicy).to.equal('display') |
321 | 321 | ||
322 | userId = user.id | 322 | userId = user.id |
@@ -334,7 +334,7 @@ describe('Test users', function () { | |||
334 | 334 | ||
335 | const user = users[ 0 ] | 335 | const user = users[ 0 ] |
336 | expect(user.username).to.equal('root') | 336 | expect(user.username).to.equal('root') |
337 | expect(user.email).to.equal('admin1@example.com') | 337 | expect(user.email).to.equal('admin' + server.internalServerNumber + '@example.com') |
338 | expect(user.roleLabel).to.equal('Administrator') | 338 | expect(user.roleLabel).to.equal('Administrator') |
339 | expect(user.nsfwPolicy).to.equal('display') | 339 | expect(user.nsfwPolicy).to.equal('display') |
340 | }) | 340 | }) |
@@ -379,7 +379,7 @@ describe('Test users', function () { | |||
379 | expect(users.length).to.equal(2) | 379 | expect(users.length).to.equal(2) |
380 | 380 | ||
381 | expect(users[ 0 ].username).to.equal('root') | 381 | expect(users[ 0 ].username).to.equal('root') |
382 | expect(users[ 0 ].email).to.equal('admin1@example.com') | 382 | expect(users[ 0 ].email).to.equal('admin' + server.internalServerNumber + '@example.com') |
383 | expect(users[ 0 ].nsfwPolicy).to.equal('display') | 383 | expect(users[ 0 ].nsfwPolicy).to.equal('display') |
384 | 384 | ||
385 | expect(users[ 1 ].username).to.equal('user_1') | 385 | expect(users[ 1 ].username).to.equal('user_1') |
diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts index 68c1e9a8d..e9625e5f7 100644 --- a/server/tests/api/videos/multiple-servers.ts +++ b/server/tests/api/videos/multiple-servers.ts | |||
@@ -9,18 +9,17 @@ import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/ | |||
9 | import { | 9 | import { |
10 | addVideoChannel, | 10 | addVideoChannel, |
11 | checkTmpIsEmpty, | 11 | checkTmpIsEmpty, |
12 | checkVideoFilesWereRemoved, cleanupTests, | 12 | checkVideoFilesWereRemoved, |
13 | cleanupTests, | ||
13 | completeVideoCheck, | 14 | completeVideoCheck, |
14 | createUser, | 15 | createUser, |
15 | dateIsValid, | 16 | dateIsValid, |
16 | doubleFollow, | 17 | doubleFollow, |
17 | flushAndRunMultipleServers, | 18 | flushAndRunMultipleServers, |
18 | flushTests, | ||
19 | getLocalVideos, | 19 | getLocalVideos, |
20 | getVideo, | 20 | getVideo, |
21 | getVideoChannelsList, | 21 | getVideoChannelsList, |
22 | getVideosList, | 22 | getVideosList, |
23 | killallServers, | ||
24 | rateVideo, | 23 | rateVideo, |
25 | removeVideo, | 24 | removeVideo, |
26 | ServerInfo, | 25 | ServerInfo, |
@@ -110,7 +109,7 @@ describe('Test multiple servers', function () { | |||
110 | // All servers should have this video | 109 | // All servers should have this video |
111 | let publishedAt: string = null | 110 | let publishedAt: string = null |
112 | for (const server of servers) { | 111 | for (const server of servers) { |
113 | const isLocal = server.url === 'http://localhost:9001' | 112 | const isLocal = server.port === servers[0].port |
114 | const checkAttributes = { | 113 | const checkAttributes = { |
115 | name: 'my super name for server 1', | 114 | name: 'my super name for server 1', |
116 | category: 5, | 115 | category: 5, |
@@ -122,7 +121,7 @@ describe('Test multiple servers', function () { | |||
122 | originallyPublishedAt: '2019-02-10T13:38:14.449Z', | 121 | originallyPublishedAt: '2019-02-10T13:38:14.449Z', |
123 | account: { | 122 | account: { |
124 | name: 'root', | 123 | name: 'root', |
125 | host: 'localhost:9001' | 124 | host: 'localhost:' + servers[0].port |
126 | }, | 125 | }, |
127 | isLocal, | 126 | isLocal, |
128 | publishedAt, | 127 | publishedAt, |
@@ -187,7 +186,7 @@ describe('Test multiple servers', function () { | |||
187 | 186 | ||
188 | // All servers should have this video | 187 | // All servers should have this video |
189 | for (const server of servers) { | 188 | for (const server of servers) { |
190 | const isLocal = server.url === 'http://localhost:9002' | 189 | const isLocal = server.url === 'http://localhost:' + servers[1].port |
191 | const checkAttributes = { | 190 | const checkAttributes = { |
192 | name: 'my super name for server 2', | 191 | name: 'my super name for server 2', |
193 | category: 4, | 192 | category: 4, |
@@ -198,7 +197,7 @@ describe('Test multiple servers', function () { | |||
198 | support: 'my super support text for server 2', | 197 | support: 'my super support text for server 2', |
199 | account: { | 198 | account: { |
200 | name: 'user1', | 199 | name: 'user1', |
201 | host: 'localhost:9002' | 200 | host: 'localhost:' + servers[1].port |
202 | }, | 201 | }, |
203 | isLocal, | 202 | isLocal, |
204 | commentsEnabled: true, | 203 | commentsEnabled: true, |
@@ -216,7 +215,7 @@ describe('Test multiple servers', function () { | |||
216 | files: [ | 215 | files: [ |
217 | { | 216 | { |
218 | resolution: 240, | 217 | resolution: 240, |
219 | size: 187000 | 218 | size: 189000 |
220 | }, | 219 | }, |
221 | { | 220 | { |
222 | resolution: 360, | 221 | resolution: 360, |
@@ -224,7 +223,7 @@ describe('Test multiple servers', function () { | |||
224 | }, | 223 | }, |
225 | { | 224 | { |
226 | resolution: 480, | 225 | resolution: 480, |
227 | size: 383000 | 226 | size: 384000 |
228 | }, | 227 | }, |
229 | { | 228 | { |
230 | resolution: 720, | 229 | resolution: 720, |
@@ -278,7 +277,7 @@ describe('Test multiple servers', function () { | |||
278 | 277 | ||
279 | // All servers should have this video | 278 | // All servers should have this video |
280 | for (const server of servers) { | 279 | for (const server of servers) { |
281 | const isLocal = server.url === 'http://localhost:9003' | 280 | const isLocal = server.url === 'http://localhost:' + servers[2].port |
282 | const res = await getVideosList(server.url) | 281 | const res = await getVideosList(server.url) |
283 | 282 | ||
284 | const videos = res.body.data | 283 | const videos = res.body.data |
@@ -306,7 +305,7 @@ describe('Test multiple servers', function () { | |||
306 | support: 'my super support text for server 3', | 305 | support: 'my super support text for server 3', |
307 | account: { | 306 | account: { |
308 | name: 'root', | 307 | name: 'root', |
309 | host: 'localhost:9003' | 308 | host: 'localhost:' + servers[2].port |
310 | }, | 309 | }, |
311 | isLocal, | 310 | isLocal, |
312 | duration: 5, | 311 | duration: 5, |
@@ -340,7 +339,7 @@ describe('Test multiple servers', function () { | |||
340 | support: 'my super support text for server 3-2', | 339 | support: 'my super support text for server 3-2', |
341 | account: { | 340 | account: { |
342 | name: 'root', | 341 | name: 'root', |
343 | host: 'localhost:9003' | 342 | host: 'localhost:' + servers[2].port |
344 | }, | 343 | }, |
345 | commentsEnabled: true, | 344 | commentsEnabled: true, |
346 | downloadEnabled: true, | 345 | downloadEnabled: true, |
@@ -646,7 +645,7 @@ describe('Test multiple servers', function () { | |||
646 | const videoUpdated = videos.find(video => video.name === 'my super video updated') | 645 | const videoUpdated = videos.find(video => video.name === 'my super video updated') |
647 | expect(!!videoUpdated).to.be.true | 646 | expect(!!videoUpdated).to.be.true |
648 | 647 | ||
649 | const isLocal = server.url === 'http://localhost:9003' | 648 | const isLocal = server.url === 'http://localhost:' + servers[2].port |
650 | const checkAttributes = { | 649 | const checkAttributes = { |
651 | name: 'my super video updated', | 650 | name: 'my super video updated', |
652 | category: 10, | 651 | category: 10, |
@@ -658,7 +657,7 @@ describe('Test multiple servers', function () { | |||
658 | originallyPublishedAt: '2019-02-11T13:38:14.449Z', | 657 | originallyPublishedAt: '2019-02-11T13:38:14.449Z', |
659 | account: { | 658 | account: { |
660 | name: 'root', | 659 | name: 'root', |
661 | host: 'localhost:9003' | 660 | host: 'localhost:' + servers[2].port |
662 | }, | 661 | }, |
663 | isLocal, | 662 | isLocal, |
664 | duration: 5, | 663 | duration: 5, |
@@ -813,7 +812,7 @@ describe('Test multiple servers', function () { | |||
813 | expect(comment).to.not.be.undefined | 812 | expect(comment).to.not.be.undefined |
814 | expect(comment.inReplyToCommentId).to.be.null | 813 | expect(comment.inReplyToCommentId).to.be.null |
815 | expect(comment.account.name).to.equal('root') | 814 | expect(comment.account.name).to.equal('root') |
816 | expect(comment.account.host).to.equal('localhost:9001') | 815 | expect(comment.account.host).to.equal('localhost:' + servers[0].port) |
817 | expect(comment.totalReplies).to.equal(3) | 816 | expect(comment.totalReplies).to.equal(3) |
818 | expect(dateIsValid(comment.createdAt as string)).to.be.true | 817 | expect(dateIsValid(comment.createdAt as string)).to.be.true |
819 | expect(dateIsValid(comment.updatedAt as string)).to.be.true | 818 | expect(dateIsValid(comment.updatedAt as string)).to.be.true |
@@ -824,7 +823,7 @@ describe('Test multiple servers', function () { | |||
824 | expect(comment).to.not.be.undefined | 823 | expect(comment).to.not.be.undefined |
825 | expect(comment.inReplyToCommentId).to.be.null | 824 | expect(comment.inReplyToCommentId).to.be.null |
826 | expect(comment.account.name).to.equal('root') | 825 | expect(comment.account.name).to.equal('root') |
827 | expect(comment.account.host).to.equal('localhost:9003') | 826 | expect(comment.account.host).to.equal('localhost:' + servers[2].port) |
828 | expect(comment.totalReplies).to.equal(0) | 827 | expect(comment.totalReplies).to.equal(0) |
829 | expect(dateIsValid(comment.createdAt as string)).to.be.true | 828 | expect(dateIsValid(comment.createdAt as string)).to.be.true |
830 | expect(dateIsValid(comment.updatedAt as string)).to.be.true | 829 | expect(dateIsValid(comment.updatedAt as string)).to.be.true |
@@ -842,25 +841,25 @@ describe('Test multiple servers', function () { | |||
842 | const tree: VideoCommentThreadTree = res2.body | 841 | const tree: VideoCommentThreadTree = res2.body |
843 | expect(tree.comment.text).equal('my super first comment') | 842 | expect(tree.comment.text).equal('my super first comment') |
844 | expect(tree.comment.account.name).equal('root') | 843 | expect(tree.comment.account.name).equal('root') |
845 | expect(tree.comment.account.host).equal('localhost:9001') | 844 | expect(tree.comment.account.host).equal('localhost:' + servers[0].port) |
846 | expect(tree.children).to.have.lengthOf(2) | 845 | expect(tree.children).to.have.lengthOf(2) |
847 | 846 | ||
848 | const firstChild = tree.children[0] | 847 | const firstChild = tree.children[0] |
849 | expect(firstChild.comment.text).to.equal('my super answer to thread 1') | 848 | expect(firstChild.comment.text).to.equal('my super answer to thread 1') |
850 | expect(firstChild.comment.account.name).equal('root') | 849 | expect(firstChild.comment.account.name).equal('root') |
851 | expect(firstChild.comment.account.host).equal('localhost:9002') | 850 | expect(firstChild.comment.account.host).equal('localhost:' + servers[1].port) |
852 | expect(firstChild.children).to.have.lengthOf(1) | 851 | expect(firstChild.children).to.have.lengthOf(1) |
853 | 852 | ||
854 | childOfFirstChild = firstChild.children[0] | 853 | childOfFirstChild = firstChild.children[0] |
855 | expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1') | 854 | expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1') |
856 | expect(childOfFirstChild.comment.account.name).equal('root') | 855 | expect(childOfFirstChild.comment.account.name).equal('root') |
857 | expect(childOfFirstChild.comment.account.host).equal('localhost:9003') | 856 | expect(childOfFirstChild.comment.account.host).equal('localhost:' + servers[2].port) |
858 | expect(childOfFirstChild.children).to.have.lengthOf(0) | 857 | expect(childOfFirstChild.children).to.have.lengthOf(0) |
859 | 858 | ||
860 | const secondChild = tree.children[1] | 859 | const secondChild = tree.children[1] |
861 | expect(secondChild.comment.text).to.equal('my second answer to thread 1') | 860 | expect(secondChild.comment.text).to.equal('my second answer to thread 1') |
862 | expect(secondChild.comment.account.name).equal('root') | 861 | expect(secondChild.comment.account.name).equal('root') |
863 | expect(secondChild.comment.account.host).equal('localhost:9003') | 862 | expect(secondChild.comment.account.host).equal('localhost:' + servers[2].port) |
864 | expect(secondChild.children).to.have.lengthOf(0) | 863 | expect(secondChild.children).to.have.lengthOf(0) |
865 | } | 864 | } |
866 | }) | 865 | }) |
@@ -915,7 +914,7 @@ describe('Test multiple servers', function () { | |||
915 | expect(comment).to.not.be.undefined | 914 | expect(comment).to.not.be.undefined |
916 | expect(comment.inReplyToCommentId).to.be.null | 915 | expect(comment.inReplyToCommentId).to.be.null |
917 | expect(comment.account.name).to.equal('root') | 916 | expect(comment.account.name).to.equal('root') |
918 | expect(comment.account.host).to.equal('localhost:9003') | 917 | expect(comment.account.host).to.equal('localhost:' + servers[2].port) |
919 | expect(comment.totalReplies).to.equal(0) | 918 | expect(comment.totalReplies).to.equal(0) |
920 | expect(dateIsValid(comment.createdAt as string)).to.be.true | 919 | expect(dateIsValid(comment.createdAt as string)).to.be.true |
921 | expect(dateIsValid(comment.updatedAt as string)).to.be.true | 920 | expect(dateIsValid(comment.updatedAt as string)).to.be.true |
@@ -971,7 +970,7 @@ describe('Test multiple servers', function () { | |||
971 | const res = await getVideosList(server.url) | 970 | const res = await getVideosList(server.url) |
972 | const video = res.body.data.find(v => v.name === 'minimum parameters') | 971 | const video = res.body.data.find(v => v.name === 'minimum parameters') |
973 | 972 | ||
974 | const isLocal = server.url === 'http://localhost:9002' | 973 | const isLocal = server.url === 'http://localhost:' + servers[1].port |
975 | const checkAttributes = { | 974 | const checkAttributes = { |
976 | name: 'minimum parameters', | 975 | name: 'minimum parameters', |
977 | category: null, | 976 | category: null, |
@@ -982,7 +981,7 @@ describe('Test multiple servers', function () { | |||
982 | support: null, | 981 | support: null, |
983 | account: { | 982 | account: { |
984 | name: 'root', | 983 | name: 'root', |
985 | host: 'localhost:9002' | 984 | host: 'localhost:' + servers[1].port |
986 | }, | 985 | }, |
987 | isLocal, | 986 | isLocal, |
988 | duration: 5, | 987 | duration: 5, |
diff --git a/server/tests/api/videos/services.ts b/server/tests/api/videos/services.ts index e9ad947b2..17172331f 100644 --- a/server/tests/api/videos/services.ts +++ b/server/tests/api/videos/services.ts | |||
@@ -27,13 +27,13 @@ describe('Test services', function () { | |||
27 | }) | 27 | }) |
28 | 28 | ||
29 | it('Should have a valid oEmbed response', async function () { | 29 | it('Should have a valid oEmbed response', async function () { |
30 | const oembedUrl = 'http://localhost:9001/videos/watch/' + server.video.uuid | 30 | const oembedUrl = 'http://localhost:' + server.port + '/videos/watch/' + server.video.uuid |
31 | 31 | ||
32 | const res = await getOEmbed(server.url, oembedUrl) | 32 | const res = await getOEmbed(server.url, oembedUrl) |
33 | const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' + | 33 | const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' + |
34 | `src="http://localhost:9001/videos/embed/${server.video.uuid}" ` + | 34 | `src="http://localhost:${server.port}/videos/embed/${server.video.uuid}" ` + |
35 | 'frameborder="0" allowfullscreen></iframe>' | 35 | 'frameborder="0" allowfullscreen></iframe>' |
36 | const expectedThumbnailUrl = 'http://localhost:9001/static/previews/' + server.video.uuid + '.jpg' | 36 | const expectedThumbnailUrl = 'http://localhost:' + server.port + '/static/previews/' + server.video.uuid + '.jpg' |
37 | 37 | ||
38 | expect(res.body.html).to.equal(expectedHtml) | 38 | expect(res.body.html).to.equal(expectedHtml) |
39 | expect(res.body.title).to.equal(server.video.name) | 39 | expect(res.body.title).to.equal(server.video.name) |
@@ -41,19 +41,19 @@ describe('Test services', function () { | |||
41 | expect(res.body.width).to.equal(560) | 41 | expect(res.body.width).to.equal(560) |
42 | expect(res.body.height).to.equal(315) | 42 | expect(res.body.height).to.equal(315) |
43 | expect(res.body.thumbnail_url).to.equal(expectedThumbnailUrl) | 43 | expect(res.body.thumbnail_url).to.equal(expectedThumbnailUrl) |
44 | expect(res.body.thumbnail_width).to.equal(560) | 44 | expect(res.body.thumbnail_width).to.equal(850) |
45 | expect(res.body.thumbnail_height).to.equal(315) | 45 | expect(res.body.thumbnail_height).to.equal(480) |
46 | }) | 46 | }) |
47 | 47 | ||
48 | it('Should have a valid oEmbed response with small max height query', async function () { | 48 | it('Should have a valid oEmbed response with small max height query', async function () { |
49 | const oembedUrl = 'http://localhost:9001/videos/watch/' + server.video.uuid | 49 | const oembedUrl = 'http://localhost:' + server.port + '/videos/watch/' + server.video.uuid |
50 | const format = 'json' | 50 | const format = 'json' |
51 | const maxHeight = 50 | 51 | const maxHeight = 50 |
52 | const maxWidth = 50 | 52 | const maxWidth = 50 |
53 | 53 | ||
54 | const res = await getOEmbed(server.url, oembedUrl, format, maxHeight, maxWidth) | 54 | const res = await getOEmbed(server.url, oembedUrl, format, maxHeight, maxWidth) |
55 | const expectedHtml = '<iframe width="50" height="50" sandbox="allow-same-origin allow-scripts" ' + | 55 | const expectedHtml = '<iframe width="50" height="50" sandbox="allow-same-origin allow-scripts" ' + |
56 | `src="http://localhost:9001/videos/embed/${server.video.uuid}" ` + | 56 | `src="http://localhost:${server.port}/videos/embed/${server.video.uuid}" ` + |
57 | 'frameborder="0" allowfullscreen></iframe>' | 57 | 'frameborder="0" allowfullscreen></iframe>' |
58 | 58 | ||
59 | expect(res.body.html).to.equal(expectedHtml) | 59 | expect(res.body.html).to.equal(expectedHtml) |
diff --git a/server/tests/api/videos/single-server.ts b/server/tests/api/videos/single-server.ts index 1f366b642..d8f394ac7 100644 --- a/server/tests/api/videos/single-server.ts +++ b/server/tests/api/videos/single-server.ts | |||
@@ -37,7 +37,7 @@ describe('Test a single server', function () { | |||
37 | let videoUUID = '' | 37 | let videoUUID = '' |
38 | let videosListBase: any[] = null | 38 | let videosListBase: any[] = null |
39 | 39 | ||
40 | const getCheckAttributes = { | 40 | const getCheckAttributes = () => ({ |
41 | name: 'my super name', | 41 | name: 'my super name', |
42 | category: 2, | 42 | category: 2, |
43 | licence: 6, | 43 | licence: 6, |
@@ -47,7 +47,7 @@ describe('Test a single server', function () { | |||
47 | support: 'my super support text', | 47 | support: 'my super support text', |
48 | account: { | 48 | account: { |
49 | name: 'root', | 49 | name: 'root', |
50 | host: 'localhost:9001' | 50 | host: 'localhost:' + server.port |
51 | }, | 51 | }, |
52 | isLocal: true, | 52 | isLocal: true, |
53 | duration: 5, | 53 | duration: 5, |
@@ -68,9 +68,9 @@ describe('Test a single server', function () { | |||
68 | size: 218910 | 68 | size: 218910 |
69 | } | 69 | } |
70 | ] | 70 | ] |
71 | } | 71 | }) |
72 | 72 | ||
73 | const updateCheckAttributes = { | 73 | const updateCheckAttributes = () => ({ |
74 | name: 'my super video updated', | 74 | name: 'my super video updated', |
75 | category: 4, | 75 | category: 4, |
76 | licence: 2, | 76 | licence: 2, |
@@ -80,7 +80,7 @@ describe('Test a single server', function () { | |||
80 | support: 'my super support text updated', | 80 | support: 'my super support text updated', |
81 | account: { | 81 | account: { |
82 | name: 'root', | 82 | name: 'root', |
83 | host: 'localhost:9001' | 83 | host: 'localhost:' + server.port |
84 | }, | 84 | }, |
85 | isLocal: true, | 85 | isLocal: true, |
86 | tags: [ 'tagup1', 'tagup2' ], | 86 | tags: [ 'tagup1', 'tagup2' ], |
@@ -101,7 +101,7 @@ describe('Test a single server', function () { | |||
101 | size: 292677 | 101 | size: 292677 |
102 | } | 102 | } |
103 | ] | 103 | ] |
104 | } | 104 | }) |
105 | 105 | ||
106 | before(async function () { | 106 | before(async function () { |
107 | this.timeout(30000) | 107 | this.timeout(30000) |
@@ -182,7 +182,7 @@ describe('Test a single server', function () { | |||
182 | expect(res.body.data.length).to.equal(1) | 182 | expect(res.body.data.length).to.equal(1) |
183 | 183 | ||
184 | const video = res.body.data[0] | 184 | const video = res.body.data[0] |
185 | await completeVideoCheck(server.url, video, getCheckAttributes) | 185 | await completeVideoCheck(server.url, video, getCheckAttributes()) |
186 | }) | 186 | }) |
187 | 187 | ||
188 | it('Should get the video by UUID', async function () { | 188 | it('Should get the video by UUID', async function () { |
@@ -191,7 +191,7 @@ describe('Test a single server', function () { | |||
191 | const res = await getVideo(server.url, videoUUID) | 191 | const res = await getVideo(server.url, videoUUID) |
192 | 192 | ||
193 | const video = res.body | 193 | const video = res.body |
194 | await completeVideoCheck(server.url, video, getCheckAttributes) | 194 | await completeVideoCheck(server.url, video, getCheckAttributes()) |
195 | }) | 195 | }) |
196 | 196 | ||
197 | it('Should have the views updated', async function () { | 197 | it('Should have the views updated', async function () { |
@@ -376,7 +376,7 @@ describe('Test a single server', function () { | |||
376 | const res = await getVideo(server.url, videoId) | 376 | const res = await getVideo(server.url, videoId) |
377 | const video = res.body | 377 | const video = res.body |
378 | 378 | ||
379 | await completeVideoCheck(server.url, video, updateCheckAttributes) | 379 | await completeVideoCheck(server.url, video, updateCheckAttributes()) |
380 | }) | 380 | }) |
381 | 381 | ||
382 | it('Should update only the tags of a video', async function () { | 382 | it('Should update only the tags of a video', async function () { |
@@ -388,7 +388,7 @@ describe('Test a single server', function () { | |||
388 | const res = await getVideo(server.url, videoId) | 388 | const res = await getVideo(server.url, videoId) |
389 | const video = res.body | 389 | const video = res.body |
390 | 390 | ||
391 | await completeVideoCheck(server.url, video, Object.assign(updateCheckAttributes, attributes)) | 391 | await completeVideoCheck(server.url, video, Object.assign(updateCheckAttributes(), attributes)) |
392 | }) | 392 | }) |
393 | 393 | ||
394 | it('Should update only the description of a video', async function () { | 394 | it('Should update only the description of a video', async function () { |
@@ -400,7 +400,8 @@ describe('Test a single server', function () { | |||
400 | const res = await getVideo(server.url, videoId) | 400 | const res = await getVideo(server.url, videoId) |
401 | const video = res.body | 401 | const video = res.body |
402 | 402 | ||
403 | await completeVideoCheck(server.url, video, Object.assign(updateCheckAttributes, attributes)) | 403 | const expectedAttributes = Object.assign(updateCheckAttributes(), { tags: [ 'supertag', 'tag1', 'tag2' ] }, attributes) |
404 | await completeVideoCheck(server.url, video, expectedAttributes) | ||
404 | }) | 405 | }) |
405 | 406 | ||
406 | it('Should like a video', async function () { | 407 | it('Should like a video', async function () { |
diff --git a/server/tests/api/videos/video-abuse.ts b/server/tests/api/videos/video-abuse.ts index 7318497d5..a2f3ee161 100644 --- a/server/tests/api/videos/video-abuse.ts +++ b/server/tests/api/videos/video-abuse.ts | |||
@@ -9,7 +9,6 @@ import { | |||
9 | flushAndRunMultipleServers, | 9 | flushAndRunMultipleServers, |
10 | getVideoAbusesList, | 10 | getVideoAbusesList, |
11 | getVideosList, | 11 | getVideosList, |
12 | killallServers, | ||
13 | reportVideoAbuse, | 12 | reportVideoAbuse, |
14 | ServerInfo, | 13 | ServerInfo, |
15 | setAccessTokensToServers, | 14 | setAccessTokensToServers, |
@@ -90,7 +89,7 @@ describe('Test video abuses', function () { | |||
90 | const abuse: VideoAbuse = res1.body.data[0] | 89 | const abuse: VideoAbuse = res1.body.data[0] |
91 | expect(abuse.reason).to.equal('my super bad reason') | 90 | expect(abuse.reason).to.equal('my super bad reason') |
92 | expect(abuse.reporterAccount.name).to.equal('root') | 91 | expect(abuse.reporterAccount.name).to.equal('root') |
93 | expect(abuse.reporterAccount.host).to.equal('localhost:9001') | 92 | expect(abuse.reporterAccount.host).to.equal('localhost:' + servers[0].port) |
94 | expect(abuse.video.id).to.equal(servers[0].video.id) | 93 | expect(abuse.video.id).to.equal(servers[0].video.id) |
95 | 94 | ||
96 | const res2 = await getVideoAbusesList(servers[1].url, servers[1].accessToken) | 95 | const res2 = await getVideoAbusesList(servers[1].url, servers[1].accessToken) |
@@ -118,7 +117,7 @@ describe('Test video abuses', function () { | |||
118 | const abuse1: VideoAbuse = res1.body.data[0] | 117 | const abuse1: VideoAbuse = res1.body.data[0] |
119 | expect(abuse1.reason).to.equal('my super bad reason') | 118 | expect(abuse1.reason).to.equal('my super bad reason') |
120 | expect(abuse1.reporterAccount.name).to.equal('root') | 119 | expect(abuse1.reporterAccount.name).to.equal('root') |
121 | expect(abuse1.reporterAccount.host).to.equal('localhost:9001') | 120 | expect(abuse1.reporterAccount.host).to.equal('localhost:' + servers[0].port) |
122 | expect(abuse1.video.id).to.equal(servers[0].video.id) | 121 | expect(abuse1.video.id).to.equal(servers[0].video.id) |
123 | expect(abuse1.state.id).to.equal(VideoAbuseState.PENDING) | 122 | expect(abuse1.state.id).to.equal(VideoAbuseState.PENDING) |
124 | expect(abuse1.state.label).to.equal('Pending') | 123 | expect(abuse1.state.label).to.equal('Pending') |
@@ -127,7 +126,7 @@ describe('Test video abuses', function () { | |||
127 | const abuse2: VideoAbuse = res1.body.data[1] | 126 | const abuse2: VideoAbuse = res1.body.data[1] |
128 | expect(abuse2.reason).to.equal('my super bad reason 2') | 127 | expect(abuse2.reason).to.equal('my super bad reason 2') |
129 | expect(abuse2.reporterAccount.name).to.equal('root') | 128 | expect(abuse2.reporterAccount.name).to.equal('root') |
130 | expect(abuse2.reporterAccount.host).to.equal('localhost:9001') | 129 | expect(abuse2.reporterAccount.host).to.equal('localhost:' + servers[0].port) |
131 | expect(abuse2.video.id).to.equal(servers[1].video.id) | 130 | expect(abuse2.video.id).to.equal(servers[1].video.id) |
132 | expect(abuse2.state.id).to.equal(VideoAbuseState.PENDING) | 131 | expect(abuse2.state.id).to.equal(VideoAbuseState.PENDING) |
133 | expect(abuse2.state.label).to.equal('Pending') | 132 | expect(abuse2.state.label).to.equal('Pending') |
@@ -141,7 +140,7 @@ describe('Test video abuses', function () { | |||
141 | abuseServer2 = res2.body.data[0] | 140 | abuseServer2 = res2.body.data[0] |
142 | expect(abuseServer2.reason).to.equal('my super bad reason 2') | 141 | expect(abuseServer2.reason).to.equal('my super bad reason 2') |
143 | expect(abuseServer2.reporterAccount.name).to.equal('root') | 142 | expect(abuseServer2.reporterAccount.name).to.equal('root') |
144 | expect(abuseServer2.reporterAccount.host).to.equal('localhost:9001') | 143 | expect(abuseServer2.reporterAccount.host).to.equal('localhost:' + servers[0].port) |
145 | expect(abuseServer2.state.id).to.equal(VideoAbuseState.PENDING) | 144 | expect(abuseServer2.state.id).to.equal(VideoAbuseState.PENDING) |
146 | expect(abuseServer2.state.label).to.equal('Pending') | 145 | expect(abuseServer2.state.label).to.equal('Pending') |
147 | expect(abuseServer2.moderationComment).to.be.null | 146 | expect(abuseServer2.moderationComment).to.be.null |
diff --git a/server/tests/api/videos/video-change-ownership.ts b/server/tests/api/videos/video-change-ownership.ts index 1c0327d40..3a3add71b 100644 --- a/server/tests/api/videos/video-change-ownership.ts +++ b/server/tests/api/videos/video-change-ownership.ts | |||
@@ -4,7 +4,8 @@ import * as chai from 'chai' | |||
4 | import 'mocha' | 4 | import 'mocha' |
5 | import { | 5 | import { |
6 | acceptChangeOwnership, | 6 | acceptChangeOwnership, |
7 | changeVideoOwnership, cleanupTests, | 7 | changeVideoOwnership, |
8 | cleanupTests, | ||
8 | createUser, | 9 | createUser, |
9 | doubleFollow, | 10 | doubleFollow, |
10 | flushAndRunMultipleServers, | 11 | flushAndRunMultipleServers, |
@@ -13,7 +14,6 @@ import { | |||
13 | getVideo, | 14 | getVideo, |
14 | getVideoChangeOwnershipList, | 15 | getVideoChangeOwnershipList, |
15 | getVideosList, | 16 | getVideosList, |
16 | killallServers, | ||
17 | refuseChangeOwnership, | 17 | refuseChangeOwnership, |
18 | ServerInfo, | 18 | ServerInfo, |
19 | setAccessTokensToServers, | 19 | setAccessTokensToServers, |
@@ -203,8 +203,8 @@ describe('Test video change ownership - nominal', function () { | |||
203 | } | 203 | } |
204 | }) | 204 | }) |
205 | 205 | ||
206 | after(function () { | 206 | after(async function () { |
207 | killallServers(servers) | 207 | await cleanupTests(servers) |
208 | }) | 208 | }) |
209 | }) | 209 | }) |
210 | 210 | ||
diff --git a/server/tests/api/videos/video-channels.ts b/server/tests/api/videos/video-channels.ts index 345e96f43..41fe3be5c 100644 --- a/server/tests/api/videos/video-channels.ts +++ b/server/tests/api/videos/video-channels.ts | |||
@@ -41,7 +41,7 @@ describe('Test video channels', function () { | |||
41 | let videoUUID: string | 41 | let videoUUID: string |
42 | 42 | ||
43 | before(async function () { | 43 | before(async function () { |
44 | this.timeout(30000) | 44 | this.timeout(60000) |
45 | 45 | ||
46 | servers = await flushAndRunMultipleServers(2) | 46 | servers = await flushAndRunMultipleServers(2) |
47 | 47 | ||
@@ -213,7 +213,7 @@ describe('Test video channels', function () { | |||
213 | this.timeout(10000) | 213 | this.timeout(10000) |
214 | 214 | ||
215 | for (const server of servers) { | 215 | for (const server of servers) { |
216 | const channelURI = 'second_video_channel@localhost:9001' | 216 | const channelURI = 'second_video_channel@localhost:' + servers[0].port |
217 | const res1 = await getVideoChannelVideos(server.url, server.accessToken, channelURI, 0, 5) | 217 | const res1 = await getVideoChannelVideos(server.url, server.accessToken, channelURI, 0, 5) |
218 | expect(res1.body.total).to.equal(1) | 218 | expect(res1.body.total).to.equal(1) |
219 | expect(res1.body.data).to.be.an('array') | 219 | expect(res1.body.data).to.be.an('array') |
@@ -234,11 +234,11 @@ describe('Test video channels', function () { | |||
234 | this.timeout(10000) | 234 | this.timeout(10000) |
235 | 235 | ||
236 | for (const server of servers) { | 236 | for (const server of servers) { |
237 | const secondChannelURI = 'second_video_channel@localhost:9001' | 237 | const secondChannelURI = 'second_video_channel@localhost:' + servers[0].port |
238 | const res1 = await getVideoChannelVideos(server.url, server.accessToken, secondChannelURI, 0, 5) | 238 | const res1 = await getVideoChannelVideos(server.url, server.accessToken, secondChannelURI, 0, 5) |
239 | expect(res1.body.total).to.equal(0) | 239 | expect(res1.body.total).to.equal(0) |
240 | 240 | ||
241 | const channelURI = 'root_channel@localhost:9001' | 241 | const channelURI = 'root_channel@localhost:' + servers[0].port |
242 | const res2 = await getVideoChannelVideos(server.url, server.accessToken, channelURI, 0, 5) | 242 | const res2 = await getVideoChannelVideos(server.url, server.accessToken, channelURI, 0, 5) |
243 | expect(res2.body.total).to.equal(1) | 243 | expect(res2.body.total).to.equal(1) |
244 | 244 | ||
diff --git a/server/tests/api/videos/video-comments.ts b/server/tests/api/videos/video-comments.ts index 22fd8c058..82182cc7c 100644 --- a/server/tests/api/videos/video-comments.ts +++ b/server/tests/api/videos/video-comments.ts | |||
@@ -66,8 +66,8 @@ describe('Test video comments', function () { | |||
66 | expect(comment.videoId).to.equal(videoId) | 66 | expect(comment.videoId).to.equal(videoId) |
67 | expect(comment.id).to.equal(comment.threadId) | 67 | expect(comment.id).to.equal(comment.threadId) |
68 | expect(comment.account.name).to.equal('root') | 68 | expect(comment.account.name).to.equal('root') |
69 | expect(comment.account.host).to.equal('localhost:9001') | 69 | expect(comment.account.host).to.equal('localhost:' + server.port) |
70 | expect(comment.account.url).to.equal('http://localhost:9001/accounts/root') | 70 | expect(comment.account.url).to.equal('http://localhost:' + server.port + '/accounts/root') |
71 | expect(comment.totalReplies).to.equal(0) | 71 | expect(comment.totalReplies).to.equal(0) |
72 | expect(dateIsValid(comment.createdAt as string)).to.be.true | 72 | expect(dateIsValid(comment.createdAt as string)).to.be.true |
73 | expect(dateIsValid(comment.updatedAt as string)).to.be.true | 73 | expect(dateIsValid(comment.updatedAt as string)).to.be.true |
@@ -86,7 +86,7 @@ describe('Test video comments', function () { | |||
86 | expect(comment.videoId).to.equal(videoId) | 86 | expect(comment.videoId).to.equal(videoId) |
87 | expect(comment.id).to.equal(comment.threadId) | 87 | expect(comment.id).to.equal(comment.threadId) |
88 | expect(comment.account.name).to.equal('root') | 88 | expect(comment.account.name).to.equal('root') |
89 | expect(comment.account.host).to.equal('localhost:9001') | 89 | expect(comment.account.host).to.equal('localhost:' + server.port) |
90 | 90 | ||
91 | await testImage(server.url, 'avatar-resized', comment.account.avatar.path, '.png') | 91 | await testImage(server.url, 'avatar-resized', comment.account.avatar.path, '.png') |
92 | 92 | ||
diff --git a/server/tests/api/videos/video-hls.ts b/server/tests/api/videos/video-hls.ts index 22031c18b..39178bb1a 100644 --- a/server/tests/api/videos/video-hls.ts +++ b/server/tests/api/videos/video-hls.ts | |||
@@ -5,13 +5,12 @@ import 'mocha' | |||
5 | import { | 5 | import { |
6 | checkDirectoryIsEmpty, | 6 | checkDirectoryIsEmpty, |
7 | checkSegmentHash, | 7 | checkSegmentHash, |
8 | checkTmpIsEmpty, cleanupTests, | 8 | checkTmpIsEmpty, |
9 | cleanupTests, | ||
9 | doubleFollow, | 10 | doubleFollow, |
10 | flushAndRunMultipleServers, | 11 | flushAndRunMultipleServers, |
11 | flushTests, | ||
12 | getPlaylist, | 12 | getPlaylist, |
13 | getVideo, | 13 | getVideo, |
14 | killallServers, | ||
15 | removeVideo, | 14 | removeVideo, |
16 | ServerInfo, | 15 | ServerInfo, |
17 | setAccessTokensToServers, | 16 | setAccessTokensToServers, |
@@ -22,12 +21,11 @@ import { | |||
22 | import { VideoDetails } from '../../../../shared/models/videos' | 21 | import { VideoDetails } from '../../../../shared/models/videos' |
23 | import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type' | 22 | import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type' |
24 | import { join } from 'path' | 23 | import { join } from 'path' |
24 | import { DEFAULT_AUDIO_RESOLUTION } from '../../../initializers/constants' | ||
25 | 25 | ||
26 | const expect = chai.expect | 26 | const expect = chai.expect |
27 | 27 | ||
28 | async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string) { | 28 | async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string, resolutions = [ 240, 360, 480, 720 ]) { |
29 | const resolutions = [ 240, 360, 480, 720 ] | ||
30 | |||
31 | for (const server of servers) { | 29 | for (const server of servers) { |
32 | const res = await getVideo(server.url, videoUUID) | 30 | const res = await getVideo(server.url, videoUUID) |
33 | const videoDetails: VideoDetails = res.body | 31 | const videoDetails: VideoDetails = res.body |
@@ -42,16 +40,15 @@ async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string) { | |||
42 | 40 | ||
43 | const masterPlaylist = res2.text | 41 | const masterPlaylist = res2.text |
44 | 42 | ||
45 | expect(masterPlaylist).to.contain('#EXT-X-STREAM-INF:BANDWIDTH=55472,RESOLUTION=640x360,FRAME-RATE=25') | ||
46 | |||
47 | for (const resolution of resolutions) { | 43 | for (const resolution of resolutions) { |
44 | expect(masterPlaylist).to.match(new RegExp('#EXT-X-STREAM-INF:BANDWIDTH=\\d+,RESOLUTION=\\d+x' + resolution + ',FRAME-RATE=\\d+')) | ||
48 | expect(masterPlaylist).to.contain(`${resolution}.m3u8`) | 45 | expect(masterPlaylist).to.contain(`${resolution}.m3u8`) |
49 | } | 46 | } |
50 | } | 47 | } |
51 | 48 | ||
52 | { | 49 | { |
53 | for (const resolution of resolutions) { | 50 | for (const resolution of resolutions) { |
54 | const res2 = await getPlaylist(`http://localhost:9001/static/streaming-playlists/hls/${videoUUID}/${resolution}.m3u8`) | 51 | const res2 = await getPlaylist(`http://localhost:${servers[0].port}/static/streaming-playlists/hls/${videoUUID}/${resolution}.m3u8`) |
55 | 52 | ||
56 | const subPlaylist = res2.text | 53 | const subPlaylist = res2.text |
57 | expect(subPlaylist).to.contain(`${videoUUID}-${resolution}-fragmented.mp4`) | 54 | expect(subPlaylist).to.contain(`${videoUUID}-${resolution}-fragmented.mp4`) |
@@ -59,7 +56,7 @@ async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string) { | |||
59 | } | 56 | } |
60 | 57 | ||
61 | { | 58 | { |
62 | const baseUrl = 'http://localhost:9001/static/streaming-playlists/hls' | 59 | const baseUrl = 'http://localhost:' + servers[0].port + '/static/streaming-playlists/hls' |
63 | 60 | ||
64 | for (const resolution of resolutions) { | 61 | for (const resolution of resolutions) { |
65 | await checkSegmentHash(baseUrl, baseUrl, videoUUID, resolution, hlsPlaylist) | 62 | await checkSegmentHash(baseUrl, baseUrl, videoUUID, resolution, hlsPlaylist) |
@@ -71,11 +68,21 @@ async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string) { | |||
71 | describe('Test HLS videos', function () { | 68 | describe('Test HLS videos', function () { |
72 | let servers: ServerInfo[] = [] | 69 | let servers: ServerInfo[] = [] |
73 | let videoUUID = '' | 70 | let videoUUID = '' |
71 | let videoAudioUUID = '' | ||
74 | 72 | ||
75 | before(async function () { | 73 | before(async function () { |
76 | this.timeout(120000) | 74 | this.timeout(120000) |
77 | 75 | ||
78 | servers = await flushAndRunMultipleServers(2, { transcoding: { enabled: true, hls: { enabled: true } } }) | 76 | const configOverride = { |
77 | transcoding: { | ||
78 | enabled: true, | ||
79 | allow_audio_files: true, | ||
80 | hls: { | ||
81 | enabled: true | ||
82 | } | ||
83 | } | ||
84 | } | ||
85 | servers = await flushAndRunMultipleServers(2, configOverride) | ||
79 | 86 | ||
80 | // Get the access tokens | 87 | // Get the access tokens |
81 | await setAccessTokensToServers(servers) | 88 | await setAccessTokensToServers(servers) |
@@ -87,17 +94,28 @@ describe('Test HLS videos', function () { | |||
87 | it('Should upload a video and transcode it to HLS', async function () { | 94 | it('Should upload a video and transcode it to HLS', async function () { |
88 | this.timeout(120000) | 95 | this.timeout(120000) |
89 | 96 | ||
90 | { | 97 | const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, { name: 'video 1', fixture: 'video_short.webm' }) |
91 | const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, { name: 'video 1', fixture: 'video_short.webm' }) | 98 | videoUUID = res.body.video.uuid |
92 | videoUUID = res.body.video.uuid | ||
93 | } | ||
94 | 99 | ||
95 | await waitJobs(servers) | 100 | await waitJobs(servers) |
96 | 101 | ||
97 | await checkHlsPlaylist(servers, videoUUID) | 102 | await checkHlsPlaylist(servers, videoUUID) |
98 | }) | 103 | }) |
99 | 104 | ||
105 | it('Should upload an audio file and transcode it to HLS', async function () { | ||
106 | this.timeout(120000) | ||
107 | |||
108 | const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, { name: 'video audio', fixture: 'sample.ogg' }) | ||
109 | videoAudioUUID = res.body.video.uuid | ||
110 | |||
111 | await waitJobs(servers) | ||
112 | |||
113 | await checkHlsPlaylist(servers, videoAudioUUID, [ DEFAULT_AUDIO_RESOLUTION ]) | ||
114 | }) | ||
115 | |||
100 | it('Should update the video', async function () { | 116 | it('Should update the video', async function () { |
117 | this.timeout(10000) | ||
118 | |||
101 | await updateVideo(servers[0].url, servers[0].accessToken, videoUUID, { name: 'video 1 updated' }) | 119 | await updateVideo(servers[0].url, servers[0].accessToken, videoUUID, { name: 'video 1 updated' }) |
102 | 120 | ||
103 | await waitJobs(servers) | 121 | await waitJobs(servers) |
@@ -105,13 +123,17 @@ describe('Test HLS videos', function () { | |||
105 | await checkHlsPlaylist(servers, videoUUID) | 123 | await checkHlsPlaylist(servers, videoUUID) |
106 | }) | 124 | }) |
107 | 125 | ||
108 | it('Should delete the video', async function () { | 126 | it('Should delete videos', async function () { |
127 | this.timeout(10000) | ||
128 | |||
109 | await removeVideo(servers[0].url, servers[0].accessToken, videoUUID) | 129 | await removeVideo(servers[0].url, servers[0].accessToken, videoUUID) |
130 | await removeVideo(servers[0].url, servers[0].accessToken, videoAudioUUID) | ||
110 | 131 | ||
111 | await waitJobs(servers) | 132 | await waitJobs(servers) |
112 | 133 | ||
113 | for (const server of servers) { | 134 | for (const server of servers) { |
114 | await getVideo(server.url, videoUUID, 404) | 135 | await getVideo(server.url, videoUUID, 404) |
136 | await getVideo(server.url, videoAudioUUID, 404) | ||
115 | } | 137 | } |
116 | }) | 138 | }) |
117 | 139 | ||
diff --git a/server/tests/api/videos/video-playlists.ts b/server/tests/api/videos/video-playlists.ts index e4d817ff8..83a2f3d4d 100644 --- a/server/tests/api/videos/video-playlists.ts +++ b/server/tests/api/videos/video-playlists.ts | |||
@@ -358,7 +358,7 @@ describe('Test video playlists', function () { | |||
358 | 358 | ||
359 | for (const server of servers) { | 359 | for (const server of servers) { |
360 | const results = [ | 360 | const results = [ |
361 | await getAccountPlaylistsList(server.url, 'root@localhost:9002', 0, 5, '-createdAt'), | 361 | await getAccountPlaylistsList(server.url, 'root@localhost:' + servers[1].port, 0, 5, '-createdAt'), |
362 | await getVideoPlaylistsList(server.url, 0, 2, '-createdAt') | 362 | await getVideoPlaylistsList(server.url, 0, 2, '-createdAt') |
363 | ] | 363 | ] |
364 | 364 | ||
@@ -757,7 +757,7 @@ describe('Test video playlists', function () { | |||
757 | this.timeout(30000) | 757 | this.timeout(30000) |
758 | 758 | ||
759 | for (const server of servers) { | 759 | for (const server of servers) { |
760 | await checkPlaylistFilesWereRemoved(playlistServer1UUID, server.serverNumber) | 760 | await checkPlaylistFilesWereRemoved(playlistServer1UUID, server.internalServerNumber) |
761 | } | 761 | } |
762 | }) | 762 | }) |
763 | 763 | ||
diff --git a/server/tests/api/videos/video-transcoder.ts b/server/tests/api/videos/video-transcoder.ts index 3cd43e99b..90ade1652 100644 --- a/server/tests/api/videos/video-transcoder.ts +++ b/server/tests/api/videos/video-transcoder.ts | |||
@@ -4,24 +4,25 @@ import * as chai from 'chai' | |||
4 | import 'mocha' | 4 | import 'mocha' |
5 | import { omit } from 'lodash' | 5 | import { omit } from 'lodash' |
6 | import { getMaxBitrate, VideoDetails, VideoResolution, VideoState } from '../../../../shared/models/videos' | 6 | import { getMaxBitrate, VideoDetails, VideoResolution, VideoState } from '../../../../shared/models/videos' |
7 | import { audio, getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils' | 7 | import { audio, canDoQuickTranscode, getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils' |
8 | import { | 8 | import { |
9 | buildAbsoluteFixturePath, cleanupTests, | 9 | buildAbsoluteFixturePath, |
10 | cleanupTests, | ||
10 | doubleFollow, | 11 | doubleFollow, |
11 | flushAndRunMultipleServers, | 12 | flushAndRunMultipleServers, |
12 | generateHighBitrateVideo, | 13 | generateHighBitrateVideo, |
13 | getMyVideos, | 14 | getMyVideos, |
14 | getVideo, | 15 | getVideo, |
15 | getVideosList, | 16 | getVideosList, |
16 | killallServers, | 17 | makeGetRequest, |
17 | root, | 18 | root, |
18 | ServerInfo, | 19 | ServerInfo, |
19 | setAccessTokensToServers, | 20 | setAccessTokensToServers, |
20 | uploadVideo, | 21 | uploadVideo, |
22 | waitJobs, | ||
21 | webtorrentAdd | 23 | webtorrentAdd |
22 | } from '../../../../shared/extra-utils' | 24 | } from '../../../../shared/extra-utils' |
23 | import { extname, join } from 'path' | 25 | import { join } from 'path' |
24 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | ||
25 | import { VIDEO_TRANSCODING_FPS } from '../../../../server/initializers/constants' | 26 | import { VIDEO_TRANSCODING_FPS } from '../../../../server/initializers/constants' |
26 | 27 | ||
27 | const expect = chai.expect | 28 | const expect = chai.expect |
@@ -121,7 +122,7 @@ describe('Test video transcoding', function () { | |||
121 | 122 | ||
122 | expect(videoDetails.files).to.have.lengthOf(4) | 123 | expect(videoDetails.files).to.have.lengthOf(4) |
123 | 124 | ||
124 | const path = join(root(), 'test2', 'videos', video.uuid + '-240.mp4') | 125 | const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', video.uuid + '-240.mp4') |
125 | const probe = await audio.get(path) | 126 | const probe = await audio.get(path) |
126 | 127 | ||
127 | if (probe.audioStream) { | 128 | if (probe.audioStream) { |
@@ -152,7 +153,7 @@ describe('Test video transcoding', function () { | |||
152 | const videoDetails: VideoDetails = res2.body | 153 | const videoDetails: VideoDetails = res2.body |
153 | 154 | ||
154 | expect(videoDetails.files).to.have.lengthOf(4) | 155 | expect(videoDetails.files).to.have.lengthOf(4) |
155 | const path = join(root(), 'test2', 'videos', video.uuid + '-240.mp4') | 156 | const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', video.uuid + '-240.mp4') |
156 | const probe = await audio.get(path) | 157 | const probe = await audio.get(path) |
157 | expect(probe).to.not.have.property('audioStream') | 158 | expect(probe).to.not.have.property('audioStream') |
158 | } | 159 | } |
@@ -179,7 +180,7 @@ describe('Test video transcoding', function () { | |||
179 | expect(videoDetails.files).to.have.lengthOf(4) | 180 | expect(videoDetails.files).to.have.lengthOf(4) |
180 | const fixturePath = buildAbsoluteFixturePath(videoAttributes.fixture) | 181 | const fixturePath = buildAbsoluteFixturePath(videoAttributes.fixture) |
181 | const fixtureVideoProbe = await audio.get(fixturePath) | 182 | const fixtureVideoProbe = await audio.get(fixturePath) |
182 | const path = join(root(), 'test2', 'videos', video.uuid + '-240.mp4') | 183 | const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', video.uuid + '-240.mp4') |
183 | const videoProbe = await audio.get(path) | 184 | const videoProbe = await audio.get(path) |
184 | if (videoProbe.audioStream && fixtureVideoProbe.audioStream) { | 185 | if (videoProbe.audioStream && fixtureVideoProbe.audioStream) { |
185 | const toOmit = [ 'max_bit_rate', 'duration', 'duration_ts', 'nb_frames', 'start_time', 'start_pts' ] | 186 | const toOmit = [ 'max_bit_rate', 'duration', 'duration_ts', 'nb_frames', 'start_time', 'start_pts' ] |
@@ -216,13 +217,13 @@ describe('Test video transcoding', function () { | |||
216 | expect(videoDetails.files[ 3 ].fps).to.be.below(31) | 217 | expect(videoDetails.files[ 3 ].fps).to.be.below(31) |
217 | 218 | ||
218 | for (const resolution of [ '240', '360', '480' ]) { | 219 | for (const resolution of [ '240', '360', '480' ]) { |
219 | const path = join(root(), 'test2', 'videos', video.uuid + '-' + resolution + '.mp4') | 220 | const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', video.uuid + '-' + resolution + '.mp4') |
220 | const fps = await getVideoFileFPS(path) | 221 | const fps = await getVideoFileFPS(path) |
221 | 222 | ||
222 | expect(fps).to.be.below(31) | 223 | expect(fps).to.be.below(31) |
223 | } | 224 | } |
224 | 225 | ||
225 | const path = join(root(), 'test2', 'videos', video.uuid + '-720.mp4') | 226 | const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', video.uuid + '-720.mp4') |
226 | const fps = await getVideoFileFPS(path) | 227 | const fps = await getVideoFileFPS(path) |
227 | 228 | ||
228 | expect(fps).to.be.above(58).and.below(62) | 229 | expect(fps).to.be.above(58).and.below(62) |
@@ -310,7 +311,7 @@ describe('Test video transcoding', function () { | |||
310 | const video = res.body.data.find(v => v.name === videoAttributes.name) | 311 | const video = res.body.data.find(v => v.name === videoAttributes.name) |
311 | 312 | ||
312 | for (const resolution of ['240', '360', '480', '720', '1080']) { | 313 | for (const resolution of ['240', '360', '480', '720', '1080']) { |
313 | const path = join(root(), 'test2', 'videos', video.uuid + '-' + resolution + '.mp4') | 314 | const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', video.uuid + '-' + resolution + '.mp4') |
314 | const bitrate = await getVideoFileBitrate(path) | 315 | const bitrate = await getVideoFileBitrate(path) |
315 | const fps = await getVideoFileFPS(path) | 316 | const fps = await getVideoFileFPS(path) |
316 | const resolution2 = await getVideoFileResolution(path) | 317 | const resolution2 = await getVideoFileResolution(path) |
@@ -324,6 +325,15 @@ describe('Test video transcoding', function () { | |||
324 | it('Should accept and transcode additional extensions', async function () { | 325 | it('Should accept and transcode additional extensions', async function () { |
325 | this.timeout(300000) | 326 | this.timeout(300000) |
326 | 327 | ||
328 | let tempFixturePath: string | ||
329 | |||
330 | { | ||
331 | tempFixturePath = await generateHighBitrateVideo() | ||
332 | |||
333 | const bitrate = await getVideoFileBitrate(tempFixturePath) | ||
334 | expect(bitrate).to.be.above(getMaxBitrate(VideoResolution.H_1080P, 60, VIDEO_TRANSCODING_FPS)) | ||
335 | } | ||
336 | |||
327 | for (const fixture of [ 'video_short.mkv', 'video_short.avi' ]) { | 337 | for (const fixture of [ 'video_short.mkv', 'video_short.avi' ]) { |
328 | const videoAttributes = { | 338 | const videoAttributes = { |
329 | name: fixture, | 339 | name: fixture, |
@@ -349,6 +359,63 @@ describe('Test video transcoding', function () { | |||
349 | } | 359 | } |
350 | }) | 360 | }) |
351 | 361 | ||
362 | it('Should correctly detect if quick transcode is possible', async function () { | ||
363 | this.timeout(10000) | ||
364 | |||
365 | expect(await canDoQuickTranscode(buildAbsoluteFixturePath('video_short.mp4'))).to.be.true | ||
366 | expect(await canDoQuickTranscode(buildAbsoluteFixturePath('video_short.webm'))).to.be.false | ||
367 | }) | ||
368 | |||
369 | it('Should merge an audio file with the preview file', async function () { | ||
370 | this.timeout(60000) | ||
371 | |||
372 | const videoAttributesArg = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' } | ||
373 | await uploadVideo(servers[ 1 ].url, servers[ 1 ].accessToken, videoAttributesArg) | ||
374 | |||
375 | await waitJobs(servers) | ||
376 | |||
377 | for (const server of servers) { | ||
378 | const res = await getVideosList(server.url) | ||
379 | |||
380 | const video = res.body.data.find(v => v.name === 'audio_with_preview') | ||
381 | const res2 = await getVideo(server.url, video.id) | ||
382 | const videoDetails: VideoDetails = res2.body | ||
383 | |||
384 | expect(videoDetails.files).to.have.lengthOf(1) | ||
385 | |||
386 | await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, statusCodeExpected: 200 }) | ||
387 | await makeGetRequest({ url: server.url, path: videoDetails.previewPath, statusCodeExpected: 200 }) | ||
388 | |||
389 | const magnetUri = videoDetails.files[ 0 ].magnetUri | ||
390 | expect(magnetUri).to.contain('.mp4') | ||
391 | } | ||
392 | }) | ||
393 | |||
394 | it('Should upload an audio file and choose a default background image', async function () { | ||
395 | this.timeout(60000) | ||
396 | |||
397 | const videoAttributesArg = { name: 'audio_without_preview', fixture: 'sample.ogg' } | ||
398 | await uploadVideo(servers[ 1 ].url, servers[ 1 ].accessToken, videoAttributesArg) | ||
399 | |||
400 | await waitJobs(servers) | ||
401 | |||
402 | for (const server of servers) { | ||
403 | const res = await getVideosList(server.url) | ||
404 | |||
405 | const video = res.body.data.find(v => v.name === 'audio_without_preview') | ||
406 | const res2 = await getVideo(server.url, video.id) | ||
407 | const videoDetails = res2.body | ||
408 | |||
409 | expect(videoDetails.files).to.have.lengthOf(1) | ||
410 | |||
411 | await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, statusCodeExpected: 200 }) | ||
412 | await makeGetRequest({ url: server.url, path: videoDetails.previewPath, statusCodeExpected: 200 }) | ||
413 | |||
414 | const magnetUri = videoDetails.files[ 0 ].magnetUri | ||
415 | expect(magnetUri).to.contain('.mp4') | ||
416 | } | ||
417 | }) | ||
418 | |||
352 | after(async function () { | 419 | after(async function () { |
353 | await cleanupTests(servers) | 420 | await cleanupTests(servers) |
354 | }) | 421 | }) |
diff --git a/server/tests/api/videos/videos-views-cleaner.ts b/server/tests/api/videos/videos-views-cleaner.ts index c21d46d56..fbddd40f4 100644 --- a/server/tests/api/videos/videos-views-cleaner.ts +++ b/server/tests/api/videos/videos-views-cleaner.ts | |||
@@ -10,7 +10,7 @@ import { | |||
10 | flushAndRunServer, | 10 | flushAndRunServer, |
11 | ServerInfo, | 11 | ServerInfo, |
12 | setAccessTokensToServers, | 12 | setAccessTokensToServers, |
13 | uploadVideo, uploadVideoAndGetId, viewVideo, wait, countVideoViewsOf, doubleFollow, waitJobs, cleanupTests | 13 | uploadVideo, uploadVideoAndGetId, viewVideo, wait, countVideoViewsOf, doubleFollow, waitJobs, cleanupTests, closeAllSequelize |
14 | } from '../../../../shared/extra-utils' | 14 | } from '../../../../shared/extra-utils' |
15 | import { getVideosOverview } from '../../../../shared/extra-utils/overviews/overviews' | 15 | import { getVideosOverview } from '../../../../shared/extra-utils/overviews/overviews' |
16 | import { VideosOverview } from '../../../../shared/models/overviews' | 16 | import { VideosOverview } from '../../../../shared/models/overviews' |
@@ -58,14 +58,14 @@ describe('Test video views cleaner', function () { | |||
58 | 58 | ||
59 | { | 59 | { |
60 | for (const server of servers) { | 60 | for (const server of servers) { |
61 | const total = await countVideoViewsOf(server.serverNumber, videoIdServer1) | 61 | const total = await countVideoViewsOf(server.internalServerNumber, videoIdServer1) |
62 | expect(total).to.equal(2) | 62 | expect(total).to.equal(2) |
63 | } | 63 | } |
64 | } | 64 | } |
65 | 65 | ||
66 | { | 66 | { |
67 | for (const server of servers) { | 67 | for (const server of servers) { |
68 | const total = await countVideoViewsOf(server.serverNumber, videoIdServer2) | 68 | const total = await countVideoViewsOf(server.internalServerNumber, videoIdServer2) |
69 | expect(total).to.equal(2) | 69 | expect(total).to.equal(2) |
70 | } | 70 | } |
71 | } | 71 | } |
@@ -74,8 +74,6 @@ describe('Test video views cleaner', function () { | |||
74 | it('Should clean old video views', async function () { | 74 | it('Should clean old video views', async function () { |
75 | this.timeout(50000) | 75 | this.timeout(50000) |
76 | 76 | ||
77 | this.timeout(50000) | ||
78 | |||
79 | killallServers([ servers[0] ]) | 77 | killallServers([ servers[0] ]) |
80 | 78 | ||
81 | await reRunServer(servers[0], { views: { videos: { remote: { max_age: '5 seconds' } } } }) | 79 | await reRunServer(servers[0], { views: { videos: { remote: { max_age: '5 seconds' } } } }) |
@@ -86,21 +84,23 @@ describe('Test video views cleaner', function () { | |||
86 | 84 | ||
87 | { | 85 | { |
88 | for (const server of servers) { | 86 | for (const server of servers) { |
89 | const total = await countVideoViewsOf(server.serverNumber, videoIdServer1) | 87 | const total = await countVideoViewsOf(server.internalServerNumber, videoIdServer1) |
90 | expect(total).to.equal(2) | 88 | expect(total).to.equal(2) |
91 | } | 89 | } |
92 | } | 90 | } |
93 | 91 | ||
94 | { | 92 | { |
95 | const totalServer1 = await countVideoViewsOf(servers[0].serverNumber, videoIdServer2) | 93 | const totalServer1 = await countVideoViewsOf(servers[0].internalServerNumber, videoIdServer2) |
96 | expect(totalServer1).to.equal(0) | 94 | expect(totalServer1).to.equal(0) |
97 | 95 | ||
98 | const totalServer2 = await countVideoViewsOf(servers[1].serverNumber, videoIdServer2) | 96 | const totalServer2 = await countVideoViewsOf(servers[1].internalServerNumber, videoIdServer2) |
99 | expect(totalServer2).to.equal(2) | 97 | expect(totalServer2).to.equal(2) |
100 | } | 98 | } |
101 | }) | 99 | }) |
102 | 100 | ||
103 | after(async function () { | 101 | after(async function () { |
102 | await closeAllSequelize(servers) | ||
103 | |||
104 | await cleanupTests(servers) | 104 | await cleanupTests(servers) |
105 | }) | 105 | }) |
106 | }) | 106 | }) |
diff --git a/server/tests/cli/optimize-old-videos.ts b/server/tests/cli/optimize-old-videos.ts index 5e12c0089..3822fca42 100644 --- a/server/tests/cli/optimize-old-videos.ts +++ b/server/tests/cli/optimize-old-videos.ts | |||
@@ -8,14 +8,16 @@ import { | |||
8 | doubleFollow, | 8 | doubleFollow, |
9 | execCLI, | 9 | execCLI, |
10 | flushAndRunMultipleServers, | 10 | flushAndRunMultipleServers, |
11 | flushTests, generateHighBitrateVideo, | 11 | generateHighBitrateVideo, |
12 | getEnvCli, | 12 | getEnvCli, |
13 | getVideo, | 13 | getVideo, |
14 | getVideosList, | 14 | getVideosList, |
15 | killallServers, root, | 15 | root, |
16 | ServerInfo, | 16 | ServerInfo, |
17 | setAccessTokensToServers, | 17 | setAccessTokensToServers, |
18 | uploadVideo, viewVideo, wait | 18 | uploadVideo, |
19 | viewVideo, | ||
20 | wait | ||
19 | } from '../../../shared/extra-utils' | 21 | } from '../../../shared/extra-utils' |
20 | import { waitJobs } from '../../../shared/extra-utils/server/jobs' | 22 | import { waitJobs } from '../../../shared/extra-utils/server/jobs' |
21 | import { getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '../../helpers/ffmpeg-utils' | 23 | import { getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '../../helpers/ffmpeg-utils' |
@@ -102,7 +104,7 @@ describe('Test optimize old videos', function () { | |||
102 | 104 | ||
103 | expect(file.size).to.be.below(5000000) | 105 | expect(file.size).to.be.below(5000000) |
104 | 106 | ||
105 | const path = join(root(), 'test1', 'videos', video.uuid + '-' + file.resolution.id + '.mp4') | 107 | const path = join(root(), 'test' + servers[0].internalServerNumber, 'videos', video.uuid + '-' + file.resolution.id + '.mp4') |
106 | const bitrate = await getVideoFileBitrate(path) | 108 | const bitrate = await getVideoFileBitrate(path) |
107 | const fps = await getVideoFileFPS(path) | 109 | const fps = await getVideoFileFPS(path) |
108 | const resolution = await getVideoFileResolution(path) | 110 | const resolution = await getVideoFileResolution(path) |
diff --git a/server/tests/fixtures/preview.jpg b/server/tests/fixtures/preview.jpg index c40ece838..cb5692281 100644 --- a/server/tests/fixtures/preview.jpg +++ b/server/tests/fixtures/preview.jpg | |||
Binary files differ | |||
diff --git a/server/tests/fixtures/sample.ogg b/server/tests/fixtures/sample.ogg new file mode 100644 index 000000000..0d7f43eb7 --- /dev/null +++ b/server/tests/fixtures/sample.ogg | |||
Binary files differ | |||
diff --git a/server/tests/fixtures/video_short1-preview.webm.jpg b/server/tests/fixtures/video_short1-preview.webm.jpg index d2a068b78..157d3ca9a 100644 --- a/server/tests/fixtures/video_short1-preview.webm.jpg +++ b/server/tests/fixtures/video_short1-preview.webm.jpg | |||
Binary files differ | |||
diff --git a/shared/extra-utils/miscs/sql.ts b/shared/extra-utils/miscs/sql.ts index 3cfae5c23..34477cb78 100644 --- a/shared/extra-utils/miscs/sql.ts +++ b/shared/extra-utils/miscs/sql.ts | |||
@@ -1,11 +1,12 @@ | |||
1 | import { QueryTypes, Sequelize } from 'sequelize' | 1 | import { QueryTypes, Sequelize } from 'sequelize' |
2 | import { ServerInfo } from '../server/servers' | ||
2 | 3 | ||
3 | let sequelizes: { [ id: number ]: Sequelize } = {} | 4 | let sequelizes: { [ id: number ]: Sequelize } = {} |
4 | 5 | ||
5 | function getSequelize (serverNumber: number) { | 6 | function getSequelize (internalServerNumber: number) { |
6 | if (sequelizes[serverNumber]) return sequelizes[serverNumber] | 7 | if (sequelizes[internalServerNumber]) return sequelizes[internalServerNumber] |
7 | 8 | ||
8 | const dbname = 'peertube_test' + serverNumber | 9 | const dbname = 'peertube_test' + internalServerNumber |
9 | const username = 'peertube' | 10 | const username = 'peertube' |
10 | const password = 'peertube' | 11 | const password = 'peertube' |
11 | const host = 'localhost' | 12 | const host = 'localhost' |
@@ -18,37 +19,37 @@ function getSequelize (serverNumber: number) { | |||
18 | logging: false | 19 | logging: false |
19 | }) | 20 | }) |
20 | 21 | ||
21 | sequelizes[serverNumber] = seq | 22 | sequelizes[internalServerNumber] = seq |
22 | 23 | ||
23 | return seq | 24 | return seq |
24 | } | 25 | } |
25 | 26 | ||
26 | function setActorField (serverNumber: number, to: string, field: string, value: string) { | 27 | function setActorField (internalServerNumber: number, to: string, field: string, value: string) { |
27 | const seq = getSequelize(serverNumber) | 28 | const seq = getSequelize(internalServerNumber) |
28 | 29 | ||
29 | const options = { type: QueryTypes.UPDATE } | 30 | const options = { type: QueryTypes.UPDATE } |
30 | 31 | ||
31 | return seq.query(`UPDATE actor SET "${field}" = '${value}' WHERE url = '${to}'`, options) | 32 | return seq.query(`UPDATE actor SET "${field}" = '${value}' WHERE url = '${to}'`, options) |
32 | } | 33 | } |
33 | 34 | ||
34 | function setVideoField (serverNumber: number, uuid: string, field: string, value: string) { | 35 | function setVideoField (internalServerNumber: number, uuid: string, field: string, value: string) { |
35 | const seq = getSequelize(serverNumber) | 36 | const seq = getSequelize(internalServerNumber) |
36 | 37 | ||
37 | const options = { type: QueryTypes.UPDATE } | 38 | const options = { type: QueryTypes.UPDATE } |
38 | 39 | ||
39 | return seq.query(`UPDATE video SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options) | 40 | return seq.query(`UPDATE video SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options) |
40 | } | 41 | } |
41 | 42 | ||
42 | function setPlaylistField (serverNumber: number, uuid: string, field: string, value: string) { | 43 | function setPlaylistField (internalServerNumber: number, uuid: string, field: string, value: string) { |
43 | const seq = getSequelize(serverNumber) | 44 | const seq = getSequelize(internalServerNumber) |
44 | 45 | ||
45 | const options = { type: QueryTypes.UPDATE } | 46 | const options = { type: QueryTypes.UPDATE } |
46 | 47 | ||
47 | return seq.query(`UPDATE "videoPlaylist" SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options) | 48 | return seq.query(`UPDATE "videoPlaylist" SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options) |
48 | } | 49 | } |
49 | 50 | ||
50 | async function countVideoViewsOf (serverNumber: number, uuid: string) { | 51 | async function countVideoViewsOf (internalServerNumber: number, uuid: string) { |
51 | const seq = getSequelize(serverNumber) | 52 | const seq = getSequelize(internalServerNumber) |
52 | 53 | ||
53 | // tslint:disable | 54 | // tslint:disable |
54 | const query = `SELECT SUM("videoView"."views") AS "total" FROM "videoView" INNER JOIN "video" ON "video"."id" = "videoView"."videoId" WHERE "video"."uuid" = '${uuid}'` | 55 | const query = `SELECT SUM("videoView"."views") AS "total" FROM "videoView" INNER JOIN "video" ON "video"."id" = "videoView"."videoId" WHERE "video"."uuid" = '${uuid}'` |
@@ -62,11 +63,11 @@ async function countVideoViewsOf (serverNumber: number, uuid: string) { | |||
62 | return parseInt(total + '', 10) | 63 | return parseInt(total + '', 10) |
63 | } | 64 | } |
64 | 65 | ||
65 | async function closeAllSequelize (servers: any[]) { | 66 | async function closeAllSequelize (servers: ServerInfo[]) { |
66 | for (let i = 1; i <= servers.length; i++) { | 67 | for (const server of servers) { |
67 | if (sequelizes[ i ]) { | 68 | if (sequelizes[ server.internalServerNumber ]) { |
68 | await sequelizes[ i ].close() | 69 | await sequelizes[ server.internalServerNumber ].close() |
69 | delete sequelizes[ i ] | 70 | delete sequelizes[ server.internalServerNumber ] |
70 | } | 71 | } |
71 | } | 72 | } |
72 | } | 73 | } |
diff --git a/shared/extra-utils/server/config.ts b/shared/extra-utils/server/config.ts index deb77e9c0..a5f5989e0 100644 --- a/shared/extra-utils/server/config.ts +++ b/shared/extra-utils/server/config.ts | |||
@@ -91,6 +91,7 @@ function updateCustomSubConfig (url: string, token: string, newConfig: any) { | |||
91 | transcoding: { | 91 | transcoding: { |
92 | enabled: true, | 92 | enabled: true, |
93 | allowAdditionalExtensions: true, | 93 | allowAdditionalExtensions: true, |
94 | allowAudioFiles: true, | ||
94 | threads: 1, | 95 | threads: 1, |
95 | resolutions: { | 96 | resolutions: { |
96 | '240p': false, | 97 | '240p': false, |
diff --git a/shared/extra-utils/server/servers.ts b/shared/extra-utils/server/servers.ts index ed41bfa48..4c7d6862a 100644 --- a/shared/extra-utils/server/servers.ts +++ b/shared/extra-utils/server/servers.ts | |||
@@ -246,7 +246,7 @@ async function checkTmpIsEmpty (server: ServerInfo) { | |||
246 | } | 246 | } |
247 | 247 | ||
248 | async function checkDirectoryIsEmpty (server: ServerInfo, directory: string) { | 248 | async function checkDirectoryIsEmpty (server: ServerInfo, directory: string) { |
249 | const testDirectory = 'test' + server.serverNumber | 249 | const testDirectory = 'test' + server.internalServerNumber |
250 | 250 | ||
251 | const directoryPath = join(root(), testDirectory, directory) | 251 | const directoryPath = join(root(), testDirectory, directory) |
252 | 252 | ||
@@ -284,7 +284,7 @@ function cleanupTests (servers: ServerInfo[]) { | |||
284 | } | 284 | } |
285 | 285 | ||
286 | async function waitUntilLog (server: ServerInfo, str: string, count = 1) { | 286 | async function waitUntilLog (server: ServerInfo, str: string, count = 1) { |
287 | const logfile = join(root(), 'test' + server.serverNumber, 'logs/peertube.log') | 287 | const logfile = join(root(), 'test' + server.internalServerNumber, 'logs/peertube.log') |
288 | 288 | ||
289 | while (true) { | 289 | while (true) { |
290 | const buf = await readFile(logfile) | 290 | const buf = await readFile(logfile) |
diff --git a/shared/extra-utils/users/user-notifications.ts b/shared/extra-utils/users/user-notifications.ts index 495ff80d9..f7de542bf 100644 --- a/shared/extra-utils/users/user-notifications.ts +++ b/shared/extra-utils/users/user-notifications.ts | |||
@@ -380,7 +380,7 @@ async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string, | |||
380 | } | 380 | } |
381 | } | 381 | } |
382 | 382 | ||
383 | const commentUrl = `http://localhost:9001/videos/watch/${uuid};threadId=${threadId}` | 383 | const commentUrl = `http://localhost:${base.server.port}/videos/watch/${uuid};threadId=${threadId}` |
384 | function emailFinder (email: object) { | 384 | function emailFinder (email: object) { |
385 | return email[ 'text' ].indexOf(commentUrl) !== -1 | 385 | return email[ 'text' ].indexOf(commentUrl) !== -1 |
386 | } | 386 | } |
diff --git a/shared/extra-utils/videos/video-playlists.ts b/shared/extra-utils/videos/video-playlists.ts index 4d110a131..fd62bef19 100644 --- a/shared/extra-utils/videos/video-playlists.ts +++ b/shared/extra-utils/videos/video-playlists.ts | |||
@@ -252,10 +252,10 @@ function reorderVideosPlaylist (options: { | |||
252 | 252 | ||
253 | async function checkPlaylistFilesWereRemoved ( | 253 | async function checkPlaylistFilesWereRemoved ( |
254 | playlistUUID: string, | 254 | playlistUUID: string, |
255 | serverNumber: number, | 255 | internalServerNumber: number, |
256 | directories = [ 'thumbnails' ] | 256 | directories = [ 'thumbnails' ] |
257 | ) { | 257 | ) { |
258 | const testDirectory = 'test' + serverNumber | 258 | const testDirectory = 'test' + internalServerNumber |
259 | 259 | ||
260 | for (const directory of directories) { | 260 | for (const directory of directories) { |
261 | const directoryPath = join(root(), testDirectory, directory) | 261 | const directoryPath = join(root(), testDirectory, directory) |
diff --git a/shared/extra-utils/videos/videos.ts b/shared/extra-utils/videos/videos.ts index b5a07b792..b64de2470 100644 --- a/shared/extra-utils/videos/videos.ts +++ b/shared/extra-utils/videos/videos.ts | |||
@@ -568,8 +568,8 @@ async function completeVideoCheck ( | |||
568 | expect(file).not.to.be.undefined | 568 | expect(file).not.to.be.undefined |
569 | 569 | ||
570 | let extension = extname(attributes.fixture) | 570 | let extension = extname(attributes.fixture) |
571 | // Transcoding enabled on server 2, extension will always be .mp4 | 571 | // Transcoding enabled: extension will always be .mp4 |
572 | if (attributes.account.host === 'localhost:9002') extension = '.mp4' | 572 | if (attributes.files.length > 1) extension = '.mp4' |
573 | 573 | ||
574 | const magnetUri = file.magnetUri | 574 | const magnetUri = file.magnetUri |
575 | expect(file.magnetUri).to.have.lengthOf.above(2) | 575 | expect(file.magnetUri).to.have.lengthOf.above(2) |
diff --git a/shared/models/server/custom-config.model.ts b/shared/models/server/custom-config.model.ts index ca52eff4b..4cc379b2a 100644 --- a/shared/models/server/custom-config.model.ts +++ b/shared/models/server/custom-config.model.ts | |||
@@ -54,6 +54,7 @@ export interface CustomConfig { | |||
54 | transcoding: { | 54 | transcoding: { |
55 | enabled: boolean | 55 | enabled: boolean |
56 | allowAdditionalExtensions: boolean | 56 | allowAdditionalExtensions: boolean |
57 | allowAudioFiles: boolean | ||
57 | threads: number | 58 | threads: number |
58 | resolutions: { | 59 | resolutions: { |
59 | '240p': boolean | 60 | '240p': boolean |
diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml index 9963e1d26..a8a064fd0 100644 --- a/support/doc/api/openapi.yaml +++ b/support/doc/api/openapi.yaml | |||
@@ -38,7 +38,7 @@ info: | |||
38 | } | 38 | } |
39 | ``` | 39 | ``` |
40 | externalDocs: | 40 | externalDocs: |
41 | url: https://docs.joinpeertube.org/api.html | 41 | url: https://docs.joinpeertube.org/#/api-rest-reference.html |
42 | tags: | 42 | tags: |
43 | - name: Accounts | 43 | - name: Accounts |
44 | description: > | 44 | description: > |
@@ -99,6 +99,7 @@ x-tagGroups: | |||
99 | - name: Videos | 99 | - name: Videos |
100 | tags: | 100 | tags: |
101 | - Video | 101 | - Video |
102 | - Video Caption | ||
102 | - Video Channel | 103 | - Video Channel |
103 | - Video Comment | 104 | - Video Comment |
104 | - Video Following | 105 | - Video Following |
@@ -152,7 +153,7 @@ paths: | |||
152 | content: | 153 | content: |
153 | application/json: | 154 | application/json: |
154 | schema: | 155 | schema: |
155 | $ref: '#/components/schemas/Video' | 156 | $ref: '#/components/schemas/VideoListResponse' |
156 | x-code-samples: | 157 | x-code-samples: |
157 | - lang: JavaScript | 158 | - lang: JavaScript |
158 | source: | | 159 | source: | |
@@ -575,9 +576,7 @@ paths: | |||
575 | content: | 576 | content: |
576 | application/json: | 577 | application/json: |
577 | schema: | 578 | schema: |
578 | type: array | 579 | $ref: '#/components/schemas/VideoListResponse' |
579 | items: | ||
580 | $ref: '#/components/schemas/Video' | ||
581 | /users/me/subscriptions: | 580 | /users/me/subscriptions: |
582 | get: | 581 | get: |
583 | summary: Get subscriptions of the current user | 582 | summary: Get subscriptions of the current user |
@@ -638,9 +637,7 @@ paths: | |||
638 | content: | 637 | content: |
639 | application/json: | 638 | application/json: |
640 | schema: | 639 | schema: |
641 | type: array | 640 | $ref: '#/components/schemas/VideoListResponse' |
642 | items: | ||
643 | $ref: '#/components/schemas/Video' | ||
644 | '/users/me/subscriptions/{uri}': | 641 | '/users/me/subscriptions/{uri}': |
645 | get: | 642 | get: |
646 | summary: Get subscription of the current user for a given uri | 643 | summary: Get subscription of the current user for a given uri |
@@ -730,9 +727,7 @@ paths: | |||
730 | content: | 727 | content: |
731 | application/json: | 728 | application/json: |
732 | schema: | 729 | schema: |
733 | type: array | 730 | $ref: '#/components/schemas/VideoListResponse' |
734 | items: | ||
735 | $ref: '#/components/schemas/Video' | ||
736 | /videos/categories: | 731 | /videos/categories: |
737 | get: | 732 | get: |
738 | summary: Get list of video licences known by the server | 733 | summary: Get list of video licences known by the server |
@@ -1025,7 +1020,7 @@ paths: | |||
1025 | description: Video preview file | 1020 | description: Video preview file |
1026 | type: string | 1021 | type: string |
1027 | privacy: | 1022 | privacy: |
1028 | $ref: '#/components/schemas/VideoPrivacy' | 1023 | $ref: '#/components/schemas/VideoPrivacySet' |
1029 | category: | 1024 | category: |
1030 | description: Video category | 1025 | description: Video category |
1031 | type: string | 1026 | type: string |
@@ -1129,7 +1124,7 @@ paths: | |||
1129 | description: Video preview file | 1124 | description: Video preview file |
1130 | type: string | 1125 | type: string |
1131 | privacy: | 1126 | privacy: |
1132 | $ref: '#/components/schemas/VideoPrivacy' | 1127 | $ref: '#/components/schemas/VideoPrivacySet' |
1133 | category: | 1128 | category: |
1134 | description: Video category | 1129 | description: Video category |
1135 | type: string | 1130 | type: string |
@@ -1247,6 +1242,58 @@ paths: | |||
1247 | type: array | 1242 | type: array |
1248 | items: | 1243 | items: |
1249 | $ref: '#/components/schemas/VideoBlacklist' | 1244 | $ref: '#/components/schemas/VideoBlacklist' |
1245 | /videos/{id}/captions: | ||
1246 | get: | ||
1247 | summary: Get list of video's captions | ||
1248 | tags: | ||
1249 | - Video Caption | ||
1250 | parameters: | ||
1251 | - $ref: '#/components/parameters/id2' | ||
1252 | responses: | ||
1253 | '200': | ||
1254 | description: successful operation | ||
1255 | content: | ||
1256 | application/json: | ||
1257 | schema: | ||
1258 | type: object | ||
1259 | properties: | ||
1260 | total: | ||
1261 | type: integer | ||
1262 | data: | ||
1263 | type: array | ||
1264 | items: | ||
1265 | $ref: '#/components/schemas/VideoCaption' | ||
1266 | /videos/{id}/captions/{captionLanguage}: | ||
1267 | put: | ||
1268 | summary: Add or replace a video caption | ||
1269 | tags: | ||
1270 | - Video Caption | ||
1271 | parameters: | ||
1272 | - $ref: '#/components/parameters/id2' | ||
1273 | - $ref: '#/components/parameters/captionLanguage' | ||
1274 | requestBody: | ||
1275 | content: | ||
1276 | multipart/form-data: | ||
1277 | schema: | ||
1278 | type: object | ||
1279 | properties: | ||
1280 | captionfile: | ||
1281 | description: The file to upload. | ||
1282 | type: string | ||
1283 | format: binary | ||
1284 | responses: | ||
1285 | '204': | ||
1286 | $ref: '#/paths/~1users~1me/put/responses/204' | ||
1287 | delete: | ||
1288 | summary: Delete a video caption | ||
1289 | tags: | ||
1290 | - Video Caption | ||
1291 | parameters: | ||
1292 | - $ref: '#/components/parameters/id2' | ||
1293 | - $ref: '#/components/parameters/captionLanguage' | ||
1294 | responses: | ||
1295 | '204': | ||
1296 | $ref: '#/paths/~1users~1me/put/responses/204' | ||
1250 | /video-channels: | 1297 | /video-channels: |
1251 | get: | 1298 | get: |
1252 | summary: Get list of video channels | 1299 | summary: Get list of video channels |
@@ -1318,6 +1365,7 @@ paths: | |||
1318 | get: | 1365 | get: |
1319 | summary: Get videos of a video channel by its id | 1366 | summary: Get videos of a video channel by its id |
1320 | tags: | 1367 | tags: |
1368 | - Video | ||
1321 | - Video Channel | 1369 | - Video Channel |
1322 | parameters: | 1370 | parameters: |
1323 | - $ref: '#/components/parameters/channelHandle' | 1371 | - $ref: '#/components/parameters/channelHandle' |
@@ -1327,7 +1375,7 @@ paths: | |||
1327 | content: | 1375 | content: |
1328 | application/json: | 1376 | application/json: |
1329 | schema: | 1377 | schema: |
1330 | $ref: '#/components/schemas/Video' | 1378 | $ref: '#/components/schemas/VideoListResponse' |
1331 | '/accounts/{name}/video-channels': | 1379 | '/accounts/{name}/video-channels': |
1332 | get: | 1380 | get: |
1333 | summary: Get video channels of an account by its name | 1381 | summary: Get video channels of an account by its name |
@@ -1443,7 +1491,7 @@ paths: | |||
1443 | schema: | 1491 | schema: |
1444 | $ref: '#/components/schemas/CommentThreadPostResponse' | 1492 | $ref: '#/components/schemas/CommentThreadPostResponse' |
1445 | delete: | 1493 | delete: |
1446 | summary: 'Delete a comment in a comment therad by its id, of a video by its id' | 1494 | summary: 'Delete a comment in a comment thread by its id, of a video by its id' |
1447 | security: | 1495 | security: |
1448 | - OAuth2: [] | 1496 | - OAuth2: [] |
1449 | tags: | 1497 | tags: |
@@ -1487,9 +1535,7 @@ paths: | |||
1487 | content: | 1535 | content: |
1488 | application/json: | 1536 | application/json: |
1489 | schema: | 1537 | schema: |
1490 | type: array | 1538 | $ref: '#/components/schemas/VideoListResponse' |
1491 | items: | ||
1492 | $ref: '#/components/schemas/Video' | ||
1493 | servers: | 1539 | servers: |
1494 | - url: 'https://peertube.cpy.re/api/v1' | 1540 | - url: 'https://peertube.cpy.re/api/v1' |
1495 | description: Live Test Server (live data - stable version) | 1541 | description: Live Test Server (live data - stable version) |
@@ -1611,6 +1657,13 @@ components: | |||
1611 | description: The video id or uuid | 1657 | description: The video id or uuid |
1612 | schema: | 1658 | schema: |
1613 | type: string | 1659 | type: string |
1660 | captionLanguage: | ||
1661 | name: captionLanguage | ||
1662 | in: path | ||
1663 | required: true | ||
1664 | description: The caption language | ||
1665 | schema: | ||
1666 | type: string | ||
1614 | channelHandle: | 1667 | channelHandle: |
1615 | name: channelHandle | 1668 | name: channelHandle |
1616 | in: path | 1669 | in: path |
@@ -1739,7 +1792,7 @@ components: | |||
1739 | 1792 | ||
1740 | - Have an account with sufficient authorization levels | 1793 | - Have an account with sufficient authorization levels |
1741 | 1794 | ||
1742 | - [Generate](https://docs.joinpeertube.org/lang/en/devdocs/rest.html) a | 1795 | - [Generate](https://docs.joinpeertube.org/#/api-rest-getting-started) a |
1743 | Bearer Token | 1796 | Bearer Token |
1744 | 1797 | ||
1745 | - Make Authenticated Requests | 1798 | - Make Authenticated Requests |
@@ -1764,12 +1817,23 @@ components: | |||
1764 | type: string | 1817 | type: string |
1765 | label: | 1818 | label: |
1766 | type: string | 1819 | type: string |
1767 | VideoPrivacy: | 1820 | VideoPrivacySet: |
1768 | type: string | 1821 | type: integer |
1769 | enum: | 1822 | enum: |
1770 | - Public | 1823 | - 1 |
1771 | - Unlisted | 1824 | - 2 |
1772 | - Private | 1825 | - 3 |
1826 | description: 'The video privacy (Public = 1, Unlisted = 2, Private = 3)' | ||
1827 | VideoPrivacyConstant: | ||
1828 | properties: | ||
1829 | id: | ||
1830 | type: integer | ||
1831 | enum: | ||
1832 | - 1 | ||
1833 | - 2 | ||
1834 | - 3 | ||
1835 | label: | ||
1836 | type: string | ||
1773 | Video: | 1837 | Video: |
1774 | properties: | 1838 | properties: |
1775 | id: | 1839 | id: |
@@ -1789,7 +1853,7 @@ components: | |||
1789 | language: | 1853 | language: |
1790 | $ref: '#/components/schemas/VideoConstantString' | 1854 | $ref: '#/components/schemas/VideoConstantString' |
1791 | privacy: | 1855 | privacy: |
1792 | $ref: '#/components/schemas/VideoPrivacy' | 1856 | $ref: '#/components/schemas/VideoPrivacyConstant' |
1793 | description: | 1857 | description: |
1794 | type: string | 1858 | type: string |
1795 | duration: | 1859 | duration: |
@@ -1917,6 +1981,12 @@ components: | |||
1917 | type: array | 1981 | type: array |
1918 | items: | 1982 | items: |
1919 | $ref: '#/components/schemas/VideoCommentThreadTree' | 1983 | $ref: '#/components/schemas/VideoCommentThreadTree' |
1984 | VideoCaption: | ||
1985 | properties: | ||
1986 | language: | ||
1987 | $ref: '#/components/schemas/VideoConstantString' | ||
1988 | captionPath: | ||
1989 | type: string | ||
1920 | Avatar: | 1990 | Avatar: |
1921 | properties: | 1991 | properties: |
1922 | path: | 1992 | path: |
@@ -1966,6 +2036,13 @@ components: | |||
1966 | autoPlayVideo: | 2036 | autoPlayVideo: |
1967 | type: boolean | 2037 | type: boolean |
1968 | role: | 2038 | role: |
2039 | type: integer | ||
2040 | enum: | ||
2041 | - 0 | ||
2042 | - 1 | ||
2043 | - 2 | ||
2044 | description: 'The user role (Admin = 0, Moderator = 1, User = 2)' | ||
2045 | roleLabel: | ||
1969 | type: string | 2046 | type: string |
1970 | enum: | 2047 | enum: |
1971 | - User | 2048 | - User |
@@ -2096,6 +2173,14 @@ components: | |||
2096 | properties: | 2173 | properties: |
2097 | comment: | 2174 | comment: |
2098 | $ref: '#/components/schemas/VideoComment' | 2175 | $ref: '#/components/schemas/VideoComment' |
2176 | VideoListResponse: | ||
2177 | properties: | ||
2178 | total: | ||
2179 | type: number | ||
2180 | data: | ||
2181 | type: array | ||
2182 | items: | ||
2183 | $ref: '#/components/schemas/Video' | ||
2099 | AddUser: | 2184 | AddUser: |
2100 | properties: | 2185 | properties: |
2101 | username: | 2186 | username: |
@@ -2115,12 +2200,11 @@ components: | |||
2115 | description: 'The user daily video quota ' | 2200 | description: 'The user daily video quota ' |
2116 | role: | 2201 | role: |
2117 | type: integer | 2202 | type: integer |
2118 | format: int32 | ||
2119 | enum: | 2203 | enum: |
2120 | - 0 | 2204 | - 0 |
2121 | - 1 | 2205 | - 1 |
2122 | - 2 | 2206 | - 2 |
2123 | description: 'The user role ' | 2207 | description: 'The user role (Admin = 0, Moderator = 1, User = 2)' |
2124 | required: | 2208 | required: |
2125 | - username | 2209 | - username |
2126 | - password | 2210 | - password |
@@ -2143,8 +2227,12 @@ components: | |||
2143 | type: string | 2227 | type: string |
2144 | description: 'The updated daily video quota of the user ' | 2228 | description: 'The updated daily video quota of the user ' |
2145 | role: | 2229 | role: |
2146 | type: string | 2230 | type: integer |
2147 | description: 'The updated role of the user ' | 2231 | enum: |
2232 | - 0 | ||
2233 | - 1 | ||
2234 | - 2 | ||
2235 | description: 'The user role (Admin = 0, Moderator = 1, User = 2)' | ||
2148 | required: | 2236 | required: |
2149 | - id | 2237 | - id |
2150 | 2238 | ||
diff --git a/support/doc/api/quickstart.md b/support/doc/api/quickstart.md index 00874a1c9..2222be741 100644 --- a/support/doc/api/quickstart.md +++ b/support/doc/api/quickstart.md | |||
@@ -47,7 +47,7 @@ $ curl -H 'Authorization: Bearer 90286a0bdf0f7315d9d3fe8dabf9e1d2be9c97d0' https | |||
47 | ``` | 47 | ``` |
48 | 48 | ||
49 | 49 | ||
50 | ### List videos | 50 | ## List videos |
51 | 51 | ||
52 | ```bash | 52 | ```bash |
53 | $ curl https://peertube.example.com/api/v1/videos | 53 | $ curl https://peertube.example.com/api/v1/videos |
diff --git a/support/doc/development/client/code.md b/support/doc/development/client/code.md deleted file mode 100644 index 235116e78..000000000 --- a/support/doc/development/client/code.md +++ /dev/null | |||
@@ -1,67 +0,0 @@ | |||
1 | # Client code documentation | ||
2 | |||
3 | The client is a HTML/CSS/JavaScript web application (single page application -> SPA) developed with [TypeScript](https://www.typescriptlang.org/)/[Angular](https://angular.io/). | ||
4 | |||
5 | |||
6 | ## Technologies | ||
7 | |||
8 | * [TypeScript](https://www.typescriptlang.org/) -> Language | ||
9 | * [Angular](https://angular.io) -> JavaScript framework | ||
10 | * [SASS](http://sass-lang.com/) -> CSS framework | ||
11 | * [Webpack](https://webpack.js.org/) -> Source builder (compile TypeScript, SASS files, bundle them...) | ||
12 | * [Bootstrap](http://getbootstrap.com/) -> CSS framework | ||
13 | * [WebTorrent](https://webtorrent.io/) -> JavaScript library to make P2P in the browser | ||
14 | * [VideoJS](http://videojs.com/) -> JavaScript player framework | ||
15 | |||
16 | |||
17 | ## Files | ||
18 | |||
19 | The client files are in the `client` directory. The Webpack 2 configurations files are in `client/config` and the source files in `client/src`. | ||
20 | The client modules description are in the [client/package.json](/client/package.json). There are many modules that are used to compile the web application in development or production mode. | ||
21 | Here is the description of the useful `client` files directory: | ||
22 | |||
23 | tslint.json -> TypeScript linter rules | ||
24 | tsconfig.json -> TypeScript configuration for the compilation | ||
25 | .bootstraprc -> Bootstrap configuration file (which module we need) | ||
26 | config -> Webpack configuration files | ||
27 | src | ||
28 | |__ app -> TypeScript files for Angular application | ||
29 | |__ assets -> static files (images...) | ||
30 | |__ sass -> SASS files that are global for the application | ||
31 | |__ standalone -> files outside the Angular application (embed HTML page...) | ||
32 | |__ index.html -> root HTML file for our Angular application | ||
33 | |__ main.ts -> Main TypeScript file that boostraps our Angular application | ||
34 | |__ polyfills.ts -> Polyfills imports (ES 2015...) | ||
35 | |||
36 | Details of the Angular application file structure. It tries to follow [the official Angular styleguide](https://angular.io/docs/ts/latest/guide/style-guide.html). | ||
37 | |||
38 | app | ||
39 | |__ +admin -> Admin components (followers, users...) | ||
40 | |__ account -> Account components (password change...) | ||
41 | |__ core -> Core components/services | ||
42 | |__ header -> Header components (logo, search...) | ||
43 | |__ login -> Login component | ||
44 | |__ menu -> Menu component (on the left) | ||
45 | |__ shared -> Shared components/services (search component, REST services...) | ||
46 | |__ signup -> Signup form | ||
47 | |__ videos -> Video components (list, watch, upload...) | ||
48 | |__ app.component.{html,scss,ts} -> Main application component | ||
49 | |__ app-routing.module.ts -> Main Angular routes | ||
50 | |__ app.module.ts -> Angular root module that imports all submodules we need | ||
51 | |||
52 | ## Conventions | ||
53 | |||
54 | Uses [TSLint](https://palantir.github.io/tslint/) for TypeScript linting and [Angular styleguide](https://angular.io/docs/ts/latest/guide/style-guide.html). | ||
55 | |||
56 | ## Concepts | ||
57 | |||
58 | In a Angular application, we create components that we put together. Each component is defined by an HTML structure, a TypeScript file and optionally a SASS file. | ||
59 | If you are not familiar with Angular I recommend you to read the [quickstart guide](https://angular.io/docs/ts/latest/quickstart.html). | ||
60 | |||
61 | ## Components tree | ||
62 | |||
63 | ![Components tree](/support/doc/development/client/components-tree.svg) | ||
64 | |||
65 | ## Newcomers | ||
66 | |||
67 | The main client component is `app.component.ts`. You can begin to look at this file. Then you could navigate in the different submodules to see how components are built. | ||
diff --git a/support/doc/development/client/components-tree.png b/support/doc/development/client/components-tree.png deleted file mode 100644 index 09582d742..000000000 --- a/support/doc/development/client/components-tree.png +++ /dev/null | |||
Binary files differ | |||
diff --git a/support/doc/development/client/components-tree.svg b/support/doc/development/client/components-tree.svg deleted file mode 100644 index fd6951d93..000000000 --- a/support/doc/development/client/components-tree.svg +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> | ||
2 | <svg xmlns="http://www.w3.org/2000/svg" style="background-color: rgb(255, 255, 255);" xmlns:xlink="http://www.w3.org/1999/xlink" width="1141px" height="311px" version="1.1" content="<mxfile userAgent="Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0" version="8.9.9" editor="www.draw.io" type="device"><diagram name="Page-1" id="8be7db5e-9885-9541-e5e4-cf9e4eb3a109">7Zpbb5swFMc/TaTtZQLMLY9d1nYP2zSp2u3RwQ54dTAypkn36WeDSSCmVdcR3FTpQwXHN/z7G59zHGZgsd5ec1hknxnCdOY5aDsDH2aeFzuB/K8M943Bd93GkHKCGlPHcEP+YG10tLUiCJe9ioIxKkjRNyYsz3EiejbIOdv0q60Y7Y9awBQbhpsEUtP6gyCRaasbzvcFHzFJMz107EVNwRImtylnVa7Hm3lgVf81xWvY9qUnWmYQsU3HBC5nYMEZE83VervAVKFtsTXtrh4o3T03x7l4SgNPT6gU9+3cMZIo9C3jImMpyyG93Fvf1/PDqgdH3mViTeWlKy/xloifyvwu0He/2pJc8PtOkbr9pTsoBeTiQqkmDTnLcWu7IpTqOjhHbY2EwrIkSWPUVdQQv7EQ93opwUowado//ifGCl1vxXKhq7nq3kSmKZas4omG4ulFCHmKdS1NTuHqNNOYrzFbYzlHWYFjCgW5668sqBdouqu3F0leaJ2GNQPOWbNnahZb08w9a/ZMzSJrmnlnzZ6pGbCmWTymZk/F9rC2gxyeRTSaiKAfvFKCPpiKYGiF4CO7yBR0/f+kq5t+ZUQO7Dk6k5jPdbCjEwm3DX7aLppn0K0ONNo9xtNkG3XreEyOl/RSTLWt6LHvIK10pxdFUWdw60K6Na1eDz6lMt1TkDcZEfimgPU8NjLh7KM+dFor6fcWjDJe9wOwiwIc1X6Ts1vcKZmHEYDhY27uDnOBt4+6MF0K/P5CbRO8TSd9bG1ZN3N0RmA76obz8Ko7hWAEmGs8tBWMtKHrOYD8Z80Ca5qN6wVe27vk29IFGP7jO0GYlTMvhGvlEPJlWewmdgQ3giCOV8mQGwmTGC9XI7mR+CDeaaOSKfyIbzC+REQFY2+qgjKIZPuqQFDgt5I6lQO/R+ROXqainnxjWvJDi2zWq3csgQIcI39IoNhbgnAkPx8c+vkofBcYEsUDCgUjKBQYCn0i5fFCp2mQ+laRhgbSH1Ak2YkzNcLRSZlGBtOLJJE+8tRXauBa3J1j891nKclPHOnu1wwbSHdBzznYa3/O6gZ77Wq3EIV70whz6pnTkGiuLdHARMcQr1A0YC3fnZuuGq2P6VUmSaNsZlHtrtlBesUoVd+knDZU9zCkBAbU+VBAGY3A1DWY1um/6nZZlfjk0Xr22AIzqfxWYn7qSA8DyyGkg/nPGEgHEqAlq15A+mPwG6D8INLQt3k4ZZ4ADpxCfVUf9nnOF6aOra7q7/Be6CnUfykRRTaVMM8JB5T4rB53l/a/Qg3mjk0NzB2mdYiLDOY5pkfcvyfB6zoTngzI2/03t823A/vvmsHlXw==</diagram></mxfile>"><defs/><g transform="translate(0.5,0.5)"><path d="M 390 80 L 390 104 L 130 104 L 130 121.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 130 126.88 L 126.5 119.88 L 130 121.63 L 133.5 119.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 390 80 L 390 104 L 280 104 L 280 121.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 280 126.88 L 276.5 119.88 L 280 121.63 L 283.5 119.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 390 80 L 390 104 L 560 104 L 560 121.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 560 126.88 L 556.5 119.88 L 560 121.63 L 563.5 119.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 390 80 L 390 104 L 430 104 L 430 121.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 430 126.88 L 426.5 119.88 L 430 121.63 L 433.5 119.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 390 80 L 390 104 L 690 104 L 690 121.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 690 126.88 L 686.5 119.88 L 690 121.63 L 693.5 119.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 390 80 L 390 104 L 820 104 L 820 121.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 820 126.88 L 816.5 119.88 L 820 121.63 L 823.5 119.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 390 80 L 390 104 L 950 104 L 950 121.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 950 126.88 L 946.5 119.88 L 950 121.63 L 953.5 119.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 390 80 L 390 104 L 1080 104 L 1080 121.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1080 126.88 L 1076.5 119.88 L 1080 121.63 L 1083.5 119.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><ellipse cx="390" cy="40" rx="60" ry="40" fill="#e1d5e7" stroke="#9673a6" pointer-events="none"/><g transform="translate(345.5,34.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="88" height="11" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 89px; white-space: nowrap; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">App component</div></div></foreignObject><text x="44" y="11" fill="#000000" text-anchor="middle" font-size="11px" font-family="Helvetica">App component</text></switch></g><path d="M 430 208 L 430 232 L 370 232 L 370 250.13" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 370 255.38 L 366.5 248.38 L 370 250.13 L 373.5 248.38 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 430 208 L 430 232 L 470 232 L 470 250.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 470 255.88 L 466.5 248.88 L 470 250.63 L 473.5 248.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 430 208 L 430 232 L 570 232 L 570 250.13" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 570 255.38 L 566.5 248.38 L 570 250.13 L 573.5 248.38 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><ellipse cx="430" cy="168" rx="60" ry="40" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/><g transform="translate(408.5,162.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="43" height="11" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 44px; white-space: nowrap; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">Videos </div></div></foreignObject><text x="22" y="11" fill="#000000" text-anchor="middle" font-size="11px" font-family="Helvetica">Videos </text></switch></g><ellipse cx="570" cy="282" rx="40" ry="25" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/><g transform="translate(524.5,262.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="91" height="38" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 91px; white-space: normal; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">Edit (upload/update)<div><br /></div></div></div></foreignObject><text x="46" y="25" fill="#000000" text-anchor="middle" font-size="11px" font-family="Helvetica">Edit (upload/update)<div><br></div></text></switch></g><ellipse cx="470" cy="282" rx="40" ry="25" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/><g transform="translate(459.5,275.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="21" height="11" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 22px; white-space: nowrap; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">List</div></div></foreignObject><text x="11" y="11" fill="#000000" text-anchor="middle" font-size="11px" font-family="Helvetica">List</text></switch></g><ellipse cx="370" cy="282" rx="40" ry="25" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/><g transform="translate(352.5,275.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="34" height="11" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 35px; white-space: nowrap; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">Watch</div></div></foreignObject><text x="17" y="11" fill="#000000" text-anchor="middle" font-size="11px" font-family="Helvetica">Watch</text></switch></g><ellipse cx="560" cy="168" rx="60" ry="40" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/><g transform="translate(537.5,162.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="45" height="11" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 46px; white-space: nowrap; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">Account</div></div></foreignObject><text x="23" y="11" fill="#000000" text-anchor="middle" font-size="11px" font-family="Helvetica">Account</text></switch></g><ellipse cx="280" cy="168" rx="60" ry="40" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/><g transform="translate(263.5,162.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="32" height="11" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 33px; white-space: nowrap; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">Login</div></div></foreignObject><text x="16" y="11" fill="#000000" text-anchor="middle" font-size="11px" font-family="Helvetica">Login</text></switch></g><path d="M 130 208 L 130 231 L 45 231 L 45 246.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 45 251.88 L 41.5 244.88 L 45 246.63 L 48.5 244.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 130 208 L 130 231 L 155 231 L 155 246.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 155 251.88 L 151.5 244.88 L 155 246.63 L 158.5 244.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 130 208 L 130 231 L 260 231 L 260 246.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 260 251.88 L 256.5 244.88 L 260 246.63 L 263.5 244.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><ellipse cx="130" cy="168" rx="60" ry="40" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/><g transform="translate(111.5,162.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="36" height="11" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 37px; white-space: nowrap; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">Admin</div></div></foreignObject><text x="18" y="11" fill="#000000" text-anchor="middle" font-size="11px" font-family="Helvetica">Admin</text></switch></g><ellipse cx="45" cy="282" rx="45" ry="28.5" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/><g transform="translate(22.5,275.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="44" height="11" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 45px; white-space: nowrap; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">Follows</div></div></foreignObject><text x="22" y="11" fill="#000000" text-anchor="middle" font-size="11px" font-family="Helvetica">Follows</text></switch></g><ellipse cx="155" cy="282" rx="45" ry="28.5" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/><g transform="translate(116.5,275.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="77" height="11" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 78px; white-space: nowrap; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">Video abuses</div></div></foreignObject><text x="39" y="11" fill="#000000" text-anchor="middle" font-size="11px" font-family="Helvetica">Video abuses</text></switch></g><ellipse cx="260" cy="282" rx="40" ry="28.5" fill="#dae8fc" stroke="#6c8ebf" pointer-events="none"/><g transform="translate(243.5,275.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="33" height="11" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 34px; white-space: nowrap; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">Users</div></div></foreignObject><text x="17" y="11" fill="#000000" text-anchor="middle" font-size="11px" font-family="Helvetica">Users</text></switch></g><ellipse cx="690" cy="168" rx="60" ry="40" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/><g transform="translate(673.5,162.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="33" height="11" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 34px; white-space: nowrap; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">About</div></div></foreignObject><text x="17" y="11" fill="#000000" text-anchor="middle" font-size="11px" font-family="Helvetica">About</text></switch></g><ellipse cx="820" cy="168" rx="60" ry="40" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/><g transform="translate(773.5,162.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="92" height="11" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 93px; white-space: nowrap; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;"><div>Page Not Found</div></div></div></foreignObject><text x="46" y="11" fill="#000000" text-anchor="middle" font-size="11px" font-family="Helvetica"><div>Page Not Found</div></text></switch></g><ellipse cx="950" cy="168" rx="60" ry="40" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/><g transform="translate(916.5,162.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="66" height="11" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 67px; white-space: nowrap; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;"><div>My Account</div></div></div></foreignObject><text x="33" y="11" fill="#000000" text-anchor="middle" font-size="11px" font-family="Helvetica"><div>My Account</div></text></switch></g><ellipse cx="1080" cy="168" rx="60" ry="40" fill="#d5e8d4" stroke="#82b366" pointer-events="none"/><g transform="translate(1034.5,162.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="90" height="11" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 91px; white-space: nowrap; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">Video Channels</div></div></foreignObject><text x="45" y="11" fill="#000000" text-anchor="middle" font-size="11px" font-family="Helvetica">Video Channels</text></switch></g></g></svg> \ No newline at end of file | ||
diff --git a/support/doc/development/client/components-tree.xml b/support/doc/development/client/components-tree.xml deleted file mode 100644 index 5a37c48bc..000000000 --- a/support/doc/development/client/components-tree.xml +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | <mxfile userAgent="Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0" version="8.9.9" editor="www.draw.io" type="device"><diagram name="Page-1" id="8be7db5e-9885-9541-e5e4-cf9e4eb3a109">7Zpbb5swFMc/TaTtZQLMLY9d1nYP2zSp2u3RwQ54dTAypkn36WeDSSCmVdcR3FTpQwXHN/z7G59zHGZgsd5ec1hknxnCdOY5aDsDH2aeFzuB/K8M943Bd93GkHKCGlPHcEP+YG10tLUiCJe9ioIxKkjRNyYsz3EiejbIOdv0q60Y7Y9awBQbhpsEUtP6gyCRaasbzvcFHzFJMz107EVNwRImtylnVa7Hm3lgVf81xWvY9qUnWmYQsU3HBC5nYMEZE83VervAVKFtsTXtrh4o3T03x7l4SgNPT6gU9+3cMZIo9C3jImMpyyG93Fvf1/PDqgdH3mViTeWlKy/xloifyvwu0He/2pJc8PtOkbr9pTsoBeTiQqkmDTnLcWu7IpTqOjhHbY2EwrIkSWPUVdQQv7EQ93opwUowado//ifGCl1vxXKhq7nq3kSmKZas4omG4ulFCHmKdS1NTuHqNNOYrzFbYzlHWYFjCgW5668sqBdouqu3F0leaJ2GNQPOWbNnahZb08w9a/ZMzSJrmnlnzZ6pGbCmWTymZk/F9rC2gxyeRTSaiKAfvFKCPpiKYGiF4CO7yBR0/f+kq5t+ZUQO7Dk6k5jPdbCjEwm3DX7aLppn0K0ONNo9xtNkG3XreEyOl/RSTLWt6LHvIK10pxdFUWdw60K6Na1eDz6lMt1TkDcZEfimgPU8NjLh7KM+dFor6fcWjDJe9wOwiwIc1X6Ts1vcKZmHEYDhY27uDnOBt4+6MF0K/P5CbRO8TSd9bG1ZN3N0RmA76obz8Ko7hWAEmGs8tBWMtKHrOYD8Z80Ca5qN6wVe27vk29IFGP7jO0GYlTMvhGvlEPJlWewmdgQ3giCOV8mQGwmTGC9XI7mR+CDeaaOSKfyIbzC+REQFY2+qgjKIZPuqQFDgt5I6lQO/R+ROXqainnxjWvJDi2zWq3csgQIcI39IoNhbgnAkPx8c+vkofBcYEsUDCgUjKBQYCn0i5fFCp2mQ+laRhgbSH1Ak2YkzNcLRSZlGBtOLJJE+8tRXauBa3J1j891nKclPHOnu1wwbSHdBzznYa3/O6gZ77Wq3EIV70whz6pnTkGiuLdHARMcQr1A0YC3fnZuuGq2P6VUmSaNsZlHtrtlBesUoVd+knDZU9zCkBAbU+VBAGY3A1DWY1um/6nZZlfjk0Xr22AIzqfxWYn7qSA8DyyGkg/nPGEgHEqAlq15A+mPwG6D8INLQt3k4ZZ4ADpxCfVUf9nnOF6aOra7q7/Be6CnUfykRRTaVMM8JB5T4rB53l/a/Qg3mjk0NzB2mdYiLDOY5pkfcvyfB6zoTngzI2/03t823A/vvmsHlXw==</diagram></mxfile> \ No newline at end of file | ||
diff --git a/support/doc/development/server/code.md b/support/doc/development/server/code.md deleted file mode 100644 index 3894c2542..000000000 --- a/support/doc/development/server/code.md +++ /dev/null | |||
@@ -1,58 +0,0 @@ | |||
1 | # Server code documentation | ||
2 | |||
3 | The server is a web server developed with [TypeScript](https://www.typescriptlang.org/)/[Express](http://expressjs.com). | ||
4 | |||
5 | |||
6 | ## Technologies | ||
7 | |||
8 | * [TypeScript](https://www.typescriptlang.org/) -> Language | ||
9 | * [PostgreSQL](https://www.postgresql.org/) -> Database | ||
10 | * [Redis](https://redis.io/) -> Job queue/cache | ||
11 | * [Express](http://expressjs.com) -> Web server framework | ||
12 | * [Sequelize](http://docs.sequelizejs.com/en/v3/) -> SQL ORM | ||
13 | * [WebTorrent](https://webtorrent.io/) -> BitTorrent tracker and torrent creation | ||
14 | * [Mocha](https://mochajs.org/) -> Test framework | ||
15 | |||
16 | |||
17 | ## Files | ||
18 | |||
19 | The server main file is [server.ts](/server.ts). | ||
20 | The server modules description are in the [package.json](/package.json) at the project root. | ||
21 | All other server files are in the [server](/server) directory: | ||
22 | |||
23 | server.ts -> app initialization, main routes configuration (static routes...) | ||
24 | config -> server YAML configurations (for tests, production...) | ||
25 | scripts -> Scripts files for npm run | ||
26 | server | ||
27 | |__ controllers -> API routes/controllers files | ||
28 | |__ helpers -> functions used by different part of the project (logger, utils...) | ||
29 | |__ initializers -> functions used at the server startup (installer, database, constants...) | ||
30 | |__ lib -> library function (WebTorrent, OAuth2, ActivityPub...) | ||
31 | |__ middlewares -> middlewares for controllers (requests validators, requests pagination...) | ||
32 | |__ models -> Sequelize models for each SQL tables (videos, users, accounts...) | ||
33 | |__ tests -> API tests and real world simulations (to test the decentralized feature...) | ||
34 | |||
35 | |||
36 | ## Conventions | ||
37 | |||
38 | Uses [JavaScript Standard Style](http://standardjs.com/). | ||
39 | |||
40 | ## Architecture | ||
41 | |||
42 | The server is composed by: | ||
43 | |||
44 | * a REST API (relying on the Express framework) documented on http://docs.joinpeertube.org/api.html | ||
45 | * a WebTorrent Tracker (slightly custom version of [webtorrent/bittorrent-tracker](https://github.com/webtorrent/bittorrent-tracker#server)) | ||
46 | |||
47 | A video is seeded by the server with the [WebSeed](http://www.bittorrent.org/beps/bep_0019.html) protocol (HTTP). | ||
48 | |||
49 | ![Architecture scheme](/support/doc/development/server/upload-video.png) | ||
50 | |||
51 | When a user uploads a video, the REST API creates the torrent file and then adds it to its database. | ||
52 | |||
53 | If a user wants to watch the video, the tracker will indicate all other users that are watching the video + the HTTP url for the WebSeed. | ||
54 | |||
55 | ## Newcomers | ||
56 | |||
57 | The server entrypoint is [server.ts](/server.ts). Looking at this file is a good start. | ||
58 | Then you can try to understand the [controllers](/server/controllers): they are the entrypoints of each API request. | ||
diff --git a/support/doc/development/server/peertube-architecture-server.xml b/support/doc/development/server/peertube-architecture-server.xml deleted file mode 100644 index 3299307a1..000000000 --- a/support/doc/development/server/peertube-architecture-server.xml +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | <mxfile userAgent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36" version="7.9.7" editor="www.draw.io" type="device"><diagram id="033390a3-e8de-cf4f-5be1-41b3d99c78ae" name="Page-1">3VpRc5s4EP41nmkekgGEwH6M3aS9md5Mpnbby6MA2dBgxIHs2P31J4EECGGbxLhNzu1MYLXC0re7364Wj8BsvfuUoTT8mwQ4HllGsBuBjyPLMm3LGvH/RrAvJeOxWQpWWRQIpVowj35hITSEdBMFOFcUKSExjVJV6JMkwT5VZCjLyLOqtiSx+q0pWmFNMPdRrEt/RAENxS4sp5Z/xtEqlN9sOpNyxEP+0yojm0R838gCy+JTDq+RfJbYaB6igDw3ROBuBGYZIbS8Wu9mOObYStjKefcHRqt1ZzihfSaAcsIWxRssV1ysi+4lFsVuMNc3R2D6HEYUz1Pk89FnZn0mC+k6FsPLKI5nJCZZMVfuHUxzmpEn3Bgxig8b0VcsNrHFGcW7hkjs4BMma0yzPVMRo7YAUzgbELfPteVcqRI2rGZPhBAJb1lVT64RYxcCtG4AbQ3AbznOmMQcFsixj32/C0hvDG04EJCmpSLpmjqUlU4TSmcAJOEhJAd2yd+CJJzAP4ckGGuI4YDxm7glGQ3JiiQovqul0xpTQ8UP7yL6DxffQHH3KJQYPNm+HLKgvH8U03KKMnrLuZgJEpJgKbuP+MLFAwKp4ccozyO/FAoV/pifmNK9SBBoQwkT1ev/QkgqrUwSKtRM85gNc7LJfIGKIG22qhWWWsJuHLCjhs5wjGi0VRPGWUwinawOgFmGEWV5kC0xxEUOzIr9sIwWMaNZTsxWPQ2iLbtc8cvbIKi1tyyHkmIWz8SIIg/l1SS2msY8zV+Ym1PVDTKcR7+QVyhw26UkSmixXTgdwY9MguJolXBTsiWywAVTHi4RS6q3YmAdBUHhazHycDytUmVXwjhtT+nlWlBWZYFYrZJau4LVuDEhtJR4vRa83tvK4uEPHJWGClkuc+ZabTeo1tDLMyaaY3y9my+Y5PbhL81yL2PDAOJxYHex4djygOMMw4aTsZqg3Q4ydOCFyNC5PBmaKhm6ChkabTKsqa7Jh+Yb4EOZpJqEODgfFlPZNtG+oSDYRA+kncyfjuJCjtuqa1v6zhge02cX5QpeG5J2V+Gs03H+lBfHD17GpBhn/O7DD+x9Xcyuupl4GtFFxfJpRnwSv0tydoYi52vGzsBUCcR6Q+QsY6bhCczAlQ01n1hkDFpe1/bKwy9jc2wyPne72HziuAANxOaOc5rOJ5dic0ODaBA2tw7SeXXzWNH+eyhtTdk2aVC5/SaoHMDW0cgelpqBXkffY+qHVV3sx1FJryhNY0aBNCLJu+RYyTwDFMAGtNQEe+2Y55GsXJB0xL28b7HAICQM9B7BILTgHq7y/j+08MdOvGbHiVfGJgOOhY446eYdefRDYTCLfYnxMy//0nCz9hIUxfnNzc2B+urMDMti0+ruHgWO58CBMixo9eE6D0zuhbpHpt7SlFB6NYqlgPugAqfz74bIgeu88M5bpmDZ6a4elE+Z42yrVELl49SvYGLvBW0K1S4iCptGFKL+XN3lIIRpL+MijEOmh5OB7A5PdrJNo8PscIjKytXM/rChdTdpk8YEcbqsG0v8JDPHOLjqCE9lrl/0sfhUqtbFJ2LzMhz+Lnga6jwN+tZvjZMTdCdqWpcvQi5b40G3dWBzjtR42uzW+xz7EhWDqb9u4PUYM3GHM38hK56LLpFPIP/XlU+c4jMMr1iGWnJXJNIgFjDpIBZzkITiaFB/K8iEP7emk3bsd0XgS19LaG8legWfrcfepGfoNQCFR4j63KJaDZC2kcq9iDnHmmTqY9qVebl/7TGvCDaJ6KV7sH0puZcXgA4G7ukFQ5HrOSci+Hsgfw/JFHQE9Ntoa7fz5Mm2NrxwW9vSqLrunTDv4ERmGaLs4xXg58Xi4UrztPfQPJH5f5AG9dh1Fbtcn9k8GbY5oh/ovosK/vQpuyrYZzJTc9VLHa6xc+Bw7U68oX4tBEGPw3XXKesVtRC7rX/LVZqu/sEcuPsP</diagram></mxfile> \ No newline at end of file | ||
diff --git a/support/doc/development/server/upload-video.png b/support/doc/development/server/upload-video.png deleted file mode 100644 index 7edc06792..000000000 --- a/support/doc/development/server/upload-video.png +++ /dev/null | |||
Binary files differ | |||
diff --git a/support/doc/production.md b/support/doc/production.md index 2eba6e6a3..4f20cf140 100644 --- a/support/doc/production.md +++ b/support/doc/production.md | |||
@@ -204,6 +204,9 @@ logs. You can set another password with: | |||
204 | $ cd /var/www/peertube/peertube-latest && NODE_CONFIG_DIR=/var/www/peertube/config NODE_ENV=production npm run reset-password -- -u root | 204 | $ cd /var/www/peertube/peertube-latest && NODE_CONFIG_DIR=/var/www/peertube/config NODE_ENV=production npm run reset-password -- -u root |
205 | ``` | 205 | ``` |
206 | 206 | ||
207 | Alternatively you can set the environment variable `PT_INITIAL_ROOT_PASSWORD`, | ||
208 | to your own administrator password, although it must be 6 characters or more. | ||
209 | |||
207 | ### What now? | 210 | ### What now? |
208 | 211 | ||
209 | Now your instance is up you can: | 212 | Now your instance is up you can: |
diff --git a/support/docker/production/.env b/support/docker/production/.env index 7b9092642..c8393d0ce 100644 --- a/support/docker/production/.env +++ b/support/docker/production/.env | |||
@@ -5,8 +5,7 @@ PEERTUBE_WEBSERVER_PORT=443 | |||
5 | PEERTUBE_WEBSERVER_HTTPS=true | 5 | PEERTUBE_WEBSERVER_HTTPS=true |
6 | # If you need more than one IP as trust_proxy | 6 | # If you need more than one IP as trust_proxy |
7 | # pass them as a comma separated array: | 7 | # pass them as a comma separated array: |
8 | PEERTUBE_TRUST_PROXY=["127.0.0.1"] | 8 | PEERTUBE_TRUST_PROXY=["127.0.0.1", "loopback", "172.18.0.0/16"] |
9 | #PEERTUBE_TRUST_PROXY=["127.0.0.1", "loopback", "192.168.1.0/24"] | ||
10 | #PEERTUBE_SMTP_USERNAME= | 9 | #PEERTUBE_SMTP_USERNAME= |
11 | #PEERTUBE_SMTP_PASSWORD= | 10 | #PEERTUBE_SMTP_PASSWORD= |
12 | PEERTUBE_SMTP_HOSTNAME=postfix | 11 | PEERTUBE_SMTP_HOSTNAME=postfix |
diff --git a/support/docker/production/docker-compose.yml b/support/docker/production/docker-compose.yml index df21a14d4..263fb6e95 100644 --- a/support/docker/production/docker-compose.yml +++ b/support/docker/production/docker-compose.yml | |||
@@ -72,3 +72,10 @@ services: | |||
72 | labels: | 72 | labels: |
73 | traefik.enable: "false" | 73 | traefik.enable: "false" |
74 | restart: "always" | 74 | restart: "always" |
75 | |||
76 | networks: | ||
77 | default: | ||
78 | ipam: | ||
79 | driver: default | ||
80 | config: | ||
81 | - subnet: 172.18.0.0/16 | ||
@@ -515,6 +515,11 @@ ansi-regex@^3.0.0: | |||
515 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" | 515 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" |
516 | integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= | 516 | integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= |
517 | 517 | ||
518 | ansi-regex@^4.1.0: | ||
519 | version "4.1.0" | ||
520 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" | ||
521 | integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== | ||
522 | |||
518 | ansi-styles@^2.2.1: | 523 | ansi-styles@^2.2.1: |
519 | version "2.2.1" | 524 | version "2.2.1" |
520 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" | 525 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" |
@@ -1446,6 +1451,11 @@ circular-json@^0.3.1: | |||
1446 | resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" | 1451 | resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" |
1447 | integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A== | 1452 | integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A== |
1448 | 1453 | ||
1454 | circular-json@^0.5.9: | ||
1455 | version "0.5.9" | ||
1456 | resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.9.tgz#932763ae88f4f7dead7a0d09c8a51a4743a53b1d" | ||
1457 | integrity sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ== | ||
1458 | |||
1449 | class-utils@^0.3.5: | 1459 | class-utils@^0.3.5: |
1450 | version "0.3.6" | 1460 | version "0.3.6" |
1451 | resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" | 1461 | resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" |
@@ -2322,6 +2332,11 @@ elliptic@=3.0.3: | |||
2322 | hash.js "^1.0.0" | 2332 | hash.js "^1.0.0" |
2323 | inherits "^2.0.1" | 2333 | inherits "^2.0.1" |
2324 | 2334 | ||
2335 | emoji-regex@^7.0.1: | ||
2336 | version "7.0.3" | ||
2337 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" | ||
2338 | integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== | ||
2339 | |||
2325 | enabled@1.0.x: | 2340 | enabled@1.0.x: |
2326 | version "1.0.2" | 2341 | version "1.0.2" |
2327 | resolved "https://registry.yarnpkg.com/enabled/-/enabled-1.0.2.tgz#965f6513d2c2d1c5f4652b64a2e3396467fc2f93" | 2342 | resolved "https://registry.yarnpkg.com/enabled/-/enabled-1.0.2.tgz#965f6513d2c2d1c5f4652b64a2e3396467fc2f93" |
@@ -3304,6 +3319,11 @@ get-caller-file@^1.0.1: | |||
3304 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" | 3319 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" |
3305 | integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== | 3320 | integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== |
3306 | 3321 | ||
3322 | get-caller-file@^2.0.1: | ||
3323 | version "2.0.5" | ||
3324 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" | ||
3325 | integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== | ||
3326 | |||
3307 | get-func-name@^2.0.0: | 3327 | get-func-name@^2.0.0: |
3308 | version "2.0.0" | 3328 | version "2.0.0" |
3309 | resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" | 3329 | resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" |
@@ -5322,6 +5342,15 @@ mkdirp@0.5.1, mkdirp@0.x.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdi | |||
5322 | dependencies: | 5342 | dependencies: |
5323 | minimist "0.0.8" | 5343 | minimist "0.0.8" |
5324 | 5344 | ||
5345 | mocha-parallel-tests@^2.1.0: | ||
5346 | version "2.1.0" | ||
5347 | resolved "https://registry.yarnpkg.com/mocha-parallel-tests/-/mocha-parallel-tests-2.1.0.tgz#94ab823b619b129fc347472f97c18595f0870c0e" | ||
5348 | integrity sha512-NElZRp6T7kpis0mSkviPTwgIU13kkvazmmPPFLl/UqBeJoEjMj9tKz47qMV9kB0txURLoA1Rd/yDYqG1hlsKoA== | ||
5349 | dependencies: | ||
5350 | circular-json "^0.5.9" | ||
5351 | debug "^4.1.1" | ||
5352 | yargs "^13.2.2" | ||
5353 | |||
5325 | mocha@^6.0.0: | 5354 | mocha@^6.0.0: |
5326 | version "6.0.2" | 5355 | version "6.0.2" |
5327 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.0.2.tgz#cdc1a6fdf66472c079b5605bac59d29807702d2c" | 5356 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.0.2.tgz#cdc1a6fdf66472c079b5605bac59d29807702d2c" |
@@ -6170,7 +6199,7 @@ os-locale@^2.0.0: | |||
6170 | lcid "^1.0.0" | 6199 | lcid "^1.0.0" |
6171 | mem "^1.1.0" | 6200 | mem "^1.1.0" |
6172 | 6201 | ||
6173 | os-locale@^3.0.0: | 6202 | os-locale@^3.0.0, os-locale@^3.1.0: |
6174 | version "3.1.0" | 6203 | version "3.1.0" |
6175 | resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" | 6204 | resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" |
6176 | integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== | 6205 | integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== |
@@ -7171,6 +7200,11 @@ require-main-filename@^1.0.1: | |||
7171 | resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" | 7200 | resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" |
7172 | integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= | 7201 | integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= |
7173 | 7202 | ||
7203 | require-main-filename@^2.0.0: | ||
7204 | version "2.0.0" | ||
7205 | resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" | ||
7206 | integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== | ||
7207 | |||
7174 | require-uncached@^1.0.2: | 7208 | require-uncached@^1.0.2: |
7175 | version "1.0.3" | 7209 | version "1.0.3" |
7176 | resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" | 7210 | resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" |
@@ -8136,6 +8170,15 @@ string-width@^1.0.1: | |||
8136 | is-fullwidth-code-point "^2.0.0" | 8170 | is-fullwidth-code-point "^2.0.0" |
8137 | strip-ansi "^4.0.0" | 8171 | strip-ansi "^4.0.0" |
8138 | 8172 | ||
8173 | string-width@^3.0.0: | ||
8174 | version "3.1.0" | ||
8175 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" | ||
8176 | integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== | ||
8177 | dependencies: | ||
8178 | emoji-regex "^7.0.1" | ||
8179 | is-fullwidth-code-point "^2.0.0" | ||
8180 | strip-ansi "^5.1.0" | ||
8181 | |||
8139 | string2compact@^1.1.1, string2compact@^1.2.5: | 8182 | string2compact@^1.1.1, string2compact@^1.2.5: |
8140 | version "1.3.0" | 8183 | version "1.3.0" |
8141 | resolved "https://registry.yarnpkg.com/string2compact/-/string2compact-1.3.0.tgz#22d946127b082d1203c51316af60117a337423c3" | 8184 | resolved "https://registry.yarnpkg.com/string2compact/-/string2compact-1.3.0.tgz#22d946127b082d1203c51316af60117a337423c3" |
@@ -8191,6 +8234,13 @@ strip-ansi@^4.0.0: | |||
8191 | dependencies: | 8234 | dependencies: |
8192 | ansi-regex "^3.0.0" | 8235 | ansi-regex "^3.0.0" |
8193 | 8236 | ||
8237 | strip-ansi@^5.1.0: | ||
8238 | version "5.2.0" | ||
8239 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" | ||
8240 | integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== | ||
8241 | dependencies: | ||
8242 | ansi-regex "^4.1.0" | ||
8243 | |||
8194 | strip-eof@^1.0.0: | 8244 | strip-eof@^1.0.0: |
8195 | version "1.0.0" | 8245 | version "1.0.0" |
8196 | resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" | 8246 | resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" |
@@ -9315,6 +9365,14 @@ yargs-parser@11.1.1, yargs-parser@^11.1.1: | |||
9315 | camelcase "^5.0.0" | 9365 | camelcase "^5.0.0" |
9316 | decamelize "^1.2.0" | 9366 | decamelize "^1.2.0" |
9317 | 9367 | ||
9368 | yargs-parser@^13.0.0: | ||
9369 | version "13.0.0" | ||
9370 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.0.0.tgz#3fc44f3e76a8bdb1cc3602e860108602e5ccde8b" | ||
9371 | integrity sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw== | ||
9372 | dependencies: | ||
9373 | camelcase "^5.0.0" | ||
9374 | decamelize "^1.2.0" | ||
9375 | |||
9318 | yargs-parser@^8.0.0: | 9376 | yargs-parser@^8.0.0: |
9319 | version "8.1.0" | 9377 | version "8.1.0" |
9320 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950" | 9378 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950" |
@@ -9374,6 +9432,23 @@ yargs@^11.0.0: | |||
9374 | y18n "^3.2.1" | 9432 | y18n "^3.2.1" |
9375 | yargs-parser "^9.0.2" | 9433 | yargs-parser "^9.0.2" |
9376 | 9434 | ||
9435 | yargs@^13.2.2: | ||
9436 | version "13.2.2" | ||
9437 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.2.tgz#0c101f580ae95cea7f39d927e7770e3fdc97f993" | ||
9438 | integrity sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA== | ||
9439 | dependencies: | ||
9440 | cliui "^4.0.0" | ||
9441 | find-up "^3.0.0" | ||
9442 | get-caller-file "^2.0.1" | ||
9443 | os-locale "^3.1.0" | ||
9444 | require-directory "^2.1.1" | ||
9445 | require-main-filename "^2.0.0" | ||
9446 | set-blocking "^2.0.0" | ||
9447 | string-width "^3.0.0" | ||
9448 | which-module "^2.0.0" | ||
9449 | y18n "^4.0.0" | ||
9450 | yargs-parser "^13.0.0" | ||
9451 | |||
9377 | yeast@0.1.2: | 9452 | yeast@0.1.2: |
9378 | version "0.1.2" | 9453 | version "0.1.2" |
9379 | resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" | 9454 | resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" |