From: Chocobozzz Date: Fri, 7 Jun 2019 13:04:41 +0000 (+0200) Subject: Merge branch 'release/v1.3.0' into develop X-Git-Tag: v1.4.0-rc.1~182 X-Git-Url: https://git.immae.eu/?a=commitdiff_plain;h=b91bc1d1f3591c35ab4426f6ab594b4bd9f1ef62;hp=03371ad9d049bab79445a1b35da44cb1272f6c28;p=github%2FChocobozzz%2FPeerTube.git Merge branch 'release/v1.3.0' into develop --- diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index b3847b8d7..822449b3a 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -9,8 +9,6 @@ Interested in contributing? Awesome! * [Write documentation](#write-documentation) * [Develop](#develop) * [Improve the website](#improve-the-website) - * [Troubleshooting](#troubleshooting) - * [Tutorials](#tutorials) ## Translate @@ -61,10 +59,11 @@ Make sure that you have followed [the steps](/support/doc/dependencies.md) to install the dependencies. -Then clone the sources and install node modules: +Fork the github repository, +and then clone the sources and install node modules: ``` -$ git clone https://github.com/Chocobozzz/PeerTube +$ git clone https://github.com/YOUR_GITHUB_USERNAME/PeerTube $ cd PeerTube $ yarn install --pure-lockfile ``` @@ -101,7 +100,7 @@ You can get a complete PeerTube development setup with Gitpod, a free one-click ### Server side -You can find a documentation of the server code/architecture [here](/support/doc/development/server/code.md). +You can find a documentation of the server code/architecture [here](https://docs.joinpeertube.org/#/contribute-architecture?id=server-code). To develop on the server-side: @@ -115,8 +114,8 @@ restart. ### Client side -You can find a documentation of the server code/architecture -[here](/support/doc/development/client/code.md). +You can find a documentation of the client code/architecture +[here](https://docs.joinpeertube.org/#/contribute-architecture?id=client-code). To develop on the client side: @@ -193,11 +192,3 @@ $ npm run mocha -- --exit --require ts-node/register/type-check --bail server/te Instance configurations are in `config/test-{1,2,3,4,5,6}.yaml`. Note that only instance 2 has transcoding enabled. - -### Troubleshooting - -Please check out the issues and [list of common errors](https://docs.joinpeertube.org/lang/en/devdocs/troubleshooting.html). - -### Tutorials - -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/.gitignore b/.gitignore index 8cd04eea1..3a91facb4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # NPM instalation /node_modules/ +/server/tools/node_modules *npm-debug.log # Testing diff --git a/.travis.yml b/.travis.yml index 5fa41fb43..8b3ec94d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,8 +29,8 @@ install: - CC=gcc-4.9 CXX=g++-4.9 yarn install before_script: - - wget --no-check-certificate "https://download.cpy.re/ffmpeg/ffmpeg-release-4.0.2-64bit-static.tar.xz" - - tar xf ffmpeg-release-4.0.2-64bit-static.tar.xz + - wget --no-check-certificate "https://download.cpy.re/ffmpeg/ffmpeg-release-4.0.3-64bit-static.tar.xz" + - tar xf ffmpeg-release-4.0.3-64bit-static.tar.xz - mkdir -p $HOME/bin - cp ffmpeg-*/{ffmpeg,ffprobe} $HOME/bin - 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 @@ -# Architecture - -## Vocabulary - - - **Fediverse:** several servers following one another, several users - following each other. Designates federated communities in general. - - **Vidiverse:** same as Fediverse, but federating videos specifically. - - **Instance:** a server which runs PeerTube in the fediverse. - - **Origin instance:** the instance on which the video was uploaded and which - is seeding (through the WebSeed protocol) the video. - - **Cache instance:** an instance that decided to make available a WebSeed - of its own for a video originating from another instance. It sends a `ptCache` - activity to notify the origin instance, which will then update its list of - WebSeeds for the video. - - **Following:** the action of a PeerTube instance which will follow another - instance (subscribe to its videos). - -## Base - -### Communications - * All the communication between the instances are signed with [Linked Data - Signatures](https://w3c-dvcg.github.io/ld-signatures/) with the private key - of the account that authored the action. - * We use the [ActivityPub](https://www.w3.org/TR/activitypub/) protocol (only - server-server for now). Object models could be found in - [shared/models/activitypub - directory](/shared/models/activitypub). - * All the requests are retried several times if they fail. - -### Instance - * An instance has a websocket tracker which is responsible for all videos - uploaded by its users. - * An instance has an administrator that can follow other instances. - * An instance can be configured to follow back automatically. - * An instance can blacklist other instances (only used in "follow back" - mode). - * An instance cannot choose which other instances follow it, but it can - decide to **reject all** followers. - * After having uploaded a video, the instance seeds it (WebSeed protocol). - * If a user wants to watch a video, they ask its instance the magnet URI and - the frontend adds the torrent (with WebTorrent), creates the HTML5 video - player and streams the file into it. - * A user watching a video seeds it too (BitTorrent). Thus another user who is - watching the same video can get the data from the origin server and other - users watching it. diff --git a/CHANGELOG.md b/CHANGELOG.md index 363fae00d..6a3b2d7a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -333,8 +333,8 @@ and update your [.env](https://github.com/Chocobozzz/PeerTube/blob/develop/suppo ### Maintenance - * Improve REST API documentation: https://docs.joinpeertube.org/api.html ([@rigelk](https://github.com/rigelk)) - * Add basic ActivityPub documentation: https://docs.joinpeertube.org/lang/en/devdocs/federation.html ([@rigelk](https://github.com/rigelk)) + * Improve REST API documentation ([@rigelk](https://github.com/rigelk)) + * Add basic ActivityPub documentation ([@rigelk](https://github.com/rigelk)) * Add CLI option to run PeerTube without client ([@rigelk](https://github.com/rigelk)) * Add manpage to peertube CLI ([@rigelk](https://github.com/rigelk)) * Make backups of files in optimize-old-videos script ([@Nutomic](https://github.com/nutomic)) @@ -419,8 +419,8 @@ and update your [.env](https://github.com/Chocobozzz/PeerTube/blob/develop/suppo ### Maintenance - * Improve REST API documentation: https://docs.joinpeertube.org/api.html ([@rigelk](https://github.com/rigelk)) - * Add basic ActivityPub documentation: https://docs.joinpeertube.org/lang/en/devdocs/federation.html ([@rigelk](https://github.com/rigelk)) + * Improve REST API documentation ([@rigelk](https://github.com/rigelk)) + * Add basic ActivityPub documentation ([@rigelk](https://github.com/rigelk)) * Add CLI option to run PeerTube without client ([@rigelk](https://github.com/rigelk)) * Add manpage to peertube CLI ([@rigelk](https://github.com/rigelk)) * Make backups of files in optimize-old-videos script ([@Nutomic](https://github.com/nutomic)) @@ -634,7 +634,7 @@ This release could contain bugs. Don't expect a stable v1.1.0 until December :) ### Features - * Video redundancy system (experimental, see [the doc](https://docs.joinpeertube.org/lang/en/devdocs/architecture.html#redundancy-between-instances)) + * Video redundancy system (experimental) * Add peertube script (see [the doc](/support/doc/tools.md#cli-wrapper)) ([@rigelk](https://github.com/rigelk)) * Improve download modal ([@rigelk](https://github.com/rigelk)) * Add redirect after login ([@BO41](https://github.com/BO41)) diff --git a/FAQ.md b/FAQ.md index 7d8be96a7..1a3b1847b 100644 --- a/FAQ.md +++ b/FAQ.md @@ -58,7 +58,7 @@ is named "Framatube". Yes, the origin server always seeds videos uploaded on it thanks to [Webseed](http://www.bittorrent.org/beps/bep_0019.html). -It can also be helped by other servers using [redundancy](https://docs.joinpeertube.org/lang/en/devdocs/architecture.html#redundancy-between-instances). +It can also be helped by other servers using [redundancy](https://docs.joinpeertube.org/#/contribute-architecture?id=redundancy-between-instances). ## What is WebSeed? diff --git a/README.md b/README.md index a5060cf0d..3edaeef2f 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ Be it as a user or an instance administrator, you can decide what your experienc

Communities that help each other

-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 redundancy guide). +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 redundancy guide).

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 incentivize alter creativity (more about that in our FAQ). @@ -151,9 +151,9 @@ Feel free to reach out if you have any questions or ideas! :speech_balloon: * **yarn >= 1.x** * **FFmpeg >= 3.x** -See the [production guide](/support/doc/production.md), which is the recommended way. +See the [production guide](/support/doc/production.md), which is the recommended way to install or upgrade PeerTube. For hardware requirements, see [Should I have a big server to run PeerTube?](https://github.com/Chocobozzz/PeerTube/blob/develop/FAQ.md#should-i-have-a-big-server-to-run-peertube) in the FAQ. -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)). +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)). :book: Documentation ---------------------------------------------------------------- @@ -162,13 +162,13 @@ If you have a question, please try to find the answer in the [FAQ](/FAQ.md) firs ### User documentation -See the [user documentation](https://docs.joinpeertube.org/lang/en/userdocs/). +See the [user documentation](https://docs.joinpeertube.org/#/use-setup-account). ### Admin documentation See [how to create your own instance](#package-create-your-own-instance). -See the more general [admin documentation](https://docs.joinpeertube.org/lang/en/docs/). +See the more general [admin documentation](https://docs.joinpeertube.org/#/admin-following-instances). #### Tools @@ -178,13 +178,13 @@ See the more general [admin documentation](https://docs.joinpeertube.org/lang/en ### Technical documentation -See the [architecture blueprint](https://docs.joinpeertube.org/lang/en/devdocs/architecture.html) for a more detailed explanation of the architectural choices. +See the [architecture blueprint](https://docs.joinpeertube.org/#/contribute-architecture) for a more detailed explanation of the architectural choices. See our REST API documentation: * OpenAPI 3.0.0 schema: [/support/doc/api/openapi.yaml](/support/doc/api/openapi.yaml) - * Spec explorer: [docs.joinpeertube.org/api.html](http://docs.joinpeertube.org/api.html) + * Spec explorer: [docs.joinpeertube.org/#/api-rest-reference.html](https://docs.joinpeertube.org/#/api-rest-reference.html) -See our [ActivityPub documentation](https://docs.joinpeertube.org/lang/en/devdocs/federation.html). +See our [ActivityPub documentation](https://docs.joinpeertube.org/#/api-activitypub). :heart: Supports of our crowdfunding ---------------------------------------------------------------- diff --git a/client/src/app/+about/about-follows/about-follows.component.html b/client/src/app/+about/about-follows/about-follows.component.html new file mode 100644 index 000000000..18689bbf7 --- /dev/null +++ b/client/src/app/+about/about-follows/about-follows.component.html @@ -0,0 +1,22 @@ +

+
+
Followers
+ +
This instance does not have followers.
+ + + {{ follower }} + +
+ +
+
Followings
+ +
This instance does not have followings.
+ + + {{ following }} + +
+ +
diff --git a/client/src/app/+about/about-follows/about-follows.component.scss b/client/src/app/+about/about-follows/about-follows.component.scss new file mode 100644 index 000000000..e0d597a96 --- /dev/null +++ b/client/src/app/+about/about-follows/about-follows.component.scss @@ -0,0 +1,14 @@ +@import '_variables'; +@import '_mixins'; + +.subtitle { + font-size: 18px; + font-weight: $font-semibold; + margin-bottom: 20px; +} + +a { + display: block; + width: fit-content; + margin-top: 3px; +} diff --git a/client/src/app/+about/about-follows/about-follows.component.ts b/client/src/app/+about/about-follows/about-follows.component.ts new file mode 100644 index 000000000..f0e1375d6 --- /dev/null +++ b/client/src/app/+about/about-follows/about-follows.component.ts @@ -0,0 +1,103 @@ +import { Component, OnInit } from '@angular/core' +import { FollowService } from '@app/shared/instance/follow.service' +import { ComponentPagination, hasMoreItems } from '@app/shared/rest/component-pagination.model' +import { Notifier } from '@app/core' +import { RestService } from '@app/shared' +import { SortMeta } from 'primeng/api' + +@Component({ + selector: 'my-about-follows', + templateUrl: './about-follows.component.html', + styleUrls: [ './about-follows.component.scss' ] +}) + +export class AboutFollowsComponent implements OnInit { + followers: string[] = [] + followings: string[] = [] + + followersPagination: ComponentPagination = { + currentPage: 1, + itemsPerPage: 40, + totalItems: null + } + + followingsPagination: ComponentPagination = { + currentPage: 1, + itemsPerPage: 40, + totalItems: null + } + + sort: SortMeta = { + field: 'createdAt', + order: -1 + } + + constructor ( + private restService: RestService, + private notifier: Notifier, + private followService: FollowService + ) { } + + ngOnInit () { + this.loadMoreFollowers() + + this.loadMoreFollowings() + } + + onNearOfBottom () { + this.onNearOfFollowersBottom() + + this.onNearOfFollowingsBottom() + } + + onNearOfFollowersBottom () { + if (!hasMoreItems(this.followersPagination)) return + + this.followersPagination.currentPage += 1 + this.loadMoreFollowers() + } + + onNearOfFollowingsBottom () { + if (!hasMoreItems(this.followingsPagination)) return + + this.followingsPagination.currentPage += 1 + this.loadMoreFollowings() + } + + buildLink (host: string) { + return window.location.protocol + '//' + host + } + + private loadMoreFollowers () { + const pagination = this.restService.componentPaginationToRestPagination(this.followersPagination) + + this.followService.getFollowers(pagination, this.sort) + .subscribe( + resultList => { + const newFollowers = resultList.data.map(r => r.follower.host) + this.followers = this.followers.concat(newFollowers) + + this.followersPagination.totalItems = resultList.total + }, + + err => this.notifier.error(err.message) + ) + } + + private loadMoreFollowings () { + const pagination = this.restService.componentPaginationToRestPagination(this.followingsPagination) + + this.followService.getFollowing(pagination, this.sort) + .subscribe( + resultList => { + const newFollowings = resultList.data.map(r => r.following.host) + this.followings = this.followings.concat(newFollowings) + + this.followingsPagination.totalItems = resultList.total + }, + + err => this.notifier.error(err.message) + ) + } + +} diff --git a/client/src/app/+about/about-routing.module.ts b/client/src/app/+about/about-routing.module.ts index c83c62c7f..33e5070cb 100644 --- a/client/src/app/+about/about-routing.module.ts +++ b/client/src/app/+about/about-routing.module.ts @@ -4,6 +4,7 @@ import { MetaGuard } from '@ngx-meta/core' import { AboutComponent } from './about.component' import { AboutInstanceComponent } from '@app/+about/about-instance/about-instance.component' import { AboutPeertubeComponent } from '@app/+about/about-peertube/about-peertube.component' +import { AboutFollowsComponent } from '@app/+about/about-follows/about-follows.component' const aboutRoutes: Routes = [ { @@ -33,6 +34,15 @@ const aboutRoutes: Routes = [ title: 'About PeerTube' } } + }, + { + path: 'follows', + component: AboutFollowsComponent, + data: { + meta: { + title: 'About follows' + } + } } ] } diff --git a/client/src/app/+about/about.component.html b/client/src/app/+about/about.component.html index 8c50835c1..0c4a5156d 100644 --- a/client/src/app/+about/about.component.html +++ b/client/src/app/+about/about.component.html @@ -5,10 +5,12 @@ Instance PeerTube + + Follows
- \ No newline at end of file + diff --git a/client/src/app/+about/about.module.ts b/client/src/app/+about/about.module.ts index 9c6b29740..49a7a52f8 100644 --- a/client/src/app/+about/about.module.ts +++ b/client/src/app/+about/about.module.ts @@ -6,6 +6,7 @@ import { SharedModule } from '../shared' import { AboutInstanceComponent } from '@app/+about/about-instance/about-instance.component' import { AboutPeertubeComponent } from '@app/+about/about-peertube/about-peertube.component' import { ContactAdminModalComponent } from '@app/+about/about-instance/contact-admin-modal.component' +import { AboutFollowsComponent } from '@app/+about/about-follows/about-follows.component' @NgModule({ imports: [ @@ -17,6 +18,7 @@ import { ContactAdminModalComponent } from '@app/+about/about-instance/contact-a AboutComponent, AboutInstanceComponent, AboutPeertubeComponent, + AboutFollowsComponent, ContactAdminModalComponent ], diff --git a/client/src/app/+accounts/account-video-channels/account-video-channels.component.html b/client/src/app/+accounts/account-video-channels/account-video-channels.component.html index c3ef1d894..43dbbebb3 100644 --- a/client/src/app/+accounts/account-video-channels/account-video-channels.component.html +++ b/client/src/app/+accounts/account-video-channels/account-video-channels.component.html @@ -1,11 +1,21 @@ -
- - Avatar - -
{{ videoChannel.displayName }}
-
{{ videoChannel.followersCount }} subscribers
-
-
\ No newline at end of file +
+ +
This account does not have channels.
+ + +
diff --git a/client/src/app/+accounts/account-video-channels/account-video-channels.component.scss b/client/src/app/+accounts/account-video-channels/account-video-channels.component.scss index 0c6de2efa..d9f78bdcd 100644 --- a/client/src/app/+accounts/account-video-channels/account-video-channels.component.scss +++ b/client/src/app/+accounts/account-video-channels/account-video-channels.component.scss @@ -1,30 +1,17 @@ @import '_variables'; @import '_mixins'; +@import '_miniature'; -.row { - justify-content: center; +.margin-content { + @include adapt-margin-content-width; } -a.video-channel { - @include disable-default-a-behaviour; +.section { + @include miniature-rows; - display: inline-block; - text-align: center; - color: var(--mainForegroundColor); - margin: 10px 30px; + padding-top: 0 !important; - img { - @include avatar(80px); - - margin-bottom: 10px; - } - - .video-channel-display-name { - font-size: 20px; - font-weight: $font-bold; + .section-title { + align-items: center; } - - .video-channel-followers { - font-size: 15px; - } -} \ No newline at end of file +} diff --git a/client/src/app/+accounts/account-video-channels/account-video-channels.component.ts b/client/src/app/+accounts/account-video-channels/account-video-channels.component.ts index 44f5626bb..ee3b5f8e4 100644 --- a/client/src/app/+accounts/account-video-channels/account-video-channels.component.ts +++ b/client/src/app/+accounts/account-video-channels/account-video-channels.component.ts @@ -3,9 +3,14 @@ import { ActivatedRoute } from '@angular/router' import { Account } from '@app/shared/account/account.model' import { AccountService } from '@app/shared/account/account.service' import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' -import { flatMap, map, tap } from 'rxjs/operators' -import { Subscription } from 'rxjs' +import { concatMap, map, switchMap, tap } from 'rxjs/operators' +import { from, Subscription } from 'rxjs' import { VideoChannel } from '@app/shared/video-channel/video-channel.model' +import { Video } from '@app/shared/video/video.model' +import { AuthService } from '@app/core' +import { VideoService } from '@app/shared/video/video.service' +import { VideoSortField } from '@app/shared/video/sort-field.type' +import { ComponentPagination, hasMoreItems } from '@app/shared/rest/component-pagination.model' @Component({ selector: 'my-account-video-channels', @@ -15,27 +20,73 @@ import { VideoChannel } from '@app/shared/video-channel/video-channel.model' export class AccountVideoChannelsComponent implements OnInit, OnDestroy { account: Account videoChannels: VideoChannel[] = [] + videos: { [id: number]: Video[] } = {} + + channelPagination: ComponentPagination = { + currentPage: 1, + itemsPerPage: 2 + } + + videosPagination: ComponentPagination = { + currentPage: 1, + itemsPerPage: 12 + } + videosSort: VideoSortField = '-publishedAt' private accountSub: Subscription constructor ( - protected route: ActivatedRoute, + private route: ActivatedRoute, + private authService: AuthService, private accountService: AccountService, - private videoChannelService: VideoChannelService + private videoChannelService: VideoChannelService, + private videoService: VideoService ) { } + get user () { + return this.authService.getUser() + } + ngOnInit () { // Parent get the account for us this.accountSub = this.accountService.accountLoaded - .pipe( - tap(account => this.account = account), - flatMap(account => this.videoChannelService.listAccountVideoChannels(account)), - map(res => res.data) - ) - .subscribe(videoChannels => this.videoChannels = videoChannels) + .subscribe(account => { + this.account = account + + this.loadMoreChannels() + }) } ngOnDestroy () { if (this.accountSub) this.accountSub.unsubscribe() } + + loadMoreChannels () { + this.videoChannelService.listAccountVideoChannels(this.account, this.channelPagination) + .pipe( + tap(res => this.channelPagination.totalItems = res.total), + switchMap(res => from(res.data)), + concatMap(videoChannel => { + return this.videoService.getVideoChannelVideos(videoChannel, this.videosPagination, this.videosSort) + .pipe(map(data => ({ videoChannel, videos: data.videos }))) + }) + ) + .subscribe(({ videoChannel, videos }) => { + this.videoChannels.push(videoChannel) + + this.videos[videoChannel.id] = videos + }) + } + + getVideosOf (videoChannel: VideoChannel) { + return this.videos[ videoChannel.id ] || [] + } + + onNearOfBottom () { + if (!hasMoreItems(this.channelPagination)) return + + this.channelPagination.currentPage += 1 + + this.loadMoreChannels() + } } diff --git a/client/src/app/+accounts/account-videos/account-videos.component.ts b/client/src/app/+accounts/account-videos/account-videos.component.ts index 0d579fa0c..5a99aadce 100644 --- a/client/src/app/+accounts/account-videos/account-videos.component.ts +++ b/client/src/app/+accounts/account-videos/account-videos.component.ts @@ -29,6 +29,7 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit, private accountSub: Subscription constructor ( + protected i18n: I18n, protected router: Router, protected serverService: ServerService, protected route: ActivatedRoute, @@ -36,13 +37,10 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit, protected notifier: Notifier, protected confirmService: ConfirmService, protected screenService: ScreenService, - private i18n: I18n, private accountService: AccountService, private videoService: VideoService ) { super() - - this.titlePage = this.i18n('Published videos') } ngOnInit () { diff --git a/client/src/app/+accounts/accounts-routing.module.ts b/client/src/app/+accounts/accounts-routing.module.ts index 531d763c4..55bce351a 100644 --- a/client/src/app/+accounts/accounts-routing.module.ts +++ b/client/src/app/+accounts/accounts-routing.module.ts @@ -14,7 +14,7 @@ const accountsRoutes: Routes = [ children: [ { path: '', - redirectTo: 'videos', + redirectTo: 'video-channels', pathMatch: 'full' }, { diff --git a/client/src/app/+accounts/accounts.component.html b/client/src/app/+accounts/accounts.component.html index c1377c1ea..038e18c4b 100644 --- a/client/src/app/+accounts/accounts.component.html +++ b/client/src/app/+accounts/accounts.component.html @@ -26,10 +26,10 @@ diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts index 71a4dfc4a..9ab883f60 100644 --- a/client/src/app/+admin/admin.module.ts +++ b/client/src/app/+admin/admin.module.ts @@ -5,7 +5,7 @@ import { TableModule } from 'primeng/table' import { SharedModule } from '../shared' import { AdminRoutingModule } from './admin-routing.module' import { AdminComponent } from './admin.component' -import { FollowersListComponent, FollowingAddComponent, FollowsComponent, FollowService } from './follows' +import { FollowersListComponent, FollowingAddComponent, FollowsComponent } from './follows' import { FollowingListComponent } from './follows/following-list/following-list.component' import { UserCreateComponent, UserListComponent, UserPasswordComponent, UsersComponent, UserUpdateComponent } from './users' import { @@ -66,7 +66,6 @@ import { DebugComponent, DebugService } from '@app/+admin/system/debug' ], providers: [ - FollowService, RedundancyService, JobService, LogsService, 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 @@ -286,6 +286,14 @@ > +
+ +
+
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 { enabled: null, threads: this.customConfigValidatorsService.TRANSCODING_THREADS, allowAdditionalExtensions: null, + allowAudioFiles: null, resolutions: {} }, autoBlacklist: { diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.ts b/client/src/app/+admin/follows/followers-list/followers-list.component.ts index b78cdf656..e25d9ab66 100644 --- a/client/src/app/+admin/follows/followers-list/followers-list.component.ts +++ b/client/src/app/+admin/follows/followers-list/followers-list.component.ts @@ -3,7 +3,7 @@ import { ConfirmService, Notifier } from '@app/core' import { SortMeta } from 'primeng/primeng' import { ActorFollow } from '../../../../../../shared/models/actors/follow.model' import { RestPagination, RestTable } from '../../../shared' -import { FollowService } from '../shared' +import { FollowService } from '@app/shared/instance/follow.service' import { I18n } from '@ngx-translate/i18n-polyfill' @Component({ diff --git a/client/src/app/+admin/follows/following-add/following-add.component.ts b/client/src/app/+admin/follows/following-add/following-add.component.ts index 2bb249746..308bbb0c5 100644 --- a/client/src/app/+admin/follows/following-add/following-add.component.ts +++ b/client/src/app/+admin/follows/following-add/following-add.component.ts @@ -3,7 +3,7 @@ import { Router } from '@angular/router' import { Notifier } from '@app/core' import { ConfirmService } from '../../../core' import { validateHost } from '../../../shared' -import { FollowService } from '../shared' +import { FollowService } from '@app/shared/instance/follow.service' import { I18n } from '@ngx-translate/i18n-polyfill' @Component({ diff --git a/client/src/app/+admin/follows/following-list/following-list.component.ts b/client/src/app/+admin/follows/following-list/following-list.component.ts index 4517a721e..ded616624 100644 --- a/client/src/app/+admin/follows/following-list/following-list.component.ts +++ b/client/src/app/+admin/follows/following-list/following-list.component.ts @@ -4,7 +4,7 @@ import { SortMeta } from 'primeng/primeng' import { ActorFollow } from '../../../../../../shared/models/actors/follow.model' import { ConfirmService } from '../../../core/confirm/confirm.service' import { RestPagination, RestTable } from '../../../shared' -import { FollowService } from '../shared' +import { FollowService } from '@app/shared/instance/follow.service' import { I18n } from '@ngx-translate/i18n-polyfill' @Component({ diff --git a/client/src/app/+admin/follows/index.ts b/client/src/app/+admin/follows/index.ts index 7849a06e7..e94f33710 100644 --- a/client/src/app/+admin/follows/index.ts +++ b/client/src/app/+admin/follows/index.ts @@ -1,6 +1,5 @@ export * from './following-add' export * from './followers-list' export * from './following-list' -export * from './shared' export * from './follows.component' export * from './follows.routes' diff --git a/client/src/app/+admin/follows/shared/index.ts b/client/src/app/+admin/follows/shared/index.ts deleted file mode 100644 index 78d456def..000000000 --- a/client/src/app/+admin/follows/shared/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './follow.service' diff --git a/client/src/app/+admin/users/user-edit/user-edit.ts b/client/src/app/+admin/users/user-edit/user-edit.ts index adce1b2d4..ee6d2c489 100644 --- a/client/src/app/+admin/users/user-edit/user-edit.ts +++ b/client/src/app/+admin/users/user-edit/user-edit.ts @@ -7,7 +7,8 @@ import { UserAdminFlag } from '@shared/models/users/user-flag.model' export abstract class UserEdit extends FormReactive { videoQuotaOptions: { value: string, label: string }[] = [] videoQuotaDailyOptions: { value: string, label: string }[] = [] - roles = Object.keys(USER_ROLE_LABELS).map(key => ({ value: key.toString(), label: USER_ROLE_LABELS[key] })) + roles = Object.keys(USER_ROLE_LABELS) + .map(key => ({ value: key.toString(), label: USER_ROLE_LABELS[key] })) username: string userId: number @@ -27,7 +28,7 @@ export abstract class UserEdit extends FormReactive { const transcodingConfig = this.serverService.getConfig().transcoding const resolutions = transcodingConfig.enabledResolutions - const higherResolution = VideoResolution.H_1080P + const higherResolution = VideoResolution.H_4K let multiplier = 0 for (const resolution of resolutions) { diff --git a/client/src/app/+my-account/my-account-history/my-account-history.component.ts b/client/src/app/+my-account/my-account-history/my-account-history.component.ts index 73340d21a..13607119e 100644 --- a/client/src/app/+my-account/my-account-history/my-account-history.component.ts +++ b/client/src/app/+my-account/my-account-history/my-account-history.component.ts @@ -27,6 +27,7 @@ export class MyAccountHistoryComponent extends AbstractVideoList implements OnIn videosHistoryEnabled: boolean constructor ( + protected i18n: I18n, protected router: Router, protected serverService: ServerService, protected route: ActivatedRoute, @@ -34,7 +35,6 @@ export class MyAccountHistoryComponent extends AbstractVideoList implements OnIn protected userService: UserService, protected notifier: Notifier, protected screenService: ScreenService, - protected i18n: I18n, private confirmService: ConfirmService, private videoService: VideoService, private userHistoryService: UserHistoryService diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html index 81fb11f45..f87df87df 100644 --- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html +++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html @@ -61,5 +61,12 @@ When you will upload a video in this channel, the video support field will be au
+
+ +
+ diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.ts b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.ts index 4dc65dd99..7479442d1 100644 --- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.ts +++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.ts @@ -11,4 +11,9 @@ export abstract class MyAccountVideoChannelEdit extends FormReactive { // FIXME: We need this method so angular does not complain in the child template onAvatarChange (formData: FormData) { /* empty */ } + + // Should be implemented by the child + isBulkUpdateVideosDisplayed () { + return false + } } diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-update.component.ts b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-update.component.ts index da4fb645a..081e956d2 100644 --- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-update.component.ts +++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-update.component.ts @@ -20,6 +20,7 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE videoChannelToUpdate: VideoChannel private paramsSub: Subscription + private oldSupportField: string constructor ( protected formValidatorService: FormValidatorService, @@ -39,7 +40,8 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE this.buildForm({ 'display-name': this.videoChannelValidatorsService.VIDEO_CHANNEL_DISPLAY_NAME, description: this.videoChannelValidatorsService.VIDEO_CHANNEL_DESCRIPTION, - support: this.videoChannelValidatorsService.VIDEO_CHANNEL_SUPPORT + support: this.videoChannelValidatorsService.VIDEO_CHANNEL_SUPPORT, + bulkVideosSupportUpdate: null }) this.paramsSub = this.route.params.subscribe(routeParams => { @@ -49,6 +51,8 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE videoChannelToUpdate => { this.videoChannelToUpdate = videoChannelToUpdate + this.oldSupportField = videoChannelToUpdate.support + this.form.patchValue({ 'display-name': videoChannelToUpdate.displayName, description: videoChannelToUpdate.description, @@ -72,7 +76,8 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE const videoChannelUpdate: VideoChannelUpdate = { displayName: body['display-name'], description: body.description || null, - support: body.support || null + support: body.support || null, + bulkVideosSupportUpdate: body.bulkVideosSupportUpdate || false } this.videoChannelService.updateVideoChannel(this.videoChannelToUpdate.name, videoChannelUpdate).subscribe( @@ -118,4 +123,10 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE getFormButtonTitle () { return this.i18n('Update') } + + isBulkUpdateVideosDisplayed () { + if (this.oldSupportField === undefined) return false + + return this.oldSupportField !== this.form.value['support'] + } } diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component.ts b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component.ts index 87a10961f..8aed8b513 100644 --- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component.ts +++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component.ts @@ -7,7 +7,6 @@ import { FormValidatorService } from '@app/shared/forms/form-validators/form-val import { VideoPlaylistValidatorsService } from '@app/shared' import { VideoPlaylistCreate } from '@shared/models/videos/playlist/video-playlist-create.model' import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service' -import { VideoConstant } from '@shared/models' import { VideoPlaylistPrivacy } from '@shared/models/videos/playlist/video-playlist-privacy.model' import { populateAsyncUserVideoChannels } from '@app/shared/misc/utils' @@ -18,7 +17,6 @@ import { populateAsyncUserVideoChannels } from '@app/shared/misc/utils' }) export class MyAccountVideoPlaylistCreateComponent extends MyAccountVideoPlaylistEdit implements OnInit { error: string - videoPlaylistPrivacies: VideoConstant[] = [] constructor ( protected formValidatorService: FormValidatorService, @@ -47,6 +45,7 @@ export class MyAccountVideoPlaylistCreateComponent extends MyAccountVideoPlaylis }) populateAsyncUserVideoChannels(this.authService, this.userVideoChannels) + .catch(err => console.error('Cannot populate user video channels.', err)) this.serverService.videoPlaylistPrivaciesLoaded.subscribe( () => { 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 @@
- + + +
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..e94188786 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,12 +1,12 @@ import { FormReactive } from '@app/shared' -import { VideoChannel } from '@app/shared/video-channel/video-channel.model' -import { ServerService } from '@app/core' import { VideoPlaylist } from '@shared/models/videos/playlist/video-playlist.model' +import { VideoConstant, VideoPlaylistPrivacy } from '@shared/models' export abstract class MyAccountVideoPlaylistEdit extends FormReactive { // Declare it here to avoid errors in create template videoPlaylistToUpdate: VideoPlaylist userVideoChannels: { id: number, label: string }[] = [] + videoPlaylistPrivacies: VideoConstant[] = [] abstract isCreation (): boolean abstract getFormButtonTitle (): string diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component.ts b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component.ts index 4887fdfb4..917ad7258 100644 --- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component.ts +++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component.ts @@ -9,9 +9,8 @@ import { populateAsyncUserVideoChannels } from '@app/shared/misc/utils' import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service' import { VideoPlaylistValidatorsService } from '@app/shared' import { VideoPlaylistUpdate } from '@shared/models/videos/playlist/video-playlist-update.model' -import { VideoConstant } from '@shared/models' -import { VideoPlaylistPrivacy } from '@shared/models/videos/playlist/video-playlist-privacy.model' import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model' +import { delayWhen, map, switchMap } from 'rxjs/operators' @Component({ selector: 'my-account-video-playlist-update', @@ -21,7 +20,6 @@ import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model' export class MyAccountVideoPlaylistUpdateComponent extends MyAccountVideoPlaylistEdit implements OnInit, OnDestroy { error: string videoPlaylistToUpdate: VideoPlaylist - videoPlaylistPrivacies: VideoConstant[] = [] private paramsSub: Subscription @@ -53,31 +51,24 @@ export class MyAccountVideoPlaylistUpdateComponent extends MyAccountVideoPlaylis }) populateAsyncUserVideoChannels(this.authService, this.userVideoChannels) - - this.paramsSub = this.route.params.subscribe(routeParams => { - const videoPlaylistId = routeParams['videoPlaylistId'] - - this.videoPlaylistService.getVideoPlaylist(videoPlaylistId).subscribe( - videoPlaylistToUpdate => { - this.videoPlaylistToUpdate = videoPlaylistToUpdate - - this.hydrateFormFromPlaylist() - - this.serverService.videoPlaylistPrivaciesLoaded.subscribe( - () => { - this.videoPlaylistPrivacies = this.serverService.getVideoPlaylistPrivacies() - .filter(p => { - // If the playlist is not private, we cannot put it in private anymore - return this.videoPlaylistToUpdate.privacy.id === VideoPlaylistPrivacy.PRIVATE || - p.id !== VideoPlaylistPrivacy.PRIVATE - }) - } - ) - }, - - err => this.error = err.message - ) - }) + .catch(err => console.error('Cannot populate user video channels.', err)) + + this.paramsSub = this.route.params + .pipe( + map(routeParams => routeParams['videoPlaylistId']), + switchMap(videoPlaylistId => this.videoPlaylistService.getVideoPlaylist(videoPlaylistId)), + delayWhen(() => this.serverService.videoPlaylistPrivaciesLoaded) + ) + .subscribe( + videoPlaylistToUpdate => { + this.videoPlaylistPrivacies = this.serverService.getVideoPlaylistPrivacies() + this.videoPlaylistToUpdate = videoPlaylistToUpdate + + this.hydrateFormFromPlaylist() + }, + + err => this.error = err.message + ) } ngOnDestroy () { 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 @@ diff --git a/client/src/app/+signup/+register/custom-stepper.component.html b/client/src/app/+signup/+register/custom-stepper.component.html new file mode 100644 index 000000000..bf507fc4f --- /dev/null +++ b/client/src/app/+signup/+register/custom-stepper.component.html @@ -0,0 +1,25 @@ +
+
+ +
+
+ {{ i + 1 }} + +
+ +
{{ step.label }}
+
+ + +
+
+
+ +
+ +
+ +
diff --git a/client/src/app/+signup/+register/custom-stepper.component.scss b/client/src/app/+signup/+register/custom-stepper.component.scss new file mode 100644 index 000000000..2371c8ae5 --- /dev/null +++ b/client/src/app/+signup/+register/custom-stepper.component.scss @@ -0,0 +1,66 @@ +@import '_variables'; +@import '_mixins'; + +$grey-color: #9CA3AB; +$index-block-height: 32px; + +header { + display: flex; + justify-content: space-between; + font-size: 15px; + margin-bottom: 30px; + + .step-info { + color: $grey-color; + display: flex; + flex-direction: column; + align-items: center; + width: $index-block-height; + + .step-index { + display: flex; + justify-content: center; + align-items: center; + width: $index-block-height; + height: $index-block-height; + border-radius: 100px; + border: 2px solid $grey-color; + margin-bottom: 10px; + + my-global-icon { + @include apply-svg-color(var(--mainBackgroundColor)); + + width: 22px; + height: 22px; + } + } + + .step-label { + width: max-content; + } + + &.active, + &.completed { + .step-index { + border-color: var(--mainColor); + background-color: var(--mainColor); + color: var(--mainBackgroundColor); + } + + .step-label { + color: var(--mainColor); + } + } + + &.completed { + cursor: pointer; + } + } + + .connector { + flex: auto; + margin: $index-block-height/2 10px 0 10px; + height: 2px; + background-color: $grey-color; + } +} diff --git a/client/src/app/+signup/+register/custom-stepper.component.ts b/client/src/app/+signup/+register/custom-stepper.component.ts new file mode 100644 index 000000000..2ae40f3a9 --- /dev/null +++ b/client/src/app/+signup/+register/custom-stepper.component.ts @@ -0,0 +1,19 @@ +import { Component } from '@angular/core' +import { CdkStep, CdkStepper } from '@angular/cdk/stepper' + +@Component({ + selector: 'my-custom-stepper', + templateUrl: './custom-stepper.component.html', + styleUrls: [ './custom-stepper.component.scss' ], + providers: [ { provide: CdkStepper, useExisting: CustomStepperComponent } ] +}) +export class CustomStepperComponent extends CdkStepper { + + onClick (index: number): void { + this.selectedIndex = index + } + + isCompleted (step: CdkStep) { + return step.stepControl && step.stepControl.dirty && step.stepControl.valid + } +} diff --git a/client/src/app/signup/signup-routing.module.ts b/client/src/app/+signup/+register/register-routing.module.ts similarity index 50% rename from client/src/app/signup/signup-routing.module.ts rename to client/src/app/+signup/+register/register-routing.module.ts index 820d16d4d..e3a5001dc 100644 --- a/client/src/app/signup/signup-routing.module.ts +++ b/client/src/app/+signup/+register/register-routing.module.ts @@ -1,17 +1,18 @@ import { NgModule } from '@angular/core' import { RouterModule, Routes } from '@angular/router' import { MetaGuard } from '@ngx-meta/core' -import { SignupComponent } from './signup.component' +import { RegisterComponent } from './register.component' import { ServerConfigResolver } from '@app/core/routing/server-config-resolver.service' +import { UnloggedGuard } from '@app/core/routing/unlogged-guard.service' -const signupRoutes: Routes = [ +const registerRoutes: Routes = [ { - path: 'signup', - component: SignupComponent, - canActivate: [ MetaGuard ], + path: '', + component: RegisterComponent, + canActivate: [ MetaGuard, UnloggedGuard ], data: { meta: { - title: 'Signup' + title: 'Register' } }, resolve: { @@ -21,7 +22,7 @@ const signupRoutes: Routes = [ ] @NgModule({ - imports: [ RouterModule.forChild(signupRoutes) ], + imports: [ RouterModule.forChild(registerRoutes) ], exports: [ RouterModule ] }) -export class SignupRoutingModule {} +export class RegisterRoutingModule {} diff --git a/client/src/app/+signup/+register/register-step-channel.component.html b/client/src/app/+signup/+register/register-step-channel.component.html new file mode 100644 index 000000000..68ea4473a --- /dev/null +++ b/client/src/app/+signup/+register/register-step-channel.component.html @@ -0,0 +1,50 @@ +
+ +
+

+ A channel is an entity in which you upload your videos. Creating several of them helps you to organize and separate your content.
+ For example, you could decide to have a channel to publish your piano concerts, and another channel in which you publish your videos talking about ecology. +

+ +

+ Other users can decide to subscribe any channel they want, to be notified when you publish a new video. +

+
+ +
+ + +
+ +
+ @{{ instanceHost }} +
+
+ +
+ {{ formErrors.name }} +
+ +
+ Channel name cannot be the same than your account name. You can click on the first step to update your account name. +
+
+ +
+ + +
+ +
+ +
+ {{ formErrors.displayName }} +
+
+
diff --git a/client/src/app/+signup/+register/register-step-channel.component.ts b/client/src/app/+signup/+register/register-step-channel.component.ts new file mode 100644 index 000000000..9e13f75b3 --- /dev/null +++ b/client/src/app/+signup/+register/register-step-channel.component.ts @@ -0,0 +1,40 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' +import { AuthService } from '@app/core' +import { FormReactive, VideoChannelValidatorsService } from '@app/shared' +import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' +import { FormGroup } from '@angular/forms' + +@Component({ + selector: 'my-register-step-channel', + templateUrl: './register-step-channel.component.html', + styleUrls: [ './register.component.scss' ] +}) +export class RegisterStepChannelComponent extends FormReactive implements OnInit { + @Input() username: string + @Output() formBuilt = new EventEmitter() + + constructor ( + protected formValidatorService: FormValidatorService, + private authService: AuthService, + private videoChannelValidatorsService: VideoChannelValidatorsService + ) { + super() + } + + get instanceHost () { + return window.location.host + } + + isSameThanUsername () { + return this.username && this.username === this.form.value['name'] + } + + ngOnInit () { + this.buildForm({ + name: this.videoChannelValidatorsService.VIDEO_CHANNEL_NAME, + displayName: this.videoChannelValidatorsService.VIDEO_CHANNEL_DISPLAY_NAME + }) + + setTimeout(() => this.formBuilt.emit(this.form)) + } +} diff --git a/client/src/app/+signup/+register/register-step-user.component.html b/client/src/app/+signup/+register/register-step-user.component.html new file mode 100644 index 000000000..cd0c78bfa --- /dev/null +++ b/client/src/app/+signup/+register/register-step-user.component.html @@ -0,0 +1,54 @@ +
+ +
+ + +
+ +
+ @{{ instanceHost }} +
+
+ +
+ {{ formErrors.username }} +
+
+ +
+ + +
+ {{ formErrors.email }} +
+
+ +
+ + +
+ {{ formErrors.password }} +
+
+ +
+ + +
+ {{ formErrors.terms }} +
+
+
diff --git a/client/src/app/+signup/+register/register-step-user.component.ts b/client/src/app/+signup/+register/register-step-user.component.ts new file mode 100644 index 000000000..3825ae371 --- /dev/null +++ b/client/src/app/+signup/+register/register-step-user.component.ts @@ -0,0 +1,37 @@ +import { Component, EventEmitter, OnInit, Output } from '@angular/core' +import { AuthService } from '@app/core' +import { FormReactive, UserValidatorsService } from '@app/shared' +import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' +import { FormGroup } from '@angular/forms' + +@Component({ + selector: 'my-register-step-user', + templateUrl: './register-step-user.component.html', + styleUrls: [ './register.component.scss' ] +}) +export class RegisterStepUserComponent extends FormReactive implements OnInit { + @Output() formBuilt = new EventEmitter() + + constructor ( + protected formValidatorService: FormValidatorService, + private authService: AuthService, + private userValidatorsService: UserValidatorsService + ) { + super() + } + + get instanceHost () { + return window.location.host + } + + ngOnInit () { + this.buildForm({ + username: this.userValidatorsService.USER_USERNAME, + password: this.userValidatorsService.USER_PASSWORD, + email: this.userValidatorsService.USER_EMAIL, + terms: this.userValidatorsService.USER_TERMS + }) + + setTimeout(() => this.formBuilt.emit(this.form)) + } +} diff --git a/client/src/app/+signup/+register/register.component.html b/client/src/app/+signup/+register/register.component.html new file mode 100644 index 000000000..24def68c1 --- /dev/null +++ b/client/src/app/+signup/+register/register.component.html @@ -0,0 +1,41 @@ +
+ +
+ Create an account +
+ + +
{{ info }}
+ +
+
+ + + + + + + + + + + + + + +
{{ error }}
+
+
+
+ +
+ + +
+
+ +
diff --git a/client/src/app/signup/signup.component.scss b/client/src/app/+signup/+register/register.component.scss similarity index 55% rename from client/src/app/signup/signup.component.scss rename to client/src/app/+signup/+register/register.component.scss index 90e1e8e74..6f61b78f7 100644 --- a/client/src/app/signup/signup.component.scss +++ b/client/src/app/+signup/+register/register.component.scss @@ -1,16 +1,32 @@ @import '_variables'; @import '_mixins'; +.alert { + font-size: 15px; + text-align: center; +} + +.wrapper { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + + & > div { + margin-bottom: 40px; + width: 450px; + + @media screen and (max-width: 500px) { + width: auto; + } + } +} + my-instance-features-table { display: block; margin-bottom: 40px; } -form { - margin: 0 60px 40px 0; -} - .form-group-terms { margin: 30px 0; } @@ -25,15 +41,18 @@ form { input:not([type=submit]) { @include peertube-input-text(400px); + display: block; - &#username { - width: auto; + &#username, + &#name { + width: auto !important; flex-grow: 1; } } -input[type=submit] { +input[type=submit], +button { @include peertube-button; @include orange-button; } diff --git a/client/src/app/+signup/+register/register.component.ts b/client/src/app/+signup/+register/register.component.ts new file mode 100644 index 000000000..cd6059728 --- /dev/null +++ b/client/src/app/+signup/+register/register.component.ts @@ -0,0 +1,89 @@ +import { Component } from '@angular/core' +import { AuthService, Notifier, RedirectService, ServerService } from '@app/core' +import { UserService, UserValidatorsService } from '@app/shared' +import { I18n } from '@ngx-translate/i18n-polyfill' +import { UserRegister } from '@shared/models/users/user-register.model' +import { FormGroup } from '@angular/forms' + +@Component({ + selector: 'my-register', + templateUrl: './register.component.html', + styleUrls: [ './register.component.scss' ] +}) +export class RegisterComponent { + info: string = null + error: string = null + success: string = null + signupDone = false + + formStepUser: FormGroup + formStepChannel: FormGroup + + constructor ( + private authService: AuthService, + private userValidatorsService: UserValidatorsService, + private notifier: Notifier, + private userService: UserService, + private serverService: ServerService, + private redirectService: RedirectService, + private i18n: I18n + ) { + } + + get requiresEmailVerification () { + return this.serverService.getConfig().signup.requiresEmailVerification + } + + hasSameChannelAndAccountNames () { + return this.getUsername() === this.getChannelName() + } + + getUsername () { + if (!this.formStepUser) return undefined + + return this.formStepUser.value['username'] + } + + getChannelName () { + if (!this.formStepChannel) return undefined + + return this.formStepChannel.value['name'] + } + + onUserFormBuilt (form: FormGroup) { + this.formStepUser = form + } + + onChannelFormBuilt (form: FormGroup) { + this.formStepChannel = form + } + + signup () { + this.error = null + + const body: UserRegister = Object.assign(this.formStepUser.value, { channel: this.formStepChannel.value }) + + this.userService.signup(body).subscribe( + () => { + this.signupDone = true + + if (this.requiresEmailVerification) { + this.info = this.i18n('Now please check your emails to verify your account and complete signup.') + return + } + + // Auto login + this.authService.login(body.username, body.password) + .subscribe( + () => { + this.success = this.i18n('You are now logged in as {{username}}!', { username: body.username }) + }, + + err => this.error = err.message + ) + }, + + err => this.error = err.message + ) + } +} diff --git a/client/src/app/+signup/+register/register.module.ts b/client/src/app/+signup/+register/register.module.ts new file mode 100644 index 000000000..46336cbd0 --- /dev/null +++ b/client/src/app/+signup/+register/register.module.ts @@ -0,0 +1,33 @@ +import { NgModule } from '@angular/core' +import { RegisterRoutingModule } from './register-routing.module' +import { RegisterComponent } from './register.component' +import { SharedModule } from '@app/shared' +import { CdkStepperModule } from '@angular/cdk/stepper' +import { RegisterStepChannelComponent } from './register-step-channel.component' +import { RegisterStepUserComponent } from './register-step-user.component' +import { CustomStepperComponent } from './custom-stepper.component' +import { SignupSharedModule } from '@app/+signup/shared/signup-shared.module' + +@NgModule({ + imports: [ + RegisterRoutingModule, + SharedModule, + CdkStepperModule, + SignupSharedModule + ], + + declarations: [ + RegisterComponent, + CustomStepperComponent, + RegisterStepChannelComponent, + RegisterStepUserComponent + ], + + exports: [ + RegisterComponent + ], + + providers: [ + ] +}) +export class RegisterModule { } diff --git a/client/src/app/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html b/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html similarity index 100% rename from client/src/app/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html rename to client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.html diff --git a/client/src/app/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.scss b/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.scss similarity index 100% rename from client/src/app/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.scss rename to client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.scss diff --git a/client/src/app/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts b/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts similarity index 100% rename from client/src/app/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts rename to client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts diff --git a/client/src/app/+verify-account/verify-account-email/verify-account-email.component.html b/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html similarity index 69% rename from client/src/app/+verify-account/verify-account-email/verify-account-email.component.html rename to client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html index a83d4a3c2..728709ca6 100644 --- a/client/src/app/+verify-account/verify-account-email/verify-account-email.component.html +++ b/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html @@ -3,9 +3,9 @@ Verify account email confirmation -
- Your email has been verified and you may now login. Redirecting... -
+ + +
An error occurred. diff --git a/client/src/app/+verify-account/verify-account-email/verify-account-email.component.ts b/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts similarity index 93% rename from client/src/app/+verify-account/verify-account-email/verify-account-email.component.ts rename to client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts index f9ecf664b..3fb2d1cd8 100644 --- a/client/src/app/+verify-account/verify-account-email/verify-account-email.component.ts +++ b/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts @@ -40,9 +40,6 @@ export class VerifyAccountEmailComponent implements OnInit { .subscribe( () => { this.success = true - setTimeout(() => { - this.router.navigate([ '/login' ]) - }, 2000) }, err => { diff --git a/client/src/app/+verify-account/verify-account-routing.module.ts b/client/src/app/+signup/+verify-account/verify-account-routing.module.ts similarity index 75% rename from client/src/app/+verify-account/verify-account-routing.module.ts rename to client/src/app/+signup/+verify-account/verify-account-routing.module.ts index a038f0336..16d5fe0d0 100644 --- a/client/src/app/+verify-account/verify-account-routing.module.ts +++ b/client/src/app/+signup/+verify-account/verify-account-routing.module.ts @@ -1,12 +1,8 @@ import { NgModule } from '@angular/core' import { RouterModule, Routes } from '@angular/router' - import { MetaGuard } from '@ngx-meta/core' - -import { VerifyAccountEmailComponent } from '@app/+verify-account/verify-account-email/verify-account-email.component' -import { - VerifyAccountAskSendEmailComponent -} from '@app/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component' +import { VerifyAccountEmailComponent } from './verify-account-email/verify-account-email.component' +import { VerifyAccountAskSendEmailComponent } from './verify-account-ask-send-email/verify-account-ask-send-email.component' const verifyAccountRoutes: Routes = [ { diff --git a/client/src/app/+signup/+verify-account/verify-account.module.ts b/client/src/app/+signup/+verify-account/verify-account.module.ts new file mode 100644 index 000000000..9fe14e81e --- /dev/null +++ b/client/src/app/+signup/+verify-account/verify-account.module.ts @@ -0,0 +1,25 @@ +import { NgModule } from '@angular/core' +import { VerifyAccountRoutingModule } from './verify-account-routing.module' +import { VerifyAccountEmailComponent } from './verify-account-email/verify-account-email.component' +import { VerifyAccountAskSendEmailComponent } from './verify-account-ask-send-email/verify-account-ask-send-email.component' +import { SharedModule } from '@app/shared' +import { SignupSharedModule } from '@app/+signup/shared/signup-shared.module' + +@NgModule({ + imports: [ + VerifyAccountRoutingModule, + SharedModule, + SignupSharedModule + ], + + declarations: [ + VerifyAccountEmailComponent, + VerifyAccountAskSendEmailComponent + ], + + exports: [], + + providers: [] +}) +export class VerifyAccountModule { +} diff --git a/client/src/app/+signup/shared/signup-shared.module.ts b/client/src/app/+signup/shared/signup-shared.module.ts new file mode 100644 index 000000000..cd21fdef3 --- /dev/null +++ b/client/src/app/+signup/shared/signup-shared.module.ts @@ -0,0 +1,21 @@ +import { NgModule } from '@angular/core' +import { SignupSuccessComponent } from '../shared/signup-success.component' +import { SharedModule } from '@app/shared' + +@NgModule({ + imports: [ + SharedModule + ], + + declarations: [ + SignupSuccessComponent + ], + + exports: [ + SignupSuccessComponent + ], + + providers: [ + ] +}) +export class SignupSharedModule { } diff --git a/client/src/app/+signup/shared/signup-success.component.html b/client/src/app/+signup/shared/signup-success.component.html new file mode 100644 index 000000000..e35f858c6 --- /dev/null +++ b/client/src/app/+signup/shared/signup-success.component.html @@ -0,0 +1,16 @@ + + + + + + + +

Welcome on PeerTube!

+ +
+

{{ message }}

+ +

+ If you need help to use PeerTube, you can take a look to the documentation. +

+
diff --git a/client/src/app/+signup/shared/signup-success.component.scss b/client/src/app/+signup/shared/signup-success.component.scss new file mode 100644 index 000000000..fbc27c8bc --- /dev/null +++ b/client/src/app/+signup/shared/signup-success.component.scss @@ -0,0 +1,76 @@ +svg { + width: 100px; + display: block; + margin: 40px auto 0; +} + +.path { + stroke-dasharray: 1000; + stroke-dashoffset: 0; + + &.circle { + -webkit-animation: dash .9s ease-in-out; + animation: dash .9s ease-in-out; + } + + &.line { + stroke-dashoffset: 1000; + -webkit-animation: dash .9s .35s ease-in-out forwards; + animation: dash .9s .35s ease-in-out forwards; + } + + &.check { + stroke-dashoffset: -100; + -webkit-animation: dash-check .9s .35s ease-in-out forwards; + animation: dash-check .9s .35s ease-in-out forwards; + } +} + +.bottom-message { + text-align: center; + margin: 20px 0 60px; + font-size: 1.25em; + color: #73AF55; +} + +.alert { + font-size: 15px; + text-align: center; +} + + +@-webkit-keyframes dash { + 0% { + stroke-dashoffset: 1000; + } + 100% { + stroke-dashoffset: 0; + } +} + +@keyframes dash { + 0% { + stroke-dashoffset: 1000; + } + 100% { + stroke-dashoffset: 0; + } +} + +@-webkit-keyframes dash-check { + 0% { + stroke-dashoffset: -100; + } + 100% { + stroke-dashoffset: 900; + } +} + +@keyframes dash-check { + 0% { + stroke-dashoffset: -100; + } + 100% { + stroke-dashoffset: 900; + } +} diff --git a/client/src/app/+signup/shared/signup-success.component.ts b/client/src/app/+signup/shared/signup-success.component.ts new file mode 100644 index 000000000..19fb5922a --- /dev/null +++ b/client/src/app/+signup/shared/signup-success.component.ts @@ -0,0 +1,10 @@ +import { Component, Input } from '@angular/core' + +@Component({ + selector: 'my-signup-success', + templateUrl: './signup-success.component.html', + styleUrls: [ './signup-success.component.scss' ] +}) +export class SignupSuccessComponent { + @Input() message: string +} diff --git a/client/src/app/+verify-account/index.ts b/client/src/app/+verify-account/index.ts deleted file mode 100644 index 733f5ba77..000000000 --- a/client/src/app/+verify-account/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from '@app/+verify-account/verify-account-routing.module' -export * from '@app/+verify-account/verify-account.module' diff --git a/client/src/app/+verify-account/verify-account.module.ts b/client/src/app/+verify-account/verify-account.module.ts deleted file mode 100644 index 9092c6b4f..000000000 --- a/client/src/app/+verify-account/verify-account.module.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { NgModule } from '@angular/core' - -import { VerifyAccountRoutingModule } from '@app/+verify-account/verify-account-routing.module' -import { VerifyAccountEmailComponent } from '@app/+verify-account/verify-account-email/verify-account-email.component' -import { - VerifyAccountAskSendEmailComponent -} from '@app/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component' -import { SharedModule } from '@app/shared' - -@NgModule({ - imports: [ - VerifyAccountRoutingModule, - SharedModule - ], - - declarations: [ - VerifyAccountEmailComponent, - VerifyAccountAskSendEmailComponent - ], - - exports: [ - ], - - providers: [ - ] -}) -export class VerifyAccountModule { } diff --git a/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.ts b/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.ts index 907aefae1..7990044a2 100644 --- a/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.ts +++ b/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.ts @@ -5,7 +5,7 @@ import { VideoChannel } from '@app/shared/video-channel/video-channel.model' import { Subscription } from 'rxjs' import { Notifier } from '@app/core' import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model' -import { ComponentPagination } from '@app/shared/rest/component-pagination.model' +import { ComponentPagination, hasMoreItems } from '@app/shared/rest/component-pagination.model' import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service' @Component({ @@ -46,8 +46,7 @@ export class VideoChannelPlaylistsComponent implements OnInit, OnDestroy { } onNearOfBottom () { - // Last page - if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return + if (!hasMoreItems(this.pagination)) return this.pagination.currentPage += 1 this.loadVideoPlaylists() diff --git a/client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts b/client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts index 5e60b34b4..629fd4450 100644 --- a/client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts +++ b/client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts @@ -29,6 +29,7 @@ export class VideoChannelVideosComponent extends AbstractVideoList implements On private videoChannelSub: Subscription constructor ( + protected i18n: I18n, protected router: Router, protected serverService: ServerService, protected route: ActivatedRoute, @@ -36,7 +37,6 @@ export class VideoChannelVideosComponent extends AbstractVideoList implements On protected notifier: Notifier, protected confirmService: ConfirmService, protected screenService: ScreenService, - private i18n: I18n, private videoChannelService: VideoChannelService, private videoService: VideoService ) { diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts index db8888dba..7ca51f226 100644 --- a/client/src/app/app-routing.module.ts +++ b/client/src/app/app-routing.module.ts @@ -16,7 +16,7 @@ const routes: Routes = [ }, { path: 'verify-account', - loadChildren: './+verify-account/verify-account.module#VerifyAccountModule' + loadChildren: './+signup/+verify-account/verify-account.module#VerifyAccountModule' }, { path: 'accounts', @@ -30,6 +30,10 @@ const routes: Routes = [ path: 'about', loadChildren: './+about/about.module#AboutModule' }, + { + path: 'signup', + loadChildren: './+signup/+register/register.module#RegisterModule' + }, { path: '', component: AppComponent // Avoid 404, app component will redirect dynamically diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index 0bbc2e08b..1e2936a37 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts @@ -14,7 +14,6 @@ import { HeaderComponent } from './header' import { LoginModule } from './login' import { AvatarNotificationComponent, LanguageChooserComponent, MenuComponent } from './menu' import { SharedModule } from './shared' -import { SignupModule } from './signup' import { VideosModule } from './videos' import { buildFileLocale, getCompleteLocale, isDefaultLocale } from '../../../shared/models/i18n' import { getDevLocale, isOnDevLocale } from '@app/shared/i18n/i18n-utils' @@ -53,7 +52,6 @@ export function metaFactory (serverService: ServerService): MetaLoader { CoreModule, LoginModule, ResetPasswordModule, - SignupModule, SearchModule, SharedModule, VideosModule, diff --git a/client/src/app/core/core.module.ts b/client/src/app/core/core.module.ts index d3e72afb4..06fa8fcf1 100644 --- a/client/src/app/core/core.module.ts +++ b/client/src/app/core/core.module.ts @@ -20,6 +20,7 @@ import { Notifier } from './notification' import { MessageService } from 'primeng/api' import { UserNotificationSocket } from '@app/core/notification/user-notification-socket.service' import { ServerConfigResolver } from './routing/server-config-resolver.service' +import { UnloggedGuard } from '@app/core/routing/unlogged-guard.service' @NgModule({ imports: [ @@ -58,6 +59,8 @@ import { ServerConfigResolver } from './routing/server-config-resolver.service' ThemeService, LoginGuard, UserRightGuard, + UnloggedGuard, + RedirectService, Notifier, MessageService, diff --git a/client/src/app/core/routing/redirect.service.ts b/client/src/app/core/routing/redirect.service.ts index e1db4097b..571822b76 100644 --- a/client/src/app/core/routing/redirect.service.ts +++ b/client/src/app/core/routing/redirect.service.ts @@ -42,7 +42,14 @@ export class RedirectService { } redirectToPreviousRoute () { - if (this.previousUrl) return this.router.navigateByUrl(this.previousUrl) + const exceptions = [ + '/verify-account' + ] + + if (this.previousUrl) { + const isException = exceptions.find(e => this.previousUrl.startsWith(e)) + if (!isException) return this.router.navigateByUrl(this.previousUrl) + } return this.redirectToHomepage() } diff --git a/client/src/app/core/routing/unlogged-guard.service.ts b/client/src/app/core/routing/unlogged-guard.service.ts new file mode 100644 index 000000000..3132a1a77 --- /dev/null +++ b/client/src/app/core/routing/unlogged-guard.service.ts @@ -0,0 +1,25 @@ +import { Injectable } from '@angular/core' +import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router, RouterStateSnapshot } from '@angular/router' +import { AuthService } from '../auth/auth.service' +import { RedirectService } from './redirect.service' + +@Injectable() +export class UnloggedGuard implements CanActivate, CanActivateChild { + + constructor ( + private router: Router, + private auth: AuthService, + private redirectService: RedirectService + ) {} + + canActivate (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + if (this.auth.isLoggedIn() === false) return true + + this.redirectService.redirectToHomepage() + return false + } + + canActivateChild (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + return this.canActivate(route, state) + } +} diff --git a/client/src/app/shared/actor/actor.model.ts b/client/src/app/shared/actor/actor.model.ts index adecec1fc..5a517c975 100644 --- a/client/src/app/shared/actor/actor.model.ts +++ b/client/src/app/shared/actor/actor.model.ts @@ -4,7 +4,6 @@ import { getAbsoluteAPIUrl } from '@app/shared/misc/utils' export abstract class Actor implements ActorServer { id: number - uuid: string url: string name: string host: string @@ -35,7 +34,6 @@ export abstract class Actor implements ActorServer { protected constructor (hash: ActorServer) { this.id = hash.id - this.uuid = hash.uuid this.url = hash.url this.name = hash.name this.host = hash.host 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 @@ @include peertube-button-link; @include button-with-icon(21px, 0, -2px); - font-weight: $font-semibold; - color: $grey-foreground-color; - background-color: $grey-background-color; - - &:hover { - background-color: $grey-background-hover-color; - } - - my-global-icon { - @include apply-svg-color($grey-foreground-color); + // FIXME: Firefox does not apply global .orange-button icon color + &.orange-button { + @include apply-svg-color(#fff) } } 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' export class ButtonComponent { @Input() label = '' - @Input() className: string = undefined + @Input() className = 'grey-button' @Input() icon: GlobalIconName = undefined @Input() title: string = undefined 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 @@ - + {{ label }} 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 @@ - + {{ label }} diff --git a/client/src/app/shared/forms/peertube-checkbox.component.scss b/client/src/app/shared/forms/peertube-checkbox.component.scss index ea321ee65..84ea788af 100644 --- a/client/src/app/shared/forms/peertube-checkbox.component.scss +++ b/client/src/app/shared/forms/peertube-checkbox.component.scss @@ -14,9 +14,6 @@ input { @include peertube-checkbox(1px); - - width: 10px; - margin-right: 10px; } } 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 @@
-
+
+ + {{ inputLabel }} +
-
(extensions: {{ allowedExtensionsMessage }}, max size: {{ maxFileSize | bytes }})
-
{{ filename }}
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 @@ .button-file { @include peertube-button-file(auto); + @include grey-button; - min-width: 190px; - } - - .file-constraints { - margin-left: 5px; - font-size: 13px; + &.with-icon { + @include button-with-icon; + } } .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 import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' import { Notifier } from '@app/core' import { I18n } from '@ngx-translate/i18n-polyfill' +import { GlobalIconName } from '@app/shared/images/global-icon.component' @Component({ selector: 'my-reactive-file', @@ -21,6 +22,7 @@ export class ReactiveFileComponent implements OnInit, ControlValueAccessor { @Input() extensions: string[] = [] @Input() maxFileSize: number @Input() displayFilename = false + @Input() icon: GlobalIconName @Output() fileChanged = new EventEmitter() 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 @@ -
- - - -
-
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 @@ -@import '_variables'; -@import '_mixins'; - -.root { - height: auto; - display: flex; - align-items: center; - - .preview { - border: 2px solid grey; - border-radius: 4px; - margin-left: 50px; - - &.no-image { - background-color: #ececec; - } - } -} 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 @@ +
+
+ + + +
+
+ +
(extensions: {{ allowedExtensionsMessage }}, max size: {{ maxVideoImageSize | bytes }})
+
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 @@ +@import '_variables'; +@import '_mixins'; + +.root { + height: auto; + display: flex; + flex-direction: column; + + .preview-container { + position: relative; + + my-reactive-file { + position: absolute; + bottom: 10px; + left: 10px; + } + + .preview { + border: 2px solid grey; + border-radius: 4px; + + &.no-image { + background-color: #ececec; + } + } + } +} diff --git a/client/src/app/shared/images/image-upload.component.ts b/client/src/app/shared/images/preview-upload.component.ts similarity index 73% rename from client/src/app/shared/images/image-upload.component.ts rename to 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 @@ -import { Component, forwardRef, Input } from '@angular/core' +import { Component, forwardRef, Input, OnInit } from '@angular/core' import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser' import { ServerService } from '@app/core' @Component({ - selector: 'my-image-upload', - styleUrls: [ './image-upload.component.scss' ], - templateUrl: './image-upload.component.html', + selector: 'my-preview-upload', + styleUrls: [ './preview-upload.component.scss' ], + templateUrl: './preview-upload.component.html', providers: [ { provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => ImageUploadComponent), + useExisting: forwardRef(() => PreviewUploadComponent), multi: true } ] }) -export class ImageUploadComponent implements ControlValueAccessor { +export class PreviewUploadComponent implements OnInit, ControlValueAccessor { @Input() inputLabel: string @Input() inputName: string @Input() previewWidth: string @Input() previewHeight: string imageSrc: SafeResourceUrl + allowedExtensionsMessage = '' private file: File @@ -38,6 +39,10 @@ export class ImageUploadComponent implements ControlValueAccessor { return this.serverService.getConfig().video.image.size.max } + ngOnInit () { + this.allowedExtensionsMessage = this.videoImageExtensions.join(', ') + } + onFileChanged (file: File) { this.file = file diff --git a/client/src/app/+admin/follows/shared/follow.service.ts b/client/src/app/shared/instance/follow.service.ts similarity index 92% rename from client/src/app/+admin/follows/shared/follow.service.ts rename to client/src/app/shared/instance/follow.service.ts index c2b8ef006..5a44c64f1 100644 --- a/client/src/app/+admin/follows/shared/follow.service.ts +++ b/client/src/app/shared/instance/follow.service.ts @@ -3,13 +3,13 @@ import { HttpClient, HttpParams } from '@angular/common/http' import { Injectable } from '@angular/core' import { SortMeta } from 'primeng/primeng' import { Observable } from 'rxjs' -import { ActorFollow, ResultList } from '../../../../../../shared' -import { environment } from '../../../../environments/environment' -import { RestExtractor, RestPagination, RestService } from '../../../shared' +import { ActorFollow, ResultList } from '@shared/index' +import { environment } from '../../../environments/environment' +import { RestExtractor, RestPagination, RestService } from '../rest' @Injectable() export class FollowService { - private static BASE_APPLICATION_URL = environment.apiUrl + '/api/v1/server' + private static BASE_APPLICATION_URL = 'https://peertube2.cpy.re' + '/api/v1/server' constructor ( private authHttp: HttpClient, diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index ded65653f..1d49c7bc8 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 import { ConfirmComponent } from '@app/shared/confirm/confirm.component' import { SmallLoaderComponent } from '@app/shared/misc/small-loader.component' import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service' -import { ImageUploadComponent } from '@app/shared/images/image-upload.component' +import { PreviewUploadComponent } from '@app/shared/images/preview-upload.component' import { GlobalIconComponent } from '@app/shared/images/global-icon.component' import { VideoPlaylistMiniatureComponent } from '@app/shared/video-playlist/video-playlist-miniature.component' import { VideoAddToPlaylistComponent } from '@app/shared/video-playlist/video-add-to-playlist.component' @@ -85,6 +85,7 @@ import { VideoBlacklistComponent } from '@app/shared/video/modals/video-blacklis import { VideoDownloadComponent } from '@app/shared/video/modals/video-download.component' import { VideoReportComponent } from '@app/shared/video/modals/video-report.component' import { ClipboardModule } from 'ngx-clipboard' +import { FollowService } from '@app/shared/instance/follow.service' @NgModule({ imports: [ @@ -154,7 +155,7 @@ import { ClipboardModule } from 'ngx-clipboard' ConfirmComponent, GlobalIconComponent, - ImageUploadComponent + PreviewUploadComponent ], exports: [ @@ -218,7 +219,7 @@ import { ClipboardModule } from 'ngx-clipboard' ConfirmComponent, GlobalIconComponent, - ImageUploadComponent, + PreviewUploadComponent, NumberFormatterPipe, ObjectLengthPipe, @@ -271,6 +272,8 @@ import { ClipboardModule } from 'ngx-clipboard' UserNotificationService, + FollowService, + I18n ] }) diff --git a/client/src/app/shared/users/user.service.ts b/client/src/app/shared/users/user.service.ts index cc5c051f1..20883456f 100644 --- a/client/src/app/shared/users/user.service.ts +++ b/client/src/app/shared/users/user.service.ts @@ -9,6 +9,7 @@ import { Avatar } from '../../../../../shared/models/avatars/avatar.model' import { SortMeta } from 'primeng/api' import { BytesPipe } from 'ngx-pipes' import { I18n } from '@ngx-translate/i18n-polyfill' +import { UserRegister } from '@shared/models/users/user-register.model' @Injectable() export class UserService { @@ -64,7 +65,7 @@ export class UserService { .pipe(catchError(err => this.restExtractor.handleError(err))) } - signup (userCreate: UserCreate) { + signup (userCreate: UserRegister) { return this.authHttp.post(UserService.BASE_USERS_URL + 'register', userCreate) .pipe( map(this.restExtractor.extractDataBool), diff --git a/client/src/app/shared/video-channel/video-channel.service.ts b/client/src/app/shared/video-channel/video-channel.service.ts index d0bec649a..0168d37d9 100644 --- a/client/src/app/shared/video-channel/video-channel.service.ts +++ b/client/src/app/shared/video-channel/video-channel.service.ts @@ -2,7 +2,7 @@ import { catchError, map, tap } from 'rxjs/operators' import { Injectable } from '@angular/core' import { Observable, ReplaySubject } from 'rxjs' import { RestExtractor } from '../rest/rest-extractor.service' -import { HttpClient } from '@angular/common/http' +import { HttpClient, HttpParams } from '@angular/common/http' import { VideoChannel as VideoChannelServer, VideoChannelCreate, VideoChannelUpdate } from '../../../../../shared/models/videos' import { AccountService } from '../account/account.service' import { ResultList } from '../../../../../shared' @@ -10,6 +10,8 @@ import { VideoChannel } from './video-channel.model' import { environment } from '../../../environments/environment' import { Account } from '@app/shared/account/account.model' import { Avatar } from '../../../../../shared/models/avatars/avatar.model' +import { ComponentPagination } from '@app/shared/rest/component-pagination.model' +import { RestService } from '@app/shared/rest' @Injectable() export class VideoChannelService { @@ -29,6 +31,7 @@ export class VideoChannelService { constructor ( private authHttp: HttpClient, + private restService: RestService, private restExtractor: RestExtractor ) { } @@ -41,8 +44,16 @@ export class VideoChannelService { ) } - listAccountVideoChannels (account: Account): Observable> { - return this.authHttp.get>(AccountService.BASE_ACCOUNT_URL + account.nameWithHost + '/video-channels') + listAccountVideoChannels (account: Account, componentPagination?: ComponentPagination): Observable> { + const pagination = componentPagination + ? this.restService.componentPaginationToRestPagination(componentPagination) + : { start: 0, count: 20 } + + let params = new HttpParams() + params = this.restService.addRestGetParams(params, pagination) + + const url = AccountService.BASE_ACCOUNT_URL + account.nameWithHost + '/video-channels' + return this.authHttp.get>(url, { params }) .pipe( map(res => VideoChannelService.extractVideoChannels(res)), catchError(err => this.restExtractor.handleError(err)) diff --git a/client/src/app/shared/video/abstract-video-list.html b/client/src/app/shared/video/abstract-video-list.html index 268677977..efd369bca 100644 --- a/client/src/app/shared/video/abstract-video-list.html +++ b/client/src/app/shared/video/abstract-video-list.html @@ -6,7 +6,7 @@
- +
- - + +
+ {{ getCurrentGroupedDateLabel(video) }} +
+ + + +
diff --git a/client/src/app/shared/video/abstract-video-list.scss b/client/src/app/shared/video/abstract-video-list.scss index 9d481d6e4..98b80fdfd 100644 --- a/client/src/app/shared/video/abstract-video-list.scss +++ b/client/src/app/shared/video/abstract-video-list.scss @@ -24,33 +24,19 @@ } } -.margin-content { - width: $video-miniature-width * 6; - margin: auto !important; - - @media screen and (max-width: 1800px) { - width: $video-miniature-width * 5; - } +.date-title { + font-size: 16px; + font-weight: $font-semibold; + margin-bottom: 20px; + margin-top: -10px; + padding-top: 20px; - @media screen and (max-width: 1800px - $video-miniature-width) { - width: $video-miniature-width * 4; + &:not(:first-child) { + border-top: 1px solid $separator-border-color; } +} - @media screen and (max-width: 1800px - (2* $video-miniature-width)) { - width: $video-miniature-width * 3; - } - - @media screen and (max-width: 1800px - (3* $video-miniature-width)) { - width: $video-miniature-width * 2; - } - - @media screen and (max-width: 500px) { - width: auto; - margin: 0 !important; - - .videos { - @include video-miniature-small-screen; - } - } +.margin-content { + @include adapt-margin-content-width; } diff --git a/client/src/app/shared/video/abstract-video-list.ts b/client/src/app/shared/video/abstract-video-list.ts index fa9d38735..dc8f9cda9 100644 --- a/client/src/app/shared/video/abstract-video-list.ts +++ b/client/src/app/shared/video/abstract-video-list.ts @@ -11,6 +11,17 @@ import { MiniatureDisplayOptions, OwnerDisplayType } from '@app/shared/video/vid import { Syndication } from '@app/shared/video/syndication.model' import { Notifier, ServerService } from '@app/core' import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook' +import { I18n } from '@ngx-translate/i18n-polyfill' +import { isLastMonth, isLastWeek, isToday, isYesterday } from '@shared/core-utils/miscs/date' + +enum GroupDate { + UNKNOWN = 0, + TODAY = 1, + YESTERDAY = 2, + LAST_WEEK = 3, + LAST_MONTH = 4, + OLDER = 5 +} export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableForReuseHook { pagination: ComponentPagination = { @@ -31,6 +42,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor displayModerationBlock = false titleTooltip: string displayVideoActions = true + groupByDate = false disabled = false @@ -50,11 +62,15 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor protected abstract serverService: ServerService protected abstract screenService: ScreenService protected abstract router: Router + protected abstract i18n: I18n abstract titlePage: string private resizeSubscription: Subscription private angularState: number + private groupedDateLabels: { [id in GroupDate]: string } + private groupedDates: { [id: number]: GroupDate } = {} + abstract getVideosObservable (page: number): Observable<{ videos: Video[], totalVideos: number }> abstract generateSyndicationList (): void @@ -64,6 +80,15 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor } ngOnInit () { + this.groupedDateLabels = { + [GroupDate.UNKNOWN]: null, + [GroupDate.TODAY]: this.i18n('Today'), + [GroupDate.YESTERDAY]: this.i18n('Yesterday'), + [GroupDate.LAST_WEEK]: this.i18n('Last week'), + [GroupDate.LAST_MONTH]: this.i18n('Last month'), + [GroupDate.OLDER]: this.i18n('Older') + } + // Subscribe to route changes const routeParams = this.route.snapshot.queryParams this.loadRouteParams(routeParams) @@ -113,6 +138,8 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor this.pagination.totalItems = totalVideos this.videos = this.videos.concat(videos) + if (this.groupByDate) this.buildGroupedDateLabels() + this.onMoreVideos() }, @@ -134,6 +161,59 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor this.videos = this.videos.filter(v => v.id !== video.id) } + buildGroupedDateLabels () { + let currentGroupedDate: GroupDate = GroupDate.UNKNOWN + + for (const video of this.videos) { + const publishedDate = video.publishedAt + + if (currentGroupedDate <= GroupDate.TODAY && isToday(publishedDate)) { + if (currentGroupedDate === GroupDate.TODAY) continue + + currentGroupedDate = GroupDate.TODAY + this.groupedDates[ video.id ] = currentGroupedDate + continue + } + + if (currentGroupedDate <= GroupDate.YESTERDAY && isYesterday(publishedDate)) { + if (currentGroupedDate === GroupDate.YESTERDAY) continue + + currentGroupedDate = GroupDate.YESTERDAY + this.groupedDates[ video.id ] = currentGroupedDate + continue + } + + if (currentGroupedDate <= GroupDate.LAST_WEEK && isLastWeek(publishedDate)) { + if (currentGroupedDate === GroupDate.LAST_WEEK) continue + + currentGroupedDate = GroupDate.LAST_WEEK + this.groupedDates[ video.id ] = currentGroupedDate + continue + } + + if (currentGroupedDate <= GroupDate.LAST_MONTH && isLastMonth(publishedDate)) { + if (currentGroupedDate === GroupDate.LAST_MONTH) continue + + currentGroupedDate = GroupDate.LAST_MONTH + this.groupedDates[ video.id ] = currentGroupedDate + continue + } + + if (currentGroupedDate <= GroupDate.OLDER) { + if (currentGroupedDate === GroupDate.OLDER) continue + + currentGroupedDate = GroupDate.OLDER + this.groupedDates[ video.id ] = currentGroupedDate + } + } + } + + getCurrentGroupedDateLabel (video: Video) { + if (this.groupByDate === false) return undefined + + return this.groupedDateLabels[this.groupedDates[video.id]] + } + // On videos hook for children that want to do something protected onMoreVideos () { /* empty */ } 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 @@ import { Component, ElementRef, ViewChild } from '@angular/core' import { VideoDetails } from '../../../shared/video/video-details.model' -import { NgbModal } from '@ng-bootstrap/ng-bootstrap' +import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap' import { I18n } from '@ngx-translate/i18n-polyfill' import { Notifier } from '@app/core' @@ -16,6 +16,7 @@ export class VideoDownloadComponent { resolutionId: number | string = -1 video: VideoDetails + activeModal: NgbActiveModal constructor ( private notifier: Notifier, @@ -26,9 +27,7 @@ export class VideoDownloadComponent { show (video: VideoDetails) { this.video = video - const m = this.modalService.open(this.modal) - m.result.then(() => this.onClose()) - .catch(() => this.onClose()) + this.activeModal = this.modalService.open(this.modal) this.resolutionId = this.video.files[0].resolution.id } @@ -39,6 +38,7 @@ export class VideoDownloadComponent { download () { window.location.assign(this.getLink()) + this.activeModal.close() } getLink () { diff --git a/client/src/app/shared/video/video-details.model.ts b/client/src/app/shared/video/video-details.model.ts index 22f024656..e4d443a06 100644 --- a/client/src/app/shared/video/video-details.model.ts +++ b/client/src/app/shared/video/video-details.model.ts @@ -1,5 +1,4 @@ -import { UserRight, VideoConstant, VideoDetails as VideoDetailsServerModel, VideoFile, VideoState } from '../../../../../shared' -import { AuthUser } from '../../core' +import { VideoConstant, VideoDetails as VideoDetailsServerModel, VideoFile, VideoState } from '../../../../../shared' import { Video } from '../../shared/video/video.model' import { Account } from '@app/shared/account/account.model' import { VideoChannel } from '@app/shared/video-channel/video-channel.model' 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 { const originallyPublishedAt = new Date(values['originallyPublishedAt']) this.originallyPublishedAt = originallyPublishedAt.toISOString() } + + // Use the same file than the preview for the thumbnail + if (this.previewfile) { + this.thumbnailfile = this.previewfile + } } toFormPatch () { diff --git a/client/src/app/shared/video/video.model.ts b/client/src/app/shared/video/video.model.ts index 0cef3eb8f..6f9de9241 100644 --- a/client/src/app/shared/video/video.model.ts +++ b/client/src/app/shared/video/video.model.ts @@ -52,7 +52,6 @@ export class Video implements VideoServerModel { account: { id: number - uuid: string name: string displayName: string url: string @@ -62,7 +61,6 @@ export class Video implements VideoServerModel { channel: { id: number - uuid: string name: string displayName: string url: string diff --git a/client/src/app/shared/video/videos-selection.component.ts b/client/src/app/shared/video/videos-selection.component.ts index 955ebca9f..d69f7b70e 100644 --- a/client/src/app/shared/video/videos-selection.component.ts +++ b/client/src/app/shared/video/videos-selection.component.ts @@ -20,6 +20,7 @@ import { Video } from '@app/shared/video/video.model' import { PeerTubeTemplateDirective } from '@app/shared/angular/peertube-template.directive' import { VideoSortField } from '@app/shared/video/sort-field.type' import { ComponentPagination } from '@app/shared/rest/component-pagination.model' +import { I18n } from '@ngx-translate/i18n-polyfill' export type SelectionType = { [ id: number ]: boolean } @@ -44,6 +45,7 @@ export class VideosSelectionComponent extends AbstractVideoList implements OnIni globalButtonsTemplate: TemplateRef constructor ( + protected i18n: I18n, protected router: Router, protected route: ActivatedRoute, protected notifier: Notifier, diff --git a/client/src/app/signup/index.ts b/client/src/app/signup/index.ts deleted file mode 100644 index b0aca9723..000000000 --- a/client/src/app/signup/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './signup-routing.module' -export * from './signup.component' -export * from './signup.module' diff --git a/client/src/app/signup/signup.component.html b/client/src/app/signup/signup.component.html deleted file mode 100644 index 07d24b381..000000000 --- a/client/src/app/signup/signup.component.html +++ /dev/null @@ -1,72 +0,0 @@ -
- -
- Create an account -
- -
{{ info }}
-
{{ error }}
- -
-
-
- - -
- -
- @{{ instanceHost }} -
-
- -
- {{ formErrors.username }} -
-
- -
- - -
- {{ formErrors.email }} -
-
- -
- - -
- {{ formErrors.password }} -
-
- -
- - -
- {{ formErrors.terms }} -
-
- - -
- -
- - -
-
- -
diff --git a/client/src/app/signup/signup.component.ts b/client/src/app/signup/signup.component.ts deleted file mode 100644 index 13941ec79..000000000 --- a/client/src/app/signup/signup.component.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { Component, OnInit } from '@angular/core' -import { AuthService, Notifier, RedirectService, ServerService } from '@app/core' -import { UserCreate } from '../../../../shared' -import { FormReactive, UserService, UserValidatorsService } from '../shared' -import { I18n } from '@ngx-translate/i18n-polyfill' -import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' - -@Component({ - selector: 'my-signup', - templateUrl: './signup.component.html', - styleUrls: [ './signup.component.scss' ] -}) -export class SignupComponent extends FormReactive implements OnInit { - info: string = null - error: string = null - signupDone = false - - constructor ( - protected formValidatorService: FormValidatorService, - private authService: AuthService, - private userValidatorsService: UserValidatorsService, - private notifier: Notifier, - private userService: UserService, - private serverService: ServerService, - private redirectService: RedirectService, - private i18n: I18n - ) { - super() - } - - get instanceHost () { - return window.location.host - } - - get requiresEmailVerification () { - return this.serverService.getConfig().signup.requiresEmailVerification - } - - ngOnInit () { - this.buildForm({ - username: this.userValidatorsService.USER_USERNAME, - password: this.userValidatorsService.USER_PASSWORD, - email: this.userValidatorsService.USER_EMAIL, - terms: this.userValidatorsService.USER_TERMS - }) - } - - signup () { - this.error = null - - const userCreate: UserCreate = this.form.value - - this.userService.signup(userCreate).subscribe( - () => { - this.signupDone = true - - if (this.requiresEmailVerification) { - this.info = this.i18n('Welcome! Now please check your emails to verify your account and complete signup.') - return - } - - // Auto login - this.authService.login(userCreate.username, userCreate.password) - .subscribe( - () => { - this.notifier.success(this.i18n('You are now logged in as {{username}}!', { username: userCreate.username })) - - this.redirectService.redirectToHomepage() - }, - - err => this.error = err.message - ) - }, - - err => this.error = err.message - ) - } -} diff --git a/client/src/app/signup/signup.module.ts b/client/src/app/signup/signup.module.ts deleted file mode 100644 index 61560ddcf..000000000 --- a/client/src/app/signup/signup.module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { NgModule } from '@angular/core' - -import { SignupRoutingModule } from './signup-routing.module' -import { SignupComponent } from './signup.component' -import { SharedModule } from '../shared' - -@NgModule({ - imports: [ - SignupRoutingModule, - SharedModule - ], - - declarations: [ - SignupComponent - ], - - exports: [ - SignupComponent - ], - - providers: [ - ] -}) -export class SignupModule { } 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 @@
-
- -
- Video preview + + + >
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 8345645f6..cea352bfb 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 @@ -13,6 +13,7 @@ import { VideoCaptionAddModalComponent } from '@app/videos/+video-edit/shared/vi import { VideoCaptionEdit } from '@app/shared/video-caption/video-caption-edit.model' import { removeElementFromArray } from '@app/shared/misc/utils' import { VideoConstant, VideoPrivacy } from '../../../../../../shared' +import { VideoService } from '@app/shared/video/video.service' @Component({ selector: 'my-video-edit', @@ -23,7 +24,6 @@ export class VideoEditComponent implements OnInit, OnDestroy { @Input() form: FormGroup @Input() formErrors: { [ id: string ]: string } = {} @Input() validationMessages: FormReactiveValidationMessages = {} - @Input() videoPrivacies: VideoConstant[] = [] @Input() userVideoChannels: { id: number, label: string, support: string }[] = [] @Input() schedulePublicationPossible = true @Input() videoCaptions: (VideoCaptionEdit & { captionPath?: string })[] = [] @@ -34,6 +34,7 @@ export class VideoEditComponent implements OnInit, OnDestroy { // So that it can be accessed in the template readonly SPECIAL_SCHEDULED_PRIVACY = VideoEdit.SPECIAL_SCHEDULED_PRIVACY + videoPrivacies: VideoConstant[] = [] videoCategories: VideoConstant[] = [] videoLicences: VideoConstant[] = [] videoLanguages: VideoConstant[] = [] @@ -58,6 +59,7 @@ export class VideoEditComponent implements OnInit, OnDestroy { private formValidatorService: FormValidatorService, private videoValidatorsService: VideoValidatorsService, private videoCaptionService: VideoCaptionService, + private videoService: VideoService, private route: ActivatedRoute, private router: Router, private notifier: Notifier, @@ -100,7 +102,6 @@ export class VideoEditComponent implements OnInit, OnDestroy { language: this.videoValidatorsService.VIDEO_LANGUAGE, description: this.videoValidatorsService.VIDEO_DESCRIPTION, tags: null, - thumbnailfile: null, previewfile: null, support: this.videoValidatorsService.VIDEO_SUPPORT, schedulePublicationAt: this.videoValidatorsService.VIDEO_SCHEDULE_PUBLICATION_AT, @@ -133,6 +134,9 @@ export class VideoEditComponent implements OnInit, OnDestroy { this.videoLicences = this.serverService.getVideoLicences() this.videoLanguages = this.serverService.getVideoLanguages() + const privacies = this.serverService.getVideoPrivacies() + this.videoPrivacies = this.videoService.explainedPrivacyLabels(privacies) + this.initialVideoCaptions = this.videoCaptions.map(c => c.language.id) this.ngZone.runOutsideAngular(() => { diff --git a/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.html b/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.html index 537d7ffa2..7a495fea5 100644 --- a/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.html +++ b/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.html @@ -58,7 +58,7 @@
diff --git a/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.ts b/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.ts index d2e9f6cfe..ed9cb5840 100644 --- a/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.ts +++ b/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.ts @@ -100,7 +100,6 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Ca previewUrl: null })) - this.explainedVideoPrivacies = this.videoService.explainedPrivacyLabels(this.videoPrivacies) this.hydrateFormFromVideo() }, diff --git a/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.html b/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.html index 984b9d590..e4f19faa8 100644 --- a/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.html +++ b/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.html @@ -51,7 +51,7 @@
diff --git a/client/src/app/videos/+video-edit/video-add-components/video-send.ts b/client/src/app/videos/+video-edit/video-add-components/video-send.ts index 8401caeec..580c123a0 100644 --- a/client/src/app/videos/+video-edit/video-add-components/video-send.ts +++ b/client/src/app/videos/+video-edit/video-add-components/video-send.ts @@ -14,7 +14,6 @@ import { CanComponentDeactivateResult } from '@app/shared/guards/can-deactivate- export abstract class VideoSend extends FormReactive implements OnInit { userVideoChannels: { id: number, label: string, support: string }[] = [] videoPrivacies: VideoConstant[] = [] - explainedVideoPrivacies: VideoConstant[] = [] videoCaptions: VideoCaptionEdit[] = [] firstStepPrivacyId = 0 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..0f904affb 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 @@
+ + +
+ + +
+ Image that will be merged with your audio file. +
+ The chosen image will be definitive and cannot be modified. +
+ + +
+ +
+ +
+
@@ -50,7 +71,7 @@ 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 @@ @import 'variables'; @import 'mixins'; -.first-step-block .form-group-channel { - margin-bottom: 20px; - margin-top: 35px; +.first-step-block { + + .form-group-channel { + margin-bottom: 20px; + margin-top: 35px; + } + + .audio-image-info { + margin-bottom: 10px; + } + + .audio-preview { + margin: 30px 0; + } } .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..69fa13a2f 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 userVideoQuotaUsed = 0 userVideoQuotaUsedDaily = 0 + isUploadingAudioFile = false isUploadingVideo = false isUpdatingVideo = false + videoUploaded = false videoUploadObservable: Subscription = null videoUploadPercents = 0 @@ -44,7 +46,9 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy id: 0, uuid: '' } + waitTranscodingEnabled = true + previewfileUpload: File error: string @@ -100,6 +104,17 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy } } + getVideoFile () { + return this.videofileInput.nativeElement.files[0] + } + + getAudioUploadLabel () { + const videofile = this.getVideoFile() + if (!videofile) return this.i18n('Upload') + + return this.i18n('Upload {{videofileName}}', { videofileName: videofile.name }) + } + fileChange () { this.uploadFirstStep() } @@ -114,38 +129,15 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy } } - uploadFirstStep () { - const videofile = this.videofileInput.nativeElement.files[0] + uploadFirstStep (clickedOnButton = false) { + const videofile = this.getVideoFile() if (!videofile) return - // Check global user quota - const bytePipes = new BytesPipe() - const videoQuota = this.authService.getUser().videoQuota - if (videoQuota !== -1 && (this.userVideoQuotaUsed + videofile.size) > videoQuota) { - const msg = this.i18n( - 'Your video quota is exceeded with this video (video size: {{videoSize}}, used: {{videoQuotaUsed}}, quota: {{videoQuota}})', - { - videoSize: bytePipes.transform(videofile.size, 0), - videoQuotaUsed: bytePipes.transform(this.userVideoQuotaUsed, 0), - videoQuota: bytePipes.transform(videoQuota, 0) - } - ) - this.notifier.error(msg) - return - } + if (!this.checkGlobalUserQuota(videofile)) return + if (!this.checkDailyUserQuota(videofile)) return - // Check daily user quota - const videoQuotaDaily = this.authService.getUser().videoQuotaDaily - if (videoQuotaDaily !== -1 && (this.userVideoQuotaUsedDaily + videofile.size) > videoQuotaDaily) { - const msg = this.i18n( - 'Your daily video quota is exceeded with this video (video size: {{videoSize}}, used: {{quotaUsedDaily}}, quota: {{quotaDaily}})', - { - videoSize: bytePipes.transform(videofile.size, 0), - quotaUsedDaily: bytePipes.transform(this.userVideoQuotaUsedDaily, 0), - quotaDaily: bytePipes.transform(videoQuotaDaily, 0) - } - ) - this.notifier.error(msg) + if (clickedOnButton === false && this.isAudioFile(videofile.name)) { + this.isUploadingAudioFile = true return } @@ -180,6 +172,11 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy formData.append('channelId', '' + channelId) formData.append('videofile', videofile) + if (this.previewfileUpload) { + formData.append('previewfile', this.previewfileUpload) + formData.append('thumbnailfile', this.previewfileUpload) + } + this.isUploadingVideo = true this.firstStepDone.emit(name) @@ -187,11 +184,10 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy name, privacy, nsfw, - channelId + channelId, + previewfile: this.previewfileUpload }) - this.explainedVideoPrivacies = this.videoService.explainedPrivacyLabels(this.videoPrivacies) - this.videoUploadObservable = this.videoService.uploadVideo(formData).subscribe( event => { if (event.type === HttpEventType.UploadProgress) { @@ -251,4 +247,52 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy } ) } + + private checkGlobalUserQuota (videofile: File) { + const bytePipes = new BytesPipe() + + // Check global user quota + const videoQuota = this.authService.getUser().videoQuota + if (videoQuota !== -1 && (this.userVideoQuotaUsed + videofile.size) > videoQuota) { + const msg = this.i18n( + 'Your video quota is exceeded with this video (video size: {{videoSize}}, used: {{videoQuotaUsed}}, quota: {{videoQuota}})', + { + videoSize: bytePipes.transform(videofile.size, 0), + videoQuotaUsed: bytePipes.transform(this.userVideoQuotaUsed, 0), + videoQuota: bytePipes.transform(videoQuota, 0) + } + ) + this.notifier.error(msg) + + return false + } + + return true + } + + private checkDailyUserQuota (videofile: File) { + const bytePipes = new BytesPipe() + + // Check daily user quota + const videoQuotaDaily = this.authService.getUser().videoQuotaDaily + if (videoQuotaDaily !== -1 && (this.userVideoQuotaUsedDaily + videofile.size) > videoQuotaDaily) { + const msg = this.i18n( + 'Your daily video quota is exceeded with this video (video size: {{videoSize}}, used: {{quotaUsedDaily}}, quota: {{quotaDaily}})', + { + videoSize: bytePipes.transform(videofile.size, 0), + quotaUsedDaily: bytePipes.transform(this.userVideoQuotaUsedDaily, 0), + quotaDaily: bytePipes.transform(videoQuotaDaily, 0) + } + ) + this.notifier.error(msg) + + return false + } + + return true + } + + private isAudioFile (filename: string) { + return filename.endsWith('.mp3') || filename.endsWith('.flac') || filename.endsWith('.ogg') + } } diff --git a/client/src/app/videos/+video-edit/video-update.component.html b/client/src/app/videos/+video-edit/video-update.component.html index b5cab7ed5..aa148311f 100644 --- a/client/src/app/videos/+video-edit/video-update.component.html +++ b/client/src/app/videos/+video-edit/video-update.component.html @@ -7,7 +7,7 @@ diff --git a/client/src/app/videos/+video-edit/video-update.component.ts b/client/src/app/videos/+video-edit/video-update.component.ts index 10f797d02..e990ceb13 100644 --- a/client/src/app/videos/+video-edit/video-update.component.ts +++ b/client/src/app/videos/+video-edit/video-update.component.ts @@ -3,7 +3,6 @@ import { Component, HostListener, OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { LoadingBarService } from '@ngx-loading-bar/core' import { Notifier } from '@app/core' -import { VideoConstant, VideoPrivacy } from '../../../../../shared/models/videos' import { ServerService } from '../../core' import { FormReactive } from '../../shared' import { VideoEdit } from '../../shared/video/video-edit.model' @@ -23,8 +22,6 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { video: VideoEdit isUpdatingVideo = false - videoPrivacies: VideoConstant[] = [] - explainedVideoPrivacies: VideoConstant[] = [] userVideoChannels: { id: number, label: string, support: string }[] = [] schedulePublicationPossible = false videoCaptions: VideoCaptionEdit[] = [] @@ -49,9 +46,6 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { ngOnInit () { this.buildForm({}) - this.serverService.videoPrivaciesLoaded - .subscribe(() => this.videoPrivacies = this.serverService.getVideoPrivacies()) - this.route.data .pipe(map(data => data.videoData)) .subscribe(({ video, videoChannels, videoCaptions }) => { @@ -59,15 +53,6 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { this.userVideoChannels = videoChannels this.videoCaptions = videoCaptions - // We cannot set private a video that was not private - if (this.video.privacy !== VideoPrivacy.PRIVATE) { - this.videoPrivacies = this.videoPrivacies.filter(p => p.id !== VideoPrivacy.PRIVATE) - } else { // We can schedule video publication only if it it is private - this.schedulePublicationPossible = this.video.privacy === VideoPrivacy.PRIVATE - } - - this.explainedVideoPrivacies = this.videoService.explainedPrivacyLabels(this.videoPrivacies) - const videoFiles = (video as VideoDetails).files if (videoFiles.length > 1) { // Already transcoded this.waitTranscodingEnabled = false 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..2d13f1b58 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts @@ -6,7 +6,7 @@ import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage' import { VideoSupportComponent } from '@app/videos/+video-watch/modal/video-support.component' import { MetaService } from '@ngx-meta/core' import { Notifier, ServerService } from '@app/core' -import { forkJoin, Subscription } from 'rxjs' +import { forkJoin, Observable, Subscription } from 'rxjs' import { Hotkey, HotkeysService } from 'angular2-hotkeys' import { UserVideoRateType, VideoCaption, VideoPrivacy, VideoState } from '../../../../../shared' import { AuthService, ConfirmService } from '../../core' @@ -135,22 +135,18 @@ export class VideoWatchComponent implements OnInit, OnDestroy { setLike () { if (this.isUserLoggedIn() === false) return - if (this.userRating === 'like') { - // Already liked this video - this.setRating('none') - } else { - this.setRating('like') - } + + // Already liked this video + if (this.userRating === 'like') this.setRating('none') + else this.setRating('like') } setDislike () { if (this.isUserLoggedIn() === false) return - if (this.userRating === 'dislike') { - // Already disliked this video - this.setRating('none') - } else { - this.setRating('dislike') - } + + // Already disliked this video + if (this.userRating === 'dislike') this.setRating('none') + else this.setRating('dislike') } showMoreDescription () { @@ -249,12 +245,15 @@ export class VideoWatchComponent implements OnInit, OnDestroy { ) .subscribe(([ video, captionsResult ]) => { const queryParams = this.route.snapshot.queryParams - const startTime = queryParams.start - const stopTime = queryParams.stop - const subtitle = queryParams.subtitle - const playerMode = queryParams.mode - this.onVideoFetched(video, captionsResult.data, { startTime, stopTime, subtitle, playerMode }) + const urlOptions = { + startTime: queryParams.start, + stopTime: queryParams.stop, + subtitle: queryParams.subtitle, + playerMode: queryParams.mode + } + + this.onVideoFetched(video, captionsResult.data, urlOptions) .catch(err => this.handleError(err)) }) } @@ -279,6 +278,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { private updateVideoDescription (description: string) { this.video.description = description this.setVideoDescriptionHTML() + .catch(err => console.error(err)) } private async setVideoDescriptionHTML () { @@ -385,7 +385,9 @@ export class VideoWatchComponent implements OnInit, OnDestroy { captions: videoCaptions.length !== 0, peertubeLink: false, - videoViewUrl: this.video.privacy.id !== VideoPrivacy.PRIVATE ? this.videoService.getVideoViewUrl(this.video.uuid) : null, + videoViewUrl: this.video.privacy.id !== VideoPrivacy.PRIVATE + ? this.videoService.getVideoViewUrl(this.video.uuid) + : null, embedUrl: this.video.embedUrl, language: this.localeId, @@ -466,20 +468,13 @@ export class VideoWatchComponent implements OnInit, OnDestroy { } private setRating (nextRating: UserVideoRateType) { - let method - switch (nextRating) { - case 'like': - method = this.videoService.setVideoLike - break - case 'dislike': - method = this.videoService.setVideoDislike - break - case 'none': - method = this.videoService.unsetVideoLike - break + const ratingMethods: { [id in UserVideoRateType]: (id: number) => Observable } = { + like: this.videoService.setVideoLike, + dislike: this.videoService.setVideoDislike, + none: this.videoService.unsetVideoLike } - method.call(this.videoService, this.video.id) + ratingMethods[nextRating].call(this.videoService, this.video.id) .subscribe( () => { // Update the video like attribute @@ -545,25 +540,29 @@ export class VideoWatchComponent implements OnInit, OnDestroy { private flushPlayer () { // Remove player if it exists if (this.player) { - this.player.dispose() - this.player = undefined + try { + this.player.dispose() + this.player = undefined + } catch (err) { + console.error('Cannot dispose player.', err) + } } } private initHotkeys () { this.hotkeys = [ - new Hotkey('shift+l', (event: KeyboardEvent): boolean => { + new Hotkey('shift+l', () => { this.setLike() return false }, undefined, this.i18n('Like the video')), - new Hotkey('shift+d', (event: KeyboardEvent): boolean => { + + new Hotkey('shift+d', () => { this.setDislike() return false }, undefined, this.i18n('Dislike the video')), - new Hotkey('shift+s', (event: KeyboardEvent): boolean => { - this.subscribeButton.subscribed ? - this.subscribeButton.unsubscribe() : - this.subscribeButton.subscribe() + + new Hotkey('shift+s', () => { + this.subscribeButton.subscribed ? this.subscribeButton.unsubscribe() : this.subscribeButton.subscribe() return false }, undefined, this.i18n('Subscribe to the account')) ] diff --git a/client/src/app/videos/video-list/video-local.component.ts b/client/src/app/videos/video-list/video-local.component.ts index 13d4023c2..65543343c 100644 --- a/client/src/app/videos/video-list/video-local.component.ts +++ b/client/src/app/videos/video-list/video-local.component.ts @@ -22,13 +22,13 @@ export class VideoLocalComponent extends AbstractVideoList implements OnInit, On filter: VideoFilter = 'local' constructor ( + protected i18n: I18n, protected router: Router, protected serverService: ServerService, protected route: ActivatedRoute, protected notifier: Notifier, protected authService: AuthService, protected screenService: ScreenService, - private i18n: I18n, private videoService: VideoService ) { super() diff --git a/client/src/app/videos/video-list/video-overview.component.html b/client/src/app/videos/video-list/video-overview.component.html index b644dd798..f59de584a 100644 --- a/client/src/app/videos/video-list/video-overview.component.html +++ b/client/src/app/videos/video-list/video-overview.component.html @@ -3,7 +3,7 @@
No results.
-
-
+ @@ -19,7 +19,7 @@
-
+
Avatar diff --git a/client/src/app/videos/video-list/video-overview.component.scss b/client/src/app/videos/video-list/video-overview.component.scss index a24766783..ade6f53b7 100644 --- a/client/src/app/videos/video-list/video-overview.component.scss +++ b/client/src/app/videos/video-list/video-overview.component.scss @@ -2,62 +2,10 @@ @import '_mixins'; @import '_miniature'; -.section { - max-height: 500px; // 2 rows max - overflow: hidden; - padding-top: 10px; - - &:first-child { - padding-top: 30px; - } - - my-video-miniature { - text-align: left; - } -} - -.section-title { - font-size: 24px; - font-weight: $font-semibold; - margin-bottom: 10px; - - a { - &:hover, &:focus:not(.focus-visible), &:active { - text-decoration: none; - outline: none; - } - - color: var(--mainForegroundColor); - } +.margin-content { + @include adapt-margin-content-width; } -.channel { - .section-title a { - display: flex; - width: fit-content; - align-items: center; - - img { - @include avatar(28px); - - margin-right: 8px; - } - } -} - -@media screen and (max-width: 500px) { - .margin-content { - margin: 0 !important; - } - - .section-title { - font-size: 17px; - } - - .section { - max-height: initial; - overflow: initial; - - @include video-miniature-small-screen; - } +.section { + @include miniature-rows; } diff --git a/client/src/app/videos/video-list/video-recently-added.component.ts b/client/src/app/videos/video-list/video-recently-added.component.ts index 80cef813e..f54bade98 100644 --- a/client/src/app/videos/video-list/video-recently-added.component.ts +++ b/client/src/app/videos/video-list/video-recently-added.component.ts @@ -17,15 +17,16 @@ import { Notifier, ServerService } from '@app/core' export class VideoRecentlyAddedComponent extends AbstractVideoList implements OnInit, OnDestroy { titlePage: string sort: VideoSortField = '-publishedAt' + groupByDate = true constructor ( + protected i18n: I18n, protected route: ActivatedRoute, protected serverService: ServerService, protected router: Router, protected notifier: Notifier, protected authService: AuthService, protected screenService: ScreenService, - private i18n: I18n, private videoService: VideoService ) { super() diff --git a/client/src/app/videos/video-list/video-trending.component.ts b/client/src/app/videos/video-list/video-trending.component.ts index e2ad95bc4..a2c819ebe 100644 --- a/client/src/app/videos/video-list/video-trending.component.ts +++ b/client/src/app/videos/video-list/video-trending.component.ts @@ -19,13 +19,13 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit, defaultSort: VideoSortField = '-trending' constructor ( + protected i18n: I18n, protected router: Router, protected serverService: ServerService, protected route: ActivatedRoute, protected notifier: Notifier, protected authService: AuthService, protected screenService: ScreenService, - private i18n: I18n, private videoService: VideoService ) { super() diff --git a/client/src/app/videos/video-list/video-user-subscriptions.component.ts b/client/src/app/videos/video-list/video-user-subscriptions.component.ts index 2f0685ccc..3caa371d8 100644 --- a/client/src/app/videos/video-list/video-user-subscriptions.component.ts +++ b/client/src/app/videos/video-list/video-user-subscriptions.component.ts @@ -19,15 +19,16 @@ export class VideoUserSubscriptionsComponent extends AbstractVideoList implement titlePage: string sort = '-publishedAt' as VideoSortField ownerDisplayType: OwnerDisplayType = 'auto' + groupByDate = true constructor ( + protected i18n: I18n, protected router: Router, protected serverService: ServerService, protected route: ActivatedRoute, protected notifier: Notifier, protected authService: AuthService, protected screenService: ScreenService, - private i18n: I18n, private videoService: VideoService ) { super() 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 { videojs(options.common.playerElement, videojsOptions, function (this: any) { const player = this - player.tech_.one('error', () => self.maybeFallbackToWebTorrent(mode, player, options)) - player.one('error', () => self.maybeFallbackToWebTorrent(mode, player, options)) + let alreadyFallback = false + + player.tech_.one('error', () => { + if (!alreadyFallback) self.maybeFallbackToWebTorrent(mode, player, options) + alreadyFallback = true + }) + + player.one('error', () => { + if (!alreadyFallback) self.maybeFallbackToWebTorrent(mode, player, options) + alreadyFallback = true + }) self.addContextMenu(mode, player, options.common.embedUrl) diff --git a/client/src/sass/include/_miniature.scss b/client/src/sass/include/_miniature.scss index b62187fd2..0c2ee2d0d 100644 --- a/client/src/sass/include/_miniature.scss +++ b/client/src/sass/include/_miniature.scss @@ -138,3 +138,100 @@ $play-overlay-width: 18px; } } } + +@mixin miniature-rows { + max-height: 540px; // 2 rows max + overflow: hidden; + padding-top: 10px; + + &:first-child { + padding-top: 30px; + } + + my-video-miniature { + text-align: left; + } + + .section-title { + font-size: 24px; + font-weight: $font-semibold; + margin-bottom: 30px; + display: flex; + justify-content: space-between; + + a { + &:hover, &:focus:not(.focus-visible), &:active { + text-decoration: none; + outline: none; + } + + color: var(--mainForegroundColor); + } + } + + &.channel { + .section-title { + a { + display: flex; + width: fit-content; + align-items: center; + + img { + @include avatar(28px); + + margin-right: 8px; + } + } + + .followers { + color: $grey-foreground-color; + font-weight: normal; + font-size: 14px; + margin-left: 10px; + position: relative; + top: 2px; + } + } + } + + @media screen and (max-width: $mobile-view) { + max-height: initial; + overflow: initial; + + @include video-miniature-small-screen; + + .section-title { + font-size: 17px; + } + } +} + +@mixin adapt-margin-content-width { + width: $video-miniature-width * 6; + margin: auto !important; + + @media screen and (max-width: 1800px) { + width: $video-miniature-width * 5; + } + + @media screen and (max-width: 1800px - $video-miniature-width) { + width: $video-miniature-width * 4; + } + + @media screen and (max-width: 1800px - (2* $video-miniature-width)) { + width: $video-miniature-width * 3; + } + + @media screen and (max-width: 1800px - (3* $video-miniature-width)) { + width: $video-miniature-width * 2; + } + + @media screen and (max-width: 500px) { + width: auto; + margin: 0 !important; + + .videos { + @include video-miniature-small-screen; + } + } +} diff --git a/client/src/sass/include/_mixins.scss b/client/src/sass/include/_mixins.scss index 262a8136f..d4a2269a1 100644 --- a/client/src/sass/include/_mixins.scss +++ b/client/src/sass/include/_mixins.scss @@ -331,7 +331,12 @@ } @mixin peertube-checkbox ($border-width) { - display: none; + opacity: 0; + position: absolute; + + &:focus + span { + outline: 1px solid #1e5180; + } & + span { position: relative; diff --git a/config/default.yaml b/config/default.yaml index 37ef4366f..e4e2d2273 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -53,6 +53,12 @@ smtp: ca_file: null # Used for self signed certificates from_address: 'admin@example.com' +email: + body: + signature: "PeerTube" + object: + prefix: "[PeerTube]" + # From the project root directory storage: tmp: 'storage/tmp/' # Used to download data (imports etc), store uploaded files before processing... @@ -174,6 +180,8 @@ transcoding: enabled: true # Allow your users to upload .mkv, .mov, .avi, .flv videos allow_additional_extensions: true + # If a user uploads an audio file, PeerTube will create a video by merging the preview file and the audio file + allow_audio_files: true threads: 1 resolutions: # Only created if the original video has a higher resolution, uses more storage! 240p: false @@ -181,6 +189,7 @@ transcoding: 480p: false 720p: false 1080p: false + 2160p: false # /!\ EXPERIMENTAL /!\ # /!\ Requires ffmpeg >= 4 # Generate HLS playlists and fragmented MP4 files. Better playback than with WebTorrent: diff --git a/config/production.yaml.example b/config/production.yaml.example index f84e15670..c025426bb 100644 --- a/config/production.yaml.example +++ b/config/production.yaml.example @@ -188,6 +188,8 @@ transcoding: enabled: true # Allow your users to upload .mkv, .mov, .avi, .flv videos allow_additional_extensions: true + # If a user uploads an audio file, PeerTube will create a video by merging the preview file and the audio file + allow_audio_files: true threads: 1 resolutions: # Only created if the original video has a higher resolution, uses more storage! 240p: false @@ -195,6 +197,7 @@ transcoding: 480p: false 720p: false 1080p: false + 2160p: false # /!\ EXPERIMENTAL /!\ # /!\ Requires ffmpeg >= 4 # Generate HLS playlists and fragmented MP4 files. Better playback than with WebTorrent: 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: transcoding: enabled: true allow_additional_extensions: true + allow_audio_files: true diff --git a/config/test.yaml b/config/test.yaml index 682530840..8d3921614 100644 --- a/config/test.yaml +++ b/config/test.yaml @@ -55,6 +55,7 @@ signup: transcoding: enabled: true allow_additional_extensions: false + allow_audio_files: false threads: 2 resolutions: 240p: true @@ -62,6 +63,7 @@ transcoding: 480p: true 720p: true 1080p: true + 2160p: true hls: enabled: true diff --git a/package.json b/package.json index bea949796..6597b055e 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "scripts": { "e2e": "scripty", "e2e:local": "scripty", + "setup:cli": "scripty", "build": "SCRIPTY_PARALLEL=true scripty", "build:server": "scripty", "build:client": "scripty", @@ -92,10 +93,9 @@ }, "dependencies": { "apicache": "^1.4.0", - "application-config": "^1.0.1", - "async": "^2.0.0", + "async": "^3.0.1", "async-lru": "^1.1.1", - "bcrypt": "3.0.5", + "bcrypt": "3.0.6", "bittorrent-tracker": "^9.0.0", "bluebird": "^3.5.0", "body-parser": "^1.12.4", @@ -103,7 +103,6 @@ "bytes": "^3.0.0", "cli-table": "^0.3.1", "commander": "^2.13.0", - "concurrently": "^4.0.1", "config": "^3.0.0", "cookie-parser": "^1.4.3", "cors": "^2.8.1", @@ -111,14 +110,14 @@ "deep-object-diff": "^1.1.0", "express": "^4.12.4", "express-oauth-server": "^2.0.0", - "express-rate-limit": "^3.1.0", + "express-rate-limit": "^4.0.4", "express-validator": "^5.0.0", "flat": "^4.1.0", "fluent-ffmpeg": "^2.1.0", - "fs-extra": "^7.0.0", + "fs-extra": "^8.0.1", "helmet": "^3.12.1", "http-signature": "^1.2.0", - "ip-anonymize": "^0.0.6", + "ip-anonymize": "^0.1.0", "ipaddr.js": "1.9.0", "is-cidr": "^3.0.0", "iso-639-3": "^1.0.1", @@ -142,25 +141,24 @@ "reflect-metadata": "^0.1.12", "request": "^2.81.0", "scripty": "^1.5.0", - "sequelize": "5.7.4", + "sequelize": "5.8.7", "sequelize-typescript": "1.0.0-beta.2", "sharp": "^0.22.0", "sitemap": "^2.1.0", "socket.io": "^2.2.0", "srt-to-vtt": "^1.1.2", - "summon-install": "^0.4.3", "useragent": "^2.3.0", "uuid": "^3.1.0", - "validator": "^10.2.0", + "validator": "^11.0.0", "webfinger.js": "^2.6.6", "webtorrent": "^0.103.0", "winston": "3.2.1", - "ws": "^6.0.0", - "youtube-dl": "^1.12.2" + "ws": "^7.0.0", + "youtube-dl": "^2.0.0" }, "devDependencies": { "@types/apicache": "^1.2.0", - "@types/async": "^2.0.40", + "@types/async": "^3.0.0", "@types/async-lock": "^1.1.0", "@types/bcrypt": "^3.0.0", "@types/bluebird": "3.5.21", @@ -174,7 +172,7 @@ "@types/express": "^4.0.35", "@types/express-rate-limit": "^3.3.0", "@types/fluent-ffmpeg": "^2.1.8", - "@types/fs-extra": "^5.0.4", + "@types/fs-extra": "^7.0.0", "@types/libxmljs": "^0.18.0", "@types/lodash": "^4.14.64", "@types/magnet-uri": "^5.1.1", @@ -185,7 +183,7 @@ "@types/morgan": "^1.7.32", "@types/multer": "^1.3.3", "@types/node": "^10.0.8", - "@types/nodemailer": "^4.3.1", + "@types/nodemailer": "^6.2.0", "@types/oauth2-server": "^3.0.8", "@types/pem": "^1.9.3", "@types/redis": "^2.8.5", @@ -199,18 +197,20 @@ "chai": "^4.1.1", "chai-json-schema": "^1.5.0", "chai-xml": "^0.3.2", - "husky": "^1.0.0-rc.4", + "concurrently": "^4.1.0", + "husky": "^2.4.0", "libxmljs": "0.19.5", "lint-staged": "^8.0.4", "maildev": "^1.0.0-rc3", - "marked-man": "^0.4.2", + "marked-man": "^0.6.0", "mocha": "^6.0.0", + "mocha-parallel-tests": "^2.1.0", "nodemon": "^1.18.6", "sass-lint": "^1.12.1", "source-map-support": "^0.5.0", "supertest": "^4.0.2", "swagger-cli": "^2.2.0", - "ts-node": "8.0.3", + "ts-node": "8.2.0", "tslint": "^5.7.0", "tslint-config-standard": "^8.0.1", "typescript": "^3.4.3", @@ -219,8 +219,5 @@ "scripty": { "silent": true }, - "summon": { - "silent": true - }, "sasslintConfig": "client/.sass-lint.yml" } diff --git a/scripts/clean/server/test.sh b/scripts/clean/server/test.sh index 5694ac922..34afd6a9d 100755 --- a/scripts/clean/server/test.sh +++ b/scripts/clean/server/test.sh @@ -17,8 +17,11 @@ removeFiles () { } dropRedis () { - redis-cli KEYS "bull-localhost:900$1*" | grep -v empty | xargs --no-run-if-empty redis-cli DEL - redis-cli KEYS "redis-localhost:900$1*" | grep -v empty | xargs --no-run-if-empty redis-cli DEL + port=$((9000+$1)) + + redis-cli KEYS "bull-localhost:$port*" | grep -v empty | xargs --no-run-if-empty redis-cli DEL + redis-cli KEYS "redis-localhost:$port*" | grep -v empty | xargs --no-run-if-empty redis-cli DEL + redis-cli KEYS "*redis-localhost:$port-" | grep -v empty | xargs --no-run-if-empty redis-cli DEL } seq=$(seq 1 6) 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' import { VideoModel } from '../server/models/video/video' import { initDatabaseModels } from '../server/initializers' import { JobQueue } from '../server/lib/job-queue' +import { VideoTranscodingPayload } from '../server/lib/job-queue/handlers/video-transcoding' program .option('-v, --video [videoUUID]', 'Video UUID') @@ -31,15 +32,9 @@ async function run () { const video = await VideoModel.loadByUUIDWithFile(program['video']) if (!video) throw new Error('Video not found.') - const dataInput = { - videoUUID: video.uuid, - isNewVideo: false, - resolution: undefined - } - - if (program.resolution !== undefined) { - dataInput.resolution = program.resolution - } + const dataInput: VideoTranscodingPayload = program.resolution !== undefined + ? { type: 'new-resolution' as 'new-resolution', videoUUID: video.uuid, isNewVideo: false, resolution: program.resolution } + : { type: 'optimize' as 'optimize', videoUUID: video.uuid, isNewVideo: false } await JobQueue.Instance.init() await JobQueue.Instance.createJob({ type: 'video-transcoding', payload: dataInput }) diff --git a/scripts/setup/cli.sh b/scripts/setup/cli.sh new file mode 100755 index 000000000..9808868d3 --- /dev/null +++ b/scripts/setup/cli.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +set -eu + +NOCLIENT=1 yarn install --pure-lockfile + +rm -rf ./dist/server/tools/ + +( + cd ./server/tools + yarn install --pure-lockfile +) + +npm run tsc -- --build ./server/tools/tsconfig.json + +mv "./server/tools/node_modules" "./dist/server/tools" diff --git a/scripts/test.sh b/scripts/test.sh index cf8895365..5ec7a5920 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -3,6 +3,8 @@ set -eu npm run build:server +npm run setup:cli + npm run travis -- lint mocha --exit --require ts-node/register/type-check --bail server/tests/index.ts diff --git a/scripts/travis.sh b/scripts/travis.sh index 3557816c8..664d9fd6c 100755 --- a/scripts/travis.sh +++ b/scripts/travis.sh @@ -17,19 +17,20 @@ if [ "$1" = "misc" ]; then server/tests/helpers/index.ts elif [ "$1" = "cli" ]; then npm run build:server + CC=gcc-4.9 CXX=g++-4.9 npm run setup:cli mocha --timeout 5000 --exit --require ts-node/register --bail server/tests/cli/index.ts elif [ "$1" = "api-1" ]; then npm run build:server - mocha --timeout 5000 --exit --require ts-node/register --bail server/tests/api/index-1.ts + sh ./server/tests/api/travis-1.sh 2 elif [ "$1" = "api-2" ]; then npm run build:server - mocha --timeout 5000 --exit --require ts-node/register --bail server/tests/api/index-2.ts + sh ./server/tests/api/travis-2.sh 2 elif [ "$1" = "api-3" ]; then npm run build:server - mocha --timeout 5000 --exit --require ts-node/register --bail server/tests/api/index-3.ts + sh ./server/tests/api/travis-3.sh 2 elif [ "$1" = "api-4" ]; then npm run build:server - mocha --timeout 5000 --exit --require ts-node/register --bail server/tests/api/index-4.ts + sh ./server/tests/api/travis-4.sh 2 elif [ "$1" = "lint" ]; then npm run tslint -- --project ./tsconfig.json -c ./tslint.json server.ts "server/**/*.ts" "shared/**/*.ts" diff --git a/server/assets/default-audio-background.jpg b/server/assets/default-audio-background.jpg new file mode 100644 index 000000000..a19173eac Binary files /dev/null and b/server/assets/default-audio-background.jpg differ diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts index 8d4db1e75..5a1d652f2 100644 --- a/server/controllers/api/accounts.ts +++ b/server/controllers/api/accounts.ts @@ -16,7 +16,8 @@ import { accountNameWithHostGetValidator, accountsSortValidator, ensureAuthUserOwnsAccountValidator, - videosSortValidator + videosSortValidator, + videoChannelsSortValidator } from '../../middlewares/validators' import { AccountModel } from '../../models/account/account' import { AccountVideoRateModel } from '../../models/account/account-video-rate' @@ -56,6 +57,10 @@ accountsRouter.get('/:accountName/videos', accountsRouter.get('/:accountName/video-channels', asyncMiddleware(accountNameWithHostGetValidator), + paginationValidator, + videoChannelsSortValidator, + setDefaultSort, + setDefaultPagination, asyncMiddleware(listAccountChannels) ) @@ -108,7 +113,14 @@ async function listAccounts (req: express.Request, res: express.Response) { } async function listAccountChannels (req: express.Request, res: express.Response) { - const resultList = await VideoChannelModel.listByAccount(res.locals.account.id) + const options = { + accountId: res.locals.account.id, + start: req.query.start, + count: req.query.count, + sort: req.query.sort + } + + const resultList = await VideoChannelModel.listByAccount(options) return res.json(getFormattedObjects(resultList.data, resultList.total)) } diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index 40012c03b..27c416a12 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts @@ -51,7 +51,7 @@ async function getConfig (req: express.Request, res: express.Response) { if (serverCommit === undefined) serverCommit = await getServerCommit() const enabledResolutions = Object.keys(CONFIG.TRANSCODING.RESOLUTIONS) - .filter(key => CONFIG.TRANSCODING.ENABLED === CONFIG.TRANSCODING.RESOLUTIONS[key] === true) + .filter(key => CONFIG.TRANSCODING.ENABLED && CONFIG.TRANSCODING.RESOLUTIONS[key] === true) .map(r => parseInt(r, 10)) const json: ServerConfig = { @@ -255,6 +255,7 @@ function customConfig (): CustomConfig { transcoding: { enabled: CONFIG.TRANSCODING.ENABLED, allowAdditionalExtensions: CONFIG.TRANSCODING.ALLOW_ADDITIONAL_EXTENSIONS, + allowAudioFiles: CONFIG.TRANSCODING.ALLOW_AUDIO_FILES, threads: CONFIG.TRANSCODING.THREADS, resolutions: { '240p': CONFIG.TRANSCODING.RESOLUTIONS[ '240p' ], diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index 0aafba66e..48a6c63b8 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts @@ -46,14 +46,18 @@ import { mySubscriptionsRouter } from './my-subscriptions' import { CONFIG } from '../../../initializers/config' import { sequelizeTypescript } from '../../../initializers/database' import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' +import { UserRegister } from '../../../../shared/models/users/user-register.model' const auditLogger = auditLoggerFactory('users') -const loginRateLimiter = new RateLimit({ +// FIXME: https://github.com/nfriedly/express-rate-limit/issues/138 +// @ts-ignore +const loginRateLimiter = RateLimit({ windowMs: RATES_LIMIT.LOGIN.WINDOW_MS, max: RATES_LIMIT.LOGIN.MAX }) +// @ts-ignore const askSendEmailLimiter = new RateLimit({ windowMs: RATES_LIMIT.ASK_SEND_EMAIL.WINDOW_MS, max: RATES_LIMIT.ASK_SEND_EMAIL.MAX @@ -189,15 +193,14 @@ async function createUser (req: express.Request, res: express.Response) { user: { id: user.id, account: { - id: account.id, - uuid: account.Actor.uuid + id: account.id } } }).end() } async function registerUser (req: express.Request, res: express.Response) { - const body: UserCreate = req.body + const body: UserRegister = req.body const userToCreate = new UserModel({ username: body.username, @@ -211,7 +214,7 @@ async function registerUser (req: express.Request, res: express.Response) { emailVerified: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION ? false : null }) - const { user } = await createUserAccountAndChannelAndPlaylist(userToCreate) + const { user } = await createUserAccountAndChannelAndPlaylist(userToCreate, body.channel) auditLogger.create(body.username, new UserAuditView(user.toFormattedJSON())) logger.info('User %s with its channel and account registered.', body.username) diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index 3d6dbfe70..81a03a62b 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts @@ -19,7 +19,7 @@ import { VideoChannelModel } from '../../models/video/video-channel' import { videoChannelsNameWithHostValidator, videosSortValidator } from '../../middlewares/validators' import { sendUpdateActor } from '../../lib/activitypub/send' import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared' -import { createVideoChannel } from '../../lib/video-channel' +import { createVideoChannel, federateAllVideosOfChannel } from '../../lib/video-channel' import { buildNSFWFilter, createReqFiles, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' import { setAsyncActorKeys } from '../../lib/activitypub' import { AccountModel } from '../../models/account/account' @@ -143,15 +143,14 @@ async function addVideoChannel (req: express.Request, res: express.Response) { }) setAsyncActorKeys(videoChannelCreated.Actor) - .catch(err => logger.error('Cannot set async actor keys for account %s.', videoChannelCreated.Actor.uuid, { err })) + .catch(err => logger.error('Cannot set async actor keys for account %s.', videoChannelCreated.Actor.url, { err })) auditLogger.create(getAuditIdFromRes(res), new VideoChannelAuditView(videoChannelCreated.toFormattedJSON())) - logger.info('Video channel with uuid %s created.', videoChannelCreated.Actor.uuid) + logger.info('Video channel %s created.', videoChannelCreated.Actor.url) return res.json({ videoChannel: { - id: videoChannelCreated.id, - uuid: videoChannelCreated.Actor.uuid + id: videoChannelCreated.id } }).end() } @@ -161,6 +160,7 @@ async function updateVideoChannel (req: express.Request, res: express.Response) const videoChannelFieldsSave = videoChannelInstance.toJSON() const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannelInstance.toFormattedJSON()) const videoChannelInfoToUpdate = req.body as VideoChannelUpdate + let doBulkVideoUpdate = false try { await sequelizeTypescript.transaction(async t => { @@ -168,9 +168,18 @@ async function updateVideoChannel (req: express.Request, res: express.Response) transaction: t } - if (videoChannelInfoToUpdate.displayName !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.displayName) - if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description) - if (videoChannelInfoToUpdate.support !== undefined) videoChannelInstance.set('support', videoChannelInfoToUpdate.support) + if (videoChannelInfoToUpdate.displayName !== undefined) videoChannelInstance.name = videoChannelInfoToUpdate.displayName + if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.description = videoChannelInfoToUpdate.description + + if (videoChannelInfoToUpdate.support !== undefined) { + const oldSupportField = videoChannelInstance.support + videoChannelInstance.support = videoChannelInfoToUpdate.support + + if (videoChannelInfoToUpdate.bulkVideosSupportUpdate === true && oldSupportField !== videoChannelInfoToUpdate.support) { + doBulkVideoUpdate = true + await VideoModel.bulkUpdateSupportField(videoChannelInstance, t) + } + } const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions) await sendUpdateActor(videoChannelInstanceUpdated, t) @@ -180,7 +189,8 @@ async function updateVideoChannel (req: express.Request, res: express.Response) new VideoChannelAuditView(videoChannelInstanceUpdated.toFormattedJSON()), oldVideoChannelAuditKeys ) - logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.Actor.uuid) + + logger.info('Video channel %s updated.', videoChannelInstance.Actor.url) }) } catch (err) { logger.debug('Cannot update the video channel.', { err }) @@ -193,7 +203,12 @@ async function updateVideoChannel (req: express.Request, res: express.Response) throw err } - return res.type('json').status(204).end() + res.type('json').status(204).end() + + // Don't process in a transaction, and after the response because it could be long + if (doBulkVideoUpdate) { + await federateAllVideosOfChannel(videoChannelInstance) + } } async function removeVideoChannel (req: express.Request, res: express.Response) { @@ -205,7 +220,7 @@ async function removeVideoChannel (req: express.Request, res: express.Response) await videoChannelInstance.destroy({ transaction: t }) auditLogger.delete(getAuditIdFromRes(res), new VideoChannelAuditView(videoChannelInstance.toFormattedJSON())) - logger.info('Video channel with name %s and uuid %s deleted.', videoChannelInstance.name, videoChannelInstance.Actor.uuid) + logger.info('Video channel %s deleted.', videoChannelInstance.Actor.url) }) return res.type('json').status(204).end() diff --git a/server/controllers/api/video-playlist.ts b/server/controllers/api/video-playlist.ts index a17136401..62490e63b 100644 --- a/server/controllers/api/video-playlist.ts +++ b/server/controllers/api/video-playlist.ts @@ -203,7 +203,9 @@ async function updateVideoPlaylist (req: express.Request, res: express.Response) const videoPlaylistInstance = res.locals.videoPlaylist const videoPlaylistFieldsSave = videoPlaylistInstance.toJSON() const videoPlaylistInfoToUpdate = req.body as VideoPlaylistUpdate + const wasPrivatePlaylist = videoPlaylistInstance.privacy === VideoPlaylistPrivacy.PRIVATE + const wasNotPrivatePlaylist = videoPlaylistInstance.privacy !== VideoPlaylistPrivacy.PRIVATE const thumbnailField = req.files['thumbnailfile'] const thumbnailModel = thumbnailField @@ -232,6 +234,10 @@ async function updateVideoPlaylist (req: express.Request, res: express.Response) if (videoPlaylistInfoToUpdate.privacy !== undefined) { videoPlaylistInstance.privacy = parseInt(videoPlaylistInfoToUpdate.privacy.toString(), 10) + + if (wasNotPrivatePlaylist === true && videoPlaylistInstance.privacy === VideoPlaylistPrivacy.PRIVATE) { + await sendDeleteVideoPlaylist(videoPlaylistInstance, t) + } } const playlistUpdated = await videoPlaylistInstance.save(sequelizeOptions) diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 1a18a8ae8..5ebd8fbc4 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -6,7 +6,14 @@ import { logger } from '../../../helpers/logger' import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' import { getFormattedObjects, getServerActor } from '../../../helpers/utils' import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' -import { MIMETYPES, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../initializers/constants' +import { + DEFAULT_AUDIO_RESOLUTION, + MIMETYPES, + VIDEO_CATEGORIES, + VIDEO_LANGUAGES, + VIDEO_LICENCES, + VIDEO_PRIVACIES +} from '../../../initializers/constants' import { changeVideoChannelShare, federateVideoIfNeeded, @@ -54,6 +61,7 @@ import { CONFIG } from '../../../initializers/config' import { sequelizeTypescript } from '../../../initializers/database' import { createVideoMiniatureFromExisting, generateVideoMiniature } from '../../../lib/thumbnail' import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' +import { VideoTranscodingPayload } from '../../../lib/job-queue/handlers/video-transcoding' const auditLogger = auditLoggerFactory('videos') const videosRouter = express.Router() @@ -191,17 +199,17 @@ async function addVideo (req: express.Request, res: express.Response) { const video = new VideoModel(videoData) video.url = getVideoActivityPubUrl(video) // We use the UUID, so set the URL after building the object - // Build the file object - const { videoFileResolution } = await getVideoFileResolution(videoPhysicalFile.path) - const fps = await getVideoFileFPS(videoPhysicalFile.path) - - const videoFileData = { + const videoFile = new VideoFileModel({ extname: extname(videoPhysicalFile.filename), - resolution: videoFileResolution, - size: videoPhysicalFile.size, - fps + size: videoPhysicalFile.size + }) + + if (videoFile.isAudio()) { + videoFile.resolution = DEFAULT_AUDIO_RESOLUTION + } else { + videoFile.fps = await getVideoFileFPS(videoPhysicalFile.path) + videoFile.resolution = (await getVideoFileResolution(videoPhysicalFile.path)).videoFileResolution } - const videoFile = new VideoFileModel(videoFileData) // Move physical file const videoDir = CONFIG.STORAGE.VIDEOS_DIR @@ -279,9 +287,21 @@ async function addVideo (req: express.Request, res: express.Response) { if (video.state === VideoState.TO_TRANSCODE) { // Put uuid because we don't have id auto incremented for now - const dataInput = { - videoUUID: videoCreated.uuid, - isNewVideo: true + let dataInput: VideoTranscodingPayload + + if (videoFile.isAudio()) { + dataInput = { + type: 'merge-audio' as 'merge-audio', + resolution: DEFAULT_AUDIO_RESOLUTION, + videoUUID: videoCreated.uuid, + isNewVideo: true + } + } else { + dataInput = { + type: 'optimize' as 'optimize', + videoUUID: videoCreated.uuid, + isNewVideo: true + } } await JobQueue.Instance.createJob({ type: 'video-transcoding', payload: dataInput }) @@ -300,7 +320,9 @@ async function updateVideo (req: express.Request, res: express.Response) { const videoFieldsSave = videoInstance.toJSON() const oldVideoAuditView = new VideoAuditView(videoInstance.toFormattedDetailsJSON()) const videoInfoToUpdate: VideoUpdate = req.body + const wasPrivateVideo = videoInstance.privacy === VideoPrivacy.PRIVATE + const wasNotPrivateVideo = videoInstance.privacy !== VideoPrivacy.PRIVATE const wasUnlistedVideo = videoInstance.privacy === VideoPrivacy.UNLISTED // Process thumbnail or create it from the video @@ -336,9 +358,15 @@ async function updateVideo (req: express.Request, res: express.Response) { const newPrivacy = parseInt(videoInfoToUpdate.privacy.toString(), 10) videoInstance.privacy = newPrivacy + // The video was private, and is not anymore -> publish it if (wasPrivateVideo === true && newPrivacy !== VideoPrivacy.PRIVATE) { videoInstance.publishedAt = new Date() } + + // The video was not private, but now it is -> we need to unfederate it + if (wasNotPrivateVideo === true && newPrivacy === VideoPrivacy.PRIVATE) { + await VideoModel.sendDelete(videoInstance, { transaction: t }) + } } const videoInstanceUpdated = await videoInstance.save(sequelizeOptions) diff --git a/server/controllers/static.ts b/server/controllers/static.ts index fb2e7742a..a6b462443 100644 --- a/server/controllers/static.ts +++ b/server/controllers/static.ts @@ -194,7 +194,7 @@ async function getVideoCaption (req: express.Request, res: express.Response) { return res.sendFile(result.path, { maxAge: STATIC_MAX_AGE }) } -async function generateNodeinfo (req: express.Request, res: express.Response, next: express.NextFunction) { +async function generateNodeinfo (req: express.Request, res: express.Response) { const { totalVideos } = await VideoModel.getStats() const { totalLocalVideoComments } = await VideoCommentModel.getStats() const { totalUsers } = await UserModel.getStats() diff --git a/server/helpers/custom-validators/accounts.ts b/server/helpers/custom-validators/accounts.ts index 146c7708e..31a2de5ca 100644 --- a/server/helpers/custom-validators/accounts.ts +++ b/server/helpers/custom-validators/accounts.ts @@ -1,7 +1,6 @@ import * as Bluebird from 'bluebird' import { Response } from 'express' import 'express-validator' -import * as validator from 'validator' import { AccountModel } from '../../models/account/account' import { isUserDescriptionValid, isUserUsernameValid } from './users' import { exists } from './misc' @@ -18,14 +17,8 @@ function isAccountDescriptionValid (value: string) { return isUserDescriptionValid(value) } -function doesAccountIdExist (id: number | string, res: Response, sendNotFound = true) { - let promise: Bluebird - - if (validator.isInt('' + id)) { - promise = AccountModel.load(+id) - } else { // UUID - promise = AccountModel.loadByUUID('' + id) - } +function doesAccountIdExist (id: number, res: Response, sendNotFound = true) { + const promise = AccountModel.load(id) return doesAccountExist(promise, res, sendNotFound) } diff --git a/server/helpers/custom-validators/activitypub/video-comments.ts b/server/helpers/custom-validators/activitypub/video-comments.ts index 26c8c4cc6..e04c5388f 100644 --- a/server/helpers/custom-validators/activitypub/video-comments.ts +++ b/server/helpers/custom-validators/activitypub/video-comments.ts @@ -36,7 +36,8 @@ function normalizeComment (comment: any) { if (!comment) return if (typeof comment.url !== 'string') { - comment.url = comment.url.href || comment.url.url + if (typeof comment.url === 'object') comment.url = comment.url.href || comment.url.url + else comment.url = comment.id } return diff --git a/server/helpers/custom-validators/video-channels.ts b/server/helpers/custom-validators/video-channels.ts index fd56b9a70..f818ce8f1 100644 --- a/server/helpers/custom-validators/video-channels.ts +++ b/server/helpers/custom-validators/video-channels.ts @@ -26,13 +26,8 @@ async function doesLocalVideoChannelNameExist (name: string, res: express.Respon return processVideoChannelExist(videoChannel, res) } -async function doesVideoChannelIdExist (id: number | string, res: express.Response) { - let videoChannel: VideoChannelModel - if (validator.isInt('' + id)) { - videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id) - } else { // UUID - videoChannel = await VideoChannelModel.loadByUUIDAndPopulateAccount('' + id) - } +async function doesVideoChannelIdExist (id: number, res: express.Response) { + const videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id) return processVideoChannelExist(videoChannel, res) } 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 ( }, filename: async (req, file, cb) => { - const extension = mimeTypes[ file.mimetype ] || extname(file.originalname) + let extension: string + const fileExtension = extname(file.originalname) + const extensionFromMimetype = mimeTypes[ file.mimetype ] + + // Take the file extension if we don't understand the mime type + // We have the OGG/OGV exception too because firefox sends a bad mime type when sending an OGG file + if (fileExtension === '.ogg' || fileExtension === '.ogv' || !extensionFromMimetype) { + extension = fileExtension + } else { + extension = extensionFromMimetype + } + let randomString = '' try { diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts index 76b744de8..8041e7b3b 100644 --- a/server/helpers/ffmpeg-utils.ts +++ b/server/helpers/ffmpeg-utils.ts @@ -1,6 +1,6 @@ import * as ffmpeg from 'fluent-ffmpeg' import { dirname, join } from 'path' -import { getTargetBitrate, VideoResolution } from '../../shared/models/videos' +import { getTargetBitrate, getMaxBitrate, VideoResolution } from '../../shared/models/videos' import { FFMPEG_NICE, VIDEO_TRANSCODING_FPS } from '../initializers/constants' import { processImage } from './image-utils' import { logger } from './logger' @@ -18,7 +18,8 @@ function computeResolutionsToTranscode (videoFileHeight: number) { VideoResolution.H_360P, VideoResolution.H_720P, VideoResolution.H_240P, - VideoResolution.H_1080P + VideoResolution.H_1080P, + VideoResolution.H_4K ] for (const resolution of resolutions) { @@ -31,7 +32,7 @@ function computeResolutionsToTranscode (videoFileHeight: number) { } async function getVideoFileSize (path: string) { - const videoStream = await getVideoFileStream(path) + const videoStream = await getVideoStreamFromFile(path) return { width: videoStream.width, @@ -49,7 +50,7 @@ async function getVideoFileResolution (path: string) { } async function getVideoFileFPS (path: string) { - const videoStream = await getVideoFileStream(path) + const videoStream = await getVideoStreamFromFile(path) for (const key of [ 'avg_frame_rate', 'r_frame_rate' ]) { const valuesText: string = videoStream[key] @@ -117,25 +118,50 @@ async function generateImageFromVideoFile (fromPath: string, folder: string, ima } } -type TranscodeOptions = { +type TranscodeOptionsType = 'hls' | 'quick-transcode' | 'video' | 'merge-audio' + +interface BaseTranscodeOptions { + type: TranscodeOptionsType inputPath: string outputPath: string resolution: VideoResolution isPortraitMode?: boolean +} - hlsPlaylist?: { +interface HLSTranscodeOptions extends BaseTranscodeOptions { + type: 'hls' + hlsPlaylist: { videoFilename: string } } +interface QuickTranscodeOptions extends BaseTranscodeOptions { + type: 'quick-transcode' +} + +interface VideoTranscodeOptions extends BaseTranscodeOptions { + type: 'video' +} + +interface MergeAudioTranscodeOptions extends BaseTranscodeOptions { + type: 'merge-audio' + audioPath: string +} + +type TranscodeOptions = HLSTranscodeOptions | VideoTranscodeOptions | MergeAudioTranscodeOptions | QuickTranscodeOptions + function transcode (options: TranscodeOptions) { return new Promise(async (res, rej) => { try { let command = ffmpeg(options.inputPath, { niceness: FFMPEG_NICE.TRANSCODING }) .output(options.outputPath) - if (options.hlsPlaylist) { + if (options.type === 'quick-transcode') { + command = await buildQuickTranscodeCommand(command) + } else if (options.type === 'hls') { command = await buildHLSCommand(command, options) + } else if (options.type === 'merge-audio') { + command = await buildAudioMergeCommand(command, options) } else { command = await buildx264Command(command, options) } @@ -151,7 +177,7 @@ function transcode (options: TranscodeOptions) { return rej(err) }) .on('end', () => { - return onTranscodingSuccess(options) + return fixHLSPlaylistIfNeeded(options) .then(() => res()) .catch(err => rej(err)) }) @@ -162,6 +188,30 @@ function transcode (options: TranscodeOptions) { }) } +async function canDoQuickTranscode (path: string): Promise { + // NOTE: This could be optimized by running ffprobe only once (but it runs fast anyway) + const videoStream = await getVideoStreamFromFile(path) + const parsedAudio = await audio.get(path) + const fps = await getVideoFileFPS(path) + const bitRate = await getVideoFileBitrate(path) + const resolution = await getVideoFileResolution(path) + + // check video params + if (videoStream[ 'codec_name' ] !== 'h264') return false + if (fps < VIDEO_TRANSCODING_FPS.MIN || fps > VIDEO_TRANSCODING_FPS.MAX) return false + if (bitRate > getMaxBitrate(resolution.videoFileResolution, fps, VIDEO_TRANSCODING_FPS)) return false + + // check audio params (if audio stream exists) + if (parsedAudio.audioStream) { + if (parsedAudio.audioStream[ 'codec_name' ] !== 'aac') return false + + const maxAudioBitrate = audio.bitrate[ 'aac' ](parsedAudio.audioStream[ 'bit_rate' ]) + if (maxAudioBitrate !== -1 && parsedAudio.audioStream[ 'bit_rate' ] > maxAudioBitrate) return false + } + + return true +} + // --------------------------------------------------------------------------- export { @@ -169,16 +219,19 @@ export { getVideoFileResolution, getDurationFromVideoFile, generateImageFromVideoFile, + TranscodeOptions, + TranscodeOptionsType, transcode, getVideoFileFPS, computeResolutionsToTranscode, audio, - getVideoFileBitrate + getVideoFileBitrate, + canDoQuickTranscode } // --------------------------------------------------------------------------- -async function buildx264Command (command: ffmpeg.FfmpegCommand, options: TranscodeOptions) { +async function buildx264Command (command: ffmpeg.FfmpegCommand, options: VideoTranscodeOptions) { let fps = await getVideoFileFPS(options.inputPath) // On small/medium resolutions, limit FPS if ( @@ -189,7 +242,7 @@ async function buildx264Command (command: ffmpeg.FfmpegCommand, options: Transco fps = VIDEO_TRANSCODING_FPS.AVERAGE } - command = await presetH264(command, options.resolution, fps) + command = await presetH264(command, options.inputPath, options.resolution, fps) if (options.resolution !== undefined) { // '?x720' or '720x?' for example @@ -208,7 +261,29 @@ async function buildx264Command (command: ffmpeg.FfmpegCommand, options: Transco return command } -async function buildHLSCommand (command: ffmpeg.FfmpegCommand, options: TranscodeOptions) { +async function buildAudioMergeCommand (command: ffmpeg.FfmpegCommand, options: MergeAudioTranscodeOptions) { + command = command.loop(undefined) + + command = await presetH264VeryFast(command, options.audioPath, options.resolution) + + command = command.input(options.audioPath) + .videoFilter('scale=trunc(iw/2)*2:trunc(ih/2)*2') // Avoid "height not divisible by 2" error + .outputOption('-tune stillimage') + .outputOption('-shortest') + + return command +} + +async function buildQuickTranscodeCommand (command: ffmpeg.FfmpegCommand) { + command = await presetCopy(command) + + command = command.outputOption('-map_metadata -1') // strip all metadata + .outputOption('-movflags faststart') + + return command +} + +async function buildHLSCommand (command: ffmpeg.FfmpegCommand, options: HLSTranscodeOptions) { const videoPath = getHLSVideoPath(options) command = await presetCopy(command) @@ -224,26 +299,26 @@ async function buildHLSCommand (command: ffmpeg.FfmpegCommand, options: Transcod return command } -function getHLSVideoPath (options: TranscodeOptions) { +function getHLSVideoPath (options: HLSTranscodeOptions) { return `${dirname(options.outputPath)}/${options.hlsPlaylist.videoFilename}` } -async function onTranscodingSuccess (options: TranscodeOptions) { - if (!options.hlsPlaylist) return +async function fixHLSPlaylistIfNeeded (options: TranscodeOptions) { + if (options.type !== 'hls') return - // Fix wrong mapping with some ffmpeg versions const fileContent = await readFile(options.outputPath) const videoFileName = options.hlsPlaylist.videoFilename const videoFilePath = getHLSVideoPath(options) + // Fix wrong mapping with some ffmpeg versions const newContent = fileContent.toString() .replace(`#EXT-X-MAP:URI="${videoFilePath}",`, `#EXT-X-MAP:URI="${videoFileName}",`) await writeFile(options.outputPath, newContent) } -function getVideoFileStream (path: string) { +function getVideoStreamFromFile (path: string) { return new Promise((res, rej) => { ffmpeg.ffprobe(path, (err, metadata) => { if (err) return rej(err) @@ -263,44 +338,27 @@ function getVideoFileStream (path: string) { * and quality. Superfast and ultrafast will give you better * performance, but then quality is noticeably worse. */ -async function presetH264VeryFast (command: ffmpeg.FfmpegCommand, resolution: VideoResolution, fps: number): Promise { - let localCommand = await presetH264(command, resolution, fps) +async function presetH264VeryFast (command: ffmpeg.FfmpegCommand, input: string, resolution: VideoResolution, fps?: number) { + let localCommand = await presetH264(command, input, resolution, fps) + localCommand = localCommand.outputOption('-preset:v veryfast') - .outputOption([ '--aq-mode=2', '--aq-strength=1.3' ]) + /* MAIN reference: https://slhck.info/video/2017/03/01/rate-control.html Our target situation is closer to a livestream than a stream, since we want to reduce as much a possible the encoding burden, - altough not to the point of a livestream where there is a hard + although not to the point of a livestream where there is a hard constraint on the frames per second to be encoded. - - why '--aq-mode=2 --aq-strength=1.3' instead of '-profile:v main'? - Make up for most of the loss of grain and macroblocking - with less computing power. */ return localCommand } -/** - * A preset optimised for a stillimage audio video - */ -async function presetStillImageWithAudio ( - command: ffmpeg.FfmpegCommand, - resolution: VideoResolution, - fps: number -): Promise { - let localCommand = await presetH264VeryFast(command, resolution, fps) - localCommand = localCommand.outputOption('-tune stillimage') - - return localCommand -} - /** * A toolbox to play with audio */ namespace audio { - export const get = (option: ffmpeg.FfmpegCommand | string) => { + export const get = (option: string) => { // without position, ffprobe considers the last input only // we make it consider the first input only // if you pass a file path to pos, then ffprobe acts on that file directly @@ -322,11 +380,7 @@ namespace audio { return res({ absolutePath: data.format.filename }) } - if (typeof option === 'string') { - return ffmpeg.ffprobe(option, parseFfprobe) - } - - return option.ffprobe(parseFfprobe) + return ffmpeg.ffprobe(option, parseFfprobe) }) } @@ -368,7 +422,7 @@ namespace audio { * As for the audio, quality '5' is the highest and ensures 96-112kbps/channel * See https://trac.ffmpeg.org/wiki/Encode/AAC#fdk_vbr */ -async function presetH264 (command: ffmpeg.FfmpegCommand, resolution: VideoResolution, fps: number): Promise { +async function presetH264 (command: ffmpeg.FfmpegCommand, input: string, resolution: VideoResolution, fps?: number) { let localCommand = command .format('mp4') .videoCodec('libx264') @@ -379,7 +433,7 @@ async function presetH264 (command: ffmpeg.FfmpegCommand, resolution: VideoResol .outputOption('-map_metadata -1') // strip all metadata .outputOption('-movflags faststart') - const parsedAudio = await audio.get(localCommand) + const parsedAudio = await audio.get(input) if (!parsedAudio.audioStream) { localCommand = localCommand.noAudio() @@ -388,28 +442,30 @@ async function presetH264 (command: ffmpeg.FfmpegCommand, resolution: VideoResol .audioCodec('libfdk_aac') .audioQuality(5) } else { - // we try to reduce the ceiling bitrate by making rough correspondances of bitrates + // we try to reduce the ceiling bitrate by making rough matches of bitrates // of course this is far from perfect, but it might save some space in the end + localCommand = localCommand.audioCodec('aac') + const audioCodecName = parsedAudio.audioStream[ 'codec_name' ] - let bitrate: number - if (audio.bitrate[ audioCodecName ]) { - localCommand = localCommand.audioCodec('aac') - bitrate = audio.bitrate[ audioCodecName ](parsedAudio.audioStream[ 'bit_rate' ]) + if (audio.bitrate[ audioCodecName ]) { + const bitrate = audio.bitrate[ audioCodecName ](parsedAudio.audioStream[ 'bit_rate' ]) if (bitrate !== undefined && bitrate !== -1) localCommand = localCommand.audioBitrate(bitrate) } } - // Constrained Encoding (VBV) - // https://slhck.info/video/2017/03/01/rate-control.html - // https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate - const targetBitrate = getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS) - localCommand = localCommand.outputOptions([`-maxrate ${ targetBitrate }`, `-bufsize ${ targetBitrate * 2 }`]) - - // Keyframe interval of 2 seconds for faster seeking and resolution switching. - // https://streaminglearningcenter.com/blogs/whats-the-right-keyframe-interval.html - // https://superuser.com/a/908325 - localCommand = localCommand.outputOption(`-g ${ fps * 2 }`) + if (fps) { + // Constrained Encoding (VBV) + // https://slhck.info/video/2017/03/01/rate-control.html + // https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate + const targetBitrate = getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS) + localCommand = localCommand.outputOptions([ `-maxrate ${targetBitrate}`, `-bufsize ${targetBitrate * 2}` ]) + + // Keyframe interval of 2 seconds for faster seeking and resolution switching. + // https://streaminglearningcenter.com/blogs/whats-the-right-keyframe-interval.html + // https://superuser.com/a/908325 + localCommand = localCommand.outputOption(`-g ${fps * 2}`) + } return localCommand } 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 = { CA_FILE: config.get('smtp.ca_file'), FROM_ADDRESS: config.get('smtp.from_address') }, + EMAIL: { + BODY: { + SIGNATURE: config.get('email.body.signature') + }, + OBJECT: { + PREFIX: config.get('email.object.prefix') + ' ' + } + }, STORAGE: { TMP_DIR: buildPath(config.get('storage.tmp')), AVATARS_DIR: buildPath(config.get('storage.avatars')), @@ -140,6 +148,7 @@ const CONFIG = { TRANSCODING: { get ENABLED () { return config.get('transcoding.enabled') }, get ALLOW_ADDITIONAL_EXTENSIONS () { return config.get('transcoding.allow_additional_extensions') }, + get ALLOW_AUDIO_FILES () { return config.get('transcoding.allow_audio_files') }, get THREADS () { return config.get('transcoding.threads') }, RESOLUTIONS: { get '240p' () { return config.get('transcoding.resolutions.240p') }, diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index b5f8fc0bc..be30be463 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts @@ -1,10 +1,10 @@ import { join } from 'path' -import { JobType, VideoRateType, VideoState } from '../../shared/models' +import { JobType, VideoRateType, VideoResolution, VideoState } from '../../shared/models' import { ActivityPubActorType } from '../../shared/models/activitypub' import { FollowState } from '../../shared/models/actors' import { VideoAbuseState, VideoImportState, VideoPrivacy, VideoTranscodingFPS } from '../../shared/models/videos' // Do not use barrels, remain constants as independent as possible -import { isTestInstance, sanitizeHost, sanitizeUrl } from '../helpers/core-utils' +import { isTestInstance, sanitizeHost, sanitizeUrl, root } from '../helpers/core-utils' import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type' import { invert } from 'lodash' import { CronRepeatOptions, EveryRepeatOptions } from 'bull' @@ -14,7 +14,7 @@ import { CONFIG, registerConfigChangedHandler } from './config' // --------------------------------------------------------------------------- -const LAST_MIGRATION_VERSION = 380 +const LAST_MIGRATION_VERSION = 385 // --------------------------------------------------------------------------- @@ -228,7 +228,7 @@ let CONSTRAINTS_FIELDS = { max: 2 * 1024 * 1024 // 2MB } }, - EXTNAME: buildVideosExtname(), + EXTNAME: [] as string[], INFO_HASH: { min: 40, max: 40 }, // Length, info hash is 20 bytes length but we represent it in hexadecimal so 20 * 2 DURATION: { min: 0 }, // Number TAGS: { min: 0, max: 5 }, // Number of total tags @@ -300,6 +300,8 @@ const VIDEO_TRANSCODING_FPS: VideoTranscodingFPS = { KEEP_ORIGIN_FPS_RESOLUTION_MIN: 720 // We keep the original FPS on high resolutions (720 minimum) } +const DEFAULT_AUDIO_RESOLUTION = VideoResolution.H_480P + const VIDEO_RATE_TYPES: { [ id: string ]: VideoRateType } = { LIKE: 'like', DISLIKE: 'dislike' @@ -380,8 +382,18 @@ const VIDEO_PLAYLIST_TYPES = { } const MIMETYPES = { + AUDIO: { + MIMETYPE_EXT: { + 'audio/mpeg': '.mp3', + 'audio/mp3': '.mp3', + 'application/ogg': '.ogg', + 'audio/ogg': '.ogg', + 'audio/flac': '.flac' + }, + EXT_MIMETYPE: null as { [ id: string ]: string } + }, VIDEO: { - MIMETYPE_EXT: buildVideoMimetypeExt(), + MIMETYPE_EXT: null as { [ id: string ]: string }, EXT_MIMETYPE: null as { [ id: string ]: string } }, IMAGE: { @@ -403,7 +415,7 @@ const MIMETYPES = { } } } -MIMETYPES.VIDEO.EXT_MIMETYPE = invert(MIMETYPES.VIDEO.MIMETYPE_EXT) +MIMETYPES.AUDIO.EXT_MIMETYPE = invert(MIMETYPES.AUDIO.MIMETYPE_EXT) // --------------------------------------------------------------------------- @@ -429,7 +441,7 @@ const ACTIVITY_PUB = { COLLECTION_ITEMS_PER_PAGE: 10, FETCH_PAGE_LIMIT: 100, URL_MIME_TYPES: { - VIDEO: Object.keys(MIMETYPES.VIDEO.MIMETYPE_EXT), + VIDEO: [] as string[], TORRENT: [ 'application/x-bittorrent' ], MAGNET: [ 'application/x-bittorrent;x-scheme-handler/magnet' ] }, @@ -497,8 +509,8 @@ const THUMBNAILS_SIZE = { height: 122 } const PREVIEWS_SIZE = { - width: 560, - height: 315 + width: 850, + height: 480 } const AVATARS_SIZE = { width: 120, @@ -543,6 +555,10 @@ const REDUNDANCY = { const ACCEPT_HEADERS = [ 'html', 'application/json' ].concat(ACTIVITY_PUB.POTENTIAL_ACCEPT_HEADERS) +const ASSETS_PATH = { + DEFAULT_AUDIO_BACKGROUND: join(root(), 'server', 'assets', 'default-audio-background.jpg') +} + // --------------------------------------------------------------------------- const CUSTOM_HTML_TAG_COMMENTS = { @@ -612,6 +628,7 @@ if (isTestInstance() === true) { } updateWebserverUrls() +updateWebserverConfig() registerConfigChangedHandler(() => { updateWebserverUrls() @@ -681,12 +698,14 @@ export { RATES_LIMIT, MIMETYPES, CRAWL_REQUEST_CONCURRENCY, + DEFAULT_AUDIO_RESOLUTION, JOB_COMPLETED_LIFETIME, HTTP_SIGNATURE, VIDEO_IMPORT_STATES, VIDEO_VIEW_LIFETIME, CONTACT_FORM_LIFETIME, VIDEO_PLAYLIST_PRIVACIES, + ASSETS_PATH, loadLanguages, buildLanguages } @@ -700,15 +719,21 @@ function buildVideoMimetypeExt () { 'video/mp4': '.mp4' } - if (CONFIG.TRANSCODING.ENABLED && CONFIG.TRANSCODING.ALLOW_ADDITIONAL_EXTENSIONS) { - Object.assign(data, { - 'video/quicktime': '.mov', - 'video/x-msvideo': '.avi', - 'video/x-flv': '.flv', - 'video/x-matroska': '.mkv', - 'application/octet-stream': '.mkv', - 'video/avi': '.avi' - }) + if (CONFIG.TRANSCODING.ENABLED) { + if (CONFIG.TRANSCODING.ALLOW_ADDITIONAL_EXTENSIONS) { + Object.assign(data, { + 'video/quicktime': '.mov', + 'video/x-msvideo': '.avi', + 'video/x-flv': '.flv', + 'video/x-matroska': '.mkv', + 'application/octet-stream': '.mkv', + 'video/avi': '.avi' + }) + } + + if (CONFIG.TRANSCODING.ALLOW_AUDIO_FILES) { + Object.assign(data, MIMETYPES.AUDIO.MIMETYPE_EXT) + } } return data @@ -724,16 +749,15 @@ function updateWebserverUrls () { } function updateWebserverConfig () { - CONSTRAINTS_FIELDS.VIDEOS.EXTNAME = buildVideosExtname() - MIMETYPES.VIDEO.MIMETYPE_EXT = buildVideoMimetypeExt() MIMETYPES.VIDEO.EXT_MIMETYPE = invert(MIMETYPES.VIDEO.MIMETYPE_EXT) + ACTIVITY_PUB.URL_MIME_TYPES.VIDEO = Object.keys(MIMETYPES.VIDEO.MIMETYPE_EXT) + + CONSTRAINTS_FIELDS.VIDEOS.EXTNAME = buildVideosExtname() } function buildVideosExtname () { - return CONFIG.TRANSCODING.ENABLED && CONFIG.TRANSCODING.ALLOW_ADDITIONAL_EXTENSIONS - ? [ '.mp4', '.ogv', '.webm', '.mkv', '.mov', '.avi', '.flv' ] - : [ '.mp4', '.ogv', '.webm' ] + return Object.keys(MIMETYPES.VIDEO.EXT_MIMETYPE) } function loadLanguages () { diff --git a/server/initializers/installer.ts b/server/initializers/installer.ts index 127449577..e14554ede 100644 --- a/server/initializers/installer.ts +++ b/server/initializers/installer.ts @@ -128,6 +128,8 @@ async function createOAuthAdminIfNotExist () { // Our password is weak so do not validate it validatePassword = false + } else if (process.env.PT_INITIAL_ROOT_PASSWORD) { + password = process.env.PT_INITIAL_ROOT_PASSWORD } else { password = passwordGenerator(16, true) } @@ -144,7 +146,7 @@ async function createOAuthAdminIfNotExist () { } const user = new UserModel(userData) - await createUserAccountAndChannelAndPlaylist(user, validatePassword) + await createUserAccountAndChannelAndPlaylist(user, undefined, validatePassword) logger.info('Username: ' + username) logger.info('User password: ' + password) } diff --git a/server/initializers/migrations/0385-remove-actor-uuid.ts b/server/initializers/migrations/0385-remove-actor-uuid.ts new file mode 100644 index 000000000..032c0562b --- /dev/null +++ b/server/initializers/migrations/0385-remove-actor-uuid.ts @@ -0,0 +1,19 @@ +import * as Sequelize from 'sequelize' + +async function up (utils: { + transaction: Sequelize.Transaction, + queryInterface: Sequelize.QueryInterface, + sequelize: Sequelize.Sequelize, + db: any +}): Promise { + await utils.queryInterface.removeColumn('actor', 'uuid') +} + +function down (options) { + throw new Error('Not implemented.') +} + +export { + up, + down +} diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts index 25cd40905..38eb87d1e 100644 --- a/server/lib/activitypub/actor.ts +++ b/server/lib/activitypub/actor.ts @@ -5,7 +5,7 @@ import * as uuidv4 from 'uuid/v4' import { ActivityPubActor, ActivityPubActorType } from '../../../shared/models/activitypub' import { ActivityPubAttributedTo } from '../../../shared/models/activitypub/objects' import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' -import { isActorObjectValid, normalizeActor } from '../../helpers/custom-validators/activitypub/actor' +import { sanitizeAndCheckActorObject } from '../../helpers/custom-validators/activitypub/actor' import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' import { retryTransactionWrapper, updateInstanceWithAnother } from '../../helpers/database-utils' import { logger } from '../../helpers/logger' @@ -33,7 +33,7 @@ function setAsyncActorKeys (actor: ActorModel) { return actor.save() }) .catch(err => { - logger.error('Cannot set public/private keys of actor %d.', actor.uuid, { err }) + logger.error('Cannot set public/private keys of actor %d.', actor.url, { err }) return actor }) } @@ -128,18 +128,17 @@ async function updateActorInstance (actorInstance: ActorModel, attributes: Activ const followersCount = await fetchActorTotalItems(attributes.followers) const followingCount = await fetchActorTotalItems(attributes.following) - actorInstance.set('type', attributes.type) - actorInstance.set('uuid', attributes.uuid) - actorInstance.set('preferredUsername', attributes.preferredUsername) - actorInstance.set('url', attributes.id) - actorInstance.set('publicKey', attributes.publicKey.publicKeyPem) - actorInstance.set('followersCount', followersCount) - actorInstance.set('followingCount', followingCount) - actorInstance.set('inboxUrl', attributes.inbox) - actorInstance.set('outboxUrl', attributes.outbox) - actorInstance.set('sharedInboxUrl', attributes.endpoints.sharedInbox) - actorInstance.set('followersUrl', attributes.followers) - actorInstance.set('followingUrl', attributes.following) + actorInstance.type = attributes.type + actorInstance.preferredUsername = attributes.preferredUsername + actorInstance.url = attributes.id + actorInstance.publicKey = attributes.publicKey.publicKeyPem + actorInstance.followersCount = followersCount + actorInstance.followingCount = followingCount + actorInstance.inboxUrl = attributes.inbox + actorInstance.outboxUrl = attributes.outbox + actorInstance.sharedInboxUrl = attributes.endpoints.sharedInbox + actorInstance.followersUrl = attributes.followers + actorInstance.followingUrl = attributes.following } async function updateActorAvatarInstance (actorInstance: ActorModel, avatarName: string, t: Transaction) { @@ -370,10 +369,9 @@ async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode?: numbe logger.info('Fetching remote actor %s.', actorUrl) const requestResult = await doRequest(options) - normalizeActor(requestResult.body) - const actorJSON = requestResult.body - if (isActorObjectValid(actorJSON) === false) { + + if (sanitizeAndCheckActorObject(actorJSON) === false) { logger.debug('Remote actor JSON is not valid.', { actorJSON }) return { result: undefined, statusCode: requestResult.response.statusCode } } @@ -388,7 +386,6 @@ async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode?: numbe const actor = new ActorModel({ type: actorJSON.type, - uuid: actorJSON.uuid, preferredUsername: actorJSON.preferredUsername, url: actorJSON.id, publicKey: actorJSON.publicKey.publicKeyPem, diff --git a/server/lib/activitypub/crawl.ts b/server/lib/activitypub/crawl.ts index 686eef04d..9e469e3e6 100644 --- a/server/lib/activitypub/crawl.ts +++ b/server/lib/activitypub/crawl.ts @@ -28,13 +28,22 @@ async function crawlCollectionPage (uri: string, handler: HandlerFunction let i = 0 let nextLink = firstBody.first while (nextLink && i < limit) { - // Don't crawl ourselves - const remoteHost = parse(nextLink).host - if (remoteHost === WEBSERVER.HOST) continue + let body: any - options.uri = nextLink + if (typeof nextLink === 'string') { + // Don't crawl ourselves + const remoteHost = parse(nextLink).host + if (remoteHost === WEBSERVER.HOST) continue + + options.uri = nextLink + + const res = await doRequest>(options) + body = res.body + } else { + // nextLink is already the object we want + body = nextLink + } - const { body } = await doRequest>(options) nextLink = body.next i++ diff --git a/server/lib/activitypub/process/process-announce.ts b/server/lib/activitypub/process/process-announce.ts index 23310b41e..bbf1bd3a8 100644 --- a/server/lib/activitypub/process/process-announce.ts +++ b/server/lib/activitypub/process/process-announce.ts @@ -5,8 +5,9 @@ import { ActorModel } from '../../../models/activitypub/actor' import { VideoShareModel } from '../../../models/video/video-share' import { forwardVideoRelatedActivity } from '../send/utils' import { getOrCreateVideoAndAccountAndChannel } from '../videos' -import { VideoPrivacy } from '../../../../shared/models/videos' import { Notifier } from '../../notifier' +import { VideoModel } from '../../../models/video/video' +import { logger } from '../../../helpers/logger' async function processAnnounceActivity (activity: ActivityAnnounce, actorAnnouncer: ActorModel) { return retryTransactionWrapper(processVideoShare, actorAnnouncer, activity) @@ -23,7 +24,17 @@ export { async function processVideoShare (actorAnnouncer: ActorModel, activity: ActivityAnnounce) { const objectUri = typeof activity.object === 'string' ? activity.object : activity.object.id - const { video, created: videoCreated } = await getOrCreateVideoAndAccountAndChannel({ videoObject: objectUri }) + let video: VideoModel + let videoCreated: boolean + + try { + const result = await getOrCreateVideoAndAccountAndChannel({ videoObject: objectUri }) + video = result.video + videoCreated = result.created + } catch (err) { + logger.debug('Cannot process share of %s. Maybe this is not a video object, so just skipping.', objectUri, { err }) + return + } await sequelizeTypescript.transaction(async t => { // Add share entry diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts index e882669ce..daf846513 100644 --- a/server/lib/activitypub/process/process-create.ts +++ b/server/lib/activitypub/process/process-create.ts @@ -9,28 +9,14 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos' import { forwardVideoRelatedActivity } from '../send/utils' import { createOrUpdateCacheFile } from '../cache-file' import { Notifier } from '../../notifier' -import { processViewActivity } from './process-view' -import { processDislikeActivity } from './process-dislike' -import { processFlagActivity } from './process-flag' import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object' import { createOrUpdateVideoPlaylist } from '../playlist' +import { VideoModel } from '../../../models/video/video' async function processCreateActivity (activity: ActivityCreate, byActor: ActorModel) { const activityObject = activity.object const activityType = activityObject.type - if (activityType === 'View') { - return processViewActivity(activity, byActor) - } - - if (activityType === 'Dislike') { - return retryTransactionWrapper(processDislikeActivity, activity, byActor) - } - - if (activityType === 'Flag') { - return retryTransactionWrapper(processFlagActivity, activity, byActor) - } - if (activityType === 'Video') { return processCreateVideo(activity) } @@ -91,7 +77,18 @@ async function processCreateVideoComment (activity: ActivityCreate, byActor: Act if (!byAccount) throw new Error('Cannot create video comment with the non account actor ' + byActor.url) - const { video } = await resolveThread(commentObject.inReplyTo) + let video: VideoModel + try { + const resolveThreadResult = await resolveThread(commentObject.inReplyTo) + video = resolveThreadResult.video + } catch (err) { + logger.debug( + 'Cannot process video comment because we could not resolve thread %s. Maybe it was not a video thread, so skip it.', + commentObject.inReplyTo, + { err } + ) + return + } const { comment, created } = await addVideoComment(video, commentObject.id) diff --git a/server/lib/activitypub/process/process-delete.ts b/server/lib/activitypub/process/process-delete.ts index 76f07fd8a..6f10a50bd 100644 --- a/server/lib/activitypub/process/process-delete.ts +++ b/server/lib/activitypub/process/process-delete.ts @@ -95,23 +95,23 @@ async function processDeleteVideoPlaylist (actor: ActorModel, playlistToDelete: } async function processDeleteAccount (accountToRemove: AccountModel) { - logger.debug('Removing remote account "%s".', accountToRemove.Actor.uuid) + logger.debug('Removing remote account "%s".', accountToRemove.Actor.url) await sequelizeTypescript.transaction(async t => { await accountToRemove.destroy({ transaction: t }) }) - logger.info('Remote account with uuid %s removed.', accountToRemove.Actor.uuid) + logger.info('Remote account %s removed.', accountToRemove.Actor.url) } async function processDeleteVideoChannel (videoChannelToRemove: VideoChannelModel) { - logger.debug('Removing remote video channel "%s".', videoChannelToRemove.Actor.uuid) + logger.debug('Removing remote video channel "%s".', videoChannelToRemove.Actor.url) await sequelizeTypescript.transaction(async t => { await videoChannelToRemove.destroy({ transaction: t }) }) - logger.info('Remote video channel with uuid %s removed.', videoChannelToRemove.Actor.uuid) + logger.info('Remote video channel %s removed.', videoChannelToRemove.Actor.url) } function processDeleteVideoComment (byActor: ActorModel, videoComment: VideoCommentModel, activity: ActivityDelete) { diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts index 54a9234bb..71a16dacc 100644 --- a/server/lib/activitypub/process/process-update.ts +++ b/server/lib/activitypub/process/process-update.ts @@ -95,7 +95,7 @@ async function processUpdateCacheFile (byActor: ActorModel, activity: ActivityUp async function processUpdateActor (actor: ActorModel, activity: ActivityUpdate) { const actorAttributesToUpdate = activity.object as ActivityPubActor - logger.debug('Updating remote account "%s".', actorAttributesToUpdate.uuid) + logger.debug('Updating remote account "%s".', actorAttributesToUpdate.url) let accountOrChannelInstance: AccountModel | VideoChannelModel let actorFieldsSave: object let accountOrChannelFieldsSave: object @@ -128,7 +128,7 @@ async function processUpdateActor (actor: ActorModel, activity: ActivityUpdate) await accountOrChannelInstance.save({ transaction: t }) }) - logger.info('Remote account with uuid %s updated', actorAttributesToUpdate.uuid) + logger.info('Remote account %s updated', actorAttributesToUpdate.url) } catch (err) { if (actor !== undefined && actorFieldsSave !== undefined) { resetSequelizeInstance(actor, actorFieldsSave) diff --git a/server/lib/activitypub/video-comments.ts b/server/lib/activitypub/video-comments.ts index 18f44d50e..c3fc6b462 100644 --- a/server/lib/activitypub/video-comments.ts +++ b/server/lib/activitypub/video-comments.ts @@ -80,7 +80,8 @@ async function addVideoComment (videoInstance: VideoModel, commentUrl: string) { return { comment, created } } -async function resolveThread (url: string, comments: VideoCommentModel[] = []) { +type ResolveThreadResult = Promise<{ video: VideoModel, parents: VideoCommentModel[] }> +async function resolveThread (url: string, comments: VideoCommentModel[] = []): ResolveThreadResult { // Already have this comment? const commentFromDatabase = await VideoCommentModel.loadByUrlAndPopulateReplyAndVideo(url) if (commentFromDatabase) { @@ -161,7 +162,6 @@ async function resolveThread (url: string, comments: VideoCommentModel[] = []) { return resolveThread(body.inReplyTo, comments.concat([ comment ])) } - } export { 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 { `You can view it on ${videoUrl} ` + `\n\n` + `Cheers,\n` + - `PeerTube.` + `${CONFIG.EMAIL.BODY.SIGNATURE}` const emailPayload: EmailPayload = { to, - subject: channelName + ' just published a new video', + subject: CONFIG.EMAIL.OBJECT.PREFIX + channelName + ' just published a new video', text } @@ -119,11 +119,11 @@ class Emailer { `Your ${followType} ${followingName} has a new subscriber: ${followerName}` + `\n\n` + `Cheers,\n` + - `PeerTube.` + `${CONFIG.EMAIL.BODY.SIGNATURE}` const emailPayload: EmailPayload = { to, - subject: 'New follower on your channel ' + followingName, + subject: CONFIG.EMAIL.OBJECT.PREFIX + 'New follower on your channel ' + followingName, text } @@ -137,11 +137,11 @@ class Emailer { `Your instance has a new follower: ${actorFollow.ActorFollower.url}${awaitingApproval}` + `\n\n` + `Cheers,\n` + - `PeerTube.` + `${CONFIG.EMAIL.BODY.SIGNATURE}` const emailPayload: EmailPayload = { to, - subject: 'New instance follower', + subject: CONFIG.EMAIL.OBJECT.PREFIX + 'New instance follower', text } @@ -157,11 +157,11 @@ class Emailer { `You can view it on ${videoUrl} ` + `\n\n` + `Cheers,\n` + - `PeerTube.` + `${CONFIG.EMAIL.BODY.SIGNATURE}` const emailPayload: EmailPayload = { to, - subject: `Your video ${video.name} is published`, + subject: CONFIG.EMAIL.OBJECT.PREFIX + `Your video ${video.name} is published`, text } @@ -177,11 +177,11 @@ class Emailer { `You can view the imported video on ${videoUrl} ` + `\n\n` + `Cheers,\n` + - `PeerTube.` + `${CONFIG.EMAIL.BODY.SIGNATURE}` const emailPayload: EmailPayload = { to, - subject: `Your video import ${videoImport.getTargetIdentifier()} is finished`, + subject: CONFIG.EMAIL.OBJECT.PREFIX + `Your video import ${videoImport.getTargetIdentifier()} is finished`, text } @@ -197,11 +197,11 @@ class Emailer { `See your videos import dashboard for more information: ${importUrl}` + `\n\n` + `Cheers,\n` + - `PeerTube.` + `${CONFIG.EMAIL.BODY.SIGNATURE}` const emailPayload: EmailPayload = { to, - subject: `Your video import ${videoImport.getTargetIdentifier()} encountered an error`, + subject: CONFIG.EMAIL.OBJECT.PREFIX + `Your video import ${videoImport.getTargetIdentifier()} encountered an error`, text } @@ -219,11 +219,11 @@ class Emailer { `You can view it on ${commentUrl} ` + `\n\n` + `Cheers,\n` + - `PeerTube.` + `${CONFIG.EMAIL.BODY.SIGNATURE}` const emailPayload: EmailPayload = { to, - subject: 'New comment on your video ' + video.name, + subject: CONFIG.EMAIL.OBJECT.PREFIX + 'New comment on your video ' + video.name, text } @@ -241,11 +241,11 @@ class Emailer { `You can view the comment on ${commentUrl} ` + `\n\n` + `Cheers,\n` + - `PeerTube.` + `${CONFIG.EMAIL.BODY.SIGNATURE}` const emailPayload: EmailPayload = { to, - subject: 'Mention on video ' + video.name, + subject: CONFIG.EMAIL.OBJECT.PREFIX + 'Mention on video ' + video.name, text } @@ -258,11 +258,11 @@ class Emailer { const text = `Hi,\n\n` + `${WEBSERVER.HOST} received an abuse for the following video ${videoUrl}\n\n` + `Cheers,\n` + - `PeerTube.` + `${CONFIG.EMAIL.BODY.SIGNATURE}` const emailPayload: EmailPayload = { to, - subject: '[PeerTube] Received a video abuse', + subject: CONFIG.EMAIL.OBJECT.PREFIX + 'Received a video abuse', text } @@ -281,11 +281,11 @@ class Emailer { `A full list of auto-blacklisted videos can be reviewed here: ${VIDEO_AUTO_BLACKLIST_URL}` + `\n\n` + `Cheers,\n` + - `PeerTube.` + `${CONFIG.EMAIL.BODY.SIGNATURE}` const emailPayload: EmailPayload = { to, - subject: '[PeerTube] An auto-blacklisted video is awaiting review', + subject: CONFIG.EMAIL.OBJECT.PREFIX + 'An auto-blacklisted video is awaiting review', text } @@ -296,11 +296,11 @@ class Emailer { const text = `Hi,\n\n` + `User ${user.username} just registered on ${WEBSERVER.HOST} PeerTube instance.\n\n` + `Cheers,\n` + - `PeerTube.` + `${CONFIG.EMAIL.BODY.SIGNATURE}` const emailPayload: EmailPayload = { to, - subject: '[PeerTube] New user registration on ' + WEBSERVER.HOST, + subject: CONFIG.EMAIL.OBJECT.PREFIX + 'New user registration on ' + WEBSERVER.HOST, text } @@ -318,11 +318,11 @@ class Emailer { blockedString + '\n\n' + 'Cheers,\n' + - `PeerTube.` + `${CONFIG.EMAIL.BODY.SIGNATURE}` const emailPayload: EmailPayload = { to, - subject: `[PeerTube] Video ${videoName} blacklisted`, + subject: CONFIG.EMAIL.OBJECT.PREFIX + `Video ${videoName} blacklisted`, text } @@ -336,11 +336,11 @@ class Emailer { `Your video ${video.name} (${videoUrl}) on ${WEBSERVER.HOST} has been unblacklisted.` + '\n\n' + 'Cheers,\n' + - `PeerTube.` + `${CONFIG.EMAIL.BODY.SIGNATURE}` const emailPayload: EmailPayload = { to, - subject: `[PeerTube] Video ${video.name} unblacklisted`, + subject: CONFIG.EMAIL.OBJECT.PREFIX + `Video ${video.name} unblacklisted`, text } @@ -353,11 +353,11 @@ class Emailer { `Please follow this link to reset it: ${resetPasswordUrl}\n\n` + `If you are not the person who initiated this request, please ignore this email.\n\n` + `Cheers,\n` + - `PeerTube.` + `${CONFIG.EMAIL.BODY.SIGNATURE}` const emailPayload: EmailPayload = { to: [ to ], - subject: 'Reset your PeerTube password', + subject: CONFIG.EMAIL.OBJECT.PREFIX + 'Reset your password', text } @@ -370,11 +370,11 @@ class Emailer { `Please follow this link to verify this email belongs to you: ${verifyEmailUrl}\n\n` + `If you are not the person who initiated this request, please ignore this email.\n\n` + `Cheers,\n` + - `PeerTube.` + `${CONFIG.EMAIL.BODY.SIGNATURE}` const emailPayload: EmailPayload = { to: [ to ], - subject: 'Verify your PeerTube email', + subject: CONFIG.EMAIL.OBJECT.PREFIX + 'Verify your email', text } @@ -390,12 +390,12 @@ class Emailer { blockedString + '\n\n' + 'Cheers,\n' + - `PeerTube.` + `${CONFIG.EMAIL.BODY.SIGNATURE}` const to = user.email const emailPayload: EmailPayload = { to: [ to ], - subject: '[PeerTube] Account ' + blockedWord, + subject: CONFIG.EMAIL.OBJECT.PREFIX + 'Account ' + blockedWord, text } @@ -415,7 +415,7 @@ class Emailer { fromDisplayName: fromEmail, replyTo: fromEmail, to: [ CONFIG.ADMIN.EMAIL ], - subject: '[PeerTube] Contact form submitted', + subject: CONFIG.EMAIL.OBJECT.PREFIX + 'Contact form submitted', text } 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 { const video = await VideoModel.loadByUUIDWithFile(videoUUID) if (!video) return undefined - if (video.isOwned()) return { isOwned: true, path: join(CONFIG.STORAGE.PREVIEWS_DIR, video.getPreview().filename) } + if (video.isOwned()) return { isOwned: true, path: video.getPreview().getPath() } return this.loadRemoteFile(videoUUID) } 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 @@ import * as Bull from 'bull' import { logger } from '../../../helpers/logger' import { VideoModel } from '../../../models/video/video' -import { publishVideoIfNeeded } from './video-transcoding' +import { publishNewResolutionIfNeeded } from './video-transcoding' import { getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils' import { copy, stat } from 'fs-extra' import { VideoFileModel } from '../../../models/video/video-file' @@ -25,7 +25,7 @@ async function processVideoFileImport (job: Bull.Job) { await updateVideoFile(video, payload.filePath) - await publishVideoIfNeeded(video) + await publishNewResolutionIfNeeded(video) return video } 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, videoImport: Vide if (videoImportUpdated.Video.state === VideoState.TO_TRANSCODE) { // Put uuid because we don't have id auto incremented for now const dataInput = { + type: 'optimize' as 'optimize', videoUUID: videoImportUpdated.Video.uuid, isNewVideo: true } 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' import { sequelizeTypescript } from '../../../initializers' import * as Bluebird from 'bluebird' import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils' -import { generateHlsPlaylist, optimizeVideofile, transcodeOriginalVideofile } from '../../video-transcoding' +import { generateHlsPlaylist, optimizeVideofile, transcodeOriginalVideofile, mergeAudioVideofile } from '../../video-transcoding' import { Notifier } from '../../notifier' import { CONFIG } from '../../../initializers/config' -export type VideoTranscodingPayload = { +interface BaseTranscodingPayload { videoUUID: string - resolution?: VideoResolution isNewVideo?: boolean +} + +interface HLSTranscodingPayload extends BaseTranscodingPayload { + type: 'hls' + isPortraitMode?: boolean + resolution: VideoResolution +} + +interface NewResolutionTranscodingPayload extends BaseTranscodingPayload { + type: 'new-resolution' isPortraitMode?: boolean - generateHlsPlaylist?: boolean + resolution: VideoResolution +} + +interface MergeAudioTranscodingPayload extends BaseTranscodingPayload { + type: 'merge-audio' + resolution: VideoResolution +} + +interface OptimizeTranscodingPayload extends BaseTranscodingPayload { + type: 'optimize' } +export type VideoTranscodingPayload = HLSTranscodingPayload | NewResolutionTranscodingPayload + | OptimizeTranscodingPayload | MergeAudioTranscodingPayload + async function processVideoTranscoding (job: Bull.Job) { const payload = job.data as VideoTranscodingPayload logger.info('Processing video file in job %d.', job.id) @@ -31,14 +52,18 @@ async function processVideoTranscoding (job: Bull.Job) { return undefined } - if (payload.generateHlsPlaylist) { + if (payload.type === 'hls') { await generateHlsPlaylist(video, payload.resolution, payload.isPortraitMode || false) await retryTransactionWrapper(onHlsPlaylistGenerationSuccess, video) - } else if (payload.resolution) { // Transcoding in other resolution + } else if (payload.type === 'new-resolution') { await transcodeOriginalVideofile(video, payload.resolution, payload.isPortraitMode || false) - await retryTransactionWrapper(publishVideoIfNeeded, video, payload) + await retryTransactionWrapper(publishNewResolutionIfNeeded, video, payload) + } else if (payload.type === 'merge-audio') { + await mergeAudioVideofile(video, payload.resolution) + + await retryTransactionWrapper(publishNewResolutionIfNeeded, video, payload) } else { await optimizeVideofile(video) @@ -62,7 +87,7 @@ async function onHlsPlaylistGenerationSuccess (video: VideoModel) { }) } -async function publishVideoIfNeeded (video: VideoModel, payload?: VideoTranscodingPayload) { +async function publishNewResolutionIfNeeded (video: VideoModel, payload?: NewResolutionTranscodingPayload | MergeAudioTranscodingPayload) { const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => { // Maybe the video changed in database, refresh it let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t) @@ -94,7 +119,7 @@ async function publishVideoIfNeeded (video: VideoModel, payload?: VideoTranscodi await createHlsJobIfEnabled(payload) } -async function onVideoFileOptimizerSuccess (videoArg: VideoModel, payload: VideoTranscodingPayload) { +async function onVideoFileOptimizerSuccess (videoArg: VideoModel, payload: OptimizeTranscodingPayload) { if (videoArg === undefined) return undefined // Outside the transaction (IO on disk) @@ -120,6 +145,7 @@ async function onVideoFileOptimizerSuccess (videoArg: VideoModel, payload: Video for (const resolution of resolutionsEnabled) { const dataInput = { + type: 'new-resolution' as 'new-resolution', videoUUID: videoDatabase.uuid, resolution } @@ -149,27 +175,27 @@ async function onVideoFileOptimizerSuccess (videoArg: VideoModel, payload: Video if (payload.isNewVideo) Notifier.Instance.notifyOnNewVideo(videoDatabase) if (videoPublished) Notifier.Instance.notifyOnVideoPublishedAfterTranscoding(videoDatabase) - await createHlsJobIfEnabled(Object.assign({}, payload, { resolution: videoDatabase.getOriginalFile().resolution })) + const hlsPayload = Object.assign({}, payload, { resolution: videoDatabase.getOriginalFile().resolution }) + await createHlsJobIfEnabled(hlsPayload) } // --------------------------------------------------------------------------- export { processVideoTranscoding, - publishVideoIfNeeded + publishNewResolutionIfNeeded } // --------------------------------------------------------------------------- -function createHlsJobIfEnabled (payload?: VideoTranscodingPayload) { +function createHlsJobIfEnabled (payload?: { videoUUID: string, resolution: number, isPortraitMode?: boolean }) { // Generate HLS playlist? if (payload && CONFIG.TRANSCODING.HLS.ENABLED) { const hlsTranscodingPayload = { + type: 'hls' as 'hls', videoUUID: payload.videoUUID, resolution: payload.resolution, - isPortraitMode: payload.isPortraitMode, - - generateHlsPlaylist: true + isPortraitMode: payload.isPortraitMode } 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 @@ import { VideoFileModel } from '../models/video/video-file' import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils' import { CONFIG } from '../initializers/config' -import { PREVIEWS_SIZE, THUMBNAILS_SIZE } from '../initializers/constants' +import { PREVIEWS_SIZE, THUMBNAILS_SIZE, ASSETS_PATH } from '../initializers/constants' import { VideoModel } from '../models/video/video' import { ThumbnailModel } from '../models/video/thumbnail' import { ThumbnailType } from '../../shared/models/videos/thumbnail.type' @@ -45,8 +45,10 @@ function createVideoMiniatureFromExisting (inputPath: string, video: VideoModel, function generateVideoMiniature (video: VideoModel, videoFile: VideoFileModel, type: ThumbnailType) { const input = video.getVideoFilePath(videoFile) - const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromVideo(video, type) - const thumbnailCreator = () => generateImageFromVideoFile(input, basePath, filename, { height, width }) + const { filename, basePath, height, width, existingThumbnail, outputPath } = buildMetadataFromVideo(video, type) + const thumbnailCreator = videoFile.isAudio() + ? () => processImage(ASSETS_PATH.DEFAULT_AUDIO_BACKGROUND, outputPath, { width, height }, true) + : () => generateImageFromVideoFile(input, basePath, filename, { height, width }) return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail }) } diff --git a/server/lib/user.ts b/server/lib/user.ts index 7badb3e72..d9fd89e15 100644 --- a/server/lib/user.ts +++ b/server/lib/user.ts @@ -13,7 +13,8 @@ import { UserNotificationSetting, UserNotificationSettingValue } from '../../sha import { createWatchLaterPlaylist } from './video-playlist' import { sequelizeTypescript } from '../initializers/database' -async function createUserAccountAndChannelAndPlaylist (userToCreate: UserModel, validateUser = true) { +type ChannelNames = { name: string, displayName: string } +async function createUserAccountAndChannelAndPlaylist (userToCreate: UserModel, channelNames?: ChannelNames, validateUser = true) { const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => { const userOptions = { transaction: t, @@ -26,18 +27,8 @@ async function createUserAccountAndChannelAndPlaylist (userToCreate: UserModel, const accountCreated = await createLocalAccountWithoutKeys(userCreated.username, userCreated.id, null, t) userCreated.Account = accountCreated - let channelName = userCreated.username + '_channel' - - // Conflict, generate uuid instead - const actor = await ActorModel.loadLocalByName(channelName) - if (actor) channelName = uuidv4() - - const videoChannelDisplayName = `Main ${userCreated.username} channel` - const videoChannelInfo = { - name: channelName, - displayName: videoChannelDisplayName - } - const videoChannel = await createVideoChannel(videoChannelInfo, accountCreated, t) + const channelAttributes = await buildChannelAttributes(userCreated, channelNames) + const videoChannel = await createVideoChannel(channelAttributes, accountCreated, t) const videoPlaylist = await createWatchLaterPlaylist(accountCreated, t) @@ -116,3 +107,20 @@ function createDefaultUserNotificationSettings (user: UserModel, t: Sequelize.Tr return UserNotificationSettingModel.create(values, { transaction: t }) } + +async function buildChannelAttributes (user: UserModel, channelNames?: ChannelNames) { + if (channelNames) return channelNames + + let channelName = user.username + '_channel' + + // Conflict, generate uuid instead + const actor = await ActorModel.loadLocalByName(channelName) + if (actor) channelName = uuidv4() + + const videoChannelDisplayName = `Main ${user.username} channel` + + return { + name: channelName, + displayName: videoChannelDisplayName + } +} diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts index 0fe95ca09..ee0482c36 100644 --- a/server/lib/video-channel.ts +++ b/server/lib/video-channel.ts @@ -3,7 +3,8 @@ import * as uuidv4 from 'uuid/v4' import { VideoChannelCreate } from '../../shared/models' import { AccountModel } from '../models/account/account' import { VideoChannelModel } from '../models/video/video-channel' -import { buildActorInstance, getVideoChannelActivityPubUrl } from './activitypub' +import { buildActorInstance, federateVideoIfNeeded, getVideoChannelActivityPubUrl } from './activitypub' +import { VideoModel } from '../models/video/video' async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountModel, t: Sequelize.Transaction) { const uuid = uuidv4() @@ -33,8 +34,19 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account return videoChannelCreated } +async function federateAllVideosOfChannel (videoChannel: VideoChannelModel) { + const videoIds = await VideoModel.getAllIdsFromChannel(videoChannel) + + for (const videoId of videoIds) { + const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoId) + + await federateVideoIfNeeded(video, false) + } +} + // --------------------------------------------------------------------------- export { - createVideoChannel + createVideoChannel, + federateAllVideosOfChannel } 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 @@ import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION, WEBSERVER } from '../initializers/constants' import { join } from 'path' -import { getVideoFileFPS, transcode } from '../helpers/ffmpeg-utils' +import { canDoQuickTranscode, getVideoFileFPS, transcode, TranscodeOptions, TranscodeOptionsType } from '../helpers/ffmpeg-utils' import { ensureDir, move, remove, stat } from 'fs-extra' import { logger } from '../helpers/logger' import { VideoResolution } from '../../shared/models/videos' @@ -11,15 +11,24 @@ import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-pla import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type' import { CONFIG } from '../initializers/config' +/** + * Optimize the original video file and replace it. The resolution is not changed. + */ async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFileModel) { const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR + const transcodeDirectory = CONFIG.STORAGE.TMP_DIR const newExtname = '.mp4' const inputVideoFile = inputVideoFileArg ? inputVideoFileArg : video.getOriginalFile() const videoInputPath = join(videosDirectory, video.getVideoFilename(inputVideoFile)) - const videoTranscodedPath = join(videosDirectory, video.id + '-transcoded' + newExtname) + const videoTranscodedPath = join(transcodeDirectory, video.id + '-transcoded' + newExtname) - const transcodeOptions = { + const transcodeType: TranscodeOptionsType = await canDoQuickTranscode(videoInputPath) + ? 'quick-transcode' + : 'video' + + const transcodeOptions: TranscodeOptions = { + type: transcodeType as any, // FIXME: typing issue inputPath: videoInputPath, outputPath: videoTranscodedPath, resolution: inputVideoFile.resolution @@ -32,18 +41,11 @@ async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFi await remove(videoInputPath) // Important to do this before getVideoFilename() to take in account the new file extension - inputVideoFile.set('extname', newExtname) + inputVideoFile.extname = newExtname const videoOutputPath = video.getVideoFilePath(inputVideoFile) - await move(videoTranscodedPath, videoOutputPath) - const stats = await stat(videoOutputPath) - const fps = await getVideoFileFPS(videoOutputPath) - inputVideoFile.set('size', stats.size) - inputVideoFile.set('fps', fps) - - await video.createTorrentAndSetInfoHash(inputVideoFile) - await inputVideoFile.save() + await onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath) } catch (err) { // Auto destruction... video.destroy().catch(err => logger.error('Cannot destruct video after transcoding failure.', { err })) @@ -52,8 +54,12 @@ async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFi } } +/** + * Transcode the original video file to a lower resolution. + */ async function transcodeOriginalVideofile (video: VideoModel, resolution: VideoResolution, isPortrait: boolean) { const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR + const transcodeDirectory = CONFIG.STORAGE.TMP_DIR const extname = '.mp4' // We are sure it's x264 in mp4 because optimizeOriginalVideofile was already executed @@ -66,27 +72,49 @@ async function transcodeOriginalVideofile (video: VideoModel, resolution: VideoR videoId: video.id }) const videoOutputPath = join(CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename(newVideoFile)) + const videoTranscodedPath = join(transcodeDirectory, video.getVideoFilename(newVideoFile)) const transcodeOptions = { + type: 'video' as 'video', inputPath: videoInputPath, - outputPath: videoOutputPath, + outputPath: videoTranscodedPath, resolution, isPortraitMode: isPortrait } await transcode(transcodeOptions) - const stats = await stat(videoOutputPath) - const fps = await getVideoFileFPS(videoOutputPath) + return onVideoFileTranscoding(video, newVideoFile, videoTranscodedPath, videoOutputPath) +} + +async function mergeAudioVideofile (video: VideoModel, resolution: VideoResolution) { + const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR + const transcodeDirectory = CONFIG.STORAGE.TMP_DIR + const newExtname = '.mp4' + + const inputVideoFile = video.getOriginalFile() - newVideoFile.set('size', stats.size) - newVideoFile.set('fps', fps) + const audioInputPath = join(videosDirectory, video.getVideoFilename(video.getOriginalFile())) + const videoTranscodedPath = join(transcodeDirectory, video.id + '-transcoded' + newExtname) - await video.createTorrentAndSetInfoHash(newVideoFile) + const transcodeOptions = { + type: 'merge-audio' as 'merge-audio', + inputPath: video.getPreview().getPath(), + outputPath: videoTranscodedPath, + audioPath: audioInputPath, + resolution + } + + await transcode(transcodeOptions) - await newVideoFile.save() + await remove(audioInputPath) - video.VideoFiles.push(newVideoFile) + // Important to do this before getVideoFilename() to take in account the new file extension + inputVideoFile.extname = newExtname + + const videoOutputPath = video.getVideoFilePath(inputVideoFile) + + return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath) } async function generateHlsPlaylist (video: VideoModel, resolution: VideoResolution, isPortraitMode: boolean) { @@ -97,6 +125,7 @@ async function generateHlsPlaylist (video: VideoModel, resolution: VideoResoluti const outputPath = join(baseHlsDirectory, VideoStreamingPlaylistModel.getHlsPlaylistFilename(resolution)) const transcodeOptions = { + type: 'hls' as 'hls', inputPath: videoInputPath, outputPath, resolution, @@ -125,8 +154,34 @@ async function generateHlsPlaylist (video: VideoModel, resolution: VideoResoluti }) } +// --------------------------------------------------------------------------- + export { generateHlsPlaylist, optimizeVideofile, - transcodeOriginalVideofile + transcodeOriginalVideofile, + mergeAudioVideofile +} + +// --------------------------------------------------------------------------- + +async function onVideoFileTranscoding (video: VideoModel, videoFile: VideoFileModel, transcodingPath: string, outputPath: string) { + const stats = await stat(transcodingPath) + const fps = await getVideoFileFPS(transcodingPath) + + await move(transcodingPath, outputPath) + + videoFile.set('size', stats.size) + videoFile.set('fps', fps) + + await video.createTorrentAndSetInfoHash(videoFile) + + const updatedVideoFile = await videoFile.save() + + // Add it if this is a new created file + if (video.VideoFiles.some(f => f.id === videoFile.id) === false) { + video.VideoFiles.push(updatedVideoFile) + } + + return video } diff --git a/server/middlewares/validators/feeds.ts b/server/middlewares/validators/feeds.ts index e4f5c98fe..dd362619d 100644 --- a/server/middlewares/validators/feeds.ts +++ b/server/middlewares/validators/feeds.ts @@ -1,21 +1,20 @@ import * as express from 'express' import { param, query } from 'express-validator/check' -import { doesAccountIdExist, isAccountNameValid, doesAccountNameWithHostExist } from '../../helpers/custom-validators/accounts' -import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc' +import { doesAccountIdExist, doesAccountNameWithHostExist } from '../../helpers/custom-validators/accounts' +import { isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc' import { logger } from '../../helpers/logger' import { areValidationErrors } from './utils' import { isValidRSSFeed } from '../../helpers/custom-validators/feeds' import { doesVideoChannelIdExist, doesVideoChannelNameWithHostExist } from '../../helpers/custom-validators/video-channels' import { doesVideoExist } from '../../helpers/custom-validators/videos' -import { isActorPreferredUsernameValid } from '../../helpers/custom-validators/activitypub/actor' const videoFeedsValidator = [ param('format').optional().custom(isValidRSSFeed).withMessage('Should have a valid format (rss, atom, json)'), query('format').optional().custom(isValidRSSFeed).withMessage('Should have a valid format (rss, atom, json)'), - query('accountId').optional().custom(isIdOrUUIDValid), - query('accountName').optional().custom(isAccountNameValid), - query('videoChannelId').optional().custom(isIdOrUUIDValid), - query('videoChannelName').optional().custom(isActorPreferredUsernameValid), + query('accountId').optional().custom(isIdValid), + query('accountName').optional(), + query('videoChannelId').optional().custom(isIdValid), + query('videoChannelName').optional(), async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking feeds parameters', { parameters: req.query }) diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts index 6d8cd7894..7a081af33 100644 --- a/server/middlewares/validators/users.ts +++ b/server/middlewares/validators/users.ts @@ -25,6 +25,10 @@ import { Redis } from '../../lib/redis' import { UserModel } from '../../models/account/user' import { areValidationErrors } from './utils' import { ActorModel } from '../../models/activitypub/actor' +import { isActorPreferredUsernameValid } from '../../helpers/custom-validators/activitypub/actor' +import { isVideoChannelNameValid } from '../../helpers/custom-validators/video-channels' +import { UserCreate } from '../../../shared/models/users' +import { UserRegister } from '../../../shared/models/users/user-register.model' const usersAddValidator = [ body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'), @@ -49,6 +53,8 @@ const usersRegisterValidator = [ body('username').custom(isUserUsernameValid).withMessage('Should have a valid username'), body('password').custom(isUserPasswordValid).withMessage('Should have a valid password'), body('email').isEmail().withMessage('Should have a valid email'), + body('channel.name').optional().custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), + body('channel.displayName').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid display name'), async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking usersRegister parameters', { parameters: omit(req.body, 'password') }) @@ -56,6 +62,28 @@ const usersRegisterValidator = [ if (areValidationErrors(req, res)) return if (!await checkUserNameOrEmailDoesNotAlreadyExist(req.body.username, req.body.email, res)) return + const body: UserRegister = req.body + if (body.channel) { + if (!body.channel.name || !body.channel.displayName) { + return res.status(400) + .send({ error: 'Channel is optional but if you specify it, channel.name and channel.displayName are required.' }) + .end() + } + + if (body.channel.name === body.username) { + return res.status(400) + .send({ error: 'Channel name cannot be the same than user username.' }) + .end() + } + + const existing = await ActorModel.loadLocalByName(body.channel.name) + if (existing) { + return res.status(409) + .send({ error: `Channel with name ${body.channel.name} already exists.` }) + .end() + } + } + return next() } ] diff --git a/server/middlewares/validators/videos/video-channels.ts b/server/middlewares/validators/videos/video-channels.ts index 4b26f0bc4..f5a59cacb 100644 --- a/server/middlewares/validators/videos/video-channels.ts +++ b/server/middlewares/validators/videos/video-channels.ts @@ -14,6 +14,7 @@ import { VideoChannelModel } from '../../../models/video/video-channel' import { areValidationErrors } from '../utils' import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor' import { ActorModel } from '../../../models/activitypub/actor' +import { isBooleanValid } from '../../../helpers/custom-validators/misc' const videoChannelsAddValidator = [ body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), @@ -40,9 +41,18 @@ const videoChannelsAddValidator = [ const videoChannelsUpdateValidator = [ param('nameWithHost').exists().withMessage('Should have an video channel name with host'), - body('displayName').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid display name'), - body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), - body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'), + body('displayName') + .optional() + .custom(isVideoChannelNameValid).withMessage('Should have a valid display name'), + body('description') + .optional() + .custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), + body('support') + .optional() + .custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'), + body('bulkVideosSupportUpdate') + .optional() + .custom(isBooleanValid).withMessage('Should have a valid bulkVideosSupportUpdate boolean field'), async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body }) diff --git a/server/middlewares/validators/videos/video-playlists.ts b/server/middlewares/validators/videos/video-playlists.ts index f68eeeeb3..9c88dd291 100644 --- a/server/middlewares/validators/videos/video-playlists.ts +++ b/server/middlewares/validators/videos/video-playlists.ts @@ -24,6 +24,9 @@ import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/ import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model' const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([ + body('displayName') + .custom(isVideoPlaylistNameValid).withMessage('Should have a valid display name'), + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking videoPlaylistsAddValidator parameters', { parameters: req.body }) @@ -46,6 +49,10 @@ const videoPlaylistsUpdateValidator = getCommonPlaylistEditAttributes().concat([ param('playlistId') .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), + body('displayName') + .optional() + .custom(isVideoPlaylistNameValid).withMessage('Should have a valid display name'), + async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking videoPlaylistsUpdateValidator parameters', { parameters: req.body }) @@ -61,12 +68,6 @@ const videoPlaylistsUpdateValidator = getCommonPlaylistEditAttributes().concat([ const body: VideoPlaylistUpdate = req.body - if (videoPlaylist.privacy !== VideoPlaylistPrivacy.PRIVATE && body.privacy === VideoPlaylistPrivacy.PRIVATE) { - cleanUpReqFiles(req) - return res.status(400) - .json({ error: 'Cannot set "private" a video playlist that was not private.' }) - } - const newPrivacy = body.privacy || videoPlaylist.privacy if (newPrivacy === VideoPlaylistPrivacy.PUBLIC && ( @@ -368,8 +369,6 @@ function getCommonPlaylistEditAttributes () { + CONSTRAINTS_FIELDS.VIDEO_PLAYLISTS.IMAGE.EXTNAME.join(', ') ), - body('displayName') - .custom(isVideoPlaylistNameValid).withMessage('Should have a valid display name'), body('description') .optional() .customSanitizer(toValueOrNull) diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts index 2b01f108d..b1c05ab2d 100644 --- a/server/middlewares/validators/videos/videos.ts +++ b/server/middlewares/validators/videos/videos.ts @@ -111,18 +111,10 @@ const videosUpdateValidator = getCommonVideoEditAttributes().concat([ if (areErrorsInScheduleUpdate(req, res)) return cleanUpReqFiles(req) if (!await doesVideoExist(req.params.id, res)) return cleanUpReqFiles(req) - const video = res.locals.video - // Check if the user who did the request is able to update the video const user = res.locals.oauth.token.User if (!checkUserCanManageVideo(user, res.locals.video, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req) - if (video.privacy !== VideoPrivacy.PRIVATE && req.body.privacy === VideoPrivacy.PRIVATE) { - cleanUpReqFiles(req) - return res.status(409) - .json({ error: 'Cannot set "private" a video that was not private.' }) - } - if (req.body.channelId && !await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) return next() diff --git a/server/models/account/account.ts b/server/models/account/account.ts index 2b04acd86..09cada096 100644 --- a/server/models/account/account.ts +++ b/server/models/account/account.ts @@ -47,7 +47,7 @@ export enum ScopeNames { attributes: [ 'id', 'name' ], include: [ { - attributes: [ 'id', 'uuid', 'preferredUsername', 'url', 'serverId', 'avatarId' ], + attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ], model: ActorModel.unscoped(), required: true, where: whereActor, @@ -180,22 +180,6 @@ export class AccountModel extends Model { return AccountModel.findByPk(id, { transaction }) } - static loadByUUID (uuid: string) { - const query = { - include: [ - { - model: ActorModel, - required: true, - where: { - uuid - } - } - ] - } - - return AccountModel.findOne(query) - } - static loadByNameWithHost (nameWithHost: string) { const [ accountName, host ] = nameWithHost.split('@') @@ -332,7 +316,6 @@ export class AccountModel extends Model { return { id: this.id, - uuid: actor.uuid, name: actor.name, displayName: this.getDisplayName(), url: actor.url, diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts index 4a466441c..bd6a2c8fd 100644 --- a/server/models/activitypub/actor.ts +++ b/server/models/activitypub/actor.ts @@ -7,13 +7,11 @@ import { Column, CreatedAt, DataType, - Default, DefaultScope, ForeignKey, HasMany, HasOne, Is, - IsUUID, Model, Scopes, Table, @@ -119,10 +117,6 @@ export const unusedActorAttributesForAPI = [ { fields: [ 'avatarId' ] }, - { - fields: [ 'uuid' ], - unique: true - }, { fields: [ 'followersUrl' ] } @@ -134,12 +128,6 @@ export class ActorModel extends Model { @Column(DataType.ENUM(...values(ACTIVITY_PUB_ACTOR_TYPES))) type: ActivityPubActorType - @AllowNull(false) - @Default(DataType.UUIDV4) - @IsUUID(4) - @Column(DataType.UUID) - uuid: string - @AllowNull(false) @Is('ActorPreferredUsername', value => throwIfNotValid(value, isActorPreferredUsernameValid, 'actor preferred username')) @Column @@ -408,7 +396,6 @@ export class ActorModel extends Model { return { id: this.id, url: this.url, - uuid: this.uuid, name: this.preferredUsername, host: this.getHost(), hostRedundancyAllowed: this.getRedundancyAllowed(), @@ -454,7 +441,6 @@ export class ActorModel extends Model { endpoints: { sharedInbox: this.sharedInboxUrl }, - uuid: this.uuid, publicKey: { id: this.getPublicKeyUrl(), owner: this.url, 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 { return WEBSERVER.URL + staticPath + this.filename } - removeThumbnail () { + getPath () { const directory = ThumbnailModel.types[this.type].directory - const thumbnailPath = join(directory, this.filename) + return join(directory, this.filename) + } - return remove(thumbnailPath) + removeThumbnail () { + return remove(this.getPath()) } } diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts index fb70e6625..b0b261c88 100644 --- a/server/models/video/video-channel.ts +++ b/server/models/video/video-channel.ts @@ -72,7 +72,7 @@ type AvailableForListOptions = { attributes: [ 'name', 'description', 'id', 'actorId' ], include: [ { - attributes: [ 'uuid', 'preferredUsername', 'url', 'serverId', 'avatarId' ], + attributes: [ 'preferredUsername', 'url', 'serverId', 'avatarId' ], model: ActorModel.unscoped(), required: true, include: [ @@ -334,14 +334,21 @@ export class VideoChannelModel extends Model { }) } - static listByAccount (accountId: number) { + static listByAccount (options: { + accountId: number, + start: number, + count: number, + sort: string + }) { const query = { - order: getSort('createdAt'), + offset: options.start, + limit: options.count, + order: getSort(options.sort), include: [ { model: AccountModel, where: { - id: accountId + id: options.accountId }, required: true } @@ -380,24 +387,6 @@ export class VideoChannelModel extends Model { .findByPk(id) } - static loadByUUIDAndPopulateAccount (uuid: string) { - const query = { - include: [ - { - model: ActorModel, - required: true, - where: { - uuid - } - } - ] - } - - return VideoChannelModel - .scope([ ScopeNames.WITH_ACCOUNT ]) - .findOne(query) - } - static loadByUrlAndPopulateAccount (url: string) { const query = { include: [ @@ -503,7 +492,6 @@ export class VideoChannelModel extends Model { return { id: this.id, - uuid: actor.uuid, name: actor.name, displayName: this.getDisplayName(), url: actor.url, 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' import { VideoRedundancyModel } from '../redundancy/video-redundancy' import { VideoStreamingPlaylistModel } from './video-streaming-playlist' import { FindOptions, QueryTypes, Transaction } from 'sequelize' +import { MIMETYPES } from '../../initializers/constants' @Table({ tableName: 'videoFile', @@ -161,6 +162,10 @@ export class VideoFileModel extends Model { })) } + isAudio () { + return !!MIMETYPES.AUDIO.EXT_MIMETYPE[this.extname] + } + hasSameUniqueKeysThan (other: VideoFileModel) { return this.fps === other.fps && this.resolution === other.resolution && diff --git a/server/models/video/video.ts b/server/models/video/video.ts index c0a7892a4..eccf0a4fa 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts @@ -1515,6 +1515,29 @@ export class VideoModel extends Model { .then(results => results.length === 1) } + static bulkUpdateSupportField (videoChannel: VideoChannelModel, t: Transaction) { + const options = { + where: { + channelId: videoChannel.id + }, + transaction: t + } + + return VideoModel.update({ support: videoChannel.support }, options) + } + + static getAllIdsFromChannel (videoChannel: VideoChannelModel) { + const query = { + attributes: [ 'id' ], + where: { + channelId: videoChannel.id + } + } + + return VideoModel.findAll(query) + .then(videos => videos.map(v => v.id)) + } + // threshold corresponds to how many video the field should have to be returned static async getRandomFieldSamples (field: 'category' | 'channelId', threshold: number, count: number) { const serverActor = await getServerActor() 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 @@ import * as chai from 'chai' import 'mocha' import { + cleanupTests, doubleFollow, flushAndRunMultipleServers, flushTests, @@ -39,7 +40,7 @@ describe('Test activitypub', function () { const object = res.body expect(object.type).to.equal('Person') - expect(object.id).to.equal('http://localhost:9001/accounts/root') + expect(object.id).to.equal('http://localhost:' + servers[0].port + '/accounts/root') expect(object.name).to.equal('root') expect(object.preferredUsername).to.equal('root') }) @@ -49,17 +50,17 @@ describe('Test activitypub', function () { const object = res.body expect(object.type).to.equal('Video') - expect(object.id).to.equal('http://localhost:9001/videos/watch/' + videoUUID) + expect(object.id).to.equal('http://localhost:' + servers[0].port + '/videos/watch/' + videoUUID) expect(object.name).to.equal('video') }) it('Should redirect to the origin video object', async function () { const res = await makeActivityPubGetRequest(servers[1].url, '/videos/watch/' + videoUUID, 302) - expect(res.header.location).to.equal('http://localhost:9001/videos/watch/' + videoUUID) + expect(res.header.location).to.equal('http://localhost:' + servers[0].port + '/videos/watch/' + videoUUID) }) - after(function () { - killallServers(servers) + after(async function () { + await cleanupTests(servers) }) }) 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 @@ import 'mocha' import { + cleanupTests, closeAllSequelize, createUser, doubleFollow, @@ -48,8 +49,16 @@ describe('Test ActivityPub fetcher', function () { const badVideoUUID = res.body.video.uuid await uploadVideo(servers[0].url, userAccessToken, { name: 'video user' }) - await setActorField(1, 'http://localhost:9001/accounts/user1', 'url', 'http://localhost:9002/accounts/user1') - await setVideoField(1, badVideoUUID, 'url', 'http://localhost:9003/videos/watch/' + badVideoUUID) + { + const to = 'http://localhost:' + servers[0].port + '/accounts/user1' + const value = 'http://localhost:' + servers[1].port + '/accounts/user1' + await setActorField(servers[0].internalServerNumber, to, 'url', value) + } + + { + const value = 'http://localhost:' + servers[2].port + '/videos/watch/' + badVideoUUID + await setVideoField(servers[0].internalServerNumber, badVideoUUID, 'url', value) + } }) it('Should add only the video with a valid actor URL', async function () { @@ -78,7 +87,9 @@ describe('Test ActivityPub fetcher', function () { }) after(async function () { - killallServers(servers) + this.timeout(10000) + + await cleanupTests(servers) await closeAllSequelize(servers) }) 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 @@ import 'mocha' import { + cleanupTests, closeAllSequelize, createVideoPlaylist, doubleFollow, flushAndRunMultipleServers, generateUserAccessToken, getVideo, getVideoPlaylist, - killallServers, rateVideo, + killallServers, reRunServer, ServerInfo, setAccessTokensToServers, @@ -48,26 +49,26 @@ describe('Test AP refresher', function () { } { - const a1 = await generateUserAccessToken(servers[1], 'user1') - await uploadVideo(servers[1].url, a1, { name: 'video4' }) + const a1 = await generateUserAccessToken(servers[ 1 ], 'user1') + await uploadVideo(servers[ 1 ].url, a1, { name: 'video4' }) - const a2 = await generateUserAccessToken(servers[1], 'user2') - await uploadVideo(servers[1].url, a2, { name: 'video5' }) + const a2 = await generateUserAccessToken(servers[ 1 ], 'user2') + await uploadVideo(servers[ 1 ].url, a2, { name: 'video5' }) } { - const playlistAttrs = { displayName: 'playlist1', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[1].videoChannel.id } - const res = await createVideoPlaylist({ url: servers[1].url, token: servers[1].accessToken, playlistAttrs }) + const playlistAttrs = { displayName: 'playlist1', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[ 1 ].videoChannel.id } + const res = await createVideoPlaylist({ url: servers[ 1 ].url, token: servers[ 1 ].accessToken, playlistAttrs }) playlistUUID1 = res.body.videoPlaylist.uuid } { - const playlistAttrs = { displayName: 'playlist2', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[1].videoChannel.id } - const res = await createVideoPlaylist({ url: servers[1].url, token: servers[1].accessToken, playlistAttrs }) + const playlistAttrs = { displayName: 'playlist2', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[ 1 ].videoChannel.id } + const res = await createVideoPlaylist({ url: servers[ 1 ].url, token: servers[ 1 ].accessToken, playlistAttrs }) playlistUUID2 = res.body.videoPlaylist.uuid } - await doubleFollow(servers[0], servers[1]) + await doubleFollow(servers[ 0 ], servers[ 1 ]) }) describe('Videos refresher', function () { @@ -78,7 +79,7 @@ describe('Test AP refresher', function () { await wait(10000) // Change UUID so the remote server returns a 404 - await setVideoField(2, videoUUID1, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b174f') + await setVideoField(servers[ 1 ].internalServerNumber, videoUUID1, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b174f') await getVideo(servers[ 0 ].url, videoUUID1) await getVideo(servers[ 0 ].url, videoUUID2) @@ -94,7 +95,7 @@ describe('Test AP refresher', function () { killallServers([ servers[ 1 ] ]) - await setVideoField(2, videoUUID3, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b174e') + await setVideoField(servers[ 1 ].internalServerNumber, videoUUID3, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b174e') // Video will need a refresh await wait(10000) @@ -121,15 +122,16 @@ describe('Test AP refresher', function () { await wait(10000) // Change actor name so the remote server returns a 404 - await setActorField(2, 'http://localhost:9002/accounts/user2', 'preferredUsername', 'toto') + const to = 'http://localhost:' + servers[ 1 ].port + '/accounts/user2' + await setActorField(servers[ 1 ].internalServerNumber, to, 'preferredUsername', 'toto') - await getAccount(servers[ 0 ].url, 'user1@localhost:9002') - await getAccount(servers[ 0 ].url, 'user2@localhost:9002') + await getAccount(servers[ 0 ].url, 'user1@localhost:' + servers[ 1 ].port) + await getAccount(servers[ 0 ].url, 'user2@localhost:' + servers[ 1 ].port) await waitJobs(servers) - await getAccount(servers[ 0 ].url, 'user1@localhost:9002', 200) - await getAccount(servers[ 0 ].url, 'user2@localhost:9002', 404) + await getAccount(servers[ 0 ].url, 'user1@localhost:' + servers[ 1 ].port, 200) + await getAccount(servers[ 0 ].url, 'user2@localhost:' + servers[ 1 ].port, 404) }) }) @@ -141,7 +143,7 @@ describe('Test AP refresher', function () { await wait(10000) // Change UUID so the remote server returns a 404 - await setPlaylistField(2, playlistUUID2, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b178e') + await setPlaylistField(servers[ 1 ].internalServerNumber, playlistUUID2, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b178e') await getVideoPlaylist(servers[ 0 ].url, playlistUUID1) await getVideoPlaylist(servers[ 0 ].url, playlistUUID2) @@ -153,7 +155,11 @@ describe('Test AP refresher', function () { }) }) - after(function () { - killallServers(servers) + after(async function () { + this.timeout(10000) + + await cleanupTests(servers) + + await closeAllSequelize(servers) }) }) 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 @@ import 'mocha' import { + cleanupTests, closeAllSequelize, flushAndRunMultipleServers, - flushTests, killallServers, ServerInfo, setActorField @@ -18,18 +18,26 @@ import { makeFollowRequest, makePOSTAPRequest } from '../../../../shared/extra-u const expect = chai.expect -function setKeysOfServer2 (serverNumber: number, publicKey: string, privateKey: string) { +function setKeysOfServer (onServer: ServerInfo, ofServer: ServerInfo, publicKey: string, privateKey: string) { return Promise.all([ - setActorField(serverNumber, 'http://localhost:9002/accounts/peertube', 'publicKey', publicKey), - setActorField(serverNumber, 'http://localhost:9002/accounts/peertube', 'privateKey', privateKey) + setActorField(onServer.internalServerNumber, 'http://localhost:' + ofServer.port + '/accounts/peertube', 'publicKey', publicKey), + setActorField(onServer.internalServerNumber, 'http://localhost:' + ofServer.port + '/accounts/peertube', 'privateKey', privateKey) ]) } -function setKeysOfServer3 (serverNumber: number, publicKey: string, privateKey: string) { - return Promise.all([ - setActorField(serverNumber, 'http://localhost:9003/accounts/peertube', 'publicKey', publicKey), - setActorField(serverNumber, 'http://localhost:9003/accounts/peertube', 'privateKey', privateKey) - ]) +function getAnnounceWithoutContext (server2: ServerInfo) { + const json = require('./json/peertube/announce-without-context.json') + const result: typeof json = {} + + for (const key of Object.keys(json)) { + if (Array.isArray(json[key])) { + result[key] = json[key].map(v => v.replace(':9002', `:${server2.port}`)) + } else { + result[ key ] = json[ key ].replace(':9002', `:${server2.port}`) + } + } + + return result } describe('Test ActivityPub security', function () { @@ -38,13 +46,13 @@ describe('Test ActivityPub security', function () { const keys = require('./json/peertube/keys.json') const invalidKeys = require('./json/peertube/invalid-keys.json') - const baseHttpSignature = { + const baseHttpSignature = () => ({ algorithm: HTTP_SIGNATURE.ALGORITHM, authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME, - keyId: 'acct:peertube@localhost:9002', + keyId: 'acct:peertube@localhost:' + servers[1].port, key: keys.privateKey, headers: HTTP_SIGNATURE.HEADERS_TO_SIGN - } + }) // --------------------------------------------------------------- @@ -55,56 +63,56 @@ describe('Test ActivityPub security', function () { url = servers[0].url + '/inbox' - await setKeysOfServer2(1, keys.publicKey, keys.privateKey) + await setKeysOfServer(servers[0], servers[1], keys.publicKey, keys.privateKey) - const to = { url: 'http://localhost:9001/accounts/peertube' } - const by = { url: 'http://localhost:9002/accounts/peertube', privateKey: keys.privateKey } + const to = { url: 'http://localhost:' + servers[0].port + '/accounts/peertube' } + const by = { url: 'http://localhost:' + servers[1].port + '/accounts/peertube', privateKey: keys.privateKey } await makeFollowRequest(to, by) }) describe('When checking HTTP signature', function () { it('Should fail with an invalid digest', async function () { - const body = activityPubContextify(require('./json/peertube/announce-without-context.json')) + const body = activityPubContextify(getAnnounceWithoutContext(servers[1])) const headers = { Digest: buildDigest({ hello: 'coucou' }) } - const { response } = await makePOSTAPRequest(url, body, baseHttpSignature, headers) + const { response } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers) expect(response.statusCode).to.equal(403) }) it('Should fail with an invalid date', async function () { - const body = activityPubContextify(require('./json/peertube/announce-without-context.json')) + const body = activityPubContextify(getAnnounceWithoutContext(servers[1])) const headers = buildGlobalHeaders(body) headers['date'] = 'Wed, 21 Oct 2015 07:28:00 GMT' - const { response } = await makePOSTAPRequest(url, body, baseHttpSignature, headers) + const { response } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers) expect(response.statusCode).to.equal(403) }) it('Should fail with bad keys', async function () { - await setKeysOfServer2(1, invalidKeys.publicKey, invalidKeys.privateKey) - await setKeysOfServer2(2, invalidKeys.publicKey, invalidKeys.privateKey) + await setKeysOfServer(servers[0], servers[1], invalidKeys.publicKey, invalidKeys.privateKey) + await setKeysOfServer(servers[1], servers[1], invalidKeys.publicKey, invalidKeys.privateKey) - const body = activityPubContextify(require('./json/peertube/announce-without-context.json')) + const body = activityPubContextify(getAnnounceWithoutContext(servers[1])) const headers = buildGlobalHeaders(body) - const { response } = await makePOSTAPRequest(url, body, baseHttpSignature, headers) + const { response } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers) expect(response.statusCode).to.equal(403) }) it('Should succeed with a valid HTTP signature', async function () { - await setKeysOfServer2(1, keys.publicKey, keys.privateKey) - await setKeysOfServer2(2, keys.publicKey, keys.privateKey) + await setKeysOfServer(servers[0], servers[1], keys.publicKey, keys.privateKey) + await setKeysOfServer(servers[1], servers[1], keys.publicKey, keys.privateKey) - const body = activityPubContextify(require('./json/peertube/announce-without-context.json')) + const body = activityPubContextify(getAnnounceWithoutContext(servers[1])) const headers = buildGlobalHeaders(body) - const { response } = await makePOSTAPRequest(url, body, baseHttpSignature, headers) + const { response } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers) expect(response.statusCode).to.equal(204) }) @@ -112,28 +120,28 @@ describe('Test ActivityPub security', function () { describe('When checking Linked Data Signature', function () { before(async () => { - await setKeysOfServer3(3, keys.publicKey, keys.privateKey) + await setKeysOfServer(servers[2], servers[2], keys.publicKey, keys.privateKey) - const to = { url: 'http://localhost:9001/accounts/peertube' } - const by = { url: 'http://localhost:9003/accounts/peertube', privateKey: keys.privateKey } + const to = { url: 'http://localhost:' + servers[0].port + '/accounts/peertube' } + const by = { url: 'http://localhost:' + servers[2].port + '/accounts/peertube', privateKey: keys.privateKey } await makeFollowRequest(to, by) }) it('Should fail with bad keys', async function () { this.timeout(10000) - await setKeysOfServer3(1, invalidKeys.publicKey, invalidKeys.privateKey) - await setKeysOfServer3(3, invalidKeys.publicKey, invalidKeys.privateKey) + await setKeysOfServer(servers[0], servers[2], invalidKeys.publicKey, invalidKeys.privateKey) + await setKeysOfServer(servers[2], servers[2], invalidKeys.publicKey, invalidKeys.privateKey) - const body = require('./json/peertube/announce-without-context.json') - body.actor = 'http://localhost:9003/accounts/peertube' + const body = getAnnounceWithoutContext(servers[1]) + body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube' - const signer: any = { privateKey: invalidKeys.privateKey, url: 'http://localhost:9003/accounts/peertube' } + const signer: any = { privateKey: invalidKeys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' } const signedBody = await buildSignedActivity(signer, body) const headers = buildGlobalHeaders(signedBody) - const { response } = await makePOSTAPRequest(url, signedBody, baseHttpSignature, headers) + const { response } = await makePOSTAPRequest(url, signedBody, baseHttpSignature(), headers) expect(response.statusCode).to.equal(403) }) @@ -141,20 +149,20 @@ describe('Test ActivityPub security', function () { it('Should fail with an altered body', async function () { this.timeout(10000) - await setKeysOfServer3(1, keys.publicKey, keys.privateKey) - await setKeysOfServer3(3, keys.publicKey, keys.privateKey) + await setKeysOfServer(servers[0], servers[2], keys.publicKey, keys.privateKey) + await setKeysOfServer(servers[0], servers[2], keys.publicKey, keys.privateKey) - const body = require('./json/peertube/announce-without-context.json') - body.actor = 'http://localhost:9003/accounts/peertube' + const body = getAnnounceWithoutContext(servers[1]) + body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube' - const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:9003/accounts/peertube' } + const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' } const signedBody = await buildSignedActivity(signer, body) - signedBody.actor = 'http://localhost:9003/account/peertube' + signedBody.actor = 'http://localhost:' + servers[2].port + '/account/peertube' const headers = buildGlobalHeaders(signedBody) - const { response } = await makePOSTAPRequest(url, signedBody, baseHttpSignature, headers) + const { response } = await makePOSTAPRequest(url, signedBody, baseHttpSignature(), headers) expect(response.statusCode).to.equal(403) }) @@ -162,22 +170,24 @@ describe('Test ActivityPub security', function () { it('Should succeed with a valid signature', async function () { this.timeout(10000) - const body = require('./json/peertube/announce-without-context.json') - body.actor = 'http://localhost:9003/accounts/peertube' + const body = getAnnounceWithoutContext(servers[1]) + body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube' - const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:9003/accounts/peertube' } + const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' } const signedBody = await buildSignedActivity(signer, body) const headers = buildGlobalHeaders(signedBody) - const { response } = await makePOSTAPRequest(url, signedBody, baseHttpSignature, headers) + const { response } = await makePOSTAPRequest(url, signedBody, baseHttpSignature(), headers) expect(response.statusCode).to.equal(204) }) }) after(async function () { - killallServers(servers) + this.timeout(10000) + + await cleanupTests(servers) await closeAllSequelize(servers) }) 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 () { transcoding: { enabled: true, allowAdditionalExtensions: true, + allowAudioFiles: true, threads: 1, resolutions: { '240p': false, diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts index 5935104a5..95097817b 100644 --- a/server/tests/api/check-params/users.ts +++ b/server/tests/api/check-params/users.ts @@ -6,6 +6,7 @@ import { join } from 'path' import { UserRole, VideoImport, VideoImportState } from '../../../../shared' import { + addVideoChannel, blockUser, cleanupTests, createUser, @@ -638,7 +639,7 @@ describe('Test users API validators', function () { }) }) - describe('When register a new user', function () { + describe('When registering a new user', function () { const registrationPath = path + '/register' const baseCorrectParams = { username: 'user3', @@ -724,12 +725,42 @@ describe('Test users API validators', function () { }) }) + it('Should fail with a bad channel name', async function () { + const fields = immutableAssign(baseCorrectParams, { channel: { name: '[]azf', displayName: 'toto' } }) + + await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) + }) + + it('Should fail with a bad channel display name', async function () { + const fields = immutableAssign(baseCorrectParams, { channel: { name: 'toto', displayName: '' } }) + + await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) + }) + + it('Should fail with a channel name that is the same than user username', async function () { + const source = { username: 'super_user', channel: { name: 'super_user', displayName: 'display name' } } + const fields = immutableAssign(baseCorrectParams, source) + + await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields }) + }) + + it('Should fail with an existing channel', async function () { + const videoChannelAttributesArg = { name: 'existing_channel', displayName: 'hello', description: 'super description' } + await addVideoChannel(server.url, server.accessToken, videoChannelAttributesArg) + + const fields = immutableAssign(baseCorrectParams, { channel: { name: 'existing_channel', displayName: 'toto' } }) + + await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, fields, statusCodeExpected: 409 }) + }) + it('Should succeed with the correct params', async function () { + const fields = immutableAssign(baseCorrectParams, { channel: { name: 'super_channel', displayName: 'toto' } }) + await makePostBodyRequest({ url: server.url, path: registrationPath, token: server.accessToken, - fields: baseCorrectParams, + fields: fields, statusCodeExpected: 204 }) }) diff --git a/server/tests/api/check-params/video-channels.ts b/server/tests/api/check-params/video-channels.ts index 65bc20613..de88298d1 100644 --- a/server/tests/api/check-params/video-channels.ts +++ b/server/tests/api/check-params/video-channels.ts @@ -24,6 +24,7 @@ import { checkBadStartPagination } from '../../../../shared/extra-utils/requests/check-api-params' import { join } from 'path' +import { VideoChannelUpdate } from '../../../../shared/models/videos' const expect = chai.expect @@ -67,8 +68,30 @@ describe('Test video channels API validator', function () { }) describe('When listing account video channels', function () { + const accountChannelPath = '/api/v1/accounts/fake/video-channels' + + it('Should fail with a bad start pagination', async function () { + await checkBadStartPagination(server.url, accountChannelPath, server.accessToken) + }) + + it('Should fail with a bad count pagination', async function () { + await checkBadCountPagination(server.url, accountChannelPath, server.accessToken) + }) + + it('Should fail with an incorrect sort', async function () { + await checkBadSortPagination(server.url, accountChannelPath, server.accessToken) + }) + it('Should fail with a unknown account', async function () { - await getAccountVideoChannelsList(server.url, 'unknown', 404) + await getAccountVideoChannelsList({ url: server.url, accountName: 'unknown', specialStatus: 404 }) + }) + + it('Should succeed with the correct parameters', async function () { + await makeGetRequest({ + url: server.url, + path: accountChannelPath, + statusCodeExpected: 200 + }) }) }) @@ -147,9 +170,11 @@ describe('Test video channels API validator', function () { }) describe('When updating a video channel', function () { - const baseCorrectParams = { + const baseCorrectParams: VideoChannelUpdate = { displayName: 'hello', - description: 'super description' + description: 'super description', + support: 'toto', + bulkVideosSupportUpdate: false } let path: string @@ -192,6 +217,11 @@ describe('Test video channels API validator', function () { await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields }) }) + it('Should fail with a bad bulkVideosSupportUpdate field', async function () { + const fields = immutableAssign(baseCorrectParams, { bulkVideosSupportUpdate: 'super' }) + await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields }) + }) + it('Should succeed with the correct parameters', async function () { await makePutBodyRequest({ url: server.url, diff --git a/server/tests/api/check-params/video-playlists.ts b/server/tests/api/check-params/video-playlists.ts index b7b94c035..8c5e44bdd 100644 --- a/server/tests/api/check-params/video-playlists.ts +++ b/server/tests/api/check-params/video-playlists.ts @@ -205,7 +205,6 @@ describe('Test video playlists API validator', function () { const params = getBase({ displayName: undefined }) await createVideoPlaylist(params) - await updateVideoPlaylist(getUpdate(params, playlistUUID)) }) it('Should fail with an incorrect display name', async function () { @@ -269,17 +268,6 @@ describe('Test video playlists API validator', function () { )) }) - it('Should fail to update to private a public/unlisted playlist', async function () { - const params = getBase({ privacy: VideoPlaylistPrivacy.PUBLIC }, { expectedStatus: 200 }) - - const res = await createVideoPlaylist(params) - const playlist = res.body.videoPlaylist - - const paramsUpdate = getBase({ privacy: VideoPlaylistPrivacy.PRIVATE }, { expectedStatus: 400 }) - - await updateVideoPlaylist(getUpdate(paramsUpdate, playlist.id)) - }) - it('Should fail to update the watch later playlist', async function () { await updateVideoPlaylist(getUpdate( getBase({}, { expectedStatus: 400 }), 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 @@ -import './check-params' -import './notifications' -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 @@ -import './server' -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 @@ -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 @@ -import './redundancy' -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 @@ // Order of the tests we want to execute -import './index-1' -import './index-2' -import './index-3' -import './index-4' +import './activitypub' +import './check-params' +import './notifications' +import './redundancy' +import './search' +import './server' +import './users' +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 () { before(async function () { this.timeout(120000) - await MockSmtpServer.Instance.collectEmails(emails) + const port = await MockSmtpServer.Instance.collectEmails(emails) const overrideConfig = { smtp: { - hostname: 'localhost' + hostname: 'localhost', + port } } servers = await flushAndRunMultipleServers(3, overrideConfig) @@ -194,7 +195,7 @@ describe('Test users notifications', function () { it('Should send a new video notification if the user follows the local video publisher', async function () { this.timeout(15000) - await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:9001') + await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:' + servers[0].port) await waitJobs(servers) const { name, uuid } = await uploadVideoByLocalAccount(servers) @@ -204,7 +205,7 @@ describe('Test users notifications', function () { it('Should send a new video notification from a remote account', async function () { this.timeout(50000) // Server 2 has transcoding enabled - await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:9002') + await addUserSubscription(servers[0].url, userAccessToken, 'root_channel@localhost:' + servers[1].port) await waitJobs(servers) const { name, uuid } = await uploadVideoByRemoteAccount(servers) @@ -578,7 +579,9 @@ describe('Test users notifications', function () { const uuid = resVideo.body.video.uuid await waitJobs(servers) - const resThread = await addVideoCommentThread(servers[1].url, servers[1].accessToken, uuid, 'hello @user_1@localhost:9001 1') + + const text1 = `hello @user_1@localhost:${servers[ 0 ].port} 1` + const resThread = await addVideoCommentThread(servers[1].url, servers[1].accessToken, uuid, text1) const server2ThreadId = resThread.body.comment.id await waitJobs(servers) @@ -588,8 +591,8 @@ describe('Test users notifications', function () { const server1ThreadId = resThread2.body.data[0].id await checkCommentMention(baseParams, uuid, server1ThreadId, server1ThreadId, 'super root 2 name', 'presence') - const text = '@user_1@localhost:9001 hello 2 @root@localhost:9001' - await addVideoCommentReply(servers[1].url, servers[1].accessToken, uuid, server2ThreadId, text) + const text2 = `@user_1@localhost:${servers[ 0 ].port} hello 2 @root@localhost:${servers[ 0 ].port}` + await addVideoCommentReply(servers[1].url, servers[1].accessToken, uuid, server2ThreadId, text2) await waitJobs(servers) @@ -889,10 +892,10 @@ describe('Test users notifications', function () { await waitJobs(servers) - await checkNewInstanceFollower(baseParams, 'localhost:9003', 'presence') + await checkNewInstanceFollower(baseParams, 'localhost:' + servers[2].port, 'presence') const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } } - await checkNewInstanceFollower(immutableAssign(baseParams, userOverride), 'localhost:9003', 'absence') + await checkNewInstanceFollower(immutableAssign(baseParams, userOverride), 'localhost:' + servers[2].port, 'absence') }) }) @@ -933,29 +936,29 @@ describe('Test users notifications', function () { it('Should notify when a local channel is following one of our channel', async function () { this.timeout(10000) - await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:9001') + await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port) await waitJobs(servers) await checkNewActorFollow(baseParams, 'channel', 'root', 'super root name', myChannelName, 'presence') - await removeUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:9001') + await removeUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port) }) it('Should notify when a remote channel is following one of our channel', async function () { this.timeout(10000) - await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:9001') + await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port) await waitJobs(servers) await checkNewActorFollow(baseParams, 'channel', 'root', 'super root 2 name', myChannelName, 'presence') - await removeUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:9001') + await removeUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port) }) it('Should notify when a local account is following one of our channel', async function () { this.timeout(10000) - await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1@localhost:9001') + await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1@localhost:' + servers[0].port) await waitJobs(servers) @@ -965,7 +968,7 @@ describe('Test users notifications', function () { it('Should notify when a remote account is following one of our channel', async function () { this.timeout(10000) - await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1@localhost:9001') + await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1@localhost:' + servers[0].port) await waitJobs(servers) @@ -1019,8 +1022,8 @@ describe('Test users notifications', function () { autoBlacklistTestsCustomConfig.transcoding.enabled = true await updateCustomConfig(servers[0].url, servers[0].accessToken, autoBlacklistTestsCustomConfig) - await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:9001') - await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:9001') + await addUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port) + await addUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port) }) @@ -1142,8 +1145,8 @@ describe('Test users notifications', function () { after(async () => { await updateCustomConfig(servers[0].url, servers[0].accessToken, currentCustomConfig) - await removeUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:9001') - await removeUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:9001') + await removeUserSubscription(servers[0].url, servers[0].accessToken, 'user_1_channel@localhost:' + servers[0].port) + await removeUserSubscription(servers[1].url, servers[1].accessToken, 'user_1_channel@localhost:' + servers[0].port) }) }) 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) { if (!videoUUID) videoUUID = video1Server2UUID const webseeds = [ - 'http://localhost:9002/static/webseed/' + videoUUID + `http://localhost:${servers[ 1 ].port}/static/webseed/${videoUUID}` ] for (const server of servers) { @@ -118,8 +118,8 @@ async function check2Webseeds (videoUUID?: string) { if (!videoUUID) videoUUID = video1Server2UUID const webseeds = [ - 'http://localhost:9001/static/redundancy/' + videoUUID, - 'http://localhost:9002/static/webseed/' + videoUUID + `http://localhost:${servers[ 0 ].port}/static/redundancy/${videoUUID}`, + `http://localhost:${servers[ 1 ].port}/static/webseed/${videoUUID}` ] for (const server of servers) { @@ -145,7 +145,12 @@ async function check2Webseeds (videoUUID?: string) { } } - for (const directory of [ 'test1/redundancy', 'test2/videos' ]) { + const directories = [ + 'test' + servers[0].internalServerNumber + '/redundancy', + 'test' + servers[1].internalServerNumber + '/videos' + ] + + for (const directory of directories) { const files = await readdir(join(root(), directory)) expect(files).to.have.length.at.least(4) @@ -194,7 +199,12 @@ async function check1PlaylistRedundancies (videoUUID?: string) { await checkSegmentHash(baseUrlPlaylist, baseUrlSegment, videoUUID, resolution, hlsPlaylist) } - for (const directory of [ 'test1/redundancy/hls', 'test2/streaming-playlists/hls' ]) { + const directories = [ + 'test' + servers[0].internalServerNumber + '/redundancy/hls', + 'test' + servers[1].internalServerNumber + '/streaming-playlists/hls' + ] + + for (const directory of directories) { const files = await readdir(join(root(), directory, videoUUID)) expect(files).to.have.length.at.least(4) @@ -239,8 +249,8 @@ async function enableRedundancyOnServer1 () { const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 5, '-createdAt') const follows: ActorFollow[] = res.body.data - const server2 = follows.find(f => f.following.host === 'localhost:9002') - const server3 = follows.find(f => f.following.host === 'localhost:9003') + const server2 = follows.find(f => f.following.host === `localhost:${servers[ 1 ].port}`) + const server3 = follows.find(f => f.following.host === `localhost:${servers[ 2 ].port}`) expect(server3).to.not.be.undefined expect(server3.following.hostRedundancyAllowed).to.be.false @@ -254,8 +264,8 @@ async function disableRedundancyOnServer1 () { const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 5, '-createdAt') const follows: ActorFollow[] = res.body.data - const server2 = follows.find(f => f.following.host === 'localhost:9002') - const server3 = follows.find(f => f.following.host === 'localhost:9003') + const server2 = follows.find(f => f.following.host === `localhost:${servers[ 1 ].port}`) + const server3 = follows.find(f => f.following.host === `localhost:${servers[ 2 ].port}`) expect(server3).to.not.be.undefined expect(server3.following.hostRedundancyAllowed).to.be.false @@ -475,12 +485,12 @@ describe('Test videos redundancy', function () { await wait(10000) try { - await checkContains(servers, 'http%3A%2F%2Flocalhost%3A9001') + await checkContains(servers, 'http%3A%2F%2Flocalhost%3A' + servers[0].port) } catch { // Maybe a server deleted a redundancy in the scheduler await wait(2000) - await checkContains(servers, 'http%3A%2F%2Flocalhost%3A9001') + await checkContains(servers, 'http%3A%2F%2Flocalhost%3A' + servers[0].port) } }) @@ -491,7 +501,7 @@ describe('Test videos redundancy', function () { await wait(15000) - await checkNotContains([ servers[1], servers[2] ], 'http%3A%2F%2Flocalhost%3A9001') + await checkNotContains([ servers[1], servers[2] ], 'http%3A%2F%2Flocalhost%3A' + servers[0].port) }) 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 @@ import * as chai from 'chai' import 'mocha' import { - addVideoChannel, cleanupTests, + addVideoChannel, + cleanupTests, createUser, deleteVideoChannel, flushAndRunMultipleServers, - flushTests, - getVideoChannelsList, getVideoChannelVideos, - killallServers, + getVideoChannelsList, + getVideoChannelVideos, ServerInfo, setAccessTokensToServers, - updateMyUser, updateVideo, + updateMyUser, + updateVideo, updateVideoChannel, uploadVideo, userLogin, @@ -24,7 +25,7 @@ import { searchVideoChannel } from '../../../../shared/extra-utils/search/video- const expect = chai.expect -describe('Test a ActivityPub video channels search', function () { +describe('Test ActivityPub video channels search', function () { let servers: ServerInfo[] let userServer2Token: string let videoServer2UUID: string @@ -67,7 +68,7 @@ describe('Test a ActivityPub video channels search', function () { it('Should not find a remote video channel', async function () { { - const search = 'http://localhost:9002/video-channels/channel1_server3' + const search = 'http://localhost:' + servers[ 1 ].port + '/video-channels/channel1_server3' const res = await searchVideoChannel(servers[ 0 ].url, search, servers[ 0 ].accessToken) expect(res.body.total).to.equal(0) @@ -77,7 +78,7 @@ describe('Test a ActivityPub video channels search', function () { { // Without token - const search = 'http://localhost:9002/video-channels/channel1_server2' + const search = 'http://localhost:' + servers[ 1 ].port + '/video-channels/channel1_server2' const res = await searchVideoChannel(servers[0].url, search) expect(res.body.total).to.equal(0) @@ -88,8 +89,8 @@ describe('Test a ActivityPub video channels search', function () { it('Should search a local video channel', async function () { const searches = [ - 'http://localhost:9001/video-channels/channel1_server1', - 'channel1_server1@localhost:9001' + 'http://localhost:' + servers[ 0 ].port + '/video-channels/channel1_server1', + 'channel1_server1@localhost:' + servers[ 0 ].port ] for (const search of searches) { @@ -105,8 +106,8 @@ describe('Test a ActivityPub video channels search', function () { it('Should search a remote video channel with URL or handle', async function () { const searches = [ - 'http://localhost:9002/video-channels/channel1_server2', - 'channel1_server2@localhost:9002' + 'http://localhost:' + servers[ 1 ].port + '/video-channels/channel1_server2', + 'channel1_server2@localhost:' + servers[ 1 ].port ] for (const search of searches) { @@ -134,13 +135,13 @@ describe('Test a ActivityPub video channels search', function () { await waitJobs(servers) - const res = await getVideoChannelVideos(servers[0].url, null, 'channel1_server2@localhost:9002', 0, 5) + const res = await getVideoChannelVideos(servers[0].url, null, 'channel1_server2@localhost:' + servers[ 1 ].port, 0, 5) expect(res.body.total).to.equal(0) expect(res.body.data).to.have.lengthOf(0) }) it('Should list video channel videos of server 2 with token', async function () { - const res = await getVideoChannelVideos(servers[0].url, servers[0].accessToken, 'channel1_server2@localhost:9002', 0, 5) + const res = await getVideoChannelVideos(servers[0].url, servers[0].accessToken, 'channel1_server2@localhost:' + servers[ 1 ].port, 0, 5) expect(res.body.total).to.equal(1) expect(res.body.data[0].name).to.equal('video 1 server 2') @@ -156,7 +157,7 @@ describe('Test a ActivityPub video channels search', function () { // Expire video channel await wait(10000) - const search = 'http://localhost:9002/video-channels/channel1_server2' + const search = 'http://localhost:' + servers[ 1 ].port + '/video-channels/channel1_server2' const res = await searchVideoChannel(servers[0].url, search, servers[0].accessToken) expect(res.body.total).to.equal(1) expect(res.body.data).to.have.lengthOf(1) @@ -179,12 +180,13 @@ describe('Test a ActivityPub video channels search', function () { // Expire video channel await wait(10000) - const search = 'http://localhost:9002/video-channels/channel1_server2' + const search = 'http://localhost:' + servers[ 1 ].port + '/video-channels/channel1_server2' await searchVideoChannel(servers[0].url, search, servers[0].accessToken) await waitJobs(servers) - const res = await getVideoChannelVideos(servers[0].url, servers[0].accessToken, 'channel1_server2@localhost:9002', 0, 5, '-createdAt') + const videoChannelName = 'channel1_server2@localhost:' + servers[ 1 ].port + const res = await getVideoChannelVideos(servers[0].url, servers[0].accessToken, videoChannelName, 0, 5, '-createdAt') expect(res.body.total).to.equal(2) expect(res.body.data[0].name).to.equal('video 2 server 2') @@ -200,7 +202,8 @@ describe('Test a ActivityPub video channels search', function () { // Expire video await wait(10000) - const res = await searchVideoChannel(servers[0].url, 'http://localhost:9002/video-channels/channel1_server2', servers[0].accessToken) + const search = 'http://localhost:' + servers[ 1 ].port + '/video-channels/channel1_server2' + const res = await searchVideoChannel(servers[0].url, search, servers[0].accessToken) expect(res.body.total).to.equal(0) expect(res.body.data).to.have.lengthOf(0) }) 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' import 'mocha' import { addVideoChannel, + cleanupTests, flushAndRunMultipleServers, - flushTests, getVideosList, - killallServers, removeVideo, + searchVideo, searchVideoWithToken, ServerInfo, setAccessTokensToServers, updateVideo, uploadVideo, - wait, - searchVideo, cleanupTests + wait } from '../../../../shared/extra-utils' import { waitJobs } from '../../../../shared/extra-utils/server/jobs' import { Video, VideoPrivacy } from '../../../../shared/models/videos' const expect = chai.expect -describe('Test a ActivityPub videos search', function () { +describe('Test ActivityPub videos search', function () { let servers: ServerInfo[] let videoServer1UUID: string let videoServer2UUID: string @@ -49,7 +48,8 @@ describe('Test a ActivityPub videos search', function () { it('Should not find a remote video', async function () { { - const res = await searchVideoWithToken(servers[ 0 ].url, 'http://localhost:9002/videos/watch/43', servers[ 0 ].accessToken) + const search = 'http://localhost:' + servers[1].port + '/videos/watch/43' + const res = await searchVideoWithToken(servers[ 0 ].url, search, servers[ 0 ].accessToken) expect(res.body.total).to.equal(0) expect(res.body.data).to.be.an('array') @@ -58,7 +58,8 @@ describe('Test a ActivityPub videos search', function () { { // Without token - const res = await searchVideo(servers[0].url, 'http://localhost:9002/videos/watch/' + videoServer2UUID) + const search = 'http://localhost:' + servers[1].port + '/videos/watch/' + videoServer2UUID + const res = await searchVideo(servers[0].url, search) expect(res.body.total).to.equal(0) expect(res.body.data).to.be.an('array') @@ -67,7 +68,8 @@ describe('Test a ActivityPub videos search', function () { }) it('Should search a local video', async function () { - const res = await searchVideo(servers[0].url, 'http://localhost:9001/videos/watch/' + videoServer1UUID) + const search = 'http://localhost:' + servers[0].port + '/videos/watch/' + videoServer1UUID + const res = await searchVideo(servers[0].url, search) expect(res.body.total).to.equal(1) expect(res.body.data).to.be.an('array') @@ -76,7 +78,8 @@ describe('Test a ActivityPub videos search', function () { }) it('Should search a remote video', async function () { - const res = await searchVideoWithToken(servers[0].url, 'http://localhost:9002/videos/watch/' + videoServer2UUID, servers[0].accessToken) + const search = 'http://localhost:' + servers[1].port + '/videos/watch/' + videoServer2UUID + const res = await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) expect(res.body.total).to.equal(1) expect(res.body.data).to.be.an('array') @@ -114,12 +117,13 @@ describe('Test a ActivityPub videos search', function () { await wait(10000) // Will run refresh async - await searchVideoWithToken(servers[0].url, 'http://localhost:9002/videos/watch/' + videoServer2UUID, servers[0].accessToken) + const search = 'http://localhost:' + servers[1].port + '/videos/watch/' + videoServer2UUID + await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) // Wait refresh await wait(5000) - const res = await searchVideoWithToken(servers[0].url, 'http://localhost:9002/videos/watch/' + videoServer2UUID, servers[0].accessToken) + const res = await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) expect(res.body.total).to.equal(1) expect(res.body.data).to.have.lengthOf(1) @@ -139,12 +143,13 @@ describe('Test a ActivityPub videos search', function () { await wait(10000) // Will run refresh async - await searchVideoWithToken(servers[0].url, 'http://localhost:9002/videos/watch/' + videoServer2UUID, servers[0].accessToken) + const search = 'http://localhost:' + servers[1].port + '/videos/watch/' + videoServer2UUID + await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) // Wait refresh await wait(5000) - const res = await searchVideoWithToken(servers[0].url, 'http://localhost:9002/videos/watch/' + videoServer2UUID, servers[0].accessToken) + const res = await searchVideoWithToken(servers[0].url, search, servers[0].accessToken) expect(res.body.total).to.equal(0) expect(res.body.data).to.have.lengthOf(0) }) 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' import 'mocha' import { advancedVideosSearch, - flushTests, - killallServers, + cleanupTests, flushAndRunServer, + immutableAssign, searchVideo, ServerInfo, setAccessTokensToServers, uploadVideo, - wait, - immutableAssign, - cleanupTests + wait } from '../../../../shared/extra-utils' const expect = chai.expect -describe('Test a videos search', function () { +describe('Test videos search', function () { let server: ServerInfo = null let startDate: string 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 { getAbout, getConfig, getCustomConfig, - killallServers, + killallServers, parallelTests, registerUser, - reRunServer, + reRunServer, ServerInfo, setAccessTokensToServers, - updateCustomConfig + updateCustomConfig, uploadVideo } from '../../../../shared/extra-utils' import { ServerConfig } from '../../../../shared/models' const expect = chai.expect -function checkInitialConfig (data: CustomConfig) { +function checkInitialConfig (server: ServerInfo, data: CustomConfig) { expect(data.instance.name).to.equal('PeerTube') expect(data.instance.shortDescription).to.equal( 'PeerTube, a federated (ActivityPub) video streaming platform using P2P (BitTorrent) directly in the web browser ' + @@ -45,13 +45,14 @@ function checkInitialConfig (data: CustomConfig) { expect(data.signup.limit).to.equal(4) expect(data.signup.requiresEmailVerification).to.be.false - expect(data.admin.email).to.equal('admin1@example.com') + expect(data.admin.email).to.equal('admin' + server.internalServerNumber + '@example.com') expect(data.contactForm.enabled).to.be.true expect(data.user.videoQuota).to.equal(5242880) expect(data.user.videoQuotaDaily).to.equal(-1) expect(data.transcoding.enabled).to.be.false expect(data.transcoding.allowAdditionalExtensions).to.be.false + expect(data.transcoding.allowAudioFiles).to.be.false expect(data.transcoding.threads).to.equal(2) expect(data.transcoding.resolutions['240p']).to.be.true expect(data.transcoding.resolutions['360p']).to.be.true @@ -89,7 +90,11 @@ function checkUpdatedConfig (data: CustomConfig) { expect(data.signup.limit).to.equal(5) expect(data.signup.requiresEmailVerification).to.be.false - expect(data.admin.email).to.equal('superadmin1@example.com') + // We override admin email in parallel tests, so skip this exception + if (parallelTests() === false) { + expect(data.admin.email).to.equal('superadmin1@example.com') + } + expect(data.contactForm.enabled).to.be.false expect(data.user.videoQuota).to.equal(5242881) @@ -98,6 +103,7 @@ function checkUpdatedConfig (data: CustomConfig) { expect(data.transcoding.enabled).to.be.true expect(data.transcoding.threads).to.equal(1) expect(data.transcoding.allowAdditionalExtensions).to.be.true + expect(data.transcoding.allowAudioFiles).to.be.true expect(data.transcoding.resolutions['240p']).to.be.false expect(data.transcoding.resolutions['360p']).to.be.true expect(data.transcoding.resolutions['480p']).to.be.true @@ -118,6 +124,7 @@ describe('Test config', function () { before(async function () { this.timeout(30000) + server = await flushAndRunServer(1) await setAccessTokensToServers([ server ]) }) @@ -153,6 +160,9 @@ describe('Test config', function () { expect(data.video.file.extensions).to.contain('.webm') expect(data.video.file.extensions).to.contain('.ogv') + await uploadVideo(server.url, server.accessToken, { fixture: 'video_short.mkv' }, 400) + await uploadVideo(server.url, server.accessToken, { fixture: 'sample.ogg' }, 400) + expect(data.contactForm.enabled).to.be.true }) @@ -160,7 +170,7 @@ describe('Test config', function () { const res = await getCustomConfig(server.url, server.accessToken) const data = res.body as CustomConfig - checkInitialConfig(data) + checkInitialConfig(server, data) }) it('Should update the customized configuration', async function () { @@ -210,6 +220,7 @@ describe('Test config', function () { transcoding: { enabled: true, allowAdditionalExtensions: true, + allowAudioFiles: true, threads: 1, resolutions: { '240p': false, @@ -264,6 +275,12 @@ describe('Test config', function () { expect(data.video.file.extensions).to.contain('.ogv') expect(data.video.file.extensions).to.contain('.flv') expect(data.video.file.extensions).to.contain('.mkv') + expect(data.video.file.extensions).to.contain('.mp3') + expect(data.video.file.extensions).to.contain('.ogg') + expect(data.video.file.extensions).to.contain('.flac') + + await uploadVideo(server.url, server.accessToken, { fixture: 'video_short.mkv' }, 200) + await uploadVideo(server.url, server.accessToken, { fixture: 'sample.ogg' }, 200) }) it('Should have the configuration updated after a restart', async function () { @@ -297,7 +314,7 @@ describe('Test config', function () { const res = await getCustomConfig(server.url, server.accessToken) const data = res.body - checkInitialConfig(data) + checkInitialConfig(server, data) }) 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 () { before(async function () { this.timeout(30000) - await MockSmtpServer.Instance.collectEmails(emails) + const port = await MockSmtpServer.Instance.collectEmails(emails) const overrideConfig = { smtp: { - hostname: 'localhost' + hostname: 'localhost', + port } } server = await flushAndRunServer(1, overrideConfig) @@ -53,7 +54,7 @@ describe('Test contact form', function () { expect(email['from'][0]['address']).equal('test-admin@localhost') expect(email['from'][0]['name']).equal('toto@example.com') - expect(email['to'][0]['address']).equal('admin1@example.com') + expect(email['to'][0]['address']).equal('admin' + server.internalServerNumber + '@example.com') expect(email['subject']).contains('Contact form') expect(email['text']).contains('my super message') }) 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 { askResetPassword, askSendVerifyEmail, blockUser, - createUser, removeVideoFromBlacklist, + cleanupTests, + createUser, + flushAndRunServer, + removeVideoFromBlacklist, reportVideoAbuse, resetPassword, - flushAndRunServer, + ServerInfo, + setAccessTokensToServers, unblockUser, uploadVideo, userLogin, - verifyEmail, - flushTests, - killallServers, - ServerInfo, - setAccessTokensToServers, cleanupTests + verifyEmail } from '../../../../shared/extra-utils' import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email' import { waitJobs } from '../../../../shared/extra-utils/server/jobs' @@ -37,15 +37,17 @@ describe('Test emails', function () { username: 'user_1', password: 'super_password' } + let emailPort: number before(async function () { this.timeout(30000) - await MockSmtpServer.Instance.collectEmails(emails) + emailPort = await MockSmtpServer.Instance.collectEmails(emails) const overrideConfig = { smtp: { - hostname: 'localhost' + hostname: 'localhost', + port: emailPort } } server = await flushAndRunServer(1, overrideConfig) @@ -87,7 +89,7 @@ describe('Test emails', function () { const email = emails[0] - expect(email['from'][0]['name']).equal('localhost:9001') + expect(email['from'][0]['name']).equal('localhost:' + server.port) expect(email['from'][0]['address']).equal('test-admin@localhost') expect(email['to'][0]['address']).equal('user_1@example.com') expect(email['subject']).contains('password') @@ -132,9 +134,9 @@ describe('Test emails', function () { const email = emails[1] - expect(email['from'][0]['name']).equal('localhost:9001') + expect(email['from'][0]['name']).equal('localhost:' + server.port) expect(email['from'][0]['address']).equal('test-admin@localhost') - expect(email['to'][0]['address']).equal('admin1@example.com') + expect(email['to'][0]['address']).equal('admin' + server.internalServerNumber + '@example.com') expect(email['subject']).contains('abuse') expect(email['text']).contains(videoUUID) }) @@ -153,7 +155,7 @@ describe('Test emails', function () { const email = emails[2] - expect(email['from'][0]['name']).equal('localhost:9001') + expect(email['from'][0]['name']).equal('localhost:' + server.port) expect(email['from'][0]['address']).equal('test-admin@localhost') expect(email['to'][0]['address']).equal('user_1@example.com') expect(email['subject']).contains(' blocked') @@ -171,7 +173,7 @@ describe('Test emails', function () { const email = emails[3] - expect(email['from'][0]['name']).equal('localhost:9001') + expect(email['from'][0]['name']).equal('localhost:' + server.port) expect(email['from'][0]['address']).equal('test-admin@localhost') expect(email['to'][0]['address']).equal('user_1@example.com') expect(email['subject']).contains(' unblocked') @@ -191,7 +193,7 @@ describe('Test emails', function () { const email = emails[4] - expect(email['from'][0]['name']).equal('localhost:9001') + expect(email['from'][0]['name']).equal('localhost:' + server.port) expect(email['from'][0]['address']).equal('test-admin@localhost') expect(email['to'][0]['address']).equal('user_1@example.com') expect(email['subject']).contains(' blacklisted') @@ -209,7 +211,7 @@ describe('Test emails', function () { const email = emails[5] - expect(email['from'][0]['name']).equal('localhost:9001') + expect(email['from'][0]['name']).equal('localhost:' + server.port) expect(email['from'][0]['address']).equal('test-admin@localhost') expect(email['to'][0]['address']).equal('user_1@example.com') expect(email['subject']).contains(' unblacklisted') @@ -229,7 +231,7 @@ describe('Test emails', function () { const email = emails[6] - expect(email['from'][0]['name']).equal('localhost:9001') + expect(email['from'][0]['name']).equal('localhost:' + server.port) expect(email['from'][0]['address']).equal('test-admin@localhost') expect(email['to'][0]['address']).equal('user_1@example.com') 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 @@ import * as chai from 'chai' import 'mocha' import { + cleanupTests, doubleFollow, + flushAndRunMultipleServers, getAccountVideos, getVideo, getVideoChannelVideos, getVideoWithToken, - flushAndRunMultipleServers, - killallServers, ServerInfo, setAccessTokensToServers, - uploadVideo, cleanupTests + uploadVideo } from '../../../../shared/extra-utils' import { unfollow } from '../../../../shared/extra-utils/server/follows' import { userLogin } from '../../../../shared/extra-utils/users/login' @@ -66,28 +66,30 @@ describe('Test follow constraints', function () { }) it('Should list local account videos', async function () { - const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:9001', 0, 5) + const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:' + servers[0].port, 0, 5) expect(res.body.total).to.equal(1) expect(res.body.data).to.have.lengthOf(1) }) it('Should list remote account videos', async function () { - const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:9002', 0, 5) + const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:' + servers[1].port, 0, 5) expect(res.body.total).to.equal(1) expect(res.body.data).to.have.lengthOf(1) }) it('Should list local channel videos', async function () { - const res = await getVideoChannelVideos(servers[0].url, undefined, 'root_channel@localhost:9001', 0, 5) + const videoChannelName = 'root_channel@localhost:' + servers[0].port + const res = await getVideoChannelVideos(servers[0].url, undefined, videoChannelName, 0, 5) expect(res.body.total).to.equal(1) expect(res.body.data).to.have.lengthOf(1) }) it('Should list remote channel videos', async function () { - const res = await getVideoChannelVideos(servers[0].url, undefined, 'root_channel@localhost:9002', 0, 5) + const videoChannelName = 'root_channel@localhost:' + servers[1].port + const res = await getVideoChannelVideos(servers[0].url, undefined, videoChannelName, 0, 5) expect(res.body.total).to.equal(1) expect(res.body.data).to.have.lengthOf(1) @@ -104,28 +106,30 @@ describe('Test follow constraints', function () { }) it('Should list local account videos', async function () { - const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:9001', 0, 5) + const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:' + servers[0].port, 0, 5) expect(res.body.total).to.equal(1) expect(res.body.data).to.have.lengthOf(1) }) it('Should list remote account videos', async function () { - const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:9002', 0, 5) + const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:' + servers[1].port, 0, 5) expect(res.body.total).to.equal(1) expect(res.body.data).to.have.lengthOf(1) }) it('Should list local channel videos', async function () { - const res = await getVideoChannelVideos(servers[0].url, userAccessToken, 'root_channel@localhost:9001', 0, 5) + const videoChannelName = 'root_channel@localhost:' + servers[0].port + const res = await getVideoChannelVideos(servers[0].url, userAccessToken, videoChannelName, 0, 5) expect(res.body.total).to.equal(1) expect(res.body.data).to.have.lengthOf(1) }) it('Should list remote channel videos', async function () { - const res = await getVideoChannelVideos(servers[0].url, userAccessToken, 'root_channel@localhost:9002', 0, 5) + const videoChannelName = 'root_channel@localhost:' + servers[1].port + const res = await getVideoChannelVideos(servers[0].url, userAccessToken, videoChannelName, 0, 5) expect(res.body.total).to.equal(1) expect(res.body.data).to.have.lengthOf(1) @@ -152,28 +156,30 @@ describe('Test follow constraints', function () { }) it('Should list local account videos', async function () { - const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:9001', 0, 5) + const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:' + servers[0].port, 0, 5) expect(res.body.total).to.equal(1) expect(res.body.data).to.have.lengthOf(1) }) it('Should not list remote account videos', async function () { - const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:9002', 0, 5) + const res = await getAccountVideos(servers[0].url, undefined, 'root@localhost:' + servers[1].port, 0, 5) expect(res.body.total).to.equal(0) expect(res.body.data).to.have.lengthOf(0) }) it('Should list local channel videos', async function () { - const res = await getVideoChannelVideos(servers[0].url, undefined, 'root_channel@localhost:9001', 0, 5) + const videoChannelName = 'root_channel@localhost:' + servers[0].port + const res = await getVideoChannelVideos(servers[0].url, undefined, videoChannelName, 0, 5) expect(res.body.total).to.equal(1) expect(res.body.data).to.have.lengthOf(1) }) it('Should not list remote channel videos', async function () { - const res = await getVideoChannelVideos(servers[0].url, undefined, 'root_channel@localhost:9002', 0, 5) + const videoChannelName = 'root_channel@localhost:' + servers[1].port + const res = await getVideoChannelVideos(servers[0].url, undefined, videoChannelName, 0, 5) expect(res.body.total).to.equal(0) expect(res.body.data).to.have.lengthOf(0) @@ -190,28 +196,30 @@ describe('Test follow constraints', function () { }) it('Should list local account videos', async function () { - const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:9001', 0, 5) + const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:' + servers[0].port, 0, 5) expect(res.body.total).to.equal(1) expect(res.body.data).to.have.lengthOf(1) }) it('Should list remote account videos', async function () { - const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:9002', 0, 5) + const res = await getAccountVideos(servers[0].url, userAccessToken, 'root@localhost:' + servers[1].port, 0, 5) expect(res.body.total).to.equal(1) expect(res.body.data).to.have.lengthOf(1) }) it('Should list local channel videos', async function () { - const res = await getVideoChannelVideos(servers[0].url, userAccessToken, 'root_channel@localhost:9001', 0, 5) + const videoChannelName = 'root_channel@localhost:' + servers[0].port + const res = await getVideoChannelVideos(servers[0].url, userAccessToken, videoChannelName, 0, 5) expect(res.body.total).to.equal(1) expect(res.body.data).to.have.lengthOf(1) }) it('Should list remote channel videos', async function () { - const res = await getVideoChannelVideos(servers[0].url, userAccessToken, 'root_channel@localhost:9002', 0, 5) + const videoChannelName = 'root_channel@localhost:' + servers[1].port + const res = await getVideoChannelVideos(servers[0].url, userAccessToken, videoChannelName, 0, 5) expect(res.body.total).to.equal(1) 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 @@ import * as chai from 'chai' import 'mocha' import { - acceptFollower, cleanupTests, + acceptFollower, + cleanupTests, flushAndRunMultipleServers, - killallServers, ServerInfo, setAccessTokensToServers, updateCustomSubConfig @@ -14,8 +14,8 @@ import { follow, getFollowersListPaginationAndSort, getFollowingListPaginationAndSort, - removeFollower, - rejectFollower + rejectFollower, + removeFollower } from '../../../../shared/extra-utils/server/follows' import { waitJobs } from '../../../../shared/extra-utils/server/jobs' import { ActorFollow } from '../../../../shared/models/actors' @@ -29,8 +29,8 @@ async function checkServer1And2HasFollowers (servers: ServerInfo[], state = 'acc const follow = res.body.data[0] as ActorFollow expect(follow.state).to.equal(state) - expect(follow.follower.url).to.equal('http://localhost:9001/accounts/peertube') - expect(follow.following.url).to.equal('http://localhost:9002/accounts/peertube') + expect(follow.follower.url).to.equal('http://localhost:' + servers[0].port + '/accounts/peertube') + expect(follow.following.url).to.equal('http://localhost:' + servers[1].port + '/accounts/peertube') } { @@ -39,8 +39,8 @@ async function checkServer1And2HasFollowers (servers: ServerInfo[], state = 'acc const follow = res.body.data[0] as ActorFollow expect(follow.state).to.equal(state) - expect(follow.follower.url).to.equal('http://localhost:9001/accounts/peertube') - expect(follow.following.url).to.equal('http://localhost:9002/accounts/peertube') + expect(follow.follower.url).to.equal('http://localhost:' + servers[0].port + '/accounts/peertube') + expect(follow.following.url).to.equal('http://localhost:' + servers[1].port + '/accounts/peertube') } } @@ -151,7 +151,7 @@ describe('Test follows moderation', function () { }) it('Should accept a follower', async function () { - await acceptFollower(servers[1].url, servers[1].accessToken, 'peertube@localhost:9001') + await acceptFollower(servers[1].url, servers[1].accessToken, 'peertube@localhost:' + servers[0].port) await waitJobs(servers) await checkServer1And2HasFollowers(servers) @@ -178,7 +178,7 @@ describe('Test follows moderation', function () { expect(res.body.total).to.equal(1) } - await rejectFollower(servers[2].url, servers[2].accessToken, 'peertube@localhost:9001') + await rejectFollower(servers[2].url, servers[2].accessToken, 'peertube@localhost:' + servers[0].port) await waitJobs(servers) 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 import { flushAndRunMultipleServers, getVideosList, - killallServers, ServerInfo, setAccessTokensToServers, uploadVideo @@ -89,8 +88,8 @@ describe('Test follows', function () { res = await getFollowingListPaginationAndSort(servers[0].url, 1, 1, 'createdAt') follows = follows.concat(res.body.data) - const server2Follow = follows.find(f => f.following.host === 'localhost:9002') - const server3Follow = follows.find(f => f.following.host === 'localhost:9003') + const server2Follow = follows.find(f => f.following.host === 'localhost:' + servers[1].port) + const server3Follow = follows.find(f => f.following.host === 'localhost:' + servers[2].port) expect(server2Follow).to.not.be.undefined expect(server3Follow).to.not.be.undefined @@ -100,12 +99,12 @@ describe('Test follows', function () { it('Should search followings on server 1', async function () { { - const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 1, 'createdAt', ':9002') + const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 1, 'createdAt', ':' + servers[1].port) const follows = res.body.data expect(res.body.total).to.equal(1) expect(follows.length).to.equal(1) - expect(follows[ 0 ].following.host).to.equal('localhost:9002') + expect(follows[ 0 ].following.host).to.equal('localhost:' + servers[1].port) } { @@ -136,18 +135,18 @@ describe('Test follows', function () { expect(res.body.total).to.equal(1) expect(follows).to.be.an('array') expect(follows.length).to.equal(1) - expect(follows[0].follower.host).to.equal('localhost:9001') + expect(follows[0].follower.host).to.equal('localhost:' + servers[0].port) } }) it('Should search followers on server 2', async function () { { - const res = await getFollowersListPaginationAndSort(servers[ 2 ].url, 0, 5, 'createdAt', '9001') + const res = await getFollowersListPaginationAndSort(servers[ 2 ].url, 0, 5, 'createdAt', servers[0].port + '') const follows = res.body.data expect(res.body.total).to.equal(1) expect(follows.length).to.equal(1) - expect(follows[ 0 ].following.host).to.equal('localhost:9003') + expect(follows[ 0 ].following.host).to.equal('localhost:' + servers[2].port) } { @@ -169,16 +168,16 @@ describe('Test follows', function () { }) it('Should have the correct follows counts', async function () { - await expectAccountFollows(servers[0].url, 'peertube@localhost:9001', 0, 2) - await expectAccountFollows(servers[0].url, 'peertube@localhost:9002', 1, 0) - await expectAccountFollows(servers[0].url, 'peertube@localhost:9003', 1, 0) + await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[0].port, 0, 2) + await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[1].port, 1, 0) + await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[2].port, 1, 0) // Server 2 and 3 does not know server 1 follow another server (there was not a refresh) - await expectAccountFollows(servers[1].url, 'peertube@localhost:9001', 0, 1) - await expectAccountFollows(servers[1].url, 'peertube@localhost:9002', 1, 0) + await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[0].port, 0, 1) + await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[1].port, 1, 0) - await expectAccountFollows(servers[2].url, 'peertube@localhost:9001', 0, 1) - await expectAccountFollows(servers[2].url, 'peertube@localhost:9003', 1, 0) + await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[0].port, 0, 1) + await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[2].port, 1, 0) }) it('Should unfollow server 3 on server 1', async function () { @@ -197,7 +196,7 @@ describe('Test follows', function () { expect(follows).to.be.an('array') expect(follows.length).to.equal(1) - expect(follows[0].following.host).to.equal('localhost:9002') + expect(follows[0].following.host).to.equal('localhost:' + servers[1].port) }) it('Should not have server 1 as follower on server 3 anymore', async function () { @@ -210,14 +209,14 @@ describe('Test follows', function () { }) it('Should have the correct follows counts 2', async function () { - await expectAccountFollows(servers[0].url, 'peertube@localhost:9001', 0, 1) - await expectAccountFollows(servers[0].url, 'peertube@localhost:9002', 1, 0) + await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[0].port, 0, 1) + await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[1].port, 1, 0) - await expectAccountFollows(servers[1].url, 'peertube@localhost:9001', 0, 1) - await expectAccountFollows(servers[1].url, 'peertube@localhost:9002', 1, 0) + await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[0].port, 0, 1) + await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[1].port, 1, 0) - await expectAccountFollows(servers[2].url, 'peertube@localhost:9001', 0, 0) - await expectAccountFollows(servers[2].url, 'peertube@localhost:9003', 0, 0) + await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[0].port, 0, 0) + await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[2].port, 0, 0) }) 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 () { }) it('Should have the correct follows counts 3', async function () { - await expectAccountFollows(servers[0].url, 'peertube@localhost:9001', 0, 2) - await expectAccountFollows(servers[0].url, 'peertube@localhost:9002', 1, 0) - await expectAccountFollows(servers[0].url, 'peertube@localhost:9003', 1, 0) + await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[0].port, 0, 2) + await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[1].port, 1, 0) + await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[2].port, 1, 0) - await expectAccountFollows(servers[1].url, 'peertube@localhost:9001', 0, 1) - await expectAccountFollows(servers[1].url, 'peertube@localhost:9002', 1, 0) + await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[0].port, 0, 1) + await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[1].port, 1, 0) - await expectAccountFollows(servers[2].url, 'peertube@localhost:9001', 0, 2) - await expectAccountFollows(servers[2].url, 'peertube@localhost:9003', 1, 0) + await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[0].port, 0, 2) + await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[2].port, 1, 0) }) it('Should have propagated videos', async function () { @@ -344,7 +343,7 @@ describe('Test follows', function () { support: 'my super support text', account: { name: 'root', - host: 'localhost:9003' + host: 'localhost:' + servers[2].port }, isLocal, commentsEnabled: true, @@ -384,7 +383,7 @@ describe('Test follows', function () { expect(comment.videoId).to.equal(video4.id) expect(comment.id).to.equal(comment.threadId) expect(comment.account.name).to.equal('root') - expect(comment.account.host).to.equal('localhost:9003') + expect(comment.account.host).to.equal('localhost:' + servers[2].port) expect(comment.totalReplies).to.equal(3) expect(dateIsValid(comment.createdAt as string)).to.be.true 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 () { privacy: VideoPrivacy.UNLISTED }) - const checkAttributes = { - name: 'my super name for server 1', - category: 5, - licence: 4, - language: 'ja', - nsfw: true, - description: 'my super description for server 1', - support: 'my super support text for server 1', - account: { - name: 'root', - host: 'localhost:9001' - }, - isLocal: false, - duration: 10, - tags: [ 'tag1p1', 'tag2p1' ], - privacy: VideoPrivacy.PUBLIC, - commentsEnabled: true, - downloadEnabled: true, - channel: { - name: 'root_channel', - displayName: 'Main root channel', - description: '', - isLocal: false - }, - fixture: 'video_short1.webm', - files: [ - { - resolution: 720, - size: 572456 - } - ] - } - - const unlistedCheckAttributes = immutableAssign(checkAttributes, { - privacy: VideoPrivacy.UNLISTED - }) + let checkAttributes: any + let unlistedCheckAttributes: any before(async function () { this.timeout(30000) servers = await flushAndRunMultipleServers(3) + checkAttributes = { + name: 'my super name for server 1', + category: 5, + licence: 4, + language: 'ja', + nsfw: true, + description: 'my super description for server 1', + support: 'my super support text for server 1', + account: { + name: 'root', + host: 'localhost:' + servers[0].port + }, + isLocal: false, + duration: 10, + tags: [ 'tag1p1', 'tag2p1' ], + privacy: VideoPrivacy.PUBLIC, + commentsEnabled: true, + downloadEnabled: true, + channel: { + name: 'root_channel', + displayName: 'Main root channel', + description: '', + isLocal: false + }, + fixture: 'video_short1.webm', + files: [ + { + resolution: 720, + size: 572456 + } + ] + } + unlistedCheckAttributes = immutableAssign(checkAttributes, { + privacy: VideoPrivacy.UNLISTED + }) + // Get the access tokens await setAccessTokensToServers(servers) }) @@ -172,7 +174,7 @@ describe('Test handle downs', function () { const res = await getFollowersListPaginationAndSort(servers[0].url, 0, 2, 'createdAt') expect(res.body.data).to.be.an('array') expect(res.body.data).to.have.lengthOf(1) - expect(res.body.data[0].follower.host).to.equal('localhost:9003') + expect(res.body.data[0].follower.host).to.equal('localhost:' + servers[2].port) }) 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 () { }) it('Should create some jobs', async function () { - this.timeout(30000) + this.timeout(60000) await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video1' }) 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 () { }) it('Should get logs with an end date', async function () { - this.timeout(10000) + this.timeout(20000) await uploadVideo(server.url, server.accessToken, { name: 'video 3' }) 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 @@ +#!/usr/bin/env sh + +set -eu + +checkParamFiles=$(find server/tests/api/check-params -type f | grep -v index.ts | xargs echo) +notificationsFiles=$(find server/tests/api/notifications -type f | grep -v index.ts | xargs echo) +searchFiles=$(find server/tests/api/search -type f | grep -v index.ts | xargs echo) + +MOCHA_PARALLEL=true mocha --timeout 5000 --exit --require ts-node/register --bail \ + $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 @@ +#!/usr/bin/env sh + +set -eu + +serverFiles=$(find server/tests/api/server -type f | grep -v index.ts | xargs echo) +usersFiles=$(find server/tests/api/users -type f | grep -v index.ts | xargs echo) + +MOCHA_PARALLEL=true mocha --timeout 5000 --exit --require ts-node/register --bail \ + $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 @@ +#!/usr/bin/env sh + +set -eu + +videosFiles=$(find server/tests/api/videos -type f | grep -v index.ts | xargs echo) + +MOCHA_PARALLEL=true mocha --timeout 5000 --exit --require ts-node/register --bail \ + $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 @@ +#!/usr/bin/env sh + +set -eu + +redundancyFiles=$(find server/tests/api/redundancy -type f | grep -v index.ts | xargs echo) +activitypubFiles=$(find server/tests/api/activitypub -type f | grep -v index.ts | xargs echo) + +MOCHA_PARALLEL=true mocha-parallel-tests --max-parallel $1 --timeout 5000 --exit --require ts-node/register --bail \ + $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 () { }) it('Should block a remote account', async function () { - await addAccountToAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user2@localhost:9002') + await addAccountToAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user2@localhost:' + servers[1].port) }) it('Should hide its videos', async function () { @@ -209,7 +209,7 @@ describe('Test blocklist', function () { expect(block.byAccount.name).to.equal('root') expect(block.blockedAccount.displayName).to.equal('user2') expect(block.blockedAccount.name).to.equal('user2') - expect(block.blockedAccount.host).to.equal('localhost:9002') + expect(block.blockedAccount.host).to.equal('localhost:' + servers[1].port) } { @@ -223,12 +223,12 @@ describe('Test blocklist', function () { expect(block.byAccount.name).to.equal('root') expect(block.blockedAccount.displayName).to.equal('user1') expect(block.blockedAccount.name).to.equal('user1') - expect(block.blockedAccount.host).to.equal('localhost:9001') + expect(block.blockedAccount.host).to.equal('localhost:' + servers[0].port) } }) it('Should unblock the remote account', async function () { - await removeAccountFromAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user2@localhost:9002') + await removeAccountFromAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user2@localhost:' + servers[1].port) }) it('Should display its videos', async function () { @@ -260,7 +260,7 @@ describe('Test blocklist', function () { }) it('Should block a remote server', async function () { - await addServerToAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:9002') + await addServerToAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:' + servers[1].port) }) it('Should hide its videos', async function () { @@ -291,11 +291,11 @@ describe('Test blocklist', function () { const block = blocks[ 0 ] expect(block.byAccount.displayName).to.equal('root') expect(block.byAccount.name).to.equal('root') - expect(block.blockedServer.host).to.equal('localhost:9002') + expect(block.blockedServer.host).to.equal('localhost:' + servers[1].port) }) it('Should unblock the remote server', async function () { - await removeServerFromAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:9002') + await removeServerFromAccountBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:' + servers[1].port) }) it('Should display its videos', function () { @@ -324,7 +324,7 @@ describe('Test blocklist', function () { }) it('Should block a remote account', async function () { - await addAccountToServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user2@localhost:9002') + await addAccountToServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user2@localhost:' + servers[1].port) }) it('Should hide its videos', async function () { @@ -387,7 +387,7 @@ describe('Test blocklist', function () { expect(block.byAccount.name).to.equal('peertube') expect(block.blockedAccount.displayName).to.equal('user2') expect(block.blockedAccount.name).to.equal('user2') - expect(block.blockedAccount.host).to.equal('localhost:9002') + expect(block.blockedAccount.host).to.equal('localhost:' + servers[1].port) } { @@ -401,12 +401,12 @@ describe('Test blocklist', function () { expect(block.byAccount.name).to.equal('peertube') expect(block.blockedAccount.displayName).to.equal('user1') expect(block.blockedAccount.name).to.equal('user1') - expect(block.blockedAccount.host).to.equal('localhost:9001') + expect(block.blockedAccount.host).to.equal('localhost:' + servers[0].port) } }) it('Should unblock the remote account', async function () { - await removeAccountFromServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user2@localhost:9002') + await removeAccountFromServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'user2@localhost:' + servers[1].port) }) it('Should display its videos', async function () { @@ -446,7 +446,7 @@ describe('Test blocklist', function () { }) it('Should block a remote server', async function () { - await addServerToServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:9002') + await addServerToServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:' + servers[1].port) }) it('Should hide its videos', async function () { @@ -478,11 +478,11 @@ describe('Test blocklist', function () { const block = blocks[ 0 ] expect(block.byAccount.displayName).to.equal('peertube') expect(block.byAccount.name).to.equal('peertube') - expect(block.blockedServer.host).to.equal('localhost:9002') + expect(block.blockedServer.host).to.equal('localhost:' + servers[1].port) }) it('Should unblock the remote server', async function () { - await removeServerFromServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:9002') + await removeServerFromServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:' + servers[1].port) }) 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 () { it('User of server 1 should follow user of server 3 and root of server 1', async function () { this.timeout(60000) - await addUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:9003') - await addUserSubscription(servers[0].url, users[0].accessToken, 'root_channel@localhost:9001') + await addUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:' + servers[2].port) + await addUserSubscription(servers[0].url, users[0].accessToken, 'root_channel@localhost:' + servers[0].port) await waitJobs(servers) @@ -116,22 +116,22 @@ describe('Test users subscriptions', function () { it('Should get subscription', async function () { { - const res = await getUserSubscription(servers[ 0 ].url, users[ 0 ].accessToken, 'user3_channel@localhost:9003') + const res = await getUserSubscription(servers[ 0 ].url, users[ 0 ].accessToken, 'user3_channel@localhost:' + servers[2].port) const videoChannel: VideoChannel = res.body expect(videoChannel.name).to.equal('user3_channel') - expect(videoChannel.host).to.equal('localhost:9003') + expect(videoChannel.host).to.equal('localhost:' + servers[2].port) expect(videoChannel.displayName).to.equal('Main user3 channel') expect(videoChannel.followingCount).to.equal(0) expect(videoChannel.followersCount).to.equal(1) } { - const res = await getUserSubscription(servers[ 0 ].url, users[ 0 ].accessToken, 'root_channel@localhost:9001') + const res = await getUserSubscription(servers[ 0 ].url, users[ 0 ].accessToken, 'root_channel@localhost:' + servers[0].port) const videoChannel: VideoChannel = res.body expect(videoChannel.name).to.equal('root_channel') - expect(videoChannel.host).to.equal('localhost:9001') + expect(videoChannel.host).to.equal('localhost:' + servers[0].port) expect(videoChannel.displayName).to.equal('Main root channel') expect(videoChannel.followingCount).to.equal(0) expect(videoChannel.followersCount).to.equal(1) @@ -140,19 +140,19 @@ describe('Test users subscriptions', function () { it('Should return the existing subscriptions', async function () { const uris = [ - 'user3_channel@localhost:9003', - 'root2_channel@localhost:9001', - 'root_channel@localhost:9001', - 'user3_channel@localhost:9001' + 'user3_channel@localhost:' + servers[2].port, + 'root2_channel@localhost:' + servers[0].port, + 'root_channel@localhost:' + servers[0].port, + 'user3_channel@localhost:' + servers[0].port ] const res = await areSubscriptionsExist(servers[ 0 ].url, users[ 0 ].accessToken, uris) const body = res.body - expect(body['user3_channel@localhost:9003']).to.be.true - expect(body['root2_channel@localhost:9001']).to.be.false - expect(body['root_channel@localhost:9001']).to.be.true - expect(body['user3_channel@localhost:9001']).to.be.false + expect(body['user3_channel@localhost:' + servers[2].port]).to.be.true + expect(body['root2_channel@localhost:' + servers[0].port]).to.be.false + expect(body['root_channel@localhost:' + servers[0].port]).to.be.true + expect(body['user3_channel@localhost:' + servers[0].port]).to.be.false }) it('Should list subscription videos', async function () { @@ -291,7 +291,7 @@ describe('Test users subscriptions', function () { it('Should remove user of server 3 subscription', async function () { this.timeout(30000) - await removeUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:9003') + await removeUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:' + servers[2].port) await waitJobs(servers) }) @@ -312,7 +312,7 @@ describe('Test users subscriptions', function () { it('Should remove the root subscription and not display the videos anymore', async function () { this.timeout(30000) - await removeUserSubscription(servers[0].url, users[0].accessToken, 'root_channel@localhost:9001') + await removeUserSubscription(servers[0].url, users[0].accessToken, 'root_channel@localhost:' + servers[0].port) await waitJobs(servers) @@ -340,7 +340,7 @@ describe('Test users subscriptions', function () { it('Should follow user of server 3 again', async function () { this.timeout(60000) - await addUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:9003') + await addUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:' + servers[2].port) await waitJobs(servers) diff --git a/server/tests/api/users/users-multiple-servers.ts b/server/tests/api/users/users-multiple-servers.ts index 9a971adb3..791418318 100644 --- a/server/tests/api/users/users-multiple-servers.ts +++ b/server/tests/api/users/users-multiple-servers.ts @@ -5,7 +5,8 @@ import 'mocha' import { Account } from '../../../../shared/models/actors' import { checkTmpIsEmpty, - checkVideoFilesWereRemoved, cleanupTests, + checkVideoFilesWereRemoved, + cleanupTests, createUser, doubleFollow, flushAndRunMultipleServers, @@ -15,14 +16,7 @@ import { updateMyUser, userLogin } from '../../../../shared/extra-utils' -import { - getMyUserInformation, - killallServers, - ServerInfo, - testImage, - updateMyAvatar, - uploadVideo -} from '../../../../shared/extra-utils/index' +import { getMyUserInformation, ServerInfo, testImage, updateMyAvatar, uploadVideo } from '../../../../shared/extra-utils/index' import { checkActorFilesWereRemoved, getAccount, getAccountsList } from '../../../../shared/extra-utils/users/accounts' import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login' import { User } from '../../../../shared/models/users' @@ -34,12 +28,10 @@ const expect = chai.expect describe('Test users with multiple servers', function () { let servers: ServerInfo[] = [] let user: User - let userAccountName: string - let userAccountUUID: string - let userVideoChannelUUID: string let userId: number let videoUUID: string let userAccessToken: string + let userAvatarFilename: string before(async function () { this.timeout(120000) @@ -74,19 +66,6 @@ describe('Test users with multiple servers', function () { userAccessToken = await userLogin(servers[ 0 ], user) } - { - const res = await getMyUserInformation(servers[0].url, userAccessToken) - const account: Account = res.body.account - userAccountName = account.name + '@' + account.host - userAccountUUID = account.uuid - } - - { - const res = await getMyUserInformation(servers[ 0 ].url, servers[ 0 ].accessToken) - const user: User = res.body - userVideoChannelUUID = user.videoChannels[0].uuid - } - { const resVideo = await uploadVideo(servers[ 0 ].url, userAccessToken, {}) videoUUID = resVideo.body.video.uuid @@ -106,6 +85,8 @@ describe('Test users with multiple servers', function () { const res = await getMyUserInformation(servers[0].url, servers[0].accessToken) user = res.body + + const account: Account = user.account expect(user.account.displayName).to.equal('my super display name') await waitJobs(servers) @@ -142,7 +123,9 @@ describe('Test users with multiple servers', function () { const res = await getMyUserInformation(servers[0].url, servers[0].accessToken) user = res.body - await testImage(servers[0].url, 'avatar2-resized', user.account.avatar.path, '.png') + userAvatarFilename = user.account.avatar.path + + await testImage(servers[0].url, 'avatar2-resized', userAvatarFilename, '.png') await waitJobs(servers) }) @@ -151,13 +134,13 @@ describe('Test users with multiple servers', function () { for (const server of servers) { const resAccounts = await getAccountsList(server.url, '-createdAt') - const rootServer1List = resAccounts.body.data.find(a => a.name === 'root' && a.host === 'localhost:9001') as Account + const rootServer1List = resAccounts.body.data.find(a => a.name === 'root' && a.host === 'localhost:' + servers[0].port) as Account expect(rootServer1List).not.to.be.undefined const resAccount = await getAccount(server.url, rootServer1List.name + '@' + rootServer1List.host) const rootServer1Get = resAccount.body as Account expect(rootServer1Get.name).to.equal('root') - expect(rootServer1Get.host).to.equal('localhost:9001') + expect(rootServer1Get.host).to.equal('localhost:' + servers[0].port) expect(rootServer1Get.displayName).to.equal('my super display name') expect(rootServer1Get.description).to.equal('my super description updated') @@ -173,7 +156,7 @@ describe('Test users with multiple servers', function () { it('Should list account videos', async function () { for (const server of servers) { - const res = await getAccountVideos(server.url, server.accessToken, userAccountName, 0, 5) + const res = await getAccountVideos(server.url, server.accessToken, 'user1@localhost:' + servers[0].port, 0, 5) expect(res.body.total).to.equal(1) expect(res.body.data).to.be.an('array') @@ -188,12 +171,12 @@ describe('Test users with multiple servers', function () { for (const server of servers) { const resAccounts = await getAccountsList(server.url, '-createdAt') - const accountDeleted = resAccounts.body.data.find(a => a.name === 'user1' && a.host === 'localhost:9001') as Account + const accountDeleted = resAccounts.body.data.find(a => a.name === 'user1' && a.host === 'localhost:' + servers[0].port) as Account expect(accountDeleted).not.to.be.undefined const resVideoChannels = await getVideoChannelsList(server.url, 0, 10) const videoChannelDeleted = resVideoChannels.body.data.find(a => { - return a.displayName === 'Main user1 channel' && a.host === 'localhost:9001' + return a.displayName === 'Main user1 channel' && a.host === 'localhost:' + servers[0].port }) as VideoChannel expect(videoChannelDeleted).not.to.be.undefined } @@ -205,12 +188,12 @@ describe('Test users with multiple servers', function () { for (const server of servers) { const resAccounts = await getAccountsList(server.url, '-createdAt') - const accountDeleted = resAccounts.body.data.find(a => a.name === 'user1' && a.host === 'localhost:9001') as Account + const accountDeleted = resAccounts.body.data.find(a => a.name === 'user1' && a.host === 'localhost:' + servers[0].port) as Account expect(accountDeleted).to.be.undefined const resVideoChannels = await getVideoChannelsList(server.url, 0, 10) const videoChannelDeleted = resVideoChannels.body.data.find(a => { - return a.name === 'Main user1 channel' && a.host === 'localhost:9001' + return a.name === 'Main user1 channel' && a.host === 'localhost:' + servers[0].port }) as VideoChannel expect(videoChannelDeleted).to.be.undefined } @@ -218,14 +201,13 @@ describe('Test users with multiple servers', function () { it('Should not have actor files', async () => { for (const server of servers) { - await checkActorFilesWereRemoved(userAccountUUID, server.serverNumber) - await checkActorFilesWereRemoved(userVideoChannelUUID, server.serverNumber) + await checkActorFilesWereRemoved(userAvatarFilename, server.internalServerNumber) } }) it('Should not have video files', async () => { for (const server of servers) { - await checkVideoFilesWereRemoved(videoUUID, server.serverNumber) + await checkVideoFilesWereRemoved(videoUUID, server.internalServerNumber) } }) 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 () { before(async function () { this.timeout(30000) - await MockSmtpServer.Instance.collectEmails(emails) + const port = await MockSmtpServer.Instance.collectEmails(emails) const overrideConfig = { smtp: { - hostname: 'localhost' + hostname: 'localhost', + port } } server = await flushAndRunServer(1, overrideConfig) diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts index c8e32f3f5..9d2ef786f 100644 --- a/server/tests/api/users/users.ts +++ b/server/tests/api/users/users.ts @@ -31,7 +31,8 @@ import { updateMyUser, updateUser, uploadVideo, - userLogin + userLogin, + registerUserWithChannel, getVideoChannel } from '../../../../shared/extra-utils' import { follow } from '../../../../shared/extra-utils/server/follows' import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login' @@ -316,7 +317,7 @@ describe('Test users', function () { const rootUser = users[ 1 ] expect(rootUser.username).to.equal('root') - expect(rootUser.email).to.equal('admin1@example.com') + expect(rootUser.email).to.equal('admin' + server.internalServerNumber + '@example.com') expect(user.nsfwPolicy).to.equal('display') userId = user.id @@ -334,7 +335,7 @@ describe('Test users', function () { const user = users[ 0 ] expect(user.username).to.equal('root') - expect(user.email).to.equal('admin1@example.com') + expect(user.email).to.equal('admin' + server.internalServerNumber + '@example.com') expect(user.roleLabel).to.equal('Administrator') expect(user.nsfwPolicy).to.equal('display') }) @@ -379,7 +380,7 @@ describe('Test users', function () { expect(users.length).to.equal(2) expect(users[ 0 ].username).to.equal('root') - expect(users[ 0 ].email).to.equal('admin1@example.com') + expect(users[ 0 ].email).to.equal('admin' + server.internalServerNumber + '@example.com') expect(users[ 0 ].nsfwPolicy).to.equal('display') expect(users[ 1 ].username).to.equal('user_1') @@ -617,7 +618,10 @@ describe('Test users', function () { describe('Registering a new user', function () { it('Should register a new user', async function () { - await registerUser(server.url, 'user_15', 'my super password') + const user = { username: 'user_15', password: 'my super password' } + const channel = { name: 'my_user_15_channel', displayName: 'my channel rocks' } + + await registerUserWithChannel({ url: server.url, user, channel }) }) it('Should be able to login with this registered user', async function () { @@ -636,6 +640,12 @@ describe('Test users', function () { expect(user.videoQuota).to.equal(5 * 1024 * 1024) }) + it('Should have created the channel', async function () { + const res = await getVideoChannel(server.url, 'my_user_15_channel') + + expect(res.body.displayName).to.equal('my channel rocks') + }) + it('Should remove me', async function () { { const res = await getUsersList(server.url, server.accessToken) 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/ import { addVideoChannel, checkTmpIsEmpty, - checkVideoFilesWereRemoved, cleanupTests, + checkVideoFilesWereRemoved, + cleanupTests, completeVideoCheck, createUser, dateIsValid, doubleFollow, flushAndRunMultipleServers, - flushTests, getLocalVideos, getVideo, getVideoChannelsList, getVideosList, - killallServers, rateVideo, removeVideo, ServerInfo, @@ -110,7 +109,7 @@ describe('Test multiple servers', function () { // All servers should have this video let publishedAt: string = null for (const server of servers) { - const isLocal = server.url === 'http://localhost:9001' + const isLocal = server.port === servers[0].port const checkAttributes = { name: 'my super name for server 1', category: 5, @@ -122,7 +121,7 @@ describe('Test multiple servers', function () { originallyPublishedAt: '2019-02-10T13:38:14.449Z', account: { name: 'root', - host: 'localhost:9001' + host: 'localhost:' + servers[0].port }, isLocal, publishedAt, @@ -187,7 +186,7 @@ describe('Test multiple servers', function () { // All servers should have this video for (const server of servers) { - const isLocal = server.url === 'http://localhost:9002' + const isLocal = server.url === 'http://localhost:' + servers[1].port const checkAttributes = { name: 'my super name for server 2', category: 4, @@ -198,7 +197,7 @@ describe('Test multiple servers', function () { support: 'my super support text for server 2', account: { name: 'user1', - host: 'localhost:9002' + host: 'localhost:' + servers[1].port }, isLocal, commentsEnabled: true, @@ -216,7 +215,7 @@ describe('Test multiple servers', function () { files: [ { resolution: 240, - size: 187000 + size: 189000 }, { resolution: 360, @@ -224,7 +223,7 @@ describe('Test multiple servers', function () { }, { resolution: 480, - size: 383000 + size: 384000 }, { resolution: 720, @@ -278,7 +277,7 @@ describe('Test multiple servers', function () { // All servers should have this video for (const server of servers) { - const isLocal = server.url === 'http://localhost:9003' + const isLocal = server.url === 'http://localhost:' + servers[2].port const res = await getVideosList(server.url) const videos = res.body.data @@ -306,7 +305,7 @@ describe('Test multiple servers', function () { support: 'my super support text for server 3', account: { name: 'root', - host: 'localhost:9003' + host: 'localhost:' + servers[2].port }, isLocal, duration: 5, @@ -340,7 +339,7 @@ describe('Test multiple servers', function () { support: 'my super support text for server 3-2', account: { name: 'root', - host: 'localhost:9003' + host: 'localhost:' + servers[2].port }, commentsEnabled: true, downloadEnabled: true, @@ -646,7 +645,7 @@ describe('Test multiple servers', function () { const videoUpdated = videos.find(video => video.name === 'my super video updated') expect(!!videoUpdated).to.be.true - const isLocal = server.url === 'http://localhost:9003' + const isLocal = server.url === 'http://localhost:' + servers[2].port const checkAttributes = { name: 'my super video updated', category: 10, @@ -658,7 +657,7 @@ describe('Test multiple servers', function () { originallyPublishedAt: '2019-02-11T13:38:14.449Z', account: { name: 'root', - host: 'localhost:9003' + host: 'localhost:' + servers[2].port }, isLocal, duration: 5, @@ -813,7 +812,7 @@ describe('Test multiple servers', function () { expect(comment).to.not.be.undefined expect(comment.inReplyToCommentId).to.be.null expect(comment.account.name).to.equal('root') - expect(comment.account.host).to.equal('localhost:9001') + expect(comment.account.host).to.equal('localhost:' + servers[0].port) expect(comment.totalReplies).to.equal(3) expect(dateIsValid(comment.createdAt as string)).to.be.true expect(dateIsValid(comment.updatedAt as string)).to.be.true @@ -824,7 +823,7 @@ describe('Test multiple servers', function () { expect(comment).to.not.be.undefined expect(comment.inReplyToCommentId).to.be.null expect(comment.account.name).to.equal('root') - expect(comment.account.host).to.equal('localhost:9003') + expect(comment.account.host).to.equal('localhost:' + servers[2].port) expect(comment.totalReplies).to.equal(0) expect(dateIsValid(comment.createdAt as string)).to.be.true expect(dateIsValid(comment.updatedAt as string)).to.be.true @@ -842,25 +841,25 @@ describe('Test multiple servers', function () { const tree: VideoCommentThreadTree = res2.body expect(tree.comment.text).equal('my super first comment') expect(tree.comment.account.name).equal('root') - expect(tree.comment.account.host).equal('localhost:9001') + expect(tree.comment.account.host).equal('localhost:' + servers[0].port) expect(tree.children).to.have.lengthOf(2) const firstChild = tree.children[0] expect(firstChild.comment.text).to.equal('my super answer to thread 1') expect(firstChild.comment.account.name).equal('root') - expect(firstChild.comment.account.host).equal('localhost:9002') + expect(firstChild.comment.account.host).equal('localhost:' + servers[1].port) expect(firstChild.children).to.have.lengthOf(1) childOfFirstChild = firstChild.children[0] expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1') expect(childOfFirstChild.comment.account.name).equal('root') - expect(childOfFirstChild.comment.account.host).equal('localhost:9003') + expect(childOfFirstChild.comment.account.host).equal('localhost:' + servers[2].port) expect(childOfFirstChild.children).to.have.lengthOf(0) const secondChild = tree.children[1] expect(secondChild.comment.text).to.equal('my second answer to thread 1') expect(secondChild.comment.account.name).equal('root') - expect(secondChild.comment.account.host).equal('localhost:9003') + expect(secondChild.comment.account.host).equal('localhost:' + servers[2].port) expect(secondChild.children).to.have.lengthOf(0) } }) @@ -915,7 +914,7 @@ describe('Test multiple servers', function () { expect(comment).to.not.be.undefined expect(comment.inReplyToCommentId).to.be.null expect(comment.account.name).to.equal('root') - expect(comment.account.host).to.equal('localhost:9003') + expect(comment.account.host).to.equal('localhost:' + servers[2].port) expect(comment.totalReplies).to.equal(0) expect(dateIsValid(comment.createdAt as string)).to.be.true expect(dateIsValid(comment.updatedAt as string)).to.be.true @@ -971,7 +970,7 @@ describe('Test multiple servers', function () { const res = await getVideosList(server.url) const video = res.body.data.find(v => v.name === 'minimum parameters') - const isLocal = server.url === 'http://localhost:9002' + const isLocal = server.url === 'http://localhost:' + servers[1].port const checkAttributes = { name: 'minimum parameters', category: null, @@ -982,7 +981,7 @@ describe('Test multiple servers', function () { support: null, account: { name: 'root', - host: 'localhost:9002' + host: 'localhost:' + servers[1].port }, isLocal, 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 () { }) it('Should have a valid oEmbed response', async function () { - const oembedUrl = 'http://localhost:9001/videos/watch/' + server.video.uuid + const oembedUrl = 'http://localhost:' + server.port + '/videos/watch/' + server.video.uuid const res = await getOEmbed(server.url, oembedUrl) const expectedHtml = '' - const expectedThumbnailUrl = 'http://localhost:9001/static/previews/' + server.video.uuid + '.jpg' + const expectedThumbnailUrl = 'http://localhost:' + server.port + '/static/previews/' + server.video.uuid + '.jpg' expect(res.body.html).to.equal(expectedHtml) expect(res.body.title).to.equal(server.video.name) @@ -41,19 +41,19 @@ describe('Test services', function () { expect(res.body.width).to.equal(560) expect(res.body.height).to.equal(315) expect(res.body.thumbnail_url).to.equal(expectedThumbnailUrl) - expect(res.body.thumbnail_width).to.equal(560) - expect(res.body.thumbnail_height).to.equal(315) + expect(res.body.thumbnail_width).to.equal(850) + expect(res.body.thumbnail_height).to.equal(480) }) it('Should have a valid oEmbed response with small max height query', async function () { - const oembedUrl = 'http://localhost:9001/videos/watch/' + server.video.uuid + const oembedUrl = 'http://localhost:' + server.port + '/videos/watch/' + server.video.uuid const format = 'json' const maxHeight = 50 const maxWidth = 50 const res = await getOEmbed(server.url, oembedUrl, format, maxHeight, maxWidth) const expectedHtml = '' 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 () { let videoUUID = '' let videosListBase: any[] = null - const getCheckAttributes = { + const getCheckAttributes = () => ({ name: 'my super name', category: 2, licence: 6, @@ -47,7 +47,7 @@ describe('Test a single server', function () { support: 'my super support text', account: { name: 'root', - host: 'localhost:9001' + host: 'localhost:' + server.port }, isLocal: true, duration: 5, @@ -68,9 +68,9 @@ describe('Test a single server', function () { size: 218910 } ] - } + }) - const updateCheckAttributes = { + const updateCheckAttributes = () => ({ name: 'my super video updated', category: 4, licence: 2, @@ -80,7 +80,7 @@ describe('Test a single server', function () { support: 'my super support text updated', account: { name: 'root', - host: 'localhost:9001' + host: 'localhost:' + server.port }, isLocal: true, tags: [ 'tagup1', 'tagup2' ], @@ -101,7 +101,7 @@ describe('Test a single server', function () { size: 292677 } ] - } + }) before(async function () { this.timeout(30000) @@ -182,7 +182,7 @@ describe('Test a single server', function () { expect(res.body.data.length).to.equal(1) const video = res.body.data[0] - await completeVideoCheck(server.url, video, getCheckAttributes) + await completeVideoCheck(server.url, video, getCheckAttributes()) }) it('Should get the video by UUID', async function () { @@ -191,7 +191,7 @@ describe('Test a single server', function () { const res = await getVideo(server.url, videoUUID) const video = res.body - await completeVideoCheck(server.url, video, getCheckAttributes) + await completeVideoCheck(server.url, video, getCheckAttributes()) }) it('Should have the views updated', async function () { @@ -376,7 +376,7 @@ describe('Test a single server', function () { const res = await getVideo(server.url, videoId) const video = res.body - await completeVideoCheck(server.url, video, updateCheckAttributes) + await completeVideoCheck(server.url, video, updateCheckAttributes()) }) it('Should update only the tags of a video', async function () { @@ -388,7 +388,7 @@ describe('Test a single server', function () { const res = await getVideo(server.url, videoId) const video = res.body - await completeVideoCheck(server.url, video, Object.assign(updateCheckAttributes, attributes)) + await completeVideoCheck(server.url, video, Object.assign(updateCheckAttributes(), attributes)) }) it('Should update only the description of a video', async function () { @@ -400,7 +400,8 @@ describe('Test a single server', function () { const res = await getVideo(server.url, videoId) const video = res.body - await completeVideoCheck(server.url, video, Object.assign(updateCheckAttributes, attributes)) + const expectedAttributes = Object.assign(updateCheckAttributes(), { tags: [ 'supertag', 'tag1', 'tag2' ] }, attributes) + await completeVideoCheck(server.url, video, expectedAttributes) }) 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 { flushAndRunMultipleServers, getVideoAbusesList, getVideosList, - killallServers, reportVideoAbuse, ServerInfo, setAccessTokensToServers, @@ -90,7 +89,7 @@ describe('Test video abuses', function () { const abuse: VideoAbuse = res1.body.data[0] expect(abuse.reason).to.equal('my super bad reason') expect(abuse.reporterAccount.name).to.equal('root') - expect(abuse.reporterAccount.host).to.equal('localhost:9001') + expect(abuse.reporterAccount.host).to.equal('localhost:' + servers[0].port) expect(abuse.video.id).to.equal(servers[0].video.id) const res2 = await getVideoAbusesList(servers[1].url, servers[1].accessToken) @@ -118,7 +117,7 @@ describe('Test video abuses', function () { const abuse1: VideoAbuse = res1.body.data[0] expect(abuse1.reason).to.equal('my super bad reason') expect(abuse1.reporterAccount.name).to.equal('root') - expect(abuse1.reporterAccount.host).to.equal('localhost:9001') + expect(abuse1.reporterAccount.host).to.equal('localhost:' + servers[0].port) expect(abuse1.video.id).to.equal(servers[0].video.id) expect(abuse1.state.id).to.equal(VideoAbuseState.PENDING) expect(abuse1.state.label).to.equal('Pending') @@ -127,7 +126,7 @@ describe('Test video abuses', function () { const abuse2: VideoAbuse = res1.body.data[1] expect(abuse2.reason).to.equal('my super bad reason 2') expect(abuse2.reporterAccount.name).to.equal('root') - expect(abuse2.reporterAccount.host).to.equal('localhost:9001') + expect(abuse2.reporterAccount.host).to.equal('localhost:' + servers[0].port) expect(abuse2.video.id).to.equal(servers[1].video.id) expect(abuse2.state.id).to.equal(VideoAbuseState.PENDING) expect(abuse2.state.label).to.equal('Pending') @@ -141,7 +140,7 @@ describe('Test video abuses', function () { abuseServer2 = res2.body.data[0] expect(abuseServer2.reason).to.equal('my super bad reason 2') expect(abuseServer2.reporterAccount.name).to.equal('root') - expect(abuseServer2.reporterAccount.host).to.equal('localhost:9001') + expect(abuseServer2.reporterAccount.host).to.equal('localhost:' + servers[0].port) expect(abuseServer2.state.id).to.equal(VideoAbuseState.PENDING) expect(abuseServer2.state.label).to.equal('Pending') 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' import 'mocha' import { acceptChangeOwnership, - changeVideoOwnership, cleanupTests, + changeVideoOwnership, + cleanupTests, createUser, doubleFollow, flushAndRunMultipleServers, @@ -13,7 +14,6 @@ import { getVideo, getVideoChangeOwnershipList, getVideosList, - killallServers, refuseChangeOwnership, ServerInfo, setAccessTokensToServers, @@ -203,8 +203,8 @@ describe('Test video change ownership - nominal', function () { } }) - after(function () { - killallServers(servers) + after(async function () { + await cleanupTests(servers) }) }) diff --git a/server/tests/api/videos/video-channels.ts b/server/tests/api/videos/video-channels.ts index 345e96f43..4f600cae8 100644 --- a/server/tests/api/videos/video-channels.ts +++ b/server/tests/api/videos/video-channels.ts @@ -2,12 +2,12 @@ import * as chai from 'chai' import 'mocha' -import { User, Video } from '../../../../shared/index' +import { User, Video, VideoChannel, VideoDetails } from '../../../../shared/index' import { cleanupTests, createUser, doubleFollow, - flushAndRunMultipleServers, + flushAndRunMultipleServers, getVideo, getVideoChannelVideos, testImage, updateVideo, @@ -18,12 +18,10 @@ import { import { addVideoChannel, deleteVideoChannel, - flushTests, getAccountVideoChannelsList, getMyUserInformation, getVideoChannel, getVideoChannelsList, - killallServers, ServerInfo, setAccessTokensToServers, updateVideoChannel @@ -35,13 +33,12 @@ const expect = chai.expect describe('Test video channels', function () { let servers: ServerInfo[] let userInfo: User - let accountUUID: string let firstVideoChannelId: number let secondVideoChannelId: number let videoUUID: string before(async function () { - this.timeout(30000) + this.timeout(60000) servers = await flushAndRunMultipleServers(2) @@ -51,7 +48,6 @@ describe('Test video channels', function () { { const res = await getMyUserInformation(servers[0].url, servers[0].accessToken) const user: User = res.body - accountUUID = user.account.uuid firstVideoChannelId = user.videoChannels[0].id } @@ -83,7 +79,8 @@ describe('Test video channels', function () { // The channel is 1 is propagated to servers 2 { - const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, { name: 'my video name', channelId: secondVideoChannelId }) + const videoAttributesArg = { name: 'my video name', channelId: secondVideoChannelId, support: 'video support field' } + const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, videoAttributesArg) videoUUID = res.body.video.uuid } @@ -108,7 +105,11 @@ describe('Test video channels', function () { }) it('Should have two video channels when getting account channels on server 1', async function () { - const res = await getAccountVideoChannelsList(servers[0].url, userInfo.account.name + '@' + userInfo.account.host) + const res = await getAccountVideoChannelsList({ + url: servers[ 0 ].url, + accountName: userInfo.account.name + '@' + userInfo.account.host + }) + expect(res.body.total).to.equal(2) expect(res.body.data).to.be.an('array') expect(res.body.data).to.have.lengthOf(2) @@ -123,8 +124,62 @@ describe('Test video channels', function () { expect(videoChannels[1].support).to.equal('super video channel support text') }) + it('Should paginate and sort account channels', async function () { + { + const res = await getAccountVideoChannelsList({ + url: servers[ 0 ].url, + accountName: userInfo.account.name + '@' + userInfo.account.host, + start: 0, + count: 1, + sort: 'createdAt' + }) + + expect(res.body.total).to.equal(2) + expect(res.body.data).to.have.lengthOf(1) + + const videoChannel: VideoChannel = res.body.data[ 0 ] + expect(videoChannel.name).to.equal('root_channel') + } + + { + const res = await getAccountVideoChannelsList({ + url: servers[ 0 ].url, + accountName: userInfo.account.name + '@' + userInfo.account.host, + start: 0, + count: 1, + sort: '-createdAt' + }) + + expect(res.body.total).to.equal(2) + expect(res.body.data).to.have.lengthOf(1) + + const videoChannel: VideoChannel = res.body.data[ 0 ] + expect(videoChannel.name).to.equal('second_video_channel') + } + + { + const res = await getAccountVideoChannelsList({ + url: servers[ 0 ].url, + accountName: userInfo.account.name + '@' + userInfo.account.host, + start: 1, + count: 1, + sort: '-createdAt' + }) + + expect(res.body.total).to.equal(2) + expect(res.body.data).to.have.lengthOf(1) + + const videoChannel: VideoChannel = res.body.data[ 0 ] + expect(videoChannel.name).to.equal('root_channel') + } + }) + it('Should have one video channel when getting account channels on server 2', async function () { - const res = await getAccountVideoChannelsList(servers[1].url, userInfo.account.name + '@' + userInfo.account.host) + const res = await getAccountVideoChannelsList({ + url: servers[ 1 ].url, + accountName: userInfo.account.name + '@' + userInfo.account.host + }) + expect(res.body.total).to.equal(1) expect(res.body.data).to.be.an('array') expect(res.body.data).to.have.lengthOf(1) @@ -147,12 +202,12 @@ describe('Test video channels', function () { }) it('Should update video channel', async function () { - this.timeout(5000) + this.timeout(15000) const videoChannelAttributes = { displayName: 'video channel updated', description: 'video channel description updated', - support: 'video channel support text updated' + support: 'support updated' } await updateVideoChannel(servers[0].url, servers[0].accessToken, 'second_video_channel', videoChannelAttributes) @@ -170,7 +225,36 @@ describe('Test video channels', function () { expect(res.body.data[0].name).to.equal('second_video_channel') expect(res.body.data[0].displayName).to.equal('video channel updated') expect(res.body.data[0].description).to.equal('video channel description updated') - expect(res.body.data[0].support).to.equal('video channel support text updated') + expect(res.body.data[0].support).to.equal('support updated') + } + }) + + it('Should not have updated the video support field', async function () { + for (const server of servers) { + const res = await getVideo(server.url, videoUUID) + const video: VideoDetails = res.body + + expect(video.support).to.equal('video support field') + } + }) + + it('Should update the channel support field and update videos too', async function () { + this.timeout(35000) + + const videoChannelAttributes = { + support: 'video channel support text updated', + bulkVideosSupportUpdate: true + } + + await updateVideoChannel(servers[0].url, servers[0].accessToken, 'second_video_channel', videoChannelAttributes) + + await waitJobs(servers) + + for (const server of servers) { + const res = await getVideo(server.url, videoUUID) + const video: VideoDetails = res.body + + expect(video.support).to.equal(videoChannelAttributes.support) } }) @@ -213,7 +297,7 @@ describe('Test video channels', function () { this.timeout(10000) for (const server of servers) { - const channelURI = 'second_video_channel@localhost:9001' + const channelURI = 'second_video_channel@localhost:' + servers[0].port const res1 = await getVideoChannelVideos(server.url, server.accessToken, channelURI, 0, 5) expect(res1.body.total).to.equal(1) expect(res1.body.data).to.be.an('array') @@ -234,11 +318,11 @@ describe('Test video channels', function () { this.timeout(10000) for (const server of servers) { - const secondChannelURI = 'second_video_channel@localhost:9001' + const secondChannelURI = 'second_video_channel@localhost:' + servers[0].port const res1 = await getVideoChannelVideos(server.url, server.accessToken, secondChannelURI, 0, 5) expect(res1.body.total).to.equal(0) - const channelURI = 'root_channel@localhost:9001' + const channelURI = 'root_channel@localhost:' + servers[0].port const res2 = await getVideoChannelVideos(server.url, server.accessToken, channelURI, 0, 5) expect(res2.body.total).to.equal(1) 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 () { expect(comment.videoId).to.equal(videoId) expect(comment.id).to.equal(comment.threadId) expect(comment.account.name).to.equal('root') - expect(comment.account.host).to.equal('localhost:9001') - expect(comment.account.url).to.equal('http://localhost:9001/accounts/root') + expect(comment.account.host).to.equal('localhost:' + server.port) + expect(comment.account.url).to.equal('http://localhost:' + server.port + '/accounts/root') expect(comment.totalReplies).to.equal(0) expect(dateIsValid(comment.createdAt as string)).to.be.true expect(dateIsValid(comment.updatedAt as string)).to.be.true @@ -86,7 +86,7 @@ describe('Test video comments', function () { expect(comment.videoId).to.equal(videoId) expect(comment.id).to.equal(comment.threadId) expect(comment.account.name).to.equal('root') - expect(comment.account.host).to.equal('localhost:9001') + expect(comment.account.host).to.equal('localhost:' + server.port) await testImage(server.url, 'avatar-resized', comment.account.avatar.path, '.png') 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' import { checkDirectoryIsEmpty, checkSegmentHash, - checkTmpIsEmpty, cleanupTests, + checkTmpIsEmpty, + cleanupTests, doubleFollow, flushAndRunMultipleServers, - flushTests, getPlaylist, getVideo, - killallServers, removeVideo, ServerInfo, setAccessTokensToServers, @@ -22,12 +21,11 @@ import { import { VideoDetails } from '../../../../shared/models/videos' import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type' import { join } from 'path' +import { DEFAULT_AUDIO_RESOLUTION } from '../../../initializers/constants' const expect = chai.expect -async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string) { - const resolutions = [ 240, 360, 480, 720 ] - +async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string, resolutions = [ 240, 360, 480, 720 ]) { for (const server of servers) { const res = await getVideo(server.url, videoUUID) const videoDetails: VideoDetails = res.body @@ -42,16 +40,15 @@ async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string) { const masterPlaylist = res2.text - expect(masterPlaylist).to.contain('#EXT-X-STREAM-INF:BANDWIDTH=55472,RESOLUTION=640x360,FRAME-RATE=25') - for (const resolution of resolutions) { + expect(masterPlaylist).to.match(new RegExp('#EXT-X-STREAM-INF:BANDWIDTH=\\d+,RESOLUTION=\\d+x' + resolution + ',FRAME-RATE=\\d+')) expect(masterPlaylist).to.contain(`${resolution}.m3u8`) } } { for (const resolution of resolutions) { - const res2 = await getPlaylist(`http://localhost:9001/static/streaming-playlists/hls/${videoUUID}/${resolution}.m3u8`) + const res2 = await getPlaylist(`http://localhost:${servers[0].port}/static/streaming-playlists/hls/${videoUUID}/${resolution}.m3u8`) const subPlaylist = res2.text expect(subPlaylist).to.contain(`${videoUUID}-${resolution}-fragmented.mp4`) @@ -59,7 +56,7 @@ async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string) { } { - const baseUrl = 'http://localhost:9001/static/streaming-playlists/hls' + const baseUrl = 'http://localhost:' + servers[0].port + '/static/streaming-playlists/hls' for (const resolution of resolutions) { await checkSegmentHash(baseUrl, baseUrl, videoUUID, resolution, hlsPlaylist) @@ -71,11 +68,21 @@ async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string) { describe('Test HLS videos', function () { let servers: ServerInfo[] = [] let videoUUID = '' + let videoAudioUUID = '' before(async function () { this.timeout(120000) - servers = await flushAndRunMultipleServers(2, { transcoding: { enabled: true, hls: { enabled: true } } }) + const configOverride = { + transcoding: { + enabled: true, + allow_audio_files: true, + hls: { + enabled: true + } + } + } + servers = await flushAndRunMultipleServers(2, configOverride) // Get the access tokens await setAccessTokensToServers(servers) @@ -87,17 +94,28 @@ describe('Test HLS videos', function () { it('Should upload a video and transcode it to HLS', async function () { this.timeout(120000) - { - const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, { name: 'video 1', fixture: 'video_short.webm' }) - videoUUID = res.body.video.uuid - } + const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, { name: 'video 1', fixture: 'video_short.webm' }) + videoUUID = res.body.video.uuid await waitJobs(servers) await checkHlsPlaylist(servers, videoUUID) }) + it('Should upload an audio file and transcode it to HLS', async function () { + this.timeout(120000) + + const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, { name: 'video audio', fixture: 'sample.ogg' }) + videoAudioUUID = res.body.video.uuid + + await waitJobs(servers) + + await checkHlsPlaylist(servers, videoAudioUUID, [ DEFAULT_AUDIO_RESOLUTION ]) + }) + it('Should update the video', async function () { + this.timeout(10000) + await updateVideo(servers[0].url, servers[0].accessToken, videoUUID, { name: 'video 1 updated' }) await waitJobs(servers) @@ -105,13 +123,17 @@ describe('Test HLS videos', function () { await checkHlsPlaylist(servers, videoUUID) }) - it('Should delete the video', async function () { + it('Should delete videos', async function () { + this.timeout(10000) + await removeVideo(servers[0].url, servers[0].accessToken, videoUUID) + await removeVideo(servers[0].url, servers[0].accessToken, videoAudioUUID) await waitJobs(servers) for (const server of servers) { await getVideo(server.url, videoUUID, 404) + await getVideo(server.url, videoAudioUUID, 404) } }) diff --git a/server/tests/api/videos/video-playlists.ts b/server/tests/api/videos/video-playlists.ts index fd5e4c4be..8690327c4 100644 --- a/server/tests/api/videos/video-playlists.ts +++ b/server/tests/api/videos/video-playlists.ts @@ -371,7 +371,7 @@ describe('Test video playlists', function () { for (const server of servers) { const results = [ - await getAccountPlaylistsList(server.url, 'root@localhost:9002', 0, 5, '-createdAt'), + await getAccountPlaylistsList(server.url, 'root@localhost:' + servers[1].port, 0, 5, '-createdAt'), await getVideoPlaylistsList(server.url, 0, 2, '-createdAt') ] @@ -754,6 +754,40 @@ describe('Test video playlists', function () { } }) + + it('Should be able to create a public playlist, and set it to private', async function () { + this.timeout(30000) + + const res = await createVideoPlaylist({ + url: servers[0].url, + token: servers[0].accessToken, + playlistAttrs: { + displayName: 'my super public playlist', + privacy: VideoPlaylistPrivacy.PUBLIC, + videoChannelId: servers[0].videoChannel.id + } + }) + const videoPlaylistIds = res.body.videoPlaylist + + await waitJobs(servers) + + for (const server of servers) { + await getVideoPlaylist(server.url, videoPlaylistIds.uuid, 200) + } + + const playlistAttrs = { privacy: VideoPlaylistPrivacy.PRIVATE } + await updateVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: videoPlaylistIds.id, playlistAttrs }) + + await waitJobs(servers) + + for (const server of [ servers[1], servers[2] ]) { + await getVideoPlaylist(server.url, videoPlaylistIds.uuid, 404) + } + await getVideoPlaylist(servers[0].url, videoPlaylistIds.uuid, 401) + + await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistIds.uuid, 200) + }) + it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () { this.timeout(30000) @@ -770,7 +804,7 @@ describe('Test video playlists', function () { this.timeout(30000) for (const server of servers) { - await checkPlaylistFilesWereRemoved(playlistServer1UUID, server.serverNumber) + await checkPlaylistFilesWereRemoved(playlistServer1UUID, server.internalServerNumber) } }) diff --git a/server/tests/api/videos/video-privacy.ts b/server/tests/api/videos/video-privacy.ts index ef1cf0f07..40b539106 100644 --- a/server/tests/api/videos/video-privacy.ts +++ b/server/tests/api/videos/video-privacy.ts @@ -6,8 +6,7 @@ import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enu import { cleanupTests, flushAndRunMultipleServers, - getVideosList, - killallServers, + getVideosList, getVideosListWithToken, ServerInfo, setAccessTokensToServers, uploadVideo @@ -153,6 +152,29 @@ describe('Test video privacy', function () { } }) + it('Should set this new video as private', async function () { + this.timeout(10000) + + await updateVideo(servers[0].url, servers[0].accessToken, privateVideoId, { privacy: VideoPrivacy.PRIVATE }) + + await waitJobs(servers) + + for (const server of servers) { + const res = await getVideosList(server.url) + + expect(res.body.total).to.equal(0) + expect(res.body.data).to.have.lengthOf(0) + } + + { + const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 5) + + expect(res.body.total).to.equal(1) + expect(res.body.data).to.have.lengthOf(1) + expect(res.body.data[0].name).to.equal('super video public') + } + }) + after(async function () { await cleanupTests(servers) }) 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' import 'mocha' import { omit } from 'lodash' import { getMaxBitrate, VideoDetails, VideoResolution, VideoState } from '../../../../shared/models/videos' -import { audio, getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils' +import { audio, canDoQuickTranscode, getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils' import { - buildAbsoluteFixturePath, cleanupTests, + buildAbsoluteFixturePath, + cleanupTests, doubleFollow, flushAndRunMultipleServers, generateHighBitrateVideo, getMyVideos, getVideo, getVideosList, - killallServers, + makeGetRequest, root, ServerInfo, setAccessTokensToServers, uploadVideo, + waitJobs, webtorrentAdd } from '../../../../shared/extra-utils' -import { extname, join } from 'path' -import { waitJobs } from '../../../../shared/extra-utils/server/jobs' +import { join } from 'path' import { VIDEO_TRANSCODING_FPS } from '../../../../server/initializers/constants' const expect = chai.expect @@ -121,7 +122,7 @@ describe('Test video transcoding', function () { expect(videoDetails.files).to.have.lengthOf(4) - const path = join(root(), 'test2', 'videos', video.uuid + '-240.mp4') + const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', video.uuid + '-240.mp4') const probe = await audio.get(path) if (probe.audioStream) { @@ -152,7 +153,7 @@ describe('Test video transcoding', function () { const videoDetails: VideoDetails = res2.body expect(videoDetails.files).to.have.lengthOf(4) - const path = join(root(), 'test2', 'videos', video.uuid + '-240.mp4') + const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', video.uuid + '-240.mp4') const probe = await audio.get(path) expect(probe).to.not.have.property('audioStream') } @@ -179,7 +180,7 @@ describe('Test video transcoding', function () { expect(videoDetails.files).to.have.lengthOf(4) const fixturePath = buildAbsoluteFixturePath(videoAttributes.fixture) const fixtureVideoProbe = await audio.get(fixturePath) - const path = join(root(), 'test2', 'videos', video.uuid + '-240.mp4') + const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', video.uuid + '-240.mp4') const videoProbe = await audio.get(path) if (videoProbe.audioStream && fixtureVideoProbe.audioStream) { const toOmit = [ 'max_bit_rate', 'duration', 'duration_ts', 'nb_frames', 'start_time', 'start_pts' ] @@ -216,13 +217,13 @@ describe('Test video transcoding', function () { expect(videoDetails.files[ 3 ].fps).to.be.below(31) for (const resolution of [ '240', '360', '480' ]) { - const path = join(root(), 'test2', 'videos', video.uuid + '-' + resolution + '.mp4') + const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', video.uuid + '-' + resolution + '.mp4') const fps = await getVideoFileFPS(path) expect(fps).to.be.below(31) } - const path = join(root(), 'test2', 'videos', video.uuid + '-720.mp4') + const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', video.uuid + '-720.mp4') const fps = await getVideoFileFPS(path) expect(fps).to.be.above(58).and.below(62) @@ -310,7 +311,7 @@ describe('Test video transcoding', function () { const video = res.body.data.find(v => v.name === videoAttributes.name) for (const resolution of ['240', '360', '480', '720', '1080']) { - const path = join(root(), 'test2', 'videos', video.uuid + '-' + resolution + '.mp4') + const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', video.uuid + '-' + resolution + '.mp4') const bitrate = await getVideoFileBitrate(path) const fps = await getVideoFileFPS(path) const resolution2 = await getVideoFileResolution(path) @@ -324,6 +325,15 @@ describe('Test video transcoding', function () { it('Should accept and transcode additional extensions', async function () { this.timeout(300000) + let tempFixturePath: string + + { + tempFixturePath = await generateHighBitrateVideo() + + const bitrate = await getVideoFileBitrate(tempFixturePath) + expect(bitrate).to.be.above(getMaxBitrate(VideoResolution.H_1080P, 60, VIDEO_TRANSCODING_FPS)) + } + for (const fixture of [ 'video_short.mkv', 'video_short.avi' ]) { const videoAttributes = { name: fixture, @@ -349,6 +359,63 @@ describe('Test video transcoding', function () { } }) + it('Should correctly detect if quick transcode is possible', async function () { + this.timeout(10000) + + expect(await canDoQuickTranscode(buildAbsoluteFixturePath('video_short.mp4'))).to.be.true + expect(await canDoQuickTranscode(buildAbsoluteFixturePath('video_short.webm'))).to.be.false + }) + + it('Should merge an audio file with the preview file', async function () { + this.timeout(60000) + + const videoAttributesArg = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' } + await uploadVideo(servers[ 1 ].url, servers[ 1 ].accessToken, videoAttributesArg) + + await waitJobs(servers) + + for (const server of servers) { + const res = await getVideosList(server.url) + + const video = res.body.data.find(v => v.name === 'audio_with_preview') + const res2 = await getVideo(server.url, video.id) + const videoDetails: VideoDetails = res2.body + + expect(videoDetails.files).to.have.lengthOf(1) + + await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, statusCodeExpected: 200 }) + await makeGetRequest({ url: server.url, path: videoDetails.previewPath, statusCodeExpected: 200 }) + + const magnetUri = videoDetails.files[ 0 ].magnetUri + expect(magnetUri).to.contain('.mp4') + } + }) + + it('Should upload an audio file and choose a default background image', async function () { + this.timeout(60000) + + const videoAttributesArg = { name: 'audio_without_preview', fixture: 'sample.ogg' } + await uploadVideo(servers[ 1 ].url, servers[ 1 ].accessToken, videoAttributesArg) + + await waitJobs(servers) + + for (const server of servers) { + const res = await getVideosList(server.url) + + const video = res.body.data.find(v => v.name === 'audio_without_preview') + const res2 = await getVideo(server.url, video.id) + const videoDetails = res2.body + + expect(videoDetails.files).to.have.lengthOf(1) + + await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, statusCodeExpected: 200 }) + await makeGetRequest({ url: server.url, path: videoDetails.previewPath, statusCodeExpected: 200 }) + + const magnetUri = videoDetails.files[ 0 ].magnetUri + expect(magnetUri).to.contain('.mp4') + } + }) + after(async function () { await cleanupTests(servers) }) 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 { flushAndRunServer, ServerInfo, setAccessTokensToServers, - uploadVideo, uploadVideoAndGetId, viewVideo, wait, countVideoViewsOf, doubleFollow, waitJobs, cleanupTests + uploadVideo, uploadVideoAndGetId, viewVideo, wait, countVideoViewsOf, doubleFollow, waitJobs, cleanupTests, closeAllSequelize } from '../../../../shared/extra-utils' import { getVideosOverview } from '../../../../shared/extra-utils/overviews/overviews' import { VideosOverview } from '../../../../shared/models/overviews' @@ -58,14 +58,14 @@ describe('Test video views cleaner', function () { { for (const server of servers) { - const total = await countVideoViewsOf(server.serverNumber, videoIdServer1) + const total = await countVideoViewsOf(server.internalServerNumber, videoIdServer1) expect(total).to.equal(2) } } { for (const server of servers) { - const total = await countVideoViewsOf(server.serverNumber, videoIdServer2) + const total = await countVideoViewsOf(server.internalServerNumber, videoIdServer2) expect(total).to.equal(2) } } @@ -74,8 +74,6 @@ describe('Test video views cleaner', function () { it('Should clean old video views', async function () { this.timeout(50000) - this.timeout(50000) - killallServers([ servers[0] ]) await reRunServer(servers[0], { views: { videos: { remote: { max_age: '5 seconds' } } } }) @@ -86,21 +84,23 @@ describe('Test video views cleaner', function () { { for (const server of servers) { - const total = await countVideoViewsOf(server.serverNumber, videoIdServer1) + const total = await countVideoViewsOf(server.internalServerNumber, videoIdServer1) expect(total).to.equal(2) } } { - const totalServer1 = await countVideoViewsOf(servers[0].serverNumber, videoIdServer2) + const totalServer1 = await countVideoViewsOf(servers[0].internalServerNumber, videoIdServer2) expect(totalServer1).to.equal(0) - const totalServer2 = await countVideoViewsOf(servers[1].serverNumber, videoIdServer2) + const totalServer2 = await countVideoViewsOf(servers[1].internalServerNumber, videoIdServer2) expect(totalServer2).to.equal(2) } }) after(async function () { + await closeAllSequelize(servers) + await cleanupTests(servers) }) }) 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 { doubleFollow, execCLI, flushAndRunMultipleServers, - flushTests, generateHighBitrateVideo, + generateHighBitrateVideo, getEnvCli, getVideo, getVideosList, - killallServers, root, + root, ServerInfo, setAccessTokensToServers, - uploadVideo, viewVideo, wait + uploadVideo, + viewVideo, + wait } from '../../../shared/extra-utils' import { waitJobs } from '../../../shared/extra-utils/server/jobs' import { getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '../../helpers/ffmpeg-utils' @@ -102,7 +104,7 @@ describe('Test optimize old videos', function () { expect(file.size).to.be.below(5000000) - const path = join(root(), 'test1', 'videos', video.uuid + '-' + file.resolution.id + '.mp4') + const path = join(root(), 'test' + servers[0].internalServerNumber, 'videos', video.uuid + '-' + file.resolution.id + '.mp4') const bitrate = await getVideoFileBitrate(path) const fps = await getVideoFileFPS(path) const resolution = await getVideoFileResolution(path) diff --git a/server/tests/feeds/feeds.ts b/server/tests/feeds/feeds.ts index 0dcdf09cf..437470327 100644 --- a/server/tests/feeds/feeds.ts +++ b/server/tests/feeds/feeds.ts @@ -7,13 +7,13 @@ import { createUser, doubleFollow, flushAndRunMultipleServers, - flushTests, - getJSONfeed, getMyUserInformation, + getJSONfeed, + getMyUserInformation, getXMLfeed, - killallServers, ServerInfo, setAccessTokensToServers, - uploadVideo, userLogin + uploadVideo, + userLogin } from '../../../shared/extra-utils' import * as libxmljs from 'libxmljs' import { addVideoCommentThread } from '../../../shared/extra-utils/videos/video-comments' @@ -28,10 +28,10 @@ const expect = chai.expect describe('Test syndication feeds', () => { let servers: ServerInfo[] = [] let userAccessToken: string - let rootAccountUUID: string - let rootChannelUUID: string - let userAccountUUID: string - let userChannelUUID: string + let rootAccountId: number + let rootChannelId: number + let userAccountId: number + let userChannelId: number before(async function () { this.timeout(120000) @@ -45,8 +45,8 @@ describe('Test syndication feeds', () => { { const res = await getMyUserInformation(servers[0].url, servers[0].accessToken) const user: User = res.body - rootAccountUUID = user.account.uuid - rootChannelUUID = user.videoChannels[0].uuid + rootAccountId = user.account.id + rootChannelId = user.videoChannels[0].id } { @@ -56,8 +56,8 @@ describe('Test syndication feeds', () => { const res = await getMyUserInformation(servers[0].url, userAccessToken) const user: User = res.body - userAccountUUID = user.account.uuid - userChannelUUID = user.videoChannels[0].uuid + userAccountId = user.account.id + userChannelId = user.videoChannels[0].id } { @@ -127,71 +127,71 @@ describe('Test syndication feeds', () => { }) it('Should filter by account', async function () { + { + const json = await getJSONfeed(servers[0].url, 'videos', { accountId: rootAccountId }) + const jsonObj = JSON.parse(json.text) + expect(jsonObj.items.length).to.be.equal(1) + expect(jsonObj.items[ 0 ].title).to.equal('my super name for server 1') + expect(jsonObj.items[ 0 ].author.name).to.equal('root') + } + + { + const json = await getJSONfeed(servers[0].url, 'videos', { accountId: userAccountId }) + const jsonObj = JSON.parse(json.text) + expect(jsonObj.items.length).to.be.equal(1) + expect(jsonObj.items[ 0 ].title).to.equal('user video') + expect(jsonObj.items[ 0 ].author.name).to.equal('john') + } + for (const server of servers) { { - const json = await getJSONfeed(server.url, 'videos', { accountId: rootAccountUUID }) + const json = await getJSONfeed(server.url, 'videos', { accountName: 'root@localhost:' + servers[0].port }) const jsonObj = JSON.parse(json.text) expect(jsonObj.items.length).to.be.equal(1) expect(jsonObj.items[ 0 ].title).to.equal('my super name for server 1') - expect(jsonObj.items[ 0 ].author.name).to.equal('root') } { - const json = await getJSONfeed(server.url, 'videos', { accountId: userAccountUUID }) + const json = await getJSONfeed(server.url, 'videos', { accountName: 'john@localhost:' + servers[0].port }) const jsonObj = JSON.parse(json.text) expect(jsonObj.items.length).to.be.equal(1) expect(jsonObj.items[ 0 ].title).to.equal('user video') - expect(jsonObj.items[ 0 ].author.name).to.equal('john') } } + }) + it('Should filter by video channel', async function () { { - const json = await getJSONfeed(servers[0].url, 'videos', { accountName: 'root' }) + const json = await getJSONfeed(servers[0].url, 'videos', { videoChannelId: rootChannelId }) const jsonObj = JSON.parse(json.text) expect(jsonObj.items.length).to.be.equal(1) expect(jsonObj.items[ 0 ].title).to.equal('my super name for server 1') + expect(jsonObj.items[ 0 ].author.name).to.equal('root') } { - const json = await getJSONfeed(servers[0].url, 'videos', { accountName: 'john' }) + const json = await getJSONfeed(servers[0].url, 'videos', { videoChannelId: userChannelId }) const jsonObj = JSON.parse(json.text) expect(jsonObj.items.length).to.be.equal(1) expect(jsonObj.items[ 0 ].title).to.equal('user video') + expect(jsonObj.items[ 0 ].author.name).to.equal('john') } - }) - it('Should filter by video channel', async function () { for (const server of servers) { { - const json = await getJSONfeed(server.url, 'videos', { videoChannelId: rootChannelUUID }) + const json = await getJSONfeed(server.url, 'videos', { videoChannelName: 'root_channel@localhost:' + servers[0].port }) const jsonObj = JSON.parse(json.text) expect(jsonObj.items.length).to.be.equal(1) expect(jsonObj.items[ 0 ].title).to.equal('my super name for server 1') - expect(jsonObj.items[ 0 ].author.name).to.equal('root') } { - const json = await getJSONfeed(server.url, 'videos', { videoChannelId: userChannelUUID }) + const json = await getJSONfeed(server.url, 'videos', { videoChannelName: 'john_channel@localhost:' + servers[0].port }) const jsonObj = JSON.parse(json.text) expect(jsonObj.items.length).to.be.equal(1) expect(jsonObj.items[ 0 ].title).to.equal('user video') - expect(jsonObj.items[ 0 ].author.name).to.equal('john') } } - - { - const json = await getJSONfeed(servers[0].url, 'videos', { videoChannelName: 'root_channel' }) - const jsonObj = JSON.parse(json.text) - expect(jsonObj.items.length).to.be.equal(1) - expect(jsonObj.items[ 0 ].title).to.equal('my super name for server 1') - } - - { - const json = await getJSONfeed(servers[0].url, 'videos', { videoChannelName: 'john_channel' }) - const jsonObj = JSON.parse(json.text) - expect(jsonObj.items.length).to.be.equal(1) - expect(jsonObj.items[ 0 ].title).to.equal('user video') - } }) }) diff --git a/server/tests/fixtures/preview.jpg b/server/tests/fixtures/preview.jpg index c40ece838..cb5692281 100644 Binary files a/server/tests/fixtures/preview.jpg and b/server/tests/fixtures/preview.jpg differ diff --git a/server/tests/fixtures/sample.ogg b/server/tests/fixtures/sample.ogg new file mode 100644 index 000000000..0d7f43eb7 Binary files /dev/null and b/server/tests/fixtures/sample.ogg 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 Binary files a/server/tests/fixtures/video_short1-preview.webm.jpg and b/server/tests/fixtures/video_short1-preview.webm.jpg differ diff --git a/server/tools/package.json b/server/tools/package.json new file mode 100644 index 000000000..2d13d41cc --- /dev/null +++ b/server/tools/package.json @@ -0,0 +1,12 @@ +{ + "name": "@peertube/cli", + "version": "1.0.0", + "private": true, + "dependencies": { + "application-config": "^1.0.1", + "webtorrent-hybrid": "^2.1.0" + }, + "summon": { + "silent": true + } +} diff --git a/server/tools/peertube-watch.ts b/server/tools/peertube-watch.ts index bf7274aab..7c27c1364 100644 --- a/server/tools/peertube-watch.ts +++ b/server/tools/peertube-watch.ts @@ -1,21 +1,15 @@ import * as program from 'commander' -import * as summon from 'summon-install' import { join } from 'path' import { execSync } from 'child_process' -import { root } from '../helpers/core-utils' - -let videoURL program .name('watch') .arguments('') - .option('-g, --gui ', 'player type', /^(airplay|stdout|chromecast|mpv|vlc|mplayer|ascii|xbmc)$/i, 'ascii') - .option('-i, --invert', 'invert colors (ascii player only)', true) - .option('-r, --resolution ', 'video resolution', /^(240|360|720|1080)$/i, '720') + .option('-g, --gui ', 'player type', /^(airplay|stdout|chromecast|mpv|vlc|mplayer|xbmc)$/i, 'vlc') + .option('-r, --resolution ', 'video resolution', '480') .on('--help', function () { console.log(' Available Players:') console.log() - console.log(' - ascii') console.log(' - mpv') console.log(' - mplayer') console.log(' - vlc') @@ -24,7 +18,6 @@ program console.log(' - airplay') console.log(' - chromecast') console.log() - console.log(' Note: \'ascii\' is the only option not using WebTorrent and not seeding back the video.') console.log() console.log(' Examples:') console.log() @@ -33,29 +26,25 @@ program console.log(' $ peertube watch https://peertube.cpy.re/videos/watch/e8a1af4e-414a-4d58-bfe6-2146eed06d10') console.log() }) - .action((url) => { - videoURL = url + .action((url, cmd) => { + run(url, cmd) + .catch(err => { + console.error(err) + process.exit(-1) + }) }) .parse(process.argv) -if (!videoURL) { - console.error(' positional argument is required.') - process.exit(-1) -} else { program['url'] = videoURL } +async function run (url: string, program: any) { + if (!url) { + console.error(' positional argument is required.') + process.exit(-1) + } -handler(program) + const cmd = 'node ' + join(__dirname, 'node_modules', 'webtorrent-hybrid', 'bin', 'cmd.js') + const args = ` --${program.gui} ` + + url.replace('videos/watch', 'download/torrents') + + `-${program.resolution}.torrent` -function handler (argv) { - if (argv['gui'] === 'ascii') { - summon('peerterminal') - const peerterminal = summon('peerterminal') - peerterminal([ '--link', videoURL, '--invert', argv['invert'] ]) - } else { - summon('webtorrent-hybrid') - const CMD = 'node ' + join(root(), 'node_modules', 'webtorrent-hybrid', 'bin', 'cmd.js') - const CMDargs = ` --${argv.gui} ` + - argv['url'].replace('videos/watch', 'download/torrents') + - `-${argv.resolution}.torrent` - execSync(CMD + CMDargs) - } + execSync(cmd + args) } diff --git a/server/tools/peertube.ts b/server/tools/peertube.ts old mode 100755 new mode 100644 diff --git a/server/tools/tsconfig.json b/server/tools/tsconfig.json new file mode 100644 index 000000000..f8a1c705c --- /dev/null +++ b/server/tools/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "exclude": [ ] // Overwrite exclude property +} diff --git a/server/tools/yarn.lock b/server/tools/yarn.lock new file mode 100644 index 000000000..3c3778d3f --- /dev/null +++ b/server/tools/yarn.lock @@ -0,0 +1,1970 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +addr-to-ip-port@^1.0.1, addr-to-ip-port@^1.4.2: + version "1.5.1" + resolved "https://registry.yarnpkg.com/addr-to-ip-port/-/addr-to-ip-port-1.5.1.tgz#bfada13fd6aeeeac19f1e9f7d84b4bbab45e5208" + integrity sha512-bA+dyydTNuQtrEDJ0g9eR7XabNhvrM5yZY0hvTbNK3yvoeC73ZqMES6E1cEqH9WPxs4uMtMsOjfwS4FmluhsAA== + +airplay-js@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/airplay-js/-/airplay-js-0.3.0.tgz#16bac2ef91b31249382924bfdeeabaddc9db7398" + integrity sha1-FrrC75GzEkk4KSS/3uq63cnbc5g= + dependencies: + mdns-js "0.5.0" + plist-with-patches "0.5.1" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +application-config-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/application-config-path/-/application-config-path-0.1.0.tgz#193c5f0a86541a4c66fba1e2dc38583362ea5e8f" + integrity sha1-GTxfCoZUGkxm+6Hi3DhYM2LqXo8= + +application-config@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/application-config/-/application-config-1.0.1.tgz#5aa2e2a5ed6abd2e5d1d473d3596f574044fe9e7" + integrity sha1-WqLipe1qvS5dHUc9NZb1dARP6ec= + dependencies: + application-config-path "^0.1.0" + mkdirp "^0.5.1" + +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +ascli@~0.3: + version "0.3.0" + resolved "https://registry.yarnpkg.com/ascli/-/ascli-0.3.0.tgz#5e66230e5219fe3e8952a4efb4f20fae596a813a" + integrity sha1-XmYjDlIZ/j6JUqTvtPIPrllqgTo= + dependencies: + colour latest + optjs latest + +async-limiter@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" + integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg== + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +bencode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/bencode/-/bencode-2.0.1.tgz#667a6a31c5e038d558608333da6b7c94e836c85b" + integrity sha512-2uhEl8FdjSBUyb69qDTgOEeeqDTa+n3yMQzLW0cOzNf1Ow5bwcg3idf+qsWisIKRH8Bk8oC7UXL8irRcPA8ZEQ== + dependencies: + safe-buffer "^5.1.1" + +binary-search@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/binary-search/-/binary-search-1.3.5.tgz#479ad009589e0273cf54e5d74ab1546c489078ce" + integrity sha512-RHFP0AdU6KAB0CCZsRMU2CJTk2EpL8GLURT+4gilpjr1f/7M91FgUMnXuQLmf3OKLet34gjuNFwO7e4agdX5pw== + +bitfield@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/bitfield/-/bitfield-2.0.0.tgz#fbe6767592fe5b4c87ecf1d04126294cc1bfa837" + integrity sha512-4xM4DYejOHQ/qWBfeqBXNA4mJ12PwcOibFYnH1kYh5U9BHciCqEJBqGNVnMJXUhm8mflujNRLSv7IiVQxovgjw== + +bittorrent-dht@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/bittorrent-dht/-/bittorrent-dht-9.0.0.tgz#08d5ebb51ed91d7e3eea5c275554f4323fb523e5" + integrity sha512-X5ax4G/PLtEPfqOUjqDZ2nmPENndWRMK4sT2jcQ4sXor904zhR40r4KqTyTvWYAljh5/hPPqM9DCUUtqWzRXoQ== + dependencies: + bencode "^2.0.0" + buffer-equals "^1.0.3" + debug "^3.1.0" + inherits "^2.0.1" + k-bucket "^5.0.0" + k-rpc "^5.0.0" + last-one-wins "^1.0.4" + lru "^3.1.0" + randombytes "^2.0.5" + record-cache "^1.0.2" + safe-buffer "^5.0.1" + simple-sha1 "^2.1.0" + +bittorrent-peerid@^1.0.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/bittorrent-peerid/-/bittorrent-peerid-1.3.0.tgz#a435d3b267c887c586c528b53359845905d7c158" + integrity sha512-SYd5H3RbN1ex+TrWAKXkEkASFWxAR7Tk6iLt9tfAT9ehBvZb/Y3AQDVRVJynlrixcWpnmsLYKI7tkRWgp7ORoQ== + +bittorrent-protocol@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/bittorrent-protocol/-/bittorrent-protocol-3.0.1.tgz#d3948f4d2b09d538095f7e5f93f64ba5df6b5c2a" + integrity sha512-hnvOzAu9u+2H0OLLL5byoFdz6oz5f3bx5f7R+ItUohTHMq9TgUhEJfcjo7xWtQHSKOVciYWwYTJ4EjczF5RX2A== + dependencies: + bencode "^2.0.0" + bitfield "^2.0.0" + debug "^3.1.0" + randombytes "^2.0.5" + readable-stream "^2.3.2" + speedometer "^1.0.0" + unordered-array-remove "^1.0.2" + xtend "^4.0.0" + +bittorrent-tracker@^9.0.0: + version "9.11.0" + resolved "https://registry.yarnpkg.com/bittorrent-tracker/-/bittorrent-tracker-9.11.0.tgz#9911f9c14e5a29f84990a0c31b3d83dd16eb2876" + integrity sha512-T1zvW/kSeEnWT4I3JE+6c7aZbO5jtleZyQe911SyzIxFF9DvtUNWXud3p5ZUkXaoI2xXwfpvlks5VFj5SKEB+A== + dependencies: + bencode "^2.0.0" + bittorrent-peerid "^1.0.2" + bn.js "^4.4.0" + compact2string "^1.2.0" + debug "^4.0.1" + ip "^1.0.1" + lru "^3.0.0" + minimist "^1.1.1" + once "^1.3.0" + random-iterate "^1.0.1" + randombytes "^2.0.3" + run-parallel "^1.1.2" + run-series "^1.0.2" + safe-buffer "^5.0.0" + simple-get "^3.0.0" + simple-peer "^9.0.0" + simple-websocket "^7.0.1" + string2compact "^1.1.1" + uniq "^1.0.1" + unordered-array-remove "^1.0.2" + ws "^6.0.0" + optionalDependencies: + bufferutil "^4.0.0" + utf-8-validate "^5.0.1" + +blob-to-buffer@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/blob-to-buffer/-/blob-to-buffer-1.2.8.tgz#78eeeb332f1280ed0ca6fb2b60693a8c6d36903a" + integrity sha512-re0AIxakF504MgeMtIyJkVcZ8T5aUxtp/QmTMlmjyb3P44E1BEv5x3LATBGApWAJATyXHtkXRD+gWTmeyYLiQA== + +block-stream2@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/block-stream2/-/block-stream2-1.1.0.tgz#c738e3a91ba977ebb5e1fef431e13ca11d8639e2" + integrity sha1-xzjjqRupd+u14f70MeE8oR2GOeI= + dependencies: + defined "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.4" + +bn.js@^4.4.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +browserify-package-json@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-package-json/-/browserify-package-json-1.0.1.tgz#98dde8aa5c561fd6d3fe49bbaa102b74b396fdea" + integrity sha1-mN3oqlxWH9bT/km7qhArdLOW/eo= + +buffer-alloc-unsafe@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" + integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== + +buffer-alloc@^1.1.0, buffer-alloc@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" + integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== + dependencies: + buffer-alloc-unsafe "^1.1.0" + buffer-fill "^1.0.0" + +buffer-equals@^1.0.3, buffer-equals@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/buffer-equals/-/buffer-equals-1.0.4.tgz#0353b54fd07fd9564170671ae6f66b9cf10d27f5" + integrity sha1-A1O1T9B/2VZBcGca5vZrnPENJ/U= + +buffer-fill@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" + integrity sha1-+PeLdniYiO858gXNY39o5wISKyw= + +buffer-from@^1.0.0, buffer-from@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +buffer-indexof@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" + integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== + +bufferutil@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.1.tgz#3a177e8e5819a1243fe16b63a199951a7ad8d4a7" + integrity sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA== + dependencies: + node-gyp-build "~3.7.0" + +bufferview@~1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/bufferview/-/bufferview-1.0.1.tgz#7afd74a45f937fa422a1d338c08bbfdc76cd725d" + integrity sha1-ev10pF+Tf6QiodM4wIu/3HbNcl0= + +"bytebuffer@~3 >=3.5": + version "3.5.5" + resolved "https://registry.yarnpkg.com/bytebuffer/-/bytebuffer-3.5.5.tgz#7a6faf1a13514b083f1fcf9541c4c9bfbe7e7fd3" + integrity sha1-em+vGhNRSwg/H8+VQcTJv75+f9M= + dependencies: + bufferview "~1" + long "~2 >=2.2.3" + +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= + +castv2-client@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/castv2-client/-/castv2-client-1.2.0.tgz#a9193b1a5448b8cb9a0415bd021c8811ed7b0544" + integrity sha1-qRk7GlRIuMuaBBW9AhyIEe17BUQ= + dependencies: + castv2 "~0.1.4" + debug "^2.2.0" + +castv2@~0.1.4: + version "0.1.9" + resolved "https://registry.yarnpkg.com/castv2/-/castv2-0.1.9.tgz#d0b0fab1fd06b0d9cca636886716ec1293a5905a" + integrity sha1-0LD6sf0GsNnMpjaIZxbsEpOlkFo= + dependencies: + debug "^2.2.0" + protobufjs "^3.2.2" + +chownr@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" + integrity sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g== + +chromecasts@^1.5.3: + version "1.9.1" + resolved "https://registry.yarnpkg.com/chromecasts/-/chromecasts-1.9.1.tgz#67b162e8414d57d6106c49fe4a0e9b08f20bbd12" + integrity sha512-nsXv7ufgrpC8s5DUm6FJEa2XJ2VvE9FmbTVi6r4zGreTFTTSRSJjvqVEqLUFX/fGo/zbSre3zdoV+Pu9DGLz0A== + dependencies: + castv2-client "^1.1.0" + debug "^2.1.3" + dns-txt "^2.0.2" + mime "^1.3.4" + multicast-dns "^6.0.1" + simple-get "^2.0.0" + thunky "^0.1.0" + xml2js "^0.4.8" + optionalDependencies: + node-ssdp "^2.2.0" + +chunk-store-stream@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/chunk-store-stream/-/chunk-store-stream-3.0.1.tgz#8e0d739226dcb386f44447b82a005b597a1d41d9" + integrity sha512-GA1NIFDZKElhkjiO6QOyzfK1QbUt6M3gFhUU/aR05JYaDqXbU5d7U92cLvGKdItJEDfojky6NQefy5VL5PpDBA== + dependencies: + block-stream2 "^1.0.0" + readable-stream "^2.0.5" + +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +clivas@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/clivas/-/clivas-0.2.0.tgz#b8d19188b3243e390f302410bd0cb1622db82649" + integrity sha1-uNGRiLMkPjkPMCQQvQyxYi24Jkk= + +closest-to@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/closest-to/-/closest-to-2.0.0.tgz#bb2a860edb7769b62d04821748ae50da24dbefaa" + integrity sha1-uyqGDtt3abYtBIIXSK5Q2iTb76o= + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +colour@latest: + version "0.7.1" + resolved "https://registry.yarnpkg.com/colour/-/colour-0.7.1.tgz#9cb169917ec5d12c0736d3e8685746df1cadf778" + integrity sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g= + +compact2string@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/compact2string/-/compact2string-1.4.1.tgz#8d34929055f8300a13cfc030ad1832e2e53c2e25" + integrity sha512-3D+EY5nsRhqnOwDxveBv5T8wGo4DEvYxjDtPGmdOX+gfr5gE92c2RC0w2wa+xEefm07QuVqqcF3nZJUZ92l/og== + dependencies: + ipaddr.js ">= 0.1.5" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concat-stream@^1.4.8: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +create-torrent@^3.23.1, create-torrent@^3.33.0: + version "3.33.0" + resolved "https://registry.yarnpkg.com/create-torrent/-/create-torrent-3.33.0.tgz#8a7a2aa2213a799c266c40e4c12f1468ede25105" + integrity sha512-KMd0KuvwVUg1grlRd5skG9ZkSbBYDDkAjDUMLnvxdRn0rL7ph3IwoOk7I8u1yLX4HYjGiLVlWYO55YWNNPjJFA== + dependencies: + bencode "^2.0.0" + block-stream2 "^1.0.0" + filestream "^4.0.0" + flatten "^1.0.2" + is-file "^1.0.0" + junk "^2.1.0" + minimist "^1.1.0" + multistream "^2.0.2" + once "^1.3.0" + piece-length "^1.0.0" + readable-stream "^3.0.2" + run-parallel "^1.0.0" + simple-sha1 "^2.0.0" + +debug@^2.1.0, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^3.1.0, debug@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +debug@^4.0.1, debug@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +decamelize@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +defined@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + +dlnacasts@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/dlnacasts/-/dlnacasts-0.1.0.tgz#f805211dcac74f6bb3a4d5d5541ad783b1b67d22" + integrity sha1-+AUhHcrHT2uzpNXVVBrXg7G2fSI= + dependencies: + debug "^2.1.3" + mime "^1.3.4" + node-ssdp "^2.7.1" + run-parallel "^1.1.6" + simple-get "^2.1.0" + thunky "^0.1.0" + upnp-mediarenderer-client "^1.2.2" + xml2js "^0.4.8" + +dns-packet@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" + integrity sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg== + dependencies: + ip "^1.1.0" + safe-buffer "^5.0.1" + +dns-txt@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" + integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY= + dependencies: + buffer-indexof "^1.0.0" + +domexception@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" + integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== + dependencies: + webidl-conversions "^4.0.2" + +ecstatic@^3.0.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/ecstatic/-/ecstatic-3.3.2.tgz#6d1dd49814d00594682c652adb66076a69d46c48" + integrity sha512-fLf9l1hnwrHI2xn9mEDT7KIi22UDqA2jaCwyCbSUJh9a1V+LEUSL/JO/6TIz/QyuBURWUHrFL5Kg2TtO1bkkog== + dependencies: + he "^1.1.1" + mime "^1.6.0" + minimist "^1.1.0" + url-join "^2.0.5" + +elementtree@^0.1.6, elementtree@~0.1.6: + version "0.1.7" + resolved "https://registry.yarnpkg.com/elementtree/-/elementtree-0.1.7.tgz#9ac91be6e52fb6e6244c4e54a4ac3ed8ae8e29c0" + integrity sha1-mskb5uUvtuYkTE5UpKw+2K6OKcA= + dependencies: + sax "1.1.4" + +end-of-stream@^1.1.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" + integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== + dependencies: + once "^1.4.0" + +error-ex@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +executable@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" + integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== + dependencies: + pify "^2.2.0" + +filestream@^4.0.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/filestream/-/filestream-4.1.3.tgz#948fcaade8221f715f5ecaddc54862faaacc9325" + integrity sha1-lI/KregiH3FfXsrdxUhi+qrMkyU= + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.5" + typedarray-to-buffer "^3.0.0" + xtend "^4.0.1" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +flatten@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" + integrity sha1-2uRqnXj74lKSJYzB54CkHZXAN4I= + +fs-chunk-store@^1.6.2: + version "1.7.0" + resolved "https://registry.yarnpkg.com/fs-chunk-store/-/fs-chunk-store-1.7.0.tgz#1c4bcbe93c99af10aa04b65348f2bb27377a4010" + integrity sha512-KhjJmZAs2eqfhCb6PdPx4RcZtheGTz86tpTC5JTvqBn/xda+Nb+0C7dCyjOSN7T76H6a56LvH0SVXQMchLXDRw== + dependencies: + mkdirp "^0.5.1" + random-access-file "^2.0.1" + randombytes "^2.0.3" + rimraf "^2.4.2" + run-parallel "^1.1.2" + thunky "^1.0.1" + +fs-minipass@^1.2.5: + version "1.2.6" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07" + integrity sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ== + dependencies: + minipass "^2.2.1" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +get-browser-rtc@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-browser-rtc/-/get-browser-rtc-1.0.2.tgz#bbcd40c8451a7ed4ef5c373b8169a409dd1d11d9" + integrity sha1-u81AyEUaftTvXDc7gWmkCd0dEdk= + +get-caller-file@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== + +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== + +glob@^7.1.3: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +graceful-fs@^4.1.2: + version "4.1.15" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" + integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + +he@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +hosted-git-info@^2.1.4: + version "2.7.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" + integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w== + +iconv-lite@^0.4.4: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ignore-walk@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" + integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ== + dependencies: + minimatch "^3.0.4" + +immediate-chunk-store@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/immediate-chunk-store/-/immediate-chunk-store-2.0.0.tgz#f313fd0cc71396d8911ad031179e1cccfda3da18" + integrity sha512-5s6NiCGbtWc+OQA60jrre54w12U7tynIyUNjO5LJjNA5lWwvCv6640roq8Wk/wIuaqnd4Pgtp453OyJ7hbONkQ== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= + +ip-set@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/ip-set/-/ip-set-1.0.2.tgz#be4f119f82c124836455993dfcd554639c7007de" + integrity sha512-Mb6kv78bTi4RNAIIWL8Bbre7hXOR2pNUi3j8FaQkLaitf/ZWxkq3/iIwXNYk2ACO3IMfdVdQrOkUtwZblO7uBA== + dependencies: + ip "^1.1.3" + +ip@^1.0.1, ip@^1.1.0, ip@^1.1.3: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= + +"ipaddr.js@>= 0.1.5", ipaddr.js@^1.0.1: + version "1.9.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" + integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA== + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-ascii@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-ascii/-/is-ascii-1.0.0.tgz#f02ad0259a0921cd199ff21ce1b09e0f6b4e3929" + integrity sha1-8CrQJZoJIc0Zn/Ic4bCeD2tOOSk= + +is-file@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-file/-/is-file-1.0.0.tgz#28a44cfbd9d3db193045f22b65fce8edf9620596" + integrity sha1-KKRM+9nT2xkwRfIrZfzo7fliBZY= + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +junk@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/junk/-/junk-2.1.0.tgz#f431b4b7f072dc500a5f10ce7f4ec71930e70134" + integrity sha1-9DG0t/By3FAKXxDOf07HGTDnATQ= + +k-bucket@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/k-bucket/-/k-bucket-4.0.1.tgz#3fc2e5693f0b7bff90d7b6b476edd6087955d542" + integrity sha512-YvDpmY3waI999h1zZoW1rJ04fZrgZ+5PAlVmvwDHT6YO/Q1AOhdel07xsKy9eAvJjQ9xZV1wz3rXKqEfaWvlcQ== + dependencies: + inherits "^2.0.1" + randombytes "^2.0.3" + +k-bucket@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/k-bucket/-/k-bucket-5.0.0.tgz#ef7a401fcd4c37cd31dceaa6ae4440ca91055e01" + integrity sha512-r/q+wV/Kde62/tk+rqyttEJn6h0jR7x+incdMVSYTqK73zVxVrzJa70kJL49cIKen8XjIgUZKSvk8ktnrQbK4w== + dependencies: + randombytes "^2.0.3" + +k-rpc-socket@^1.7.2: + version "1.8.0" + resolved "https://registry.yarnpkg.com/k-rpc-socket/-/k-rpc-socket-1.8.0.tgz#9a4dd6a4f3795ed847ffa156579cc389990bd1f2" + integrity sha512-f/9TynsO8YYjZ6JjNNtSSH7CJcIHcio1buy3zqByGxb/GX8AWLdL6FZEWTrN8V3/J7W4/E0ZTQQ+Jt2rVq7ELg== + dependencies: + bencode "^2.0.0" + buffer-equals "^1.0.4" + safe-buffer "^5.1.1" + +k-rpc@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/k-rpc/-/k-rpc-5.0.0.tgz#a72651860c96db440579e4c9f38dce8a42b481a8" + integrity sha512-vCH2rQdfMOS+MlUuTSuar1pS2EMrltURf9LmAR9xR6Jik0XPlMX3vEixgqMn17wKmFVCublJqSJ4hJIP7oKZ3Q== + dependencies: + buffer-equals "^1.0.3" + k-bucket "^4.0.0" + k-rpc-socket "^1.7.2" + randombytes "^2.0.5" + safe-buffer "^5.1.1" + +last-one-wins@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/last-one-wins/-/last-one-wins-1.0.4.tgz#c1bfd0cbcb46790ec9156b8d1aee8fcb86cda22a" + integrity sha1-wb/Qy8tGeQ7JFWuNGu6Py4bNoio= + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= + dependencies: + invert-kv "^1.0.0" + +load-ip-set@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/load-ip-set/-/load-ip-set-2.1.0.tgz#2d50b737cae41de4e413d213991d4083a3e1784b" + integrity sha512-taz7U6B+F7Zq90dfIKwqsB1CrFKelSEmMGC68OUqem8Cgd1QZygQBYb2Fk9i6muBSfH4xwF/Pjt4KKlAdOyWZw== + dependencies: + ip-set "^1.0.0" + netmask "^1.0.6" + once "^1.3.0" + simple-get "^3.0.0" + split "^1.0.0" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +"long@~2 >=2.2.3": + version "2.4.0" + resolved "https://registry.yarnpkg.com/long/-/long-2.4.0.tgz#9fa180bb1d9500cdc29c4156766a1995e1f4524f" + integrity sha1-n6GAux2VAM3CnEFWdmoZleH0Uk8= + +lru@^3.0.0, lru@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lru/-/lru-3.1.0.tgz#ea7fb8546d83733396a13091d76cfeb4c06837d5" + integrity sha1-6n+4VG2DczOWoTCR12z+tMBoN9U= + dependencies: + inherits "^2.0.1" + +magnet-uri@^5.1.3: + version "5.2.4" + resolved "https://registry.yarnpkg.com/magnet-uri/-/magnet-uri-5.2.4.tgz#7afe5b736af04445aff744c93a890a3710077688" + integrity sha512-VYaJMxhr8B9BrCiNINUsuhaEe40YnG+AQBwcqUKO66lSVaI9I3A1iH/6EmEwRI8OYUg5Gt+4lLE7achg676lrg== + dependencies: + thirty-two "^1.0.1" + uniq "^1.0.1" + +mdns-js-packet@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/mdns-js-packet/-/mdns-js-packet-0.2.0.tgz#642409e8183c7561cc60615bbd1420ec2fad7616" + integrity sha1-ZCQJ6Bg8dWHMYGFbvRQg7C+tdhY= + dependencies: + debug "^2.1.0" + qap "^3.1.2" + +mdns-js@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/mdns-js/-/mdns-js-0.5.0.tgz#4c8abb6ba7cabdc892d39228c3faa2556e09cf87" + integrity sha1-TIq7a6fKvciS05Iow/qiVW4Jz4c= + dependencies: + debug "^2.1.1" + mdns-js-packet "~0.2.0" + semver "~5.1.0" + +mediasource@^2.1.0, mediasource@^2.2.2: + version "2.3.0" + resolved "https://registry.yarnpkg.com/mediasource/-/mediasource-2.3.0.tgz#4c7b49e7ea4fb88f1cc181d8fcf0d94649271dc6" + integrity sha512-fqm86UwHvAnneIv40Uy1sDQaFtAByq/k0SQ3uCtbnEeSQNT1s5TDHCZOD1VmYCHwfY1jL2NjoZVwzZKYqy3L7A== + dependencies: + inherits "^2.0.1" + readable-stream "^3.0.0" + to-arraybuffer "^1.0.1" + +memory-chunk-store@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/memory-chunk-store/-/memory-chunk-store-1.3.0.tgz#ae99e7e3b58b52db43d49d94722930d39459d0c4" + integrity sha512-6LsOpHKKhxYrLhHmOJdBCUtSO7op5rUs1pag0fhjHo0QiXRyna0bwYf4EmQuL7InUeF2J7dUMPr6VMogRyf9NA== + +mime@^1.3.4, mime@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^2.1.0, mime@^2.4.0: + version "2.4.3" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.3.tgz#229687331e86f68924e6cb59e1cdd937f18275fe" + integrity sha512-QgrPRJfE+riq5TPZMcHZOtm8c6K/yYrMbKIoRfapfiGLxS8OTeIfRhUGW5LU7MlRa52KOAGCfUNruqLrIBvWZw== + +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +minimist@^1.1.0, minimist@^1.1.1, minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= + +minipass@^2.2.1, minipass@^2.3.4: + version "2.3.5" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" + integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" + integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA== + dependencies: + minipass "^2.2.1" + +mkdirp@^0.5.0, mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +moment@^2.12.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" + integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== + +mp4-box-encoding@^1.1.0, mp4-box-encoding@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/mp4-box-encoding/-/mp4-box-encoding-1.3.0.tgz#2a6f750947ff68c3a498fd76cd6424c53d995d48" + integrity sha512-U4pMLpjT/UzB8d36dxj6Mf1bG9xypEvgbuRIa1fztRXNKKTCAtRxsnFZhNOd7YDFOKtjBgssYGvo4H/Q3ZY1MA== + dependencies: + buffer-alloc "^1.2.0" + buffer-from "^1.1.0" + uint64be "^2.0.2" + +mp4-stream@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/mp4-stream/-/mp4-stream-2.0.3.tgz#30acee07709d323f8dcd87a07b3ce9c3c4bfb364" + integrity sha512-5NzgI0+bGakoZEwnIYINXqB3mnewkt3Y7jcvkXsTubnCNUSdM8cpP0Vemxf6FLg0qUN8fydTgNMVAc3QU8B92g== + dependencies: + buffer-alloc "^1.1.0" + inherits "^2.0.1" + mp4-box-encoding "^1.1.0" + next-event "^1.0.0" + readable-stream "^2.0.3" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +multicast-dns@^6.0.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" + integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== + dependencies: + dns-packet "^1.3.1" + thunky "^1.0.2" + +multistream@^2.0.2, multistream@^2.0.5: + version "2.1.1" + resolved "https://registry.yarnpkg.com/multistream/-/multistream-2.1.1.tgz#629d3a29bd76623489980d04519a2c365948148c" + integrity sha512-xasv76hl6nr1dEy3lPvy7Ej7K/Lx3O/FCvwge8PeVJpciPPoNCbaANcNiBug3IpdvTveZUcAV0DJzdnUDMesNQ== + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.5" + +nan@*, nan@^2.3.2: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + +needle@^2.2.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" + integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + +netmask@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" + integrity sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU= + +network-address@^1.0.0, network-address@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/network-address/-/network-address-1.1.2.tgz#4aa7bfd43f03f0b81c9702b13d6a858ddb326f3e" + integrity sha1-Sqe/1D8D8LgclwKxPWqFjdsybz4= + +next-event@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-event/-/next-event-1.0.0.tgz#e7778acde2e55802e0ad1879c39cf6f75eda61d8" + integrity sha1-53eKzeLlWALgrRh5w5z2917aYdg= + +node-cmake@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/node-cmake/-/node-cmake-2.3.2.tgz#e0fbc54b11405b07705e4d6d41865ae95ad289d0" + integrity sha1-4PvFSxFAWwdwXk1tQYZa6VrSidA= + dependencies: + nan "*" + which "^1.2.14" + yargs "^7.0.2" + +node-gyp-build@~3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-3.7.0.tgz#daa77a4f547b9aed3e2aac779eaf151afd60ec8d" + integrity sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w== + +node-pre-gyp@0.11.x: + version "0.11.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz#db1f33215272f692cd38f03238e3e9b47c5dd054" + integrity sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + +node-ssdp@^2.2.0, node-ssdp@^2.7.1: + version "2.9.1" + resolved "https://registry.yarnpkg.com/node-ssdp/-/node-ssdp-2.9.1.tgz#2d6ba8e7eff9bf5b338564f91f7ac5d5cdddc55b" + integrity sha1-LWuo5+/5v1szhWT5H3rF1c3dxVs= + dependencies: + debug "^2.2.0" + ip "^1.0.1" + +nodebmc@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/nodebmc/-/nodebmc-0.0.7.tgz#fae179165265509302cefbebeabd29bd4035184d" + integrity sha1-+uF5FlJlUJMCzvvr6r0pvUA1GE0= + dependencies: + mdns-js "0.5.0" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-package-data@^2.3.2: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +npm-bundled@^1.0.1: + version "1.0.6" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" + integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== + +npm-packlist@^1.1.6: + version "1.4.1" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.1.tgz#19064cdf988da80ea3cee45533879d90192bbfbc" + integrity sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + +npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +open@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/open/-/open-0.0.5.tgz#42c3e18ec95466b6bf0dc42f3a2945c3f0cad8fc" + integrity sha1-QsPhjslUZra/DcQvOilFw/DK2Pw= + +optjs@latest: + version "3.2.2" + resolved "https://registry.yarnpkg.com/optjs/-/optjs-3.2.2.tgz#69a6ce89c442a44403141ad2f9b370bd5bb6f4ee" + integrity sha1-aabOicRCpEQDFBrS+bNwvVu29O4= + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= + dependencies: + lcid "^1.0.0" + +os-tmpdir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +package-json-versionify@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/package-json-versionify/-/package-json-versionify-1.0.4.tgz#5860587a944873a6b7e6d26e8e51ffb22315bf17" + integrity sha1-WGBYepRIc6a35tJujlH/siMVvxc= + dependencies: + browserify-package-json "^1.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +parse-numeric-range@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/parse-numeric-range/-/parse-numeric-range-0.0.2.tgz#b4f09d413c7adbcd987f6e9233c7b4b210c938e4" + integrity sha1-tPCdQTx6282Yf26SM8e0shDJOOQ= + +parse-torrent@^6.0.0, parse-torrent@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/parse-torrent/-/parse-torrent-6.1.2.tgz#99da5bdd23435a1cb7e8e7a63847c4efb21b1956" + integrity sha512-Z/vig84sHwtrTEbOzisT4xnYTFlOgAaLQccPruMPgRahZUppVE/BUXzAos3jZM7c64o0lfukQdQ4ozWa5lN39w== + dependencies: + bencode "^2.0.0" + blob-to-buffer "^1.2.6" + get-stdin "^6.0.0" + magnet-uri "^5.1.3" + simple-get "^3.0.1" + simple-sha1 "^2.0.0" + uniq "^1.0.1" + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +piece-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/piece-length/-/piece-length-1.0.0.tgz#4db7167157fd69fef14caf7262cd39f189b24508" + integrity sha1-TbcWcVf9af7xTK9yYs058YmyRQg= + dependencies: + closest-to "~2.0.0" + +pify@^2.0.0, pify@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +plist-with-patches@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/plist-with-patches/-/plist-with-patches-0.5.1.tgz#868aae2e0df8989b026562b35cbc19cfd8bb780d" + integrity sha1-hoquLg34mJsCZWKzXLwZz9i7eA0= + dependencies: + xmlbuilder "0.4.x" + xmldom "0.1.x" + +prettier-bytes@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prettier-bytes/-/prettier-bytes-1.0.4.tgz#994b02aa46f699c50b6257b5faaa7fe2557e62d6" + integrity sha1-mUsCqkb2mcULYle1+qp/4lV+YtY= + +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== + +protobufjs@^3.2.2: + version "3.8.2" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-3.8.2.tgz#bc826e34c3af4697e8d0af7a669e4d612aedcd17" + integrity sha1-vIJuNMOvRpfo0K96Zp5NYSrtzRc= + dependencies: + ascli "~0.3" + bytebuffer "~3 >=3.5" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +qap@^3.1.2: + version "3.3.1" + resolved "https://registry.yarnpkg.com/qap/-/qap-3.3.1.tgz#11f9e8fa8890fe7cb99210c0f44d0613b7372cac" + integrity sha1-Efno+oiQ/ny5khDA9E0GE7c3LKw= + +random-access-file@^2.0.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/random-access-file/-/random-access-file-2.1.2.tgz#eeb32e50b9831f32060516862381152ae4e05aa6" + integrity sha512-dZo7HEcEPbZ/6XLXC4GXypiWvFbXVkdeMrJTi0B94pBJwddt/AvJh8GaQhso6KGYROGYCI/VWdHbmRDtkwT9pQ== + dependencies: + mkdirp "^0.5.1" + random-access-storage "^1.1.1" + +random-access-storage@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/random-access-storage/-/random-access-storage-1.3.0.tgz#d27e4d897b79dc4358afc2bbe553044e5c8cfe35" + integrity sha512-pdS9Mcb9TB7oICypPRALlheaSuszuAKmLVEPKJMuYor7R/zDuHh5ALuQoS+ox31XRwQUL+tDwWH2GPdyspwelA== + dependencies: + inherits "^2.0.3" + +random-iterate@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/random-iterate/-/random-iterate-1.0.1.tgz#f7d97d92dee6665ec5f6da08c7f963cad4b2ac99" + integrity sha1-99l9kt7mZl7F9toIx/ljytSyrJk= + +randombytes@^2.0.3, randombytes@^2.0.5: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +range-slice-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/range-slice-stream/-/range-slice-stream-2.0.0.tgz#1f25fc7a2cacf9ccd140c46f9cf670a1a7fe3ce6" + integrity sha512-PPYLwZ63lXi6Tv2EZ8w3M4FzC0rVqvxivaOVS8pXSp5FMIHFnvi4MWHL3UdFLhwSy50aNtJsgjY0mBC6oFL26Q== + dependencies: + readable-stream "^3.0.2" + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +readable-stream@^2.0.3, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.3.2, readable-stream@^2.3.4: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.0.6: + version "3.3.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.3.0.tgz#cb8011aad002eb717bf040291feba8569c986fb9" + integrity sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +record-cache@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/record-cache/-/record-cache-1.1.0.tgz#f8a467a691a469584b26e88d36b18afdb3932037" + integrity sha512-u8rbtLEJV7HRacl/ZYwSBFD8NFyB3PfTTfGLP37IW3hftQCwu6z4Q2RLyxo1YJUNRTEzJfpLpGwVuEYdaIkG9Q== + +render-media@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/render-media/-/render-media-3.1.3.tgz#aa8c8cd3f720049370067180709b551d3c566254" + integrity sha512-K7ziKKlIcgYpAovRsABDiSaNn7TzDDyyuFGpRwM52cloNcajInB6sCxFPUEzOuTJUeyvKCqT/k5INOjpKLCjhQ== + dependencies: + debug "^3.1.0" + is-ascii "^1.0.0" + mediasource "^2.1.0" + stream-to-blob-url "^2.0.0" + videostream "^2.5.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= + +resolve@^1.10.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.0.tgz#4014870ba296176b86343d50b60f3b50609ce232" + integrity sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw== + dependencies: + path-parse "^1.0.6" + +rimraf@^2.4.2, rimraf@^2.6.1: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +run-parallel-limit@^1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/run-parallel-limit/-/run-parallel-limit-1.0.5.tgz#c29a4fd17b4df358cb52a8a697811a63c984f1b7" + integrity sha512-NsY+oDngvrvMxKB3G8ijBzIema6aYbQMD2bHOamvN52BysbIGTnEY2xsNyfrcr9GhY995/t/0nQN3R3oZvaDlg== + +run-parallel@^1.0.0, run-parallel@^1.1.2, run-parallel@^1.1.6: + version "1.1.9" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" + integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== + +run-series@^1.0.2: + version "1.1.8" + resolved "https://registry.yarnpkg.com/run-series/-/run-series-1.1.8.tgz#2c4558f49221e01cd6371ff4e0a1e203e460fc36" + integrity sha512-+GztYEPRpIsQoCSraWHDBs9WVy4eVME16zhOtDB4H9J4xN0XRhknnmLOl+4gRgZtu8dpp9N/utSPjKH/xmDzXg== + +rusha@^0.8.1: + version "0.8.13" + resolved "https://registry.yarnpkg.com/rusha/-/rusha-0.8.13.tgz#9a084e7b860b17bff3015b92c67a6a336191513a" + integrity sha1-mghOe4YLF7/zAVuSxnpqM2GRUTo= + +safe-buffer@^5.0.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.1.4.tgz#74b6d33c9ae1e001510f179a91168588f1aedaa9" + integrity sha1-dLbTPJrh4AFRDxeakRaFiPGu2qk= + +sax@>=0.6.0, sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +"semver@2 || 3 || 4 || 5", semver@^5.3.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" + integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== + +semver@~5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.1.1.tgz#a3292a373e6f3e0798da0b20641b9a9c5bc47e19" + integrity sha1-oykqNz5vPgeY2gsgZBuanFvEfhk= + +set-blocking@^2.0.0, set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +signal-exit@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + +simple-concat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" + integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY= + +simple-get@^2.0.0, simple-get@^2.1.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.1.tgz#0e22e91d4575d87620620bc91308d57a77f44b5d" + integrity sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw== + dependencies: + decompress-response "^3.3.0" + once "^1.3.1" + simple-concat "^1.0.0" + +simple-get@^3.0.0, simple-get@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.0.3.tgz#924528ac3f9d7718ce5e9ec1b1a69c0be4d62efa" + integrity sha512-Wvre/Jq5vgoz31Z9stYWPLn0PqRqmBDpFSdypAnHu5AvRVCYPRYGnvryNLiXu8GOBNDH82J2FRHUGMjjHUpXFw== + dependencies: + decompress-response "^3.3.0" + once "^1.3.1" + simple-concat "^1.0.0" + +simple-peer@^9.0.0: + version "9.3.0" + resolved "https://registry.yarnpkg.com/simple-peer/-/simple-peer-9.3.0.tgz#85ecb126b23d8730f3904f199db65e84141e0f4e" + integrity sha512-5dLDfrRomrS2LuZUuH2aO7yTGtHFEl5Eb+8ZzqM0KC0lHcYUyJudUomP9ZY/lPUKBx2broL/Eee9bQ53yycEgQ== + dependencies: + debug "^4.0.1" + get-browser-rtc "^1.0.0" + inherits "^2.0.1" + randombytes "^2.0.3" + readable-stream "^2.3.4" + +simple-sha1@^2.0.0, simple-sha1@^2.0.8, simple-sha1@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/simple-sha1/-/simple-sha1-2.1.2.tgz#de40cbd5aae278fde8e3bb3250a35d74c67326b1" + integrity sha512-TQl9rm4rdKAVmhO++sXAb8TNN0D6JAD5iyI1mqEPNpxUzTRrtm4aOG1pDf/5W/qCFihiaoK6uuL9rvQz1x1VKw== + dependencies: + rusha "^0.8.1" + +simple-websocket@^7.0.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/simple-websocket/-/simple-websocket-7.2.0.tgz#c3190555d74399372b96b51435f2d8c4b04611df" + integrity sha512-wdxFg1fHw1yqFKWDcw+yNb4VIYqtl+vknZMlpLhvZSlR6l7/iVuwozqo+Qtl73mB1IH5QnXzonD1S+hAaLNTvQ== + dependencies: + debug "^3.1.0" + inherits "^2.0.1" + randombytes "^2.0.3" + readable-stream "^2.0.5" + ws "^6.0.0" + +spdx-correct@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" + integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" + integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== + +spdx-expression-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz#75ecd1a88de8c184ef015eafb51b5b48bfd11bb1" + integrity sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA== + +speedometer@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/speedometer/-/speedometer-1.1.0.tgz#a30b13abda45687a1a76977012c060f2ac8a7934" + integrity sha512-z/wAiTESw2XVPssY2XRcme4niTc4S5FkkJ4gknudtVoc33Zil8TdTxHy5torRcgqMqksJV2Yz8HQcvtbsnw0mQ== + +split@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" + integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== + dependencies: + through "2" + +stream-to-blob-url@^2.0.0, stream-to-blob-url@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/stream-to-blob-url/-/stream-to-blob-url-2.1.1.tgz#e1ac97f86ca8e9f512329a48e7830ce9a50beef2" + integrity sha512-DKJPEmCmIZoBfGVle9IhSfERiWaN5cuOtmfPxP2dZbLDRZxkBWZ4QbYxEJOSALk1Kf+WjBgedAMO6qkkf7Lmrg== + dependencies: + stream-to-blob "^1.0.0" + +stream-to-blob@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-to-blob/-/stream-to-blob-1.0.1.tgz#2dc1e09b71677a234d00445f8eb7ff70c4fe9948" + integrity sha512-aRy4neA4rf+qMtLT9fCRLPGWdrsIKtCx4kUdNTIPgPQ2hkHkdxbViVAvABMx9oRM6yCWfngHx6pwXfbYkVuPuw== + dependencies: + once "^1.3.3" + +stream-with-known-length-to-buffer@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/stream-with-known-length-to-buffer/-/stream-with-known-length-to-buffer-1.0.2.tgz#b8ea5a92086a1ed5d27fc4c529636682118c945b" + integrity sha512-UxSISjxmguvfYzZdq6d4XAjc3gAocqTIOS1CjgwkDkkGT/LMTsIYiV8agIw42IHFFHf8k4lPOoroCCf4W9oqzg== + dependencies: + once "^1.3.3" + +string-width@^1.0.1, string-width@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string2compact@^1.1.1, string2compact@^1.2.5: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string2compact/-/string2compact-1.3.0.tgz#22d946127b082d1203c51316af60117a337423c3" + integrity sha512-004ulKKANDuQilQsNxy2lisrpMG0qUJxBU+2YCEF7KziRyNR0Nredm2qk0f1V82nva59H3y9GWeHXE63HzGRFw== + dependencies: + addr-to-ip-port "^1.0.1" + ipaddr.js "^1.0.1" + +string_decoder@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d" + integrity sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w== + dependencies: + safe-buffer "~5.1.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +tar@^4: + version "4.4.8" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d" + integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.3.4" + minizlib "^1.1.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.2" + +thirty-two@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/thirty-two/-/thirty-two-1.0.2.tgz#4ca2fffc02a51290d2744b9e3f557693ca6b627a" + integrity sha1-TKL//AKlEpDSdEueP1V2k8prYno= + +through@2: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +thunky@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-0.1.0.tgz#bf30146824e2b6e67b0f2d7a4ac8beb26908684e" + integrity sha1-vzAUaCTituZ7Dy16Ssi+smkIaE4= + +thunky@^1.0.1, thunky@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.0.3.tgz#f5df732453407b09191dae73e2a8cc73f381a826" + integrity sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow== + +to-arraybuffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= + +torrent-discovery@^9.1.1: + version "9.1.1" + resolved "https://registry.yarnpkg.com/torrent-discovery/-/torrent-discovery-9.1.1.tgz#56704e6747b24fe00dbb75b442d202051f78d37d" + integrity sha512-3mHf+bxVCVLrlkPJdAoMbPMY1hpTZVeWw5hNc2pPFm+HCc2DS0HgVFTBTSWtB8vQPWA1hSEZpqJ+3QfdXxDE1g== + dependencies: + bittorrent-dht "^9.0.0" + bittorrent-tracker "^9.0.0" + debug "^3.1.0" + run-parallel "^1.1.2" + +torrent-piece@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/torrent-piece/-/torrent-piece-2.0.0.tgz#6598ae67d93699e887f178db267ba16d89d7ec9b" + integrity sha512-H/Z/yCuvZJj1vl1IQHI8dvF2QrUuXRJoptT5DW5967/dsLpXlCg+uyhFR5lfNj5mNaYePUbKtnL+qKWZGXv4Nw== + +typedarray-to-buffer@^3.0.0: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +uint64be@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/uint64be/-/uint64be-2.0.2.tgz#ef4a179752fe8f9ddaa29544ecfc13490031e8e5" + integrity sha512-9QqdvpGQTXgxthP+lY4e/gIBy+RuqcBaC6JVwT5I3bDLgT/btL6twZMR0pI3/Fgah9G/pdwzIprE5gL6v9UvyQ== + dependencies: + buffer-alloc "^1.1.0" + +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= + +unordered-array-remove@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unordered-array-remove/-/unordered-array-remove-1.0.2.tgz#c546e8f88e317a0cf2644c97ecb57dba66d250ef" + integrity sha1-xUbo+I4xegzyZEyX7LV9umbSUO8= + +upnp-device-client@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/upnp-device-client/-/upnp-device-client-1.0.2.tgz#91f84705f2349bf89082855fff4e3006ac435337" + integrity sha1-kfhHBfI0m/iQgoVf/04wBqxDUzc= + dependencies: + concat-stream "^1.4.8" + debug "^2.1.3" + elementtree "~0.1.6" + network-address "^1.0.0" + +upnp-mediarenderer-client@^1.2.2: + version "1.2.4" + resolved "https://registry.yarnpkg.com/upnp-mediarenderer-client/-/upnp-mediarenderer-client-1.2.4.tgz#0c63a51802082b6b03b596c475cc64fc1e0877c8" + integrity sha1-DGOlGAIIK2sDtZbEdcxk/B4Id8g= + dependencies: + debug "^2.1.3" + elementtree "^0.1.6" + upnp-device-client "^1.0.0" + +url-join@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/url-join/-/url-join-2.0.5.tgz#5af22f18c052a000a48d7b82c5e9c2e2feeda728" + integrity sha1-WvIvGMBSoACkjXuCxenC4v7tpyg= + +ut_metadata@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/ut_metadata/-/ut_metadata-3.3.0.tgz#a0e0e861ebc39ed96e506601d1463ade3b548a7e" + integrity sha512-IK+ke9yL6a4oPLz/3oSW9TW7m9Wr4RG+5kW5aS2YulzEU1QDGAtago/NnOlno91fo3fSO7mnsqzn3NXNXdv8nA== + dependencies: + bencode "^2.0.0" + bitfield "^2.0.0" + debug "^3.1.0" + simple-sha1 "^2.0.0" + +ut_pex@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ut_pex/-/ut_pex-1.2.1.tgz#472ed0ea5e9bbc9148b833339d56d7b17cf3dad0" + integrity sha512-ZrxMCbffYtxQDqvREN9kBXK2CB9tPnd5PylHoqQX9ai+3HV9/S39FnA5JnhLOC82dxIQQg0nTN2wmhtAdGNtOA== + dependencies: + bencode "^2.0.0" + compact2string "^1.2.0" + inherits "^2.0.1" + string2compact "^1.2.5" + +utf-8-validate@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.2.tgz#63cfbccd85dc1f2b66cf7a1d0eebc08ed056bfb3" + integrity sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw== + dependencies: + node-gyp-build "~3.7.0" + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +videostream@^2.5.1: + version "2.6.0" + resolved "https://registry.yarnpkg.com/videostream/-/videostream-2.6.0.tgz#7f0b2b84bc457c12cfe599aa2345f5cc06241ab6" + integrity sha512-nSsullx1BYClJxVSt4Fa+Ulsv0Cf7UwaHq+4LQdLkAUdmqNhY1DlGxXDWVY2gui5XV4FvDiSbXmSbGryMrrUCQ== + dependencies: + binary-search "^1.3.4" + inherits "^2.0.1" + mediasource "^2.2.2" + mp4-box-encoding "^1.3.0" + mp4-stream "^2.0.0" + multistream "^2.0.2" + pump "^3.0.0" + range-slice-stream "^2.0.0" + +vlc-command@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vlc-command/-/vlc-command-1.1.2.tgz#61a9b4249a0001c0bcac8cdaf36d3a8e674cffce" + integrity sha512-KZ15RTHz96OEiQDA8oNFn1edYDWyKJIWI4gF74Am9woZo5XmVYryk5RYXSwOMvsaAgL5ejICEGCl0suQyDBu+Q== + dependencies: + run-parallel "^1.1.6" + winreg "^1.2.1" + +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== + +webtorrent-cli@^1.12.3: + version "1.12.3" + resolved "https://registry.yarnpkg.com/webtorrent-cli/-/webtorrent-cli-1.12.3.tgz#e6a1060cd3f104346da91e67763276ca897f238c" + integrity sha512-NnBAGkD64CRsl9edM9q0QU+ku6nCX32nM0U+YC8Gs/36c8y+5m9Tya3mWIux3oZKZ54yGiVtnok4tUpqDE5tMA== + dependencies: + clivas "^0.2.0" + create-torrent "^3.23.1" + dlnacasts "^0.1.0" + ecstatic "^3.0.0" + executable "^4.0.0" + mime "^2.1.0" + minimist "^1.2.0" + moment "^2.12.0" + network-address "^1.1.0" + open "0.0.5" + parse-torrent "^6.0.0" + prettier-bytes "^1.0.3" + vlc-command "^1.0.0" + webtorrent "0.x" + winreg "^1.0.1" + optionalDependencies: + airplay-js "^0.3.0" + chromecasts "^1.5.3" + nodebmc "0.0.7" + +webtorrent-hybrid@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/webtorrent-hybrid/-/webtorrent-hybrid-2.1.0.tgz#c14d33d6769667d8ae524ca2d9dfdcd18d4cfbf2" + integrity sha512-S8tUgUbPLwGazPrBMTqjsuxlmhaCZaiC+KlgS7ECRGaHVVZTJjKG2kUw8uf558DdZbsEA3jNxOOsMvXiA62sFw== + dependencies: + create-torrent "^3.33.0" + webtorrent "^0.x" + webtorrent-cli "^1.12.3" + wrtc "^0.3.3" + +webtorrent@0.x, webtorrent@^0.x: + version "0.103.1" + resolved "https://registry.yarnpkg.com/webtorrent/-/webtorrent-0.103.1.tgz#18ead369bbcaa60dc8ea138784c33451edd34479" + integrity sha512-rqMD8sAaPzrUzEpA6gDEOgcN9Xyz4WGrQiHoVY6HlymLoNSkScMnmnEX1bsdMcIU9iOrnUlghDEW9sJ9jYCmwQ== + dependencies: + addr-to-ip-port "^1.4.2" + bitfield "^2.0.0" + bittorrent-dht "^9.0.0" + bittorrent-protocol "^3.0.0" + chunk-store-stream "^3.0.1" + create-torrent "^3.33.0" + debug "^4.1.0" + end-of-stream "^1.1.0" + fs-chunk-store "^1.6.2" + immediate-chunk-store "^2.0.0" + load-ip-set "^2.1.0" + memory-chunk-store "^1.2.0" + mime "^2.4.0" + multistream "^2.0.5" + package-json-versionify "^1.0.2" + parse-numeric-range "^0.0.2" + parse-torrent "^6.1.2" + pump "^3.0.0" + random-iterate "^1.0.1" + randombytes "^2.0.3" + range-parser "^1.2.0" + readable-stream "^3.0.6" + render-media "^3.0.0" + run-parallel "^1.1.6" + run-parallel-limit "^1.0.3" + safe-buffer "^5.0.1" + simple-concat "^1.0.0" + simple-get "^3.0.1" + simple-peer "^9.0.0" + simple-sha1 "^2.0.8" + speedometer "^1.0.0" + stream-to-blob "^1.0.0" + stream-to-blob-url "^2.1.0" + stream-with-known-length-to-buffer "^1.0.0" + torrent-discovery "^9.1.1" + torrent-piece "^2.0.0" + uniq "^1.0.1" + unordered-array-remove "^1.0.2" + ut_metadata "^3.3.0" + ut_pex "^1.1.1" + +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= + +which@^1.2.14: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +winreg@^1.0.1, winreg@^1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/winreg/-/winreg-1.2.4.tgz#ba065629b7a925130e15779108cf540990e98d1b" + integrity sha1-ugZWKbepJRMOFXeRCM9UCZDpjRs= + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +wrtc@^0.3.3: + version "0.3.7" + resolved "https://registry.yarnpkg.com/wrtc/-/wrtc-0.3.7.tgz#2279f1cb3a83573e77b3d9b7e720071fab2ae4af" + integrity sha512-mDFNFqAB+3IYVKlP15vpGD0EhXjsQlj/GLNje4KLpClLSq8pyTG0xqJFoU+Oq43XvDIUMmIJ/r1aNfrjT7KUQw== + dependencies: + nan "^2.3.2" + node-cmake "2.3.2" + node-pre-gyp "0.11.x" + optionalDependencies: + domexception "^1.0.1" + +ws@^6.0.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" + integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== + dependencies: + async-limiter "~1.0.0" + +xml2js@^0.4.8: + version "0.4.19" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" + integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== + dependencies: + sax ">=0.6.0" + xmlbuilder "~9.0.1" + +xmlbuilder@0.4.x: + version "0.4.3" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-0.4.3.tgz#c4614ba74e0ad196e609c9272cd9e1ddb28a8a58" + integrity sha1-xGFLp04K0ZbmCcknLNnh3bKKilg= + +xmlbuilder@~9.0.1: + version "9.0.7" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" + integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= + +xmldom@0.1.x: + version "0.1.27" + resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.27.tgz#d501f97b3bdb403af8ef9ecc20573187aadac0e9" + integrity sha1-1QH5ezvbQDr4757MIFcxh6rawOk= + +xtend@^4.0.0, xtend@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= + +y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= + +yallist@^3.0.0, yallist@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" + integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== + +yargs-parser@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" + integrity sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo= + dependencies: + camelcase "^3.0.0" + +yargs@^7.0.2: + version "7.1.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" + integrity sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg= + dependencies: + camelcase "^3.0.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.2" + which-module "^1.0.0" + y18n "^3.2.1" + yargs-parser "^5.0.0" diff --git a/shared/core-utils/miscs/date.ts b/shared/core-utils/miscs/date.ts new file mode 100644 index 000000000..4f92f758f --- /dev/null +++ b/shared/core-utils/miscs/date.ts @@ -0,0 +1,67 @@ +function isToday (d: Date) { + const today = new Date() + + return areDatesEqual(d, today) +} + +function isYesterday (d: Date) { + const yesterday = new Date() + yesterday.setDate(yesterday.getDate() - 1) + + return areDatesEqual(d, yesterday) +} + +function isThisWeek (d: Date) { + const minDateOfThisWeek = new Date() + minDateOfThisWeek.setHours(0, 0, 0) + + // getDay() -> Sunday - Saturday : 0 - 6 + // We want to start our week on Monday + let dayOfWeek = minDateOfThisWeek.getDay() - 1 + if (dayOfWeek < 0) dayOfWeek = 6 // Sunday + + minDateOfThisWeek.setDate(minDateOfThisWeek.getDate() - dayOfWeek) + + return d >= minDateOfThisWeek +} + +function isThisMonth (d: Date) { + const thisMonth = new Date().getMonth() + + return d.getMonth() === thisMonth +} + +function isLastMonth (d: Date) { + const now = new Date() + + return getDaysDifferences(now, d) <= 30 +} + +function isLastWeek (d: Date) { + const now = new Date() + + return getDaysDifferences(now, d) <= 7 +} + +// --------------------------------------------------------------------------- + +export { + isYesterday, + isThisWeek, + isThisMonth, + isToday, + isLastMonth, + isLastWeek +} + +// --------------------------------------------------------------------------- + +function areDatesEqual (d1: Date, d2: Date) { + return d1.getFullYear() === d2.getFullYear() && + d1.getMonth() === d2.getMonth() && + d1.getDate() === d2.getDate() +} + +function getDaysDifferences (d1: Date, d2: Date) { + return (d1.getTime() - d2.getTime()) / (86400000) +} 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 @@ import { QueryTypes, Sequelize } from 'sequelize' +import { ServerInfo } from '../server/servers' let sequelizes: { [ id: number ]: Sequelize } = {} -function getSequelize (serverNumber: number) { - if (sequelizes[serverNumber]) return sequelizes[serverNumber] +function getSequelize (internalServerNumber: number) { + if (sequelizes[internalServerNumber]) return sequelizes[internalServerNumber] - const dbname = 'peertube_test' + serverNumber + const dbname = 'peertube_test' + internalServerNumber const username = 'peertube' const password = 'peertube' const host = 'localhost' @@ -18,37 +19,37 @@ function getSequelize (serverNumber: number) { logging: false }) - sequelizes[serverNumber] = seq + sequelizes[internalServerNumber] = seq return seq } -function setActorField (serverNumber: number, to: string, field: string, value: string) { - const seq = getSequelize(serverNumber) +function setActorField (internalServerNumber: number, to: string, field: string, value: string) { + const seq = getSequelize(internalServerNumber) const options = { type: QueryTypes.UPDATE } return seq.query(`UPDATE actor SET "${field}" = '${value}' WHERE url = '${to}'`, options) } -function setVideoField (serverNumber: number, uuid: string, field: string, value: string) { - const seq = getSequelize(serverNumber) +function setVideoField (internalServerNumber: number, uuid: string, field: string, value: string) { + const seq = getSequelize(internalServerNumber) const options = { type: QueryTypes.UPDATE } return seq.query(`UPDATE video SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options) } -function setPlaylistField (serverNumber: number, uuid: string, field: string, value: string) { - const seq = getSequelize(serverNumber) +function setPlaylistField (internalServerNumber: number, uuid: string, field: string, value: string) { + const seq = getSequelize(internalServerNumber) const options = { type: QueryTypes.UPDATE } return seq.query(`UPDATE "videoPlaylist" SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options) } -async function countVideoViewsOf (serverNumber: number, uuid: string) { - const seq = getSequelize(serverNumber) +async function countVideoViewsOf (internalServerNumber: number, uuid: string) { + const seq = getSequelize(internalServerNumber) // tslint:disable 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) { return parseInt(total + '', 10) } -async function closeAllSequelize (servers: any[]) { - for (let i = 1; i <= servers.length; i++) { - if (sequelizes[ i ]) { - await sequelizes[ i ].close() - delete sequelizes[ i ] +async function closeAllSequelize (servers: ServerInfo[]) { + for (const server of servers) { + if (sequelizes[ server.internalServerNumber ]) { + await sequelizes[ server.internalServerNumber ].close() + delete sequelizes[ server.internalServerNumber ] } } } diff --git a/shared/extra-utils/search/videos.ts b/shared/extra-utils/search/videos.ts index ba4627017..5cf8d8cf0 100644 --- a/shared/extra-utils/search/videos.ts +++ b/shared/extra-utils/search/videos.ts @@ -6,9 +6,11 @@ import { immutableAssign } from '../miscs/miscs' function searchVideo (url: string, search: string) { const path = '/api/v1/search/videos' + + const query = { sort: '-publishedAt', search: search } const req = request(url) .get(path) - .query({ sort: '-publishedAt', search }) + .query(query) .set('Accept', 'application/json') return req.expect(200) @@ -30,11 +32,15 @@ function searchVideoWithToken (url: string, search: string, token: string, query function searchVideoWithPagination (url: string, search: string, start: number, count: number, sort?: string) { const path = '/api/v1/search/videos' + const query = { + start, + search, + count + } + const req = request(url) .get(path) - .query({ start }) - .query({ search }) - .query({ count }) + .query(query) if (sort) req.query({ sort }) @@ -46,10 +52,11 @@ function searchVideoWithPagination (url: string, search: string, start: number, function searchVideoWithSort (url: string, search: string, sort: string) { const path = '/api/v1/search/videos' + const query = { search, sort } + return request(url) .get(path) - .query({ search }) - .query({ sort }) + .query(query) .set('Accept', 'application/json') .expect(200) .expect('Content-Type', /json/) 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) { transcoding: { enabled: true, allowAdditionalExtensions: true, + allowAudioFiles: true, threads: 1, resolutions: { '240p': false, diff --git a/shared/extra-utils/server/follows.ts b/shared/extra-utils/server/follows.ts index 1505804de..0379bb109 100644 --- a/shared/extra-utils/server/follows.ts +++ b/shared/extra-utils/server/follows.ts @@ -1,17 +1,21 @@ import * as request from 'supertest' import { ServerInfo } from './servers' import { waitJobs } from './jobs' -import { makeGetRequest, makePostBodyRequest } from '..' +import { makePostBodyRequest } from '../requests/requests' function getFollowersListPaginationAndSort (url: string, start: number, count: number, sort: string, search?: string) { const path = '/api/v1/server/followers' + const query = { + start, + count, + sort, + search + } + return request(url) .get(path) - .query({ start }) - .query({ count }) - .query({ sort }) - .query({ search }) + .query(query) .set('Accept', 'application/json') .expect(200) .expect('Content-Type', /json/) @@ -42,12 +46,16 @@ function rejectFollower (url: string, token: string, follower: string, statusCod function getFollowingListPaginationAndSort (url: string, start: number, count: number, sort: string, search?: string) { const path = '/api/v1/server/following' + const query = { + start, + count, + sort, + search + } + return request(url) .get(path) - .query({ start }) - .query({ count }) - .query({ sort }) - .query({ search }) + .query(query) .set('Accept', 'application/json') .expect(200) .expect('Content-Type', /json/) 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) { } async function checkDirectoryIsEmpty (server: ServerInfo, directory: string) { - const testDirectory = 'test' + server.serverNumber + const testDirectory = 'test' + server.internalServerNumber const directoryPath = join(root(), testDirectory, directory) @@ -284,7 +284,7 @@ function cleanupTests (servers: ServerInfo[]) { } async function waitUntilLog (server: ServerInfo, str: string, count = 1) { - const logfile = join(root(), 'test' + server.serverNumber, 'logs/peertube.log') + const logfile = join(root(), 'test' + server.internalServerNumber, 'logs/peertube.log') while (true) { const buf = await readFile(logfile) diff --git a/shared/extra-utils/users/accounts.ts b/shared/extra-utils/users/accounts.ts index f64a2dbad..627e17cc3 100644 --- a/shared/extra-utils/users/accounts.ts +++ b/shared/extra-utils/users/accounts.ts @@ -39,7 +39,7 @@ async function expectAccountFollows (url: string, nameWithDomain: string, follow expect(account.followingCount).to.equal(followingCount, message) } -async function checkActorFilesWereRemoved (actorUUID: string, serverNumber: number) { +async function checkActorFilesWereRemoved (filename: string, serverNumber: number) { const testDirectory = 'test' + serverNumber for (const directory of [ 'avatars' ]) { @@ -50,7 +50,7 @@ async function checkActorFilesWereRemoved (actorUUID: string, serverNumber: numb const files = await readdir(directoryPath) for (const file of files) { - expect(file).to.not.contain(actorUUID) + expect(file).to.not.contain(filename) } } } 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, } } - const commentUrl = `http://localhost:9001/videos/watch/${uuid};threadId=${threadId}` + const commentUrl = `http://localhost:${base.server.port}/videos/watch/${uuid};threadId=${threadId}` function emailFinder (email: object) { return email[ 'text' ].indexOf(commentUrl) !== -1 } diff --git a/shared/extra-utils/users/users.ts b/shared/extra-utils/users/users.ts index 2bd37b8be..0f2f0ae15 100644 --- a/shared/extra-utils/users/users.ts +++ b/shared/extra-utils/users/users.ts @@ -1,10 +1,11 @@ import * as request from 'supertest' -import { makePostBodyRequest, makePutBodyRequest, updateAvatarRequest } from '../requests/requests' +import { makeGetRequest, makePostBodyRequest, makePutBodyRequest, updateAvatarRequest } from '../requests/requests' -import { UserRole } from '../../index' +import { UserCreate, UserRole } from '../../index' import { NSFWPolicyType } from '../../models/videos/nsfw-policy.type' import { ServerInfo, userLogin } from '..' import { UserAdminFlag } from '../../models/users/user-flag.model' +import { UserRegister } from '../../models/users/user-register.model' type CreateUserArgs = { url: string, accessToken: string, @@ -70,6 +71,27 @@ function registerUser (url: string, username: string, password: string, specialS .expect(specialStatus) } +function registerUserWithChannel (options: { + url: string, + user: { username: string, password: string }, + channel: { name: string, displayName: string } +}) { + const path = '/api/v1/users/register' + const body: UserRegister = { + username: options.user.username, + password: options.user.password, + email: options.user.username + '@example.com', + channel: options.channel + } + + return makePostBodyRequest({ + url: options.url, + path, + fields: body, + statusCodeExpected: 204 + }) +} + function getMyUserInformation (url: string, accessToken: string, specialStatus = 200) { const path = '/api/v1/users/me' @@ -138,12 +160,16 @@ function getUsersList (url: string, accessToken: string) { function getUsersListPaginationAndSort (url: string, accessToken: string, start: number, count: number, sort: string, search?: string) { const path = '/api/v1/users' + const query = { + start, + count, + sort, + search + } + return request(url) .get(path) - .query({ start }) - .query({ count }) - .query({ sort }) - .query({ search }) + .query(query) .set('Accept', 'application/json') .set('Authorization', 'Bearer ' + accessToken) .expect(200) @@ -312,6 +338,7 @@ export { getMyUserInformation, getMyUserVideoRating, deleteMe, + registerUserWithChannel, getMyUserVideoQuotaUsed, getUsersList, getUsersListPaginationAndSort, diff --git a/shared/extra-utils/videos/video-channels.ts b/shared/extra-utils/videos/video-channels.ts index 93a257bf9..3e79cf15a 100644 --- a/shared/extra-utils/videos/video-channels.ts +++ b/shared/extra-utils/videos/video-channels.ts @@ -1,6 +1,6 @@ import * as request from 'supertest' import { VideoChannelCreate, VideoChannelUpdate } from '../../models/videos' -import { updateAvatarRequest } from '../requests/requests' +import { makeGetRequest, updateAvatarRequest } from '../requests/requests' import { getMyUserInformation, ServerInfo } from '..' import { User } from '../..' @@ -19,14 +19,28 @@ function getVideoChannelsList (url: string, start: number, count: number, sort?: .expect('Content-Type', /json/) } -function getAccountVideoChannelsList (url: string, accountName: string, specialStatus = 200) { +function getAccountVideoChannelsList (parameters: { + url: string, + accountName: string, + start?: number, + count?: number, + sort?: string, + specialStatus?: number +}) { + const { url, accountName, start, count, sort = 'createdAt', specialStatus = 200 } = parameters + const path = '/api/v1/accounts/' + accountName + '/video-channels' - return request(url) - .get(path) - .set('Accept', 'application/json') - .expect(specialStatus) - .expect('Content-Type', /json/) + return makeGetRequest({ + url, + path, + query: { + start, + count, + sort + }, + statusCodeExpected: specialStatus + }) } function addVideoChannel ( @@ -60,12 +74,13 @@ function updateVideoChannel ( attributes: VideoChannelUpdate, expectedStatus = 204 ) { - const body = {} + const body: any = {} const path = '/api/v1/video-channels/' + channelName - if (attributes.displayName) body['displayName'] = attributes.displayName - if (attributes.description) body['description'] = attributes.description - if (attributes.support) body['support'] = attributes.support + if (attributes.displayName) body.displayName = attributes.displayName + if (attributes.description) body.description = attributes.description + if (attributes.support) body.support = attributes.support + if (attributes.bulkVideosSupportUpdate) body.bulkVideosSupportUpdate = attributes.bulkVideosSupportUpdate return request(url) .put(path) 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: { async function checkPlaylistFilesWereRemoved ( playlistUUID: string, - serverNumber: number, + internalServerNumber: number, directories = [ 'thumbnails' ] ) { - const testDirectory = 'test' + serverNumber + const testDirectory = 'test' + internalServerNumber for (const directory of directories) { const directoryPath = join(root(), testDirectory, directory) diff --git a/shared/extra-utils/videos/videos.ts b/shared/extra-utils/videos/videos.ts index b5a07b792..debaaf9a7 100644 --- a/shared/extra-utils/videos/videos.ts +++ b/shared/extra-utils/videos/videos.ts @@ -355,6 +355,7 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg .set('Accept', 'application/json') .set('Authorization', 'Bearer ' + accessToken) .field('name', attributes.name) + .field('support', attributes.support) .field('nsfw', JSON.stringify(attributes.nsfw)) .field('commentsEnabled', JSON.stringify(attributes.commentsEnabled)) .field('downloadEnabled', JSON.stringify(attributes.downloadEnabled)) @@ -524,7 +525,6 @@ async function completeVideoCheck ( expect(video.nsfw).to.equal(attributes.nsfw) expect(video.description).to.equal(attributes.description) expect(video.account.id).to.be.a('number') - expect(video.account.uuid).to.be.a('string') expect(video.account.host).to.equal(attributes.account.host) expect(video.account.name).to.equal(attributes.account.name) expect(video.channel.displayName).to.equal(attributes.channel.displayName) @@ -568,8 +568,8 @@ async function completeVideoCheck ( expect(file).not.to.be.undefined let extension = extname(attributes.fixture) - // Transcoding enabled on server 2, extension will always be .mp4 - if (attributes.account.host === 'localhost:9002') extension = '.mp4' + // Transcoding enabled: extension will always be .mp4 + if (attributes.files.length > 1) extension = '.mp4' const magnetUri = file.magnetUri expect(file.magnetUri).to.have.lengthOf.above(2) diff --git a/shared/models/activitypub/activitypub-actor.ts b/shared/models/activitypub/activitypub-actor.ts index 5e30bf783..53ec579bc 100644 --- a/shared/models/activitypub/activitypub-actor.ts +++ b/shared/models/activitypub/activitypub-actor.ts @@ -21,7 +21,6 @@ export interface ActivityPubActor { attributedTo: ActivityPubAttributedTo[] support?: string - uuid: string publicKey: { id: string owner: string diff --git a/shared/models/actors/account.model.ts b/shared/models/actors/account.model.ts index 043a2507e..2ff4b9f5e 100644 --- a/shared/models/actors/account.model.ts +++ b/shared/models/actors/account.model.ts @@ -10,7 +10,6 @@ export interface Account extends Actor { export interface AccountSummary { id: number - uuid: string name: string displayName: string url: string diff --git a/shared/models/actors/actor.model.ts b/shared/models/actors/actor.model.ts index a3953874d..1dbf5f638 100644 --- a/shared/models/actors/actor.model.ts +++ b/shared/models/actors/actor.model.ts @@ -2,7 +2,6 @@ import { Avatar } from '../avatars/avatar.model' export interface Actor { id: number - uuid: string url: string name: string host: string 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 { transcoding: { enabled: boolean allowAdditionalExtensions: boolean + allowAudioFiles: boolean threads: number resolutions: { '240p': boolean diff --git a/shared/models/users/user-register.model.ts b/shared/models/users/user-register.model.ts new file mode 100644 index 000000000..ce5c9c3d2 --- /dev/null +++ b/shared/models/users/user-register.model.ts @@ -0,0 +1,10 @@ +export interface UserRegister { + username: string + password: string + email: string + + channel?: { + name: string + displayName: string + } +} diff --git a/shared/models/videos/channel/video-channel-update.model.ts b/shared/models/videos/channel/video-channel-update.model.ts index 3626ce8a9..8dde9188b 100644 --- a/shared/models/videos/channel/video-channel-update.model.ts +++ b/shared/models/videos/channel/video-channel-update.model.ts @@ -1,5 +1,7 @@ export interface VideoChannelUpdate { - displayName: string + displayName?: string description?: string support?: string + + bulkVideosSupportUpdate?: boolean } diff --git a/shared/models/videos/channel/video-channel.model.ts b/shared/models/videos/channel/video-channel.model.ts index 14a813f8f..de4c26b3d 100644 --- a/shared/models/videos/channel/video-channel.model.ts +++ b/shared/models/videos/channel/video-channel.model.ts @@ -12,7 +12,6 @@ export interface VideoChannel extends Actor { export interface VideoChannelSummary { id: number - uuid: string name: string displayName: string url: string diff --git a/shared/models/videos/playlist/video-playlist-update.model.ts b/shared/models/videos/playlist/video-playlist-update.model.ts index 0ff5bcb0f..a6a3f74d9 100644 --- a/shared/models/videos/playlist/video-playlist-update.model.ts +++ b/shared/models/videos/playlist/video-playlist-update.model.ts @@ -1,8 +1,8 @@ import { VideoPlaylistPrivacy } from './video-playlist-privacy.model' export interface VideoPlaylistUpdate { - displayName: string - privacy: VideoPlaylistPrivacy + displayName?: string + privacy?: VideoPlaylistPrivacy description?: string videoChannelId?: number diff --git a/shared/models/videos/video-resolution.enum.ts b/shared/models/videos/video-resolution.enum.ts index 7da5e7100..51efa2e8b 100644 --- a/shared/models/videos/video-resolution.enum.ts +++ b/shared/models/videos/video-resolution.enum.ts @@ -5,7 +5,8 @@ export enum VideoResolution { H_360P = 360, H_480P = 480, H_720P = 720, - H_1080P = 1080 + H_1080P = 1080, + H_4K = 2160 } /** @@ -33,11 +34,14 @@ function getBaseBitrate (resolution: VideoResolution) { // quality according to Google Live Encoder: 1,500 - 4,000 Kbps // Quality according to YouTube Video Info: 1752 Kbps return 1750 * 1000 - case VideoResolution.H_1080P: // fallthrough - default: + case VideoResolution.H_1080P: // quality according to Google Live Encoder: 3000 - 6000 Kbps // Quality according to YouTube Video Info: 3277 Kbps return 3300 * 1000 + case VideoResolution.H_4K: // fallthrough + default: + // quality according to Google Live Encoder: 13000 - 34000 Kbps + return 15000 * 1000 } } diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml index de610893c..5a4f6fcb2 100644 --- a/support/doc/api/openapi.yaml +++ b/support/doc/api/openapi.yaml @@ -38,7 +38,7 @@ info: } ``` externalDocs: - url: https://docs.joinpeertube.org/api.html + url: https://docs.joinpeertube.org/#/api-rest-reference.html tags: - name: Accounts description: > @@ -99,6 +99,7 @@ x-tagGroups: - name: Videos tags: - Video + - Video Caption - Video Channel - Video Comment - Video Following @@ -152,7 +153,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Video' + $ref: '#/components/schemas/VideoListResponse' x-code-samples: - lang: JavaScript source: | @@ -575,9 +576,7 @@ paths: content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/Video' + $ref: '#/components/schemas/VideoListResponse' /users/me/subscriptions: get: summary: Get subscriptions of the current user @@ -638,9 +637,7 @@ paths: content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/Video' + $ref: '#/components/schemas/VideoListResponse' '/users/me/subscriptions/{uri}': get: summary: Get subscription of the current user for a given uri @@ -730,9 +727,7 @@ paths: content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/Video' + $ref: '#/components/schemas/VideoListResponse' /videos/categories: get: summary: Get list of video licences known by the server @@ -1025,7 +1020,7 @@ paths: description: Video preview file type: string privacy: - $ref: '#/components/schemas/VideoPrivacy' + $ref: '#/components/schemas/VideoPrivacySet' category: description: Video category type: string @@ -1129,7 +1124,7 @@ paths: description: Video preview file type: string privacy: - $ref: '#/components/schemas/VideoPrivacy' + $ref: '#/components/schemas/VideoPrivacySet' category: description: Video category type: string @@ -1247,6 +1242,58 @@ paths: type: array items: $ref: '#/components/schemas/VideoBlacklist' + /videos/{id}/captions: + get: + summary: Get list of video's captions + tags: + - Video Caption + parameters: + - $ref: '#/components/parameters/id2' + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + total: + type: integer + data: + type: array + items: + $ref: '#/components/schemas/VideoCaption' + /videos/{id}/captions/{captionLanguage}: + put: + summary: Add or replace a video caption + tags: + - Video Caption + parameters: + - $ref: '#/components/parameters/id2' + - $ref: '#/components/parameters/captionLanguage' + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + captionfile: + description: The file to upload. + type: string + format: binary + responses: + '204': + $ref: '#/paths/~1users~1me/put/responses/204' + delete: + summary: Delete a video caption + tags: + - Video Caption + parameters: + - $ref: '#/components/parameters/id2' + - $ref: '#/components/parameters/captionLanguage' + responses: + '204': + $ref: '#/paths/~1users~1me/put/responses/204' /video-channels: get: summary: Get list of video channels @@ -1275,7 +1322,10 @@ paths: '204': $ref: '#/paths/~1users~1me/put/responses/204' requestBody: - $ref: '#/components/requestBodies/VideoChannelInput' + content: + application/json: + schema: + $ref: '#/components/schemas/VideoChannelCreate' '/video-channels/{channelHandle}': get: summary: Get a video channel by its id @@ -1302,7 +1352,10 @@ paths: '204': $ref: '#/paths/~1users~1me/put/responses/204' requestBody: - $ref: '#/components/requestBodies/VideoChannelInput' + content: + application/json: + schema: + $ref: '#/components/schemas/VideoChannelUpdate' delete: summary: Delete a video channel by its id security: @@ -1318,6 +1371,7 @@ paths: get: summary: Get videos of a video channel by its id tags: + - Video - Video Channel parameters: - $ref: '#/components/parameters/channelHandle' @@ -1327,7 +1381,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Video' + $ref: '#/components/schemas/VideoListResponse' '/accounts/{name}/video-channels': get: summary: Get video channels of an account by its name @@ -1443,7 +1497,7 @@ paths: schema: $ref: '#/components/schemas/CommentThreadPostResponse' delete: - summary: 'Delete a comment in a comment therad by its id, of a video by its id' + summary: 'Delete a comment in a comment thread by its id, of a video by its id' security: - OAuth2: [] tags: @@ -1487,9 +1541,7 @@ paths: content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/Video' + $ref: '#/components/schemas/VideoListResponse' servers: - url: 'https://peertube.cpy.re/api/v1' description: Live Test Server (live data - stable version) @@ -1611,6 +1663,13 @@ components: description: The video id or uuid schema: type: string + captionLanguage: + name: captionLanguage + in: path + required: true + description: The caption language + schema: + type: string channelHandle: name: channelHandle in: path @@ -1722,12 +1781,6 @@ components: type: array items: type: string - requestBodies: - VideoChannelInput: - content: - application/json: - schema: - $ref: '#/components/schemas/VideoChannelInput' securitySchemes: OAuth2: description: > @@ -1739,7 +1792,7 @@ components: - Have an account with sufficient authorization levels - - [Generate](https://docs.joinpeertube.org/lang/en/devdocs/rest.html) a + - [Generate](https://docs.joinpeertube.org/#/api-rest-getting-started) a Bearer Token - Make Authenticated Requests @@ -1764,12 +1817,23 @@ components: type: string label: type: string - VideoPrivacy: - type: string + VideoPrivacySet: + type: integer enum: - - Public - - Unlisted - - Private + - 1 + - 2 + - 3 + description: 'The video privacy (Public = 1, Unlisted = 2, Private = 3)' + VideoPrivacyConstant: + properties: + id: + type: integer + enum: + - 1 + - 2 + - 3 + label: + type: string Video: properties: id: @@ -1789,7 +1853,7 @@ components: language: $ref: '#/components/schemas/VideoConstantString' privacy: - $ref: '#/components/schemas/VideoPrivacy' + $ref: '#/components/schemas/VideoPrivacyConstant' description: type: string duration: @@ -1917,6 +1981,12 @@ components: type: array items: $ref: '#/components/schemas/VideoCommentThreadTree' + VideoCaption: + properties: + language: + $ref: '#/components/schemas/VideoConstantString' + captionPath: + type: string Avatar: properties: path: @@ -1966,6 +2036,13 @@ components: autoPlayVideo: type: boolean role: + type: integer + enum: + - 0 + - 1 + - 2 + description: 'The user role (Admin = 0, Moderator = 1, User = 2)' + roleLabel: type: string enum: - User @@ -2096,6 +2173,14 @@ components: properties: comment: $ref: '#/components/schemas/VideoComment' + VideoListResponse: + properties: + total: + type: number + data: + type: array + items: + $ref: '#/components/schemas/Video' AddUser: properties: username: @@ -2115,12 +2200,11 @@ components: description: 'The user daily video quota ' role: type: integer - format: int32 enum: - 0 - 1 - 2 - description: 'The user role ' + description: 'The user role (Admin = 0, Moderator = 1, User = 2)' required: - username - password @@ -2143,8 +2227,12 @@ components: type: string description: 'The updated daily video quota of the user ' role: - type: string - description: 'The updated role of the user ' + type: integer + enum: + - 0 + - 1 + - 2 + description: 'The user role (Admin = 0, Moderator = 1, User = 2)' required: - id - email @@ -2206,10 +2294,28 @@ components: - username - password - email - VideoChannelInput: + VideoChannelCreate: properties: name: type: string + displayName: + type: string + description: + type: string + support: + type: string + required: + - name + - displayName + VideoChannelUpdate: + properties: + displayName: + type: string description: type: string + support: + type: string + bulkVideosSupportUpdate: + type: boolean + description: 'Update all videos support field of this channel' 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 ``` -### List videos +## List videos ```bash $ 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 @@ -# Client code documentation - -The client is a HTML/CSS/JavaScript web application (single page application -> SPA) developed with [TypeScript](https://www.typescriptlang.org/)/[Angular](https://angular.io/). - - -## Technologies - - * [TypeScript](https://www.typescriptlang.org/) -> Language - * [Angular](https://angular.io) -> JavaScript framework - * [SASS](http://sass-lang.com/) -> CSS framework - * [Webpack](https://webpack.js.org/) -> Source builder (compile TypeScript, SASS files, bundle them...) - * [Bootstrap](http://getbootstrap.com/) -> CSS framework - * [WebTorrent](https://webtorrent.io/) -> JavaScript library to make P2P in the browser - * [VideoJS](http://videojs.com/) -> JavaScript player framework - - -## Files - -The client files are in the `client` directory. The Webpack 2 configurations files are in `client/config` and the source files in `client/src`. -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. -Here is the description of the useful `client` files directory: - - tslint.json -> TypeScript linter rules - tsconfig.json -> TypeScript configuration for the compilation - .bootstraprc -> Bootstrap configuration file (which module we need) - config -> Webpack configuration files - src - |__ app -> TypeScript files for Angular application - |__ assets -> static files (images...) - |__ sass -> SASS files that are global for the application - |__ standalone -> files outside the Angular application (embed HTML page...) - |__ index.html -> root HTML file for our Angular application - |__ main.ts -> Main TypeScript file that boostraps our Angular application - |__ polyfills.ts -> Polyfills imports (ES 2015...) - -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). - - app - |__ +admin -> Admin components (followers, users...) - |__ account -> Account components (password change...) - |__ core -> Core components/services - |__ header -> Header components (logo, search...) - |__ login -> Login component - |__ menu -> Menu component (on the left) - |__ shared -> Shared components/services (search component, REST services...) - |__ signup -> Signup form - |__ videos -> Video components (list, watch, upload...) - |__ app.component.{html,scss,ts} -> Main application component - |__ app-routing.module.ts -> Main Angular routes - |__ app.module.ts -> Angular root module that imports all submodules we need - -## Conventions - -Uses [TSLint](https://palantir.github.io/tslint/) for TypeScript linting and [Angular styleguide](https://angular.io/docs/ts/latest/guide/style-guide.html). - -## Concepts - -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. -If you are not familiar with Angular I recommend you to read the [quickstart guide](https://angular.io/docs/ts/latest/quickstart.html). - -## Components tree - -![Components tree](/support/doc/development/client/components-tree.svg) - -## Newcomers - -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 Binary files a/support/doc/development/client/components-tree.png and /dev/null 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 @@ - -
App component
App component
Videos 
Videos 
Edit (upload/update)

Edit (upload/update)<div><br></div>
List
List
Watch
Watch
Account
Account
Login
Login
Admin
Admin
Follows
Follows
Video abuses
Video abuses
Users
Users
About
About
Page Not Found
<div>Page Not Found</div>
My Account
<div>My Account</div>
Video Channels
Video Channels
\ 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 @@ -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== \ 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 @@ -# Server code documentation - -The server is a web server developed with [TypeScript](https://www.typescriptlang.org/)/[Express](http://expressjs.com). - - -## Technologies - - * [TypeScript](https://www.typescriptlang.org/) -> Language - * [PostgreSQL](https://www.postgresql.org/) -> Database - * [Redis](https://redis.io/) -> Job queue/cache - * [Express](http://expressjs.com) -> Web server framework - * [Sequelize](http://docs.sequelizejs.com/en/v3/) -> SQL ORM - * [WebTorrent](https://webtorrent.io/) -> BitTorrent tracker and torrent creation - * [Mocha](https://mochajs.org/) -> Test framework - - -## Files - -The server main file is [server.ts](/server.ts). -The server modules description are in the [package.json](/package.json) at the project root. -All other server files are in the [server](/server) directory: - - server.ts -> app initialization, main routes configuration (static routes...) - config -> server YAML configurations (for tests, production...) - scripts -> Scripts files for npm run - server - |__ controllers -> API routes/controllers files - |__ helpers -> functions used by different part of the project (logger, utils...) - |__ initializers -> functions used at the server startup (installer, database, constants...) - |__ lib -> library function (WebTorrent, OAuth2, ActivityPub...) - |__ middlewares -> middlewares for controllers (requests validators, requests pagination...) - |__ models -> Sequelize models for each SQL tables (videos, users, accounts...) - |__ tests -> API tests and real world simulations (to test the decentralized feature...) - - -## Conventions - -Uses [JavaScript Standard Style](http://standardjs.com/). - -## Architecture - -The server is composed by: - - * a REST API (relying on the Express framework) documented on http://docs.joinpeertube.org/api.html - * a WebTorrent Tracker (slightly custom version of [webtorrent/bittorrent-tracker](https://github.com/webtorrent/bittorrent-tracker#server)) - -A video is seeded by the server with the [WebSeed](http://www.bittorrent.org/beps/bep_0019.html) protocol (HTTP). - -![Architecture scheme](/support/doc/development/server/upload-video.png) - -When a user uploads a video, the REST API creates the torrent file and then adds it to its database. - -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. - -## Newcomers - -The server entrypoint is [server.ts](/server.ts). Looking at this file is a good start. -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 @@ -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 \ 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 Binary files a/support/doc/development/server/upload-video.png and /dev/null 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: $ cd /var/www/peertube/peertube-latest && NODE_CONFIG_DIR=/var/www/peertube/config NODE_ENV=production npm run reset-password -- -u root ``` +Alternatively you can set the environment variable `PT_INITIAL_ROOT_PASSWORD`, +to your own administrator password, although it must be 6 characters or more. + ### What now? Now your instance is up you can: diff --git a/support/doc/tools.md b/support/doc/tools.md index 086cd5cff..c0f0b4ba1 100644 --- a/support/doc/tools.md +++ b/support/doc/tools.md @@ -35,7 +35,7 @@ You need to follow all the following steps even if you are on a PeerTube server ### Dependencies -Install the [PeerTube dependencies](dependencies.md). +Install the [PeerTube dependencies](dependencies.md) except PostgreSQL and Redis. ### Installation @@ -44,18 +44,14 @@ Clone the PeerTube repo to get the latest version (even if you are on your PeerT ``` $ git clone https://github.com/Chocobozzz/PeerTube.git $ CLONE="$(pwd)/PeerTube" -``` - -Run ``yarn install --pure-lockfile`` -``` $ cd ${CLONE} -$ yarn install --pure-lockfile ``` -Build server tools: +Install dependencies and build CLI tools: + ``` -$ cd ${CLONE} -$ npm run build:server +$ NOCLIENT=1 yarn install --pure-lockfile +$ npm run setup:cli ``` ### CLI wrapper 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 PEERTUBE_WEBSERVER_HTTPS=true # If you need more than one IP as trust_proxy # pass them as a comma separated array: -PEERTUBE_TRUST_PROXY=["127.0.0.1"] -#PEERTUBE_TRUST_PROXY=["127.0.0.1", "loopback", "192.168.1.0/24"] +PEERTUBE_TRUST_PROXY=["127.0.0.1", "loopback", "172.18.0.0/16"] #PEERTUBE_SMTP_USERNAME= #PEERTUBE_SMTP_PASSWORD= PEERTUBE_SMTP_HOSTNAME=postfix diff --git a/support/docker/production/config/custom-environment-variables.yaml b/support/docker/production/config/custom-environment-variables.yaml index bd4ac1215..d5b878830 100644 --- a/support/docker/production/config/custom-environment-variables.yaml +++ b/support/docker/production/config/custom-environment-variables.yaml @@ -106,6 +106,9 @@ transcoding: 1080: __name: "PEERTUBE_TRANSCODING_1080P" __format: "json" + 2160: + __name: "PEERTUBE_TRANSCODING_2160P" + __format: "json" instance: name: "PEERTUBE_INSTANCE_NAME" 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: labels: traefik.enable: "false" restart: "always" + +networks: + default: + ipam: + driver: default + config: + - subnet: 172.18.0.0/16 diff --git a/tsconfig.json b/tsconfig.json index 5ad4b3e10..4d2bdd6ba 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,6 +17,7 @@ "typeRoots": [ "node_modules/@types", "server/typings" ] }, "exclude": [ + "server/tools/", "client/node_modules", "node_modules", "dist", diff --git a/yarn.lock b/yarn.lock index f2cc0ee05..ce086c03c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,12 +2,28 @@ # yarn lockfile v1 -"@babel/runtime@7.0.0": +"@babel/code-frame@^7.0.0": version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0.tgz#adeb78fedfc855aa05bc041640f3f6f98e85424c" - integrity sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA== + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" + integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA== dependencies: - regenerator-runtime "^0.12.0" + "@babel/highlight" "^7.0.0" + +"@babel/highlight@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" + integrity sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw== + dependencies: + chalk "^2.0.0" + esutils "^2.0.2" + js-tokens "^4.0.0" + +"@babel/runtime@^7.0.0": + version "7.4.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.5.tgz#582bb531f5f9dc67d2fcb682979894f75e253f12" + integrity sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ== + dependencies: + regenerator-runtime "^0.13.2" "@samverschueren/stream-to-observable@^0.3.0": version "0.3.0" @@ -28,10 +44,10 @@ resolved "https://registry.yarnpkg.com/@types/async-lock/-/async-lock-1.1.1.tgz#81f218213bebcc5f740efe9648272c774a2e4b4b" integrity sha512-TU1X8jmAU2BjwKryBFV/GDezz7Ge0xu9ZuYC7dy6wKj4hnL0JcxeseCOr/G2JkGylff6hdUBrR+Ee5ApAQeU5g== -"@types/async@^2.0.40": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@types/async/-/async-2.4.1.tgz#43c3b2c60eab41c25ca0009c07ca7d619d943119" - integrity sha512-C09BK/wXzbW+/JK9zckhe+FeSbg7NmvVjUWwApnw7ksRpUq3ecGLiq2Aw1LlY4Z/VmtdhSaIs7jO5/MWRYMcOA== +"@types/async@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/async/-/async-3.0.0.tgz#d403560ee2aabccdb7936cb9a3fee5f147d626bd" + integrity sha512-DvEhEeG8ynipwkg7LtNHlO99+vEVbin+E+3YuzeJCM9TREewJ1B5fdZsQzykR7fKuh6rKh8mEir36zKd3uafOA== "@types/bcrypt@^3.0.0": version "3.0.0" @@ -125,17 +141,17 @@ "@types/express" "*" "@types/express-serve-static-core@*": - version "4.16.2" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.16.2.tgz#5ee8a22e602005be6767df6b2cba9879df3f75aa" - integrity sha512-qgc8tjnDrc789rAQed8NoiFLV5VGcItA4yWNFphqGU0RcuuQngD00g3LHhWIK3HQ2XeDgVCmlNPDlqi3fWBHnQ== + version "4.16.6" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.16.6.tgz#66d4b29ece3e2fb6e5aac2232723002426e651bd" + integrity sha512-8wr3CA/EMybyb6/V8qvTRKiNkPmgUA26uA9XWD6hlA0yFDuqi4r2L0C2B0U2HAYltJamoYJszlkaWM31vrKsHg== dependencies: "@types/node" "*" "@types/range-parser" "*" "@types/express@*", "@types/express@^4.0.35": - version "4.16.1" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.16.1.tgz#d756bd1a85c34d87eaf44c888bad27ba8a4b7cf0" - integrity sha512-V0clmJow23WeyblmACoxbHBu2JKlE5TiIme6Lem14FnPW9gsttyHtk6wq7njcdIWH1njAaFgR8gW09lgY98gQg== + version "4.17.0" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.0.tgz#49eaedb209582a86f12ed9b725160f12d04ef287" + integrity sha512-CjaMu57cjgjuZbh9DpkloeGxV45CnMGlVd+XpG7Gm9QgVrd7KFq+X4HY0vM+2v0bczS48Wg7bvnMY5TN+Xmcfw== dependencies: "@types/body-parser" "*" "@types/express-serve-static-core" "*" @@ -155,17 +171,17 @@ dependencies: "@types/node" "*" -"@types/fs-extra@^5.0.4": - version "5.0.5" - resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-5.0.5.tgz#080d90a792f3fa2c5559eb44bd8ef840aae9104b" - integrity sha512-w7iqhDH9mN8eLClQOYTkhdYUOSpp25eXxfc6VbFOGtzxW34JcvctH2bKjj4jD4++z4R5iO5D+pg48W2e03I65A== +"@types/fs-extra@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-7.0.0.tgz#9c4ad9e1339e7448a76698829def1f159c1b636c" + integrity sha512-ndoMMbGyuToTy4qB6Lex/inR98nPiNHacsgMPvy+zqMLgSxbt8VtWpDArpGp69h1fEDQHn1KB+9DWD++wgbwYA== dependencies: "@types/node" "*" "@types/ioredis@*": - version "4.0.10" - resolved "https://registry.yarnpkg.com/@types/ioredis/-/ioredis-4.0.10.tgz#ca8bd95ca7d5fee32cbc5a0bf92fc29264bee237" - integrity sha512-1ImAFcW5eg4f9UrftlVQPmGCkK1VyCqJ12c2wy3NlEQvzBC0GX64WlN01xj5iETqvsjczJHU+CRMPraBy9pX+g== + version "4.0.11" + resolved "https://registry.yarnpkg.com/@types/ioredis/-/ioredis-4.0.11.tgz#4acf9d7912944bcf1e96dd6adba2f16c07ed9ae7" + integrity sha512-spuEM4/UxlFH95NCrMojH9FTP3SLm8N2Itb9f0z0pca1IVdlPHobjps+KccEiUuYdfhzpdKOox8yGoqO9vahaw== dependencies: "@types/node" "*" @@ -177,9 +193,9 @@ "@types/node" "*" "@types/lodash@^4.14.64": - version "4.14.123" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.123.tgz#39be5d211478c8dd3bdae98ee75bb7efe4abfe4d" - integrity sha512-pQvPkc4Nltyx7G1Ww45OjVqUsJP4UsZm+GWJpigXgkikZqJgRm4c48g027o6tdgubWHwFRF15iFd+Y4Pmqv6+Q== + version "4.14.133" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.133.tgz#430721c96da22dd1694443e68e6cec7ba1c1003d" + integrity sha512-/3JqnvPnY58GLzG3Y7fpphOhATV1DDZ/Ak3DQufjlRK5E4u+s0CfClfNFtAGBabw+jDGtRFbOZe+Z02ZMWCBNQ== "@types/magnet-uri@*", "@types/magnet-uri@^5.1.1": version "5.1.2" @@ -213,9 +229,9 @@ "@types/node" "*" "@types/mocha@^5.0.0": - version "5.2.6" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.6.tgz#b8622d50557dd155e9f2f634b7d68fd38de5e94b" - integrity sha512-1axi39YdtBI7z957vdqXI4Ac25e7YihYQtJa+Clnxg1zTJEaIRbndt71O3sP4GAMgiAm0pY26/b9BrY4MR/PMw== + version "5.2.7" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea" + integrity sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ== "@types/morgan@^1.7.32": version "1.7.35" @@ -232,22 +248,27 @@ "@types/express" "*" "@types/node@*": - version "11.13.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-11.13.0.tgz#b0df8d6ef9b5001b2be3a94d909ce3c29a80f9e1" - integrity sha512-rx29MMkRdVmzunmiA4lzBYJNnXsW/PhG4kMBy2ATsYaDjGGR75dCFEVVROKpNwlVdcUX3xxlghKQOeDPBJobng== + version "12.0.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.5.tgz#ac14404c33d1a789973c45379a67f7f7e58a01b9" + integrity sha512-CFLSALoE+93+Hcb5pFjp0J1uMrrbLRe+L1+gFwerJ776R3TACSF0kTVRQ7AvRa7aFx70nqYHAc7wQPlt9kY2Mg== "@types/node@^10.0.8": - version "10.14.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.4.tgz#1c586b991457cbb58fef51bc4e0cfcfa347714b5" - integrity sha512-DT25xX/YgyPKiHFOpNuANIQIVvYEwCWXgK2jYYwqgaMrYE6+tq+DtmMwlD3drl6DJbUwtlIDnn0d7tIn/EbXBg== + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== -"@types/nodemailer@^4.3.1": - version "4.6.7" - resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-4.6.7.tgz#ac3c594fac6daf7f71eb4d31c9e4c88b62dbcb87" - integrity sha512-slR1wz8I8O20CHNBNhYhObRZ8zeG5FnfFUWLZKk1f0UDYaLZOsBjUfCC9VEFi7oSRCC886DfKmq1JncPDMOrng== +"@types/nodemailer@^6.2.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.2.0.tgz#369d3c6ff51201670f3e5469b00c1d204ec8f894" + integrity sha512-WGGEk/BGRLuYF3gyoTwbtKg5tCexZzb5lkTsis2k7GkAzlg4x2299/SC6Ssdj3X/5TzT1BHVc8zcFg/7KSzBLw== dependencies: "@types/node" "*" +"@types/normalize-package-data@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" + integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== + "@types/oauth2-server@^3.0.8": version "3.0.10" resolved "https://registry.yarnpkg.com/@types/oauth2-server/-/oauth2-server-3.0.10.tgz#ea671a6ad3d02062aac5f7c1ba1fb9c468314db0" @@ -284,9 +305,9 @@ integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== "@types/redis@*", "@types/redis@^2.8.5": - version "2.8.12" - resolved "https://registry.yarnpkg.com/@types/redis/-/redis-2.8.12.tgz#6405d7ece0d6cc037151b7141cef9ad3cd06f3ac" - integrity sha512-eT5cGYr08OnF6OlAHdc2hVOBAKBpfQQNQHsWEvUwRPFiXRd+vv+hOHSSIo4xB7M5vZOZdjMT2OUlXYqo3YlIGQ== + version "2.8.13" + resolved "https://registry.yarnpkg.com/@types/redis/-/redis-2.8.13.tgz#fdd76d9a8b7c36ff24d8506a94ef2a890c87f498" + integrity sha512-p86cm5P6DMotUqCS6odQRz0JJwc5QXZw9eyH0ALVIqmq12yqtex5ighWyGFHKxak9vaA/GF/Ilu0KZ0MuXXUbg== dependencies: "@types/node" "*" @@ -309,9 +330,9 @@ "@types/mime" "*" "@types/sharp@^0.22.1": - version "0.22.1" - resolved "https://registry.yarnpkg.com/@types/sharp/-/sharp-0.22.1.tgz#4fccaa9cc580d859916bee693aa7ae7447d0e0ba" - integrity sha512-Reri4hhX77JBx6HWt2a2CLO0xjHMFHDaeBYMslUsTSHGRFQnk+VpoH25g7/L6QzyyNNmxTekK+j+lFFjK3OkqA== + version "0.22.2" + resolved "https://registry.yarnpkg.com/@types/sharp/-/sharp-0.22.2.tgz#c9b613816ec000d94f153259b25ae41f874a75f5" + integrity sha512-oH49f42h3nf/qys0weYsaTGiMv67wPB769ynCoPfBAVwjjxFF3QtIPEe3MfhwyNjQAhQhTEfnmMKvVZfcFkhIw== dependencies: "@types/node" "*" @@ -377,15 +398,7 @@ "@types/events" "*" "@types/node" "*" -JSONStream@^1.3.4, JSONStream@^1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" - integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== - dependencies: - jsonparse "^1.2.0" - through ">=2.2.7 <3" - -abbrev@1, abbrev@~1.1.1: +abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== @@ -406,13 +419,13 @@ accepts@~1.2.12: mime-types "~2.1.6" negotiator "0.5.3" -accepts@~1.3.4, accepts@~1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" - integrity sha1-63d99gEXI6OxTopywIBcjoZ0a9I= +accepts@~1.3.4, accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== dependencies: - mime-types "~2.1.18" - negotiator "0.6.1" + mime-types "~2.1.24" + negotiator "0.6.2" acorn-jsx@^3.0.0: version "3.0.1" @@ -446,20 +459,6 @@ after@0.8.2: resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= -agent-base@4, agent-base@^4.1.0, agent-base@~4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" - integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== - dependencies: - es6-promisify "^5.0.0" - -agentkeepalive@^3.4.1: - version "3.5.2" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.5.2.tgz#a113924dd3fa24a0bc3b78108c450c2abee00f67" - integrity sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ== - dependencies: - humanize-ms "^1.2.1" - ajv-keywords@^1.0.0: version "1.5.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" @@ -515,28 +514,23 @@ ansi-regex@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= -ansi-styles@^3.2.1: +ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" -ansicolors@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" - integrity sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk= - -ansistyles@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/ansistyles/-/ansistyles-0.1.3.tgz#5de60415bda071bb37127854c864f41b23254539" - integrity sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk= - any-observable@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" @@ -565,34 +559,11 @@ append-field@^1.0.0: resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" integrity sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY= -application-config-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/application-config-path/-/application-config-path-0.1.0.tgz#193c5f0a86541a4c66fba1e2dc38583362ea5e8f" - integrity sha1-GTxfCoZUGkxm+6Hi3DhYM2LqXo8= - -application-config@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/application-config/-/application-config-1.0.1.tgz#5aa2e2a5ed6abd2e5d1d473d3596f574044fe9e7" - integrity sha1-WqLipe1qvS5dHUc9NZb1dARP6ec= - dependencies: - application-config-path "^0.1.0" - mkdirp "^0.5.1" - -aproba@^1.0.3, aproba@^1.1.1, aproba@^1.1.2: +aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== -"aproba@^1.1.2 || 2", aproba@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" - integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== - -archy@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" - integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= - are-we-there-yet@~1.1.2: version "1.1.5" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" @@ -665,11 +636,6 @@ arrify@^1.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= -asap@^2.0.0: - version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" - integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= - asn1@~0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" @@ -698,11 +664,11 @@ assign-symbols@^1.0.0: integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= async-each@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.2.tgz#8b8a7ca2a658f927e9f307d6d1a42f4199f0f735" - integrity sha512-6xrbvN0MOBKSJDdonmSSz2OwFSgxRaVtBDes26mj9KIGtDo+g9xosFRSC+i1gQh2oAN/tQ62AI/pGZGQjVOiRg== + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== -async-limiter@~1.0.0: +async-limiter@^1.0.0, async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg== @@ -719,7 +685,12 @@ async@1.5.1: resolved "https://registry.yarnpkg.com/async/-/async-1.5.1.tgz#b05714f4b11b357bf79adaffdd06da42d0766c10" integrity sha1-sFcU9LEbNXv3mtr/3QbaQtB2bBA= -async@>=0.2.9, async@^2.0.0, async@^2.6.1: +async@>=0.2.9, async@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/async/-/async-3.0.1.tgz#dfeb34657d1e63c94c0eee424297bf8a2c9a8182" + integrity sha512-ZswD8vwPtmBZzbn9xyi8XBQWXH3AvOQ43Za1KWYq7JeycrZuUYzx01KvHcVbXltjqH4y0MWrQ33008uLTqXuDw== + +async@^2.6.1: version "2.6.2" resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" integrity sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg== @@ -756,15 +727,6 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== -babel-code-frame@^6.22.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" - integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= - dependencies: - chalk "^1.1.3" - esutils "^2.0.2" - js-tokens "^3.0.2" - backo2@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" @@ -817,12 +779,12 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -bcrypt@3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-3.0.5.tgz#37a296c48ebf39fe6b28e4da3a221bf80da5aa26" - integrity sha512-m4o91nB+Ce8696Ao4R3B/WtVWTc1Lszgd098/OIjU9D/URmdYwT3ooBs9uv1b97J5YhZweTq9lldPefTYZ0TwA== +bcrypt@3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-3.0.6.tgz#f607846df62d27e60d5e795612c4f67d70206eb2" + integrity sha512-taA5bCTfXe7FUjKroKky9EXpdhkVvhE5owfxfLYodbrAR1Ul3juLmIQmIQBK4L9a5BuUcE6cqmwT+Da20lF9tg== dependencies: - nan "2.13.1" + nan "2.13.2" node-pre-gyp "0.12.0" bencode@^2.0.0: @@ -839,17 +801,6 @@ better-assert@~1.0.0: dependencies: callsite "1.0.0" -bin-links@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-1.1.2.tgz#fb74bd54bae6b7befc6c6221f25322ac830d9757" - integrity sha512-8eEHVgYP03nILphilltWjeIjMbKyJo3wvp9K816pHbhP301ismzw15mxAAEVQ/USUwcP++1uNrbERbp8lOA6Fg== - dependencies: - bluebird "^3.5.0" - cmd-shim "^2.0.2" - gentle-fs "^2.0.0" - graceful-fs "^4.1.11" - write-file-atomic "^2.3.0" - binary-extensions@^1.0.0: version "1.13.1" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" @@ -860,18 +811,13 @@ binary-search@^1.3.4: resolved "https://registry.yarnpkg.com/binary-search/-/binary-search-1.3.5.tgz#479ad009589e0273cf54e5d74ab1546c489078ce" integrity sha512-RHFP0AdU6KAB0CCZsRMU2CJTk2EpL8GLURT+4gilpjr1f/7M91FgUMnXuQLmf3OKLet34gjuNFwO7e4agdX5pw== -bindings@^1.3.0, bindings@^1.5.0: +bindings@^1.3.0: version "1.5.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== dependencies: file-uri-to-path "1.0.0" -bindings@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11" - integrity sha1-FK1hE4EtLTfXLme0ystLtyZQXxE= - bindings@~1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.1.tgz#21fc7c6d67c18516ec5aaa2815b145ff77b26ea5" @@ -938,16 +884,15 @@ bittorrent-protocol@^3.0.0: xtend "^4.0.0" bittorrent-tracker@^9.0.0: - version "9.10.1" - resolved "https://registry.yarnpkg.com/bittorrent-tracker/-/bittorrent-tracker-9.10.1.tgz#5de14aac012a287af394d3cc9eda1ec6cc956f11" - integrity sha512-n5zTL/g6Wt0rb2EnkiyiaGYhth7I/N0/xMqGUpvGX/7g1scDGBVPhJnXR8lfp3/OMj681fv40o4q/otECMtZSA== + version "9.11.0" + resolved "https://registry.yarnpkg.com/bittorrent-tracker/-/bittorrent-tracker-9.11.0.tgz#9911f9c14e5a29f84990a0c31b3d83dd16eb2876" + integrity sha512-T1zvW/kSeEnWT4I3JE+6c7aZbO5jtleZyQe911SyzIxFF9DvtUNWXud3p5ZUkXaoI2xXwfpvlks5VFj5SKEB+A== dependencies: bencode "^2.0.0" bittorrent-peerid "^1.0.2" bn.js "^4.4.0" compact2string "^1.2.0" - debug "^3.1.0" - inherits "^2.0.1" + debug "^4.0.1" ip "^1.0.1" lru "^3.0.0" minimist "^1.1.1" @@ -964,7 +909,6 @@ bittorrent-tracker@^9.0.0: uniq "^1.0.1" unordered-array-remove "^1.0.2" ws "^6.0.0" - xtend "^4.0.0" optionalDependencies: bufferutil "^4.0.0" utf-8-validate "^5.0.1" @@ -1001,13 +945,6 @@ block-stream2@^1.0.0: inherits "^2.0.1" readable-stream "^2.0.4" -block-stream@*: - version "0.0.9" - resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" - integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo= - dependencies: - inherits "~2.0.0" - bluebird@3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" @@ -1018,10 +955,10 @@ bluebird@^2.10.0: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" integrity sha1-U0uQM8AiyVecVro7Plpcqvu2UOE= -bluebird@^3.0.5, bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7" - integrity sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw== +bluebird@^3.0.5, bluebird@^3.5.0, bluebird@^3.5.3: + version "3.5.5" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" + integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== bn.js@=2.0.4: version "2.0.4" @@ -1038,21 +975,21 @@ bn.js@^4.4.0: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== -body-parser@1.18.3, body-parser@^1.12.4: - version "1.18.3" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4" - integrity sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ= +body-parser@1.19.0, body-parser@^1.12.4: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== dependencies: - bytes "3.0.0" + bytes "3.1.0" content-type "~1.0.4" debug "2.6.9" depd "~1.1.2" - http-errors "~1.6.3" - iconv-lite "0.4.23" + http-errors "1.7.2" + iconv-lite "0.4.24" on-finished "~2.3.0" - qs "6.5.2" - raw-body "2.3.3" - type-is "~1.6.16" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" boxen@^1.2.1: version "1.3.0" @@ -1166,24 +1103,19 @@ builtin-modules@^1.1.1: resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= -builtins@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" - integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= - bull@^3.4.2: - version "3.7.0" - resolved "https://registry.yarnpkg.com/bull/-/bull-3.7.0.tgz#ec9a8721a2cfb0421c501d28553ac1f9f025414d" - integrity sha512-DHCALp+OOahK+q2hB3sZQew0CJn4W3zYIQsdMlnBCy7JYCnJ/bdj0MFHjo5k0ZhNZxzwhLErXt1yd3llV494UQ== + version "3.10.0" + resolved "https://registry.yarnpkg.com/bull/-/bull-3.10.0.tgz#5879b1201d0ed0c987fa1f42114d3dc5abf3da68" + integrity sha512-LbQsc7c+eYd7IaJD7tS373yKLYttjTfoPZ+9xYYlPM5+gutAjofSTsESOGGyaxyX2lE1dkg+eWhUK5kAPl5Zow== dependencies: cron-parser "^2.7.3" debuglog "^1.0.0" - get-port latest + get-port "^5.0.0" ioredis "^4.5.1" lodash "^4.17.11" - p-timeout "^2.0.1" + p-timeout "^3.1.0" promise.prototype.finally "^3.1.0" - semver "^5.6.0" + semver "^6.1.1" util.promisify "^1.0.0" uuid "^3.2.1" @@ -1195,46 +1127,11 @@ busboy@^0.2.11: dicer "0.2.5" readable-stream "1.1.x" -byline@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" - integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE= - -byte-size@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-5.0.1.tgz#4b651039a5ecd96767e71a3d7ed380e48bed4191" - integrity sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw== - -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= - -bytes@^3.0.0: +bytes@3.1.0, bytes@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== -cacache@^11.0.1, cacache@^11.3.2: - version "11.3.2" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.2.tgz#2d81e308e3d258ca38125b676b98b2ac9ce69bfa" - integrity sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg== - dependencies: - bluebird "^3.5.3" - chownr "^1.1.1" - figgy-pudding "^3.5.1" - glob "^7.1.3" - graceful-fs "^4.1.15" - lru-cache "^5.1.1" - mississippi "^3.0.0" - mkdirp "^0.5.1" - move-concurrently "^1.0.1" - promise-inflight "^1.0.1" - rimraf "^2.6.2" - ssri "^6.0.1" - unique-filename "^1.1.1" - y18n "^4.0.0" - cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -1250,11 +1147,6 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" -call-limit@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/call-limit/-/call-limit-1.1.0.tgz#6fd61b03f3da42a2cd0ec2b60f02bd0e71991fea" - integrity sha1-b9YbA/PaQqLNDsK2DwK9DnGZH+o= - call-me-maybe@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" @@ -1302,9 +1194,9 @@ camelcase@^4.0.0, camelcase@^4.1.0: integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= camelcase@^5.0.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.0.tgz#0a110882cbeba41f72f99fcf918f4a0a92a13ebf" - integrity sha512-Y05ICatFYPAfykDIB7VdwSJ0LUl1yq/BwO2OpyGGLjiRe1fgzTwVypPiWnzkGFOVFHXrCXUNBl86bpjBhZWSJg== + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelize@1.0.0: version "1.0.0" @@ -1322,12 +1214,12 @@ caseless@~0.12.0: integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= chai-json-schema@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/chai-json-schema/-/chai-json-schema-1.5.0.tgz#6960719e40f71fd5b377c9282e5c9a46799474f6" - integrity sha1-aWBxnkD3H9Wzd8koLlyaRnmUdPY= + version "1.5.1" + resolved "https://registry.yarnpkg.com/chai-json-schema/-/chai-json-schema-1.5.1.tgz#d9ae4c8f8c6e24ff4d402ceddfaa865d1ca107f4" + integrity sha512-TR/xPDxRhqwFFCWg1HgL8nNWbpNfUwaib6pBN++QKpnd0t+o3+MBvAn5CM1mpdUMaM76oJAtUjGKdjGad01lIA== dependencies: jsonpointer.js "0.4.0" - tv4 "~1.2.7" + tv4 "^1.3.0" chai-xml@^0.3.2: version "0.3.2" @@ -1368,7 +1260,7 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.1, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1392,10 +1284,10 @@ check-error@^1.0.2: resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= -chokidar@^2.1.0: - version "2.1.5" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.5.tgz#0ae8434d962281a5f56c72869e79cb6d9d86ad4d" - integrity sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A== +chokidar@^2.1.5: + version "2.1.6" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.6.tgz#b6cad653a929e244ce8a834244164d241fa954c5" + integrity sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g== dependencies: anymatch "^2.0.0" async-each "^1.0.1" @@ -1446,6 +1338,11 @@ circular-json@^0.3.1: resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A== +circular-json@^0.5.9: + version "0.5.9" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.9.tgz#932763ae88f4f7dead7a0d09c8a51a4743a53b1d" + integrity sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ== + class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -1461,14 +1358,6 @@ cli-boxes@^1.0.0: resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM= -cli-columns@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/cli-columns/-/cli-columns-3.1.2.tgz#6732d972979efc2ae444a1f08e08fa139c96a18e" - integrity sha1-ZzLZcpee/CrkRKHwjgj6E5yWoY4= - dependencies: - string-width "^2.0.0" - strip-ansi "^3.0.1" - cli-cursor@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" @@ -1483,16 +1372,6 @@ cli-cursor@^2.0.0, cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" -cli-table3@^0.5.0, cli-table3@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" - integrity sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw== - dependencies: - object-assign "^4.1.0" - string-width "^2.1.1" - optionalDependencies: - colors "^1.1.2" - cli-table@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23" @@ -1522,10 +1401,14 @@ cliui@^4.0.0: strip-ansi "^4.0.0" wrap-ansi "^2.0.0" -clone@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" closest-to@~2.0.0: version "2.0.0" @@ -1545,14 +1428,6 @@ cluster-key-slot@^1.0.6: resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.0.12.tgz#d5deff2a520717bc98313979b687309b2d368e29" integrity sha512-21O0kGmvED5OJ7ZTdqQ5lQQ+sjuez33R+d35jZKLwqUb5mqcPHUsxOSzj61+LHVtxGZd1kShbQM3MjB/gBJkVg== -cmd-shim@^2.0.2, cmd-shim@~2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-2.0.2.tgz#6fcbda99483a8fd15d7d30a196ca69d688a2efdb" - integrity sha1-b8vamUg6j9FdfTChlspp1oii79s= - dependencies: - graceful-fs "^4.1.2" - mkdirp "~0.5.0" - co-bluebird@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/co-bluebird/-/co-bluebird-1.1.0.tgz#c8b9f3a9320a7ed30987dcca1a5c3cff59655c7c" @@ -1617,10 +1492,10 @@ color@3.0.x: color-convert "^1.9.1" color-string "^1.5.2" -color@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/color/-/color-3.1.0.tgz#d8e9fb096732875774c84bf922815df0308d0ffc" - integrity sha512-CwyopLkuRYO5ei2EpzpIh6LqJMt6Mt+jZhO5VI5f/wJLZriXQE32/SSqzmrh+QB+AZT81Cj8yv+7zwToW8ahZg== +color@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10" + integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg== dependencies: color-convert "^1.9.1" color-string "^1.5.2" @@ -1641,25 +1516,17 @@ colors@^1.1.2, colors@^1.2.1: integrity sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg== colorspace@1.1.x: - version "1.1.1" - resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.1.tgz#9ac2491e1bc6f8fb690e2176814f8d091636d972" - integrity sha512-pI3btWyiuz7Ken0BWh9Elzsmv2bM9AhA7psXib4anUXy/orfZ/E0MbQwhSOG/9L8hLlalqrU0UhOuqxW1YjmVw== + version "1.1.2" + resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.2.tgz#e0128950d082b86a2168580796a0aa5d6c68d8c5" + integrity sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ== dependencies: color "3.0.x" text-hex "1.0.x" -columnify@~1.5.4: - version "1.5.4" - resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" - integrity sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs= - dependencies: - strip-ansi "^3.0.0" - wcwidth "^1.0.0" - combined-stream@^1.0.6, combined-stream@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" - integrity sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w== + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" @@ -1671,9 +1538,9 @@ commander@2.9.0: graceful-readlink ">= 1.0.0" commander@^2.12.1, commander@^2.13.0, commander@^2.14.1, commander@^2.7.1, commander@^2.8.1, commander@^2.9.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" - integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== + version "2.20.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" + integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== compact2string@^1.2.0: version "1.4.1" @@ -1692,11 +1559,16 @@ component-emitter@1.1.2: resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.1.2.tgz#296594f2753daa63996d2af08d15a95116c9aec3" integrity sha1-KWWU8nU9qmOZbSrwjRWpURbJrsM= -component-emitter@1.2.1, component-emitter@^1.2.0, component-emitter@^1.2.1: +component-emitter@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= +component-emitter@^1.2.0, component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + component-inherit@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" @@ -1707,7 +1579,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.4.6, concat-stream@^1.5.0, concat-stream@^1.5.2: +concat-stream@^1.4.6, concat-stream@^1.5.2: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -1717,7 +1589,7 @@ concat-stream@^1.4.6, concat-stream@^1.5.0, concat-stream@^1.5.2: readable-stream "^2.2.2" typedarray "^0.0.6" -concurrently@^4.0.1: +concurrently@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-4.1.0.tgz#17fdf067da71210685d9ea554423ef239da30d33" integrity sha512-pwzXCE7qtOB346LyO9eFWpkFJVO3JQZ/qU/feGeaAHiX1M3Rw3zgXKc5cZ8vSH5DGygkjzLFDzA/pwoQDkRNGg== @@ -1732,18 +1604,10 @@ concurrently@^4.0.1: tree-kill "^1.1.0" yargs "^12.0.1" -config-chain@^1.1.12: - version "1.1.12" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" - integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== - dependencies: - ini "^1.3.4" - proto-list "~1.2.1" - config@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/config/-/config-3.0.1.tgz#c3118e2eb45fdfd135277339f87e2492559cb147" - integrity sha512-TBNrrk2b6AybUohqXw2AydglFBL9b/+1GG93Di6Fm6x1SyVJ5PYgo+mqY2X0KpU9m0PJDSbFaC5H95utSphtLw== + version "3.1.0" + resolved "https://registry.yarnpkg.com/config/-/config-3.1.0.tgz#c7d49c4171e8f6cb61c1d69e22647ffd60492548" + integrity sha512-t6oDeNQbsIWa+D/KF4959TANzjSHLv1BA/hvL8tHEA3OUSWgBXELKaONSI6nr9oanbKs0DXonjOWLcrtZ3yTAA== dependencies: json5 "^1.0.1" @@ -1759,7 +1623,7 @@ configstore@^3.0.0: write-file-atomic "^2.0.0" xdg-basedir "^3.0.0" -console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control-strings@~1.1.0: +console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= @@ -1769,10 +1633,12 @@ content-disposition@0.5.1: resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.1.tgz#87476c6a67c8daa87e32e87616df883ba7fb071b" integrity sha1-h0dsamfI2qh+Muh2Ft+IO6f7Bxs= -content-disposition@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" - integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" content-security-policy-builder@2.0.0: version "2.0.0" @@ -1807,32 +1673,25 @@ cookie@0.3.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + cookiejar@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA== -copy-concurrently@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" - integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== - dependencies: - aproba "^1.1.1" - fs-write-stream-atomic "^1.0.8" - iferr "^0.1.5" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.0" - copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= core-js@^2.5.7: - version "2.6.5" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.5.tgz#44bc8d249e7fb2ff5d00e0341a7ffb94fbf67895" - integrity sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A== + version "2.6.9" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2" + integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -1847,14 +1706,14 @@ cors@^2.8.1: object-assign "^4" vary "^1" -cosmiconfig@^5.0.2, cosmiconfig@^5.0.7: - version "5.2.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.0.tgz#45038e4d28a7fe787203aede9c25bca4a08b12c8" - integrity sha512-nxt+Nfc3JAqf4WIWd0jXLjTJZmsPLrA9DDc4nRw2KFJQJK7DNooqSXrNI7tzLG50CF8axczly5UV929tBmh/7g== +cosmiconfig@^5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== dependencies: import-fresh "^2.0.0" is-directory "^0.3.1" - js-yaml "^3.13.0" + js-yaml "^3.13.1" parse-json "^4.0.0" create-error-class@^3.0.0: @@ -1884,9 +1743,9 @@ create-torrent@^3.24.5, create-torrent@^3.33.0: simple-sha1 "^2.0.0" cron-parser@^2.7.3: - version "2.10.0" - resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-2.10.0.tgz#d6bed39cfa6163930f89b1ade82df7b46f52b82b" - integrity sha512-E181Gbg+wYT0hSikwBOokL7VHgJDYUlFsRFHIlnTP8GGefhcIyf8PSc2IXztmghj5mhAZupU0n3jKfEpZVEmVg== + version "2.11.0" + resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-2.11.0.tgz#c3bf477e01de6a56938d6625b92efd6cec30a8a5" + integrity sha512-L5LAGlvq2xmCLErhjQRX8IL5v72y8jhGOaxrarYOhse0kJjJGb/vY/0sV/c7F/SylJGkUIY2iZPPJXZD3glZqA== dependencies: is-nan "^1.2.1" moment-timezone "^0.5.23" @@ -1926,11 +1785,6 @@ cycle@1.0.x: resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2" integrity sha1-IegLK+hYD5i0aPN5QwZisEbDStI= -cyclist@~0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" - integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA= - d@1: version "1.0.0" resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" @@ -1955,14 +1809,6 @@ date-fns@^1.23.0, date-fns@^1.27.2: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== -deasync@^0.1.4: - version "0.1.14" - resolved "https://registry.yarnpkg.com/deasync/-/deasync-0.1.14.tgz#232ea2252b443948cad033d792eb3b24b0a3d828" - integrity sha512-wN8sIuEqIwyQh72AG7oY6YQODCxIp1eXzEZlZznBuwDF8Q03Tdy9QNp1BNZXeadXoklNrw+Ip1fch+KXo/+ASw== - dependencies: - bindings "~1.2.1" - node-addon-api "^1.6.0" - debug@2.2.0, debug@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" @@ -1977,21 +1823,14 @@ debug@2.3.3: dependencies: ms "0.7.2" -debug@2.6.9, debug@^2.1.1, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3: +debug@2.6.9, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@3.1.0, debug@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - -debug@3.2.6, debug@^3.1.0: +debug@3.2.6, debug@^3.1.0, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -2005,12 +1844,19 @@ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@~4.1.0: dependencies: ms "^2.1.1" -debuglog@^1.0.0, debuglog@^1.0.1: +debug@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debuglog@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= -decamelize@^1.1.1, decamelize@^1.2.0: +decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -2066,13 +1912,6 @@ deep-object-diff@^1.1.0: resolved "https://registry.yarnpkg.com/deep-object-diff/-/deep-object-diff-1.1.0.tgz#d6fabf476c2ed1751fc94d5ca693d2ed8c18bc5a" integrity sha512-b+QLs5vHgS+IoSNcUE4n9HP2NwcHj7aqnJWsjPtuG75Rh5TOaGt0OjAYInh77d5T16V5cRDC+Pw/6ZZZiETBGw== -defaults@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" - integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= - dependencies: - clone "^1.0.2" - define-properties@^1.1.1, define-properties@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -2130,9 +1969,9 @@ delegates@^1.0.0: integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= denque@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.0.tgz#79e2f0490195502107f24d9553f374837dabc916" - integrity sha512-gh513ac7aiKrAgjiIBWZG0EASyDF9p4JMWwKA8YU5s9figrL5SRNEMT6FDynsegakuhWd1wVqTvqvqAoDxw7wQ== + version "1.4.1" + resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf" + integrity sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ== depd@2.0.0: version "2.0.0" @@ -2144,46 +1983,16 @@ depd@~1.1.0, depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= -descrevit@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/descrevit/-/descrevit-0.1.1.tgz#c0f5840de0a0f7b1b8b4078569b173327947d5da" - integrity sha1-wPWEDeCg97G4tAeFabFzMnlH1do= - dependencies: - deasync "^0.1.4" - destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= -detect-file@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" - integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= - -detect-indent@~5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" - integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= - detect-libc@^1.0.2, detect-libc@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= -detect-newline@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" - integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= - -dezalgo@^1.0.0, dezalgo@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" - integrity sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY= - dependencies: - asap "^2.0.0" - wrappy "1" - diagnostics@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/diagnostics/-/diagnostics-1.1.1.tgz#cab6ac33df70c9d9a727490ae43ac995a769b22a" @@ -2201,21 +2010,21 @@ dicer@0.2.5: readable-stream "1.1.x" streamsearch "0.1.2" -diff@3.5.0, diff@^3.1.0, diff@^3.2.0: +diff@3.5.0, diff@^3.2.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== +diff@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.1.tgz#0c667cb467ebbb5cea7f14f135cc2dba7780a8ff" + integrity sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q== + dns-prefetch-control@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/dns-prefetch-control/-/dns-prefetch-control-0.1.0.tgz#60ddb457774e178f1f9415f0cabb0e85b0b300b2" integrity sha1-YN20V3dOF48flBXwyrsOhbCzALI= -docopt@~0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/docopt/-/docopt-0.6.2.tgz#b28e9e2220da5ec49f7ea5bb24a47787405eeb11" - integrity sha1-so6eIiDaXsSffqW7JKR3h0Be6xE= - doctrine@0.7.2: version "0.7.2" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-0.7.2.tgz#7cb860359ba3be90e040b26b729ce4bfa654c523" @@ -2237,14 +2046,6 @@ dont-sniff-mimetype@1.0.0: resolved "https://registry.yarnpkg.com/dont-sniff-mimetype/-/dont-sniff-mimetype-1.0.0.tgz#5932890dc9f4e2f19e5eb02a20026e5e5efc8f58" integrity sha1-WTKJDcn04vGeXrAqIAJuXl78j1g= -dot-json@^1.0.3: - version "1.1.0" - resolved "https://registry.yarnpkg.com/dot-json/-/dot-json-1.1.0.tgz#36dc7d40c00afaef2823b715b94325ba39f1bd89" - integrity sha512-PiQZW9/C8xILPYK2bOye/cbPZrakNEkt28jFb8RlPCwsoMAHYYw9T8JoACxgttHL9Y2AmdqVvibbZJHtLgeqTQ== - dependencies: - docopt "~0.6.2" - underscore-keypath "~0.0.22" - dot-prop@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" @@ -2252,11 +2053,6 @@ dot-prop@^4.1.0: dependencies: is-obj "^1.0.0" -dotenv@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-5.0.1.tgz#a5317459bd3d79ab88cff6e44057a6a3fbb1fcef" - integrity sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow== - dottie@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/dottie/-/dottie-2.0.1.tgz#697ad9d72004db7574d21f892466a3c285893659" @@ -2272,7 +2068,7 @@ duplexer3@^0.1.4: resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= -duplexify@^3.2.0, duplexify@^3.4.2, duplexify@^3.5.0, duplexify@^3.6.0: +duplexify@^3.2.0, duplexify@^3.5.0, duplexify@^3.6.0: version "3.7.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== @@ -2297,11 +2093,6 @@ ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer "^5.0.1" -editor@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/editor/-/editor-1.0.0.tgz#60c7f87bd62bcc6a894fa8ccd6afb7823a24f742" - integrity sha1-YMf4e9YrzGqJT6jM1q+3gjok90I= - ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -2322,6 +2113,11 @@ elliptic@=3.0.3: hash.js "^1.0.0" inherits "^2.0.1" +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + enabled@1.0.x: version "1.0.2" resolved "https://registry.yarnpkg.com/enabled/-/enabled-1.0.2.tgz#965f6513d2c2d1c5f4652b64a2e3396467fc2f93" @@ -2334,7 +2130,7 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= -encoding@^0.1.11, encoding@^0.1.12, encoding@~0.1.12: +encoding@^0.1.12, encoding@~0.1.12: version "0.1.12" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" integrity sha1-U4tm8+5izRq1HsMjgp0flIDHS+s= @@ -2435,18 +2231,6 @@ env-variable@0.0.x: resolved "https://registry.yarnpkg.com/env-variable/-/env-variable-0.0.5.tgz#913dd830bef11e96a039c038d4130604eba37f88" integrity sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA== -err-code@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960" - integrity sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA= - -errno@~0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" - integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg== - dependencies: - prr "~1.0.1" - error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -2476,9 +2260,9 @@ es-to-primitive@^1.2.0: is-symbol "^1.0.2" es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.45, es5-ext@^0.10.9, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: - version "0.10.49" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.49.tgz#059a239de862c94494fec28f8150c977028c6c5e" - integrity sha512-3NMEhi57E31qdzmYp2jwRArIUsj1HI/RxbQ4bgnSB+AIKIxsAmTiK83bYMifIcpWvEc3P1X30DhUKOqEtF/kvg== + version "0.10.50" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.50.tgz#6d0e23a0abdb27018e5ac4fd09b412bc5517a778" + integrity sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw== dependencies: es6-iterator "~2.0.3" es6-symbol "~3.1.1" @@ -2505,18 +2289,6 @@ es6-map@^0.1.3: es6-symbol "~3.1.1" event-emitter "~0.3.5" -es6-promise@^4.0.3: - version "4.2.6" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.6.tgz#b685edd8258886365ea62b57d30de28fadcd974f" - integrity sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q== - -es6-promisify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" - integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= - dependencies: - es6-promise "^4.0.3" - es6-promisify@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-6.0.1.tgz#6edaa45f3bd570ffe08febce66f7116be4b1cdb6" @@ -2725,17 +2497,10 @@ expand-template@^2.0.3: resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== -expand-tilde@^2.0.0, expand-tilde@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" - integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI= - dependencies: - homedir-polyfill "^1.0.1" - -expect-ct@0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/expect-ct/-/expect-ct-0.1.1.tgz#de84476a2dbcb85000d5903737e9bc8a5ba7b897" - integrity sha512-ngXzTfoRGG7fYens3/RMb6yYoVLvLMfmsSllP/mZPxNHgFq41TmPSLF/nLY7fwoclI2vElvAmILFWGUYqdjfCg== +expect-ct@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/expect-ct/-/expect-ct-0.2.0.tgz#3a54741b6ed34cc7a93305c605f63cd268a54a62" + integrity sha512-6SK3MG/Bbhm8MsgyJAylg+ucIOU71/FzyFalcfu5nY19dH8y/z0tBJU0wrNBXD4B27EoQtqPF/9wqH0iYAd04g== express-oauth-server@^2.0.0: version "2.0.0" @@ -2746,12 +2511,10 @@ express-oauth-server@^2.0.0: express "^4.13.3" oauth2-server "3.0.0" -express-rate-limit@^3.1.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-3.4.0.tgz#c053ee294feac6d3529863de549438749fd83ef1" - integrity sha512-SktWQGHhTQfIOZykiVIaoqmHCptqq177fEbumVytWsMpEqe+g78IFrfzivJTimoCdMZ5+vYJ5/a/w1darXMv+A== - dependencies: - defaults "^1.0.3" +express-rate-limit@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-4.0.4.tgz#a495338ae9e58c856b66d1346ec0d86f43ba2e43" + integrity sha512-DLRj2vMO7Xgai8qWKU9O6ZztF2bdDmfFNFi9k3G9BPzJ+7MG7eWaaBikbe0eBpNGSxU8JziwW0PQKG78aNWa6g== express-validator@^5.0.0: version "5.3.1" @@ -2793,38 +2556,38 @@ express@4.13.4: vary "~1.0.1" express@^4.12.4, express@^4.13.3: - version "4.16.4" - resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e" - integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg== + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== dependencies: - accepts "~1.3.5" + accepts "~1.3.7" array-flatten "1.1.1" - body-parser "1.18.3" - content-disposition "0.5.2" + body-parser "1.19.0" + content-disposition "0.5.3" content-type "~1.0.4" - cookie "0.3.1" + cookie "0.4.0" cookie-signature "1.0.6" debug "2.6.9" depd "~1.1.2" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "1.1.1" + finalhandler "~1.1.2" fresh "0.5.2" merge-descriptors "1.0.1" methods "~1.1.2" on-finished "~2.3.0" - parseurl "~1.3.2" + parseurl "~1.3.3" path-to-regexp "0.1.7" - proxy-addr "~2.0.4" - qs "6.5.2" - range-parser "~1.2.0" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" safe-buffer "5.1.2" - send "0.16.2" - serve-static "1.13.2" - setprototypeof "1.1.0" - statuses "~1.4.0" - type-is "~1.6.16" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" utils-merge "1.0.1" vary "~1.1.2" @@ -2897,21 +2660,16 @@ fast-safe-stringify@^2.0.4: resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz#04b26106cc56681f51a044cfc0d76cf0008ac2c2" integrity sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg== -feature-policy@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/feature-policy/-/feature-policy-0.2.0.tgz#22096de49ab240176878ffe2bde2f6ff04d48c43" - integrity sha512-2hGrlv6efG4hscYVZeaYjpzpT6I2OZgYqE2yDUzeAcKj2D1SH0AsEzqJNXzdoglEddcIXQQYop3lD97XpG75Jw== +feature-policy@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/feature-policy/-/feature-policy-0.3.0.tgz#7430e8e54a40da01156ca30aaec1a381ce536069" + integrity sha512-ZtijOTFN7TzCujt1fnNhfWPFPSHeZkesff9AXZj+UEjYBynWNUIYpC87Ve4wHzyexQsImicLu7WsC2LHq7/xrQ== fecha@^2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fecha/-/fecha-2.3.3.tgz#948e74157df1a32fd1b12c3a3c3cdcb6ec9d96cd" integrity sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg== -figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" - integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== - figures@^1.3.5, figures@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" @@ -2970,52 +2728,32 @@ finalhandler@0.4.1: on-finished "~2.3.0" unpipe "~1.0.0" -finalhandler@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" - integrity sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg== +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== dependencies: debug "2.6.9" encodeurl "~1.0.2" escape-html "~1.0.3" on-finished "~2.3.0" - parseurl "~1.3.2" - statuses "~1.4.0" + parseurl "~1.3.3" + statuses "~1.5.0" unpipe "~1.0.0" -find-npm-prefix@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/find-npm-prefix/-/find-npm-prefix-1.0.2.tgz#8d8ce2c78b3b4b9e66c8acc6a37c231eb841cfdf" - integrity sha512-KEftzJ+H90x6pcKtdXZEPsQse8/y/UnvzRKrOSQFprnrGaFuJ62fVkP34Iu2IYuMvyauCyoLTNkJZgrrGA2wkA== - -find-parent-dir@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/find-parent-dir/-/find-parent-dir-0.3.0.tgz#33c44b429ab2b2f0646299c5f9f718f376ff8d54" - integrity sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ= - -find-up@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - -find-up@^3.0.0: +find-up@3.0.0, find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== dependencies: locate-path "^3.0.0" -findup-sync@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" - integrity sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw= +find-up@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.0.0.tgz#c367f8024de92efb75f2d4906536d24682065c3a" + integrity sha512-zoH7ZWPkRdgwYCDVoQTzqjG8JSPANhtvLhh4KVUHyKnaUJJrNeFmWIkTcNuJmR3GLMEmGYEf2S2bjgx26JTF+Q== dependencies: - detect-file "^1.0.0" - is-glob "^3.1.0" - micromatch "^3.0.4" - resolve-dir "^1.0.1" + locate-path "^5.0.0" flat-cache@^1.2.1: version "1.3.4" @@ -3039,11 +2777,6 @@ flatten@^1.0.2: resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" integrity sha1-2uRqnXj74lKSJYzB54CkHZXAN4I= -flexbuffer@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/flexbuffer/-/flexbuffer-0.0.6.tgz#039fdf23f8823e440c38f3277e6fef1174215b30" - integrity sha1-A5/fI/iCPkQMOPMnfm/vEXQhWzA= - fluent-ffmpeg@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/fluent-ffmpeg/-/fluent-ffmpeg-2.1.2.tgz#c952de2240f812ebda0aa8006d7776ee2acf7d74" @@ -3052,14 +2785,6 @@ fluent-ffmpeg@^2.1.0: async ">=0.2.9" which "^1.1.1" -flush-write-stream@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" - integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== - dependencies: - inherits "^2.0.3" - readable-stream "^2.3.6" - fn-name@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/fn-name/-/fn-name-2.0.1.tgz#5214d7537a4d06a4a301c0cc262feb84188002e7" @@ -3106,10 +2831,10 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" -frameguard@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/frameguard/-/frameguard-3.0.0.tgz#7bcad469ee7b96e91d12ceb3959c78235a9272e9" - integrity sha1-e8rUae57lukdEs6zlZx4I1qScuk= +frameguard@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/frameguard/-/frameguard-3.1.0.tgz#bd1442cca1d67dc346a6751559b6d04502103a22" + integrity sha512-TxgSKM+7LTA6sidjOiSZK9wxY0ffMPY3Wta//MqwmX0nZuEHc8QrkV8Fh3ZhMJeiH+Uyh/tcaarImRy8u77O7g== fresh@0.3.0: version "0.3.0" @@ -3121,22 +2846,6 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= -from2@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/from2/-/from2-1.3.0.tgz#88413baaa5f9a597cfde9221d86986cd3c061dfd" - integrity sha1-iEE7qqX5pZfP3pIh2GmGzTwGHf0= - dependencies: - inherits "~2.0.1" - readable-stream "~1.1.10" - -from2@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" - integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= - dependencies: - inherits "^2.0.1" - readable-stream "^2.0.0" - front-matter@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/front-matter/-/front-matter-2.1.2.tgz#f75983b9f2f413be658c93dfd7bd8ce4078f5cdb" @@ -3175,63 +2884,34 @@ fs-extra@^3.0.1: jsonfile "^3.0.0" universalify "^0.1.0" -fs-extra@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" - integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== +fs-extra@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.0.1.tgz#90294081f978b1f182f347a440a209154344285b" + integrity sha512-W+XLrggcDzlle47X/XnS7FXrXu9sDo+Ze9zpndeBxdgv88FHLm1HtmkhEwavruS6koanBjp098rUpHs65EmG7A== dependencies: graceful-fs "^4.1.2" jsonfile "^4.0.0" universalify "^0.1.0" fs-minipass@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" - integrity sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ== + version "1.2.6" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07" + integrity sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ== dependencies: minipass "^2.2.1" -fs-vacuum@^1.2.10, fs-vacuum@~1.2.10: - version "1.2.10" - resolved "https://registry.yarnpkg.com/fs-vacuum/-/fs-vacuum-1.2.10.tgz#b7629bec07a4031a2548fdf99f5ecf1cc8b31e36" - integrity sha1-t2Kb7AekAxolSP35n17PHMizHjY= - dependencies: - graceful-fs "^4.1.2" - path-is-inside "^1.0.1" - rimraf "^2.5.2" - -fs-write-stream-atomic@^1.0.8, fs-write-stream-atomic@~1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" - integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= - dependencies: - graceful-fs "^4.1.2" - iferr "^0.1.5" - imurmurhash "^0.1.4" - readable-stream "1 || 2" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fsevents@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.7.tgz#4851b664a3783e52003b3c66eb0eee1074933aa4" - integrity sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw== + version "1.2.9" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f" + integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw== dependencies: - nan "^2.9.2" - node-pre-gyp "^0.10.0" - -fstream@^1.0.0, fstream@^1.0.2: - version "1.0.11" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" - integrity sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE= - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" + nan "^2.12.1" + node-pre-gyp "^0.12.0" function-bind@^1.1.1: version "1.1.1" @@ -3275,25 +2955,6 @@ generate-object-property@^1.1.0: dependencies: is-property "^1.0.0" -genfun@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537" - integrity sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA== - -gentle-fs@^2.0.0, gentle-fs@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/gentle-fs/-/gentle-fs-2.0.1.tgz#585cfd612bfc5cd52471fdb42537f016a5ce3687" - integrity sha512-cEng5+3fuARewXktTEGbwsktcldA+YsnUEaXZwcK/3pjSE1X9ObnTs+/8rYf8s+RnIcQm2D5x3rwpN7Zom8Bew== - dependencies: - aproba "^1.1.2" - fs-vacuum "^1.2.10" - graceful-fs "^4.1.11" - iferr "^0.1.5" - mkdirp "^0.5.1" - path-is-inside "^1.0.2" - read-cmd-shim "^1.0.1" - slide "^1.1.6" - get-browser-rtc@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/get-browser-rtc/-/get-browser-rtc-1.0.2.tgz#bbcd40c8451a7ed4ef5c373b8169a409dd1d11d9" @@ -3304,6 +2965,11 @@ get-caller-file@^1.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + get-func-name@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" @@ -3314,22 +2980,29 @@ get-own-enumerable-property-symbols@^3.0.0: resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz#b877b49a5c16aefac3655f2ed2ea5b684df8d203" integrity sha512-CIJYJC4GGF06TakLg8z4GQKvDsx9EMspVxOYih7LerEL/WosUnFIww45CGfxfeKHqlg3twgUrYRT1O3WQqjGCg== -get-port@latest: - version "4.2.0" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-4.2.0.tgz#e37368b1e863b7629c43c5a323625f95cf24b119" - integrity sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw== +get-port@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.0.0.tgz#aa22b6b86fd926dd7884de3e23332c9f70c031a6" + integrity sha512-imzMU0FjsZqNa6BqOjbbW6w5BivHIuQKopjpPqcnx0AVHJQKCxK1O+Ab3OrVXhrekqfVMjwA9ZYu062R+KcIsQ== + dependencies: + type-fest "^0.3.0" get-stdin@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== +get-stdin@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-7.0.0.tgz#8d5de98f15171a125c5e516643c7a6d0ea8a96f6" + integrity sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ== + get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= -get-stream@^4.0.0, get-stream@^4.1.0: +get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== @@ -3373,7 +3046,7 @@ glob@7.1.2: once "^1.3.0" path-is-absolute "^1.0.0" -glob@7.1.3, glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.3, glob@~7.1.1: +glob@7.1.3: version "7.1.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== @@ -3385,6 +3058,18 @@ glob@7.1.3, glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.3, glob@~7.1.1: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.3, glob@~7.1.1: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + global-dirs@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" @@ -3392,26 +3077,6 @@ global-dirs@^0.1.0: dependencies: ini "^1.3.4" -global-modules@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" - integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== - dependencies: - global-prefix "^1.0.1" - is-windows "^1.0.1" - resolve-dir "^1.0.0" - -global-prefix@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" - integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4= - dependencies: - expand-tilde "^2.0.2" - homedir-polyfill "^1.0.1" - ini "^1.3.4" - is-windows "^1.0.1" - which "^1.2.14" - globals@^9.2.0: version "9.18.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" @@ -3461,7 +3126,7 @@ got@^6.7.1: unzip-response "^2.0.1" url-parse-lax "^1.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6: +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6: version "4.1.15" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== @@ -3530,7 +3195,7 @@ has-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= -has-unicode@^2.0.0, has-unicode@~2.0.1: +has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= @@ -3609,24 +3274,24 @@ helmet-csp@2.7.1: platform "1.3.5" helmet@^3.12.1: - version "3.16.0" - resolved "https://registry.yarnpkg.com/helmet/-/helmet-3.16.0.tgz#7df41a4bfe4c83d90147c1e30d70893f92a9d97c" - integrity sha512-rsTKRogc5OYGlvSHuq5QsmOsOzF6uDoMqpfh+Np8r23+QxDq+SUx90Rf8HyIKQVl7H6NswZEwfcykinbAeZ6UQ== + version "3.18.0" + resolved "https://registry.yarnpkg.com/helmet/-/helmet-3.18.0.tgz#37666f7c861bd1ff3015e0cdb903a43501e3da3e" + integrity sha512-TsKlGE5UVkV0NiQ4PllV9EVfZklPjyzcMEMjWlyI/8S6epqgRT+4s4GHVgc25x0TixsKvp3L7c91HQQt5l0+QA== dependencies: depd "2.0.0" dns-prefetch-control "0.1.0" dont-sniff-mimetype "1.0.0" - expect-ct "0.1.1" - feature-policy "0.2.0" - frameguard "3.0.0" + expect-ct "0.2.0" + feature-policy "0.3.0" + frameguard "3.1.0" helmet-crossdomain "0.3.0" helmet-csp "2.7.1" hide-powered-by "1.0.0" hpkp "2.0.0" hsts "2.2.0" ienoopen "1.1.0" - nocache "2.0.0" - referrer-policy "1.1.0" + nocache "2.1.0" + referrer-policy "1.2.0" x-xss-protection "1.1.0" hh-mm-ss@~1.2.0: @@ -3641,14 +3306,7 @@ hide-powered-by@1.0.0: resolved "https://registry.yarnpkg.com/hide-powered-by/-/hide-powered-by-1.0.0.tgz#4a85ad65881f62857fc70af7174a1184dccce32b" integrity sha1-SoWtZYgfYoV/xwr3F0oRhNzM4ys= -homedir-polyfill@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" - integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== - dependencies: - parse-passwd "^1.0.0" - -hosted-git-info@^2.1.4, hosted-git-info@^2.6.0, hosted-git-info@^2.7.1: +hosted-git-info@^2.1.4: version "2.7.1" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w== @@ -3665,20 +3323,16 @@ hsts@2.2.0: dependencies: depd "2.0.0" -http-cache-semantics@^3.8.1: - version "3.8.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" - integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== - -http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= +http-errors@1.7.2, http-errors@~1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== dependencies: depd "~1.1.2" inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" http-errors@~1.3.1: version "1.3.1" @@ -3688,14 +3342,6 @@ http-errors@~1.3.1: inherits "~2.0.1" statuses "1" -http-proxy-agent@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" - integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== - dependencies: - agent-base "4" - debug "3.1.0" - http-signature@^1.2.0, http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -3705,50 +3351,28 @@ http-signature@^1.2.0, http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -https-proxy-agent@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0" - integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ== - dependencies: - agent-base "^4.1.0" - debug "^3.1.0" - -humanize-ms@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" - integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0= - dependencies: - ms "^2.0.0" - -husky@^1.0.0-rc.4: - version "1.3.1" - resolved "https://registry.yarnpkg.com/husky/-/husky-1.3.1.tgz#26823e399300388ca2afff11cfa8a86b0033fae0" - integrity sha512-86U6sVVVf4b5NYSZ0yvv88dRgBSSXXmHaiq5pP4KDj5JVzdwKgBjEtUPOm8hcoytezFwbU+7gotXNhpHdystlg== +husky@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/husky/-/husky-2.4.0.tgz#1bac7c44588f6e91f808b72efc82d24a57194f36" + integrity sha512-3k1wuZU20gFkphNWMjh2ISCFaqfbaLY7R9FST2Mj9HeRhUK9ydj9qQR8qfXlog3EctVGsyeilcZkIT7uBZDDVA== dependencies: - cosmiconfig "^5.0.7" + cosmiconfig "^5.2.0" execa "^1.0.0" find-up "^3.0.0" - get-stdin "^6.0.0" + get-stdin "^7.0.0" is-ci "^2.0.0" - pkg-dir "^3.0.0" + pkg-dir "^4.1.0" please-upgrade-node "^3.1.1" - read-pkg "^4.0.1" + read-pkg "^5.1.1" run-node "^1.0.0" - slash "^2.0.0" + slash "^3.0.0" i@0.3.x: version "0.3.6" resolved "https://registry.yarnpkg.com/i/-/i-0.3.6.tgz#d96c92732076f072711b6b10fd7d4f65ad8ee23d" integrity sha1-2WyScyB28HJxG2sQ/X1PZa2O4j0= -iconv-lite@0.4.23: - version "0.4.23" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" - integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -iconv-lite@^0.4.4, iconv-lite@~0.4.13: +iconv-lite@0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -3760,16 +3384,6 @@ ienoopen@1.1.0: resolved "https://registry.yarnpkg.com/ienoopen/-/ienoopen-1.1.0.tgz#411e5d530c982287dbdc3bb31e7a9c9e32630974" integrity sha512-MFs36e/ca6ohEKtinTJ5VvAJ6oDRAYFdYXweUnGY9L9vcoqFOU4n2ZhmJ0C4z/cwGZ3YIQRSB3XZ1+ghZkY5NQ== -iferr@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" - integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= - -iferr@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/iferr/-/iferr-1.0.2.tgz#e9fde49a9da06dc4a4194c6c9ed6d08305037a6d" - integrity sha512-9AfeLfji44r5TKInjhz3W9DyZI1zR1JAf2hVBMGhddAKPqBsupb89jGfbCTHIGZd6fGZl9WlHdn4AObygyMKwg== - ignore-by-default@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" @@ -3825,7 +3439,7 @@ inflection@1.12.0: resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.12.0.tgz#a200935656d6f5f6bc4dc7502e1aecb703228416" integrity sha1-ogCTVlbW9fa8TcdQLhrstwMihBY= -inflight@^1.0.4, inflight@~1.0.6: +inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= @@ -3833,7 +3447,7 @@ inflight@^1.0.4, inflight@~1.0.6: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= @@ -3843,25 +3457,11 @@ inherits@=2.0.1: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= -ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: +ini@^1.3.4, ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== -init-package-json@^1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-1.10.3.tgz#45ffe2f610a8ca134f2bd1db5637b235070f6cbe" - integrity sha512-zKSiXKhQveNteyhcj1CoOP8tqp1QuxPIPBl8Bid99DGLFqA1p87M6lNgfjJHSBoWJJlidGOv5rWjyYKEB3g2Jw== - dependencies: - glob "^7.1.1" - npm-package-arg "^4.0.0 || ^5.0.0 || ^6.0.0" - promzard "^0.3.0" - read "~1.0.1" - read-package-json "1 || 2" - semver "2.x || 3.x || 4 || 5" - validate-npm-package-license "^3.0.1" - validate-npm-package-name "^3.0.0" - inquirer@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" @@ -3881,25 +3481,19 @@ inquirer@^0.12.0: strip-ansi "^3.0.0" through "^2.3.6" -invert-kv@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" - integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= - invert-kv@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== ioredis@^4.5.1: - version "4.9.0" - resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.9.0.tgz#0c52de498363309ebd48b5f6695d9d432b0f6669" - integrity sha512-YzfCLsN++Ct43QqGK9CWxaEK6OUvJ7rnENieAPNw3DVp/oF2uBrP2NJChbhO74Ng3LWA+i5zdIEUsZYr6dKDIQ== + version "4.9.5" + resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.9.5.tgz#0bbba0a9faae93485d3231e1b819d2d4e23271d9" + integrity sha512-L9MVfvX4F3LScTMEgriCGixzqinJsYy7Mt0NPX8RyuOTmx5JW0744pM4Ze2KVQcP3J0zvKYZ1LywAB6KIq7PYg== dependencies: cluster-key-slot "^1.0.6" debug "^3.1.0" denque "^1.1.0" - flexbuffer "0.0.6" lodash.defaults "^4.2.0" lodash.flatten "^4.4.0" redis-commands "1.4.0" @@ -3907,10 +3501,10 @@ ioredis@^4.5.1: redis-parser "^3.0.0" standard-as-callback "^2.0.1" -ip-anonymize@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/ip-anonymize/-/ip-anonymize-0.0.6.tgz#d2c513e448e874e8cc380d03404691b94b018e68" - integrity sha1-0sUT5EjodOjMOA0DQEaRuUsBjmg= +ip-anonymize@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/ip-anonymize/-/ip-anonymize-0.1.0.tgz#5ead504d01871c5c28189a25382f852036b57f7e" + integrity sha512-cZJu+N5JKKFGMK0eEQWNaQMn2EhCysciVM6eotCJwfqotj16BTfVchKsJCH6mQAT9N0GC7oWRcsZ6Lb8dDiwTA== ip-regex@^2.1.0: version "2.1.0" @@ -3918,13 +3512,13 @@ ip-regex@^2.1.0: integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= ip-set@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ip-set/-/ip-set-1.0.1.tgz#633b66d0bd6c8d0de968d053263c9120d3b6727e" - integrity sha1-Yztm0L1sjQ3paNBTJjyRINO2cn4= + version "1.0.2" + resolved "https://registry.yarnpkg.com/ip-set/-/ip-set-1.0.2.tgz#be4f119f82c124836455993dfcd554639c7007de" + integrity sha512-Mb6kv78bTi4RNAIIWL8Bbre7hXOR2pNUi3j8FaQkLaitf/ZWxkq3/iIwXNYk2ACO3IMfdVdQrOkUtwZblO7uBA== dependencies: ip "^1.1.3" -ip@^1.0.1, ip@^1.1.3, ip@^1.1.5: +ip@^1.0.1, ip@^1.1.3: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= @@ -3934,11 +3528,6 @@ ipaddr.js@1.0.5: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.0.5.tgz#5fa78cf301b825c78abc3042d812723049ea23c7" integrity sha1-X6eM8wG4JceKvDBC2BJyMEnqI8c= -ipaddr.js@1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e" - integrity sha1-6qM9bd16zo9/b+DJygRA5wZzix4= - ipaddr.js@1.9.0, "ipaddr.js@>= 0.1.5", ipaddr.js@^1.0.1: version "1.9.0" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" @@ -4135,9 +3724,9 @@ is-my-ip-valid@^1.0.0: integrity sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ== is-my-json-valid@^2.10.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.19.0.tgz#8fd6e40363cd06b963fa877d444bfb5eddc62175" - integrity sha512-mG0f/unGX1HZ5ep4uhRaPOS8EkAY8/j6mDRMJrutq4CqhoJWYp7qAlonIPy3TV7p3ju4TK9fo/PbnoksWmsp5Q== + version "2.20.0" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.20.0.tgz#1345a6fca3e8daefc10d0fa77067f54cedafd59a" + integrity sha512-XTHBZSIIxNsIsZXg7XB5l8z/OBFosl1Wao4tXLpeC7eKU4Vm/kdop2azkPqULwnfGQjmeDIyey9g7afMMtdWAA== dependencies: generate-function "^2.0.0" generate-object-property "^1.1.0" @@ -4256,7 +3845,7 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= -is-windows@^1.0.1, is-windows@^1.0.2: +is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== @@ -4282,9 +3871,9 @@ isexe@^2.0.0: integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= iso-639-3@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/iso-639-3/-/iso-639-3-1.1.0.tgz#83722daf55490a707c318ae18a33ba3bab06c843" - integrity sha512-l3BAnxNpyRIZA4mEzI2md/YVrxQ3hI8hiQe7TFyQknjyOh8vCzobZuAXTFHELco0FBkYRx4FkAlIqkKrHhnzgw== + version "1.2.0" + resolved "https://registry.yarnpkg.com/iso-639-3/-/iso-639-3-1.2.0.tgz#eee1f5e6ca2bbb33e3ecc910857c1c12e8b295be" + integrity sha512-jNvD2P4JHNckQH7pc0R0SQ4oPCpyEtgs0nTtjB+DZCUDdygz0cOAxlcnq5KgNjjsqMHbR4Sbgwz2+DflzAZvlQ== isobject@^2.0.0: version "2.1.0" @@ -4303,23 +3892,15 @@ isstream@0.1.x, isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -js-tokens@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" - integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= - -js-yaml@3.12.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" - integrity sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.12.1, js-yaml@^3.13.0, js-yaml@^3.4.6, js-yaml@^3.5.1, js-yaml@^3.5.4: - version "3.13.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.0.tgz#38ee7178ac0eea2c97ff6d96fff4b18c7d8cf98e" - integrity sha512-pZZoSxcCYco+DIKBTimr67J6Hy+EYGZDY/HCWC+iAEA9h1ByhMXAIVUXMcMFpOCxQ/xjXmPI2MkDL5HRm5eFrQ== +js-yaml@3.13.1, js-yaml@^3.12.1, js-yaml@^3.13.1, js-yaml@^3.4.6, js-yaml@^3.5.1, js-yaml@^3.5.4: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== dependencies: argparse "^1.0.7" esprima "^4.0.0" @@ -4329,7 +3910,7 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: +json-parse-better-errors@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== @@ -4425,11 +4006,6 @@ jsonld@~1.1.0: semver "^5.5.0" xmldom "0.1.19" -jsonparse@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" - integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= - jsonpointer.js@0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/jsonpointer.js/-/jsonpointer.js-0.4.0.tgz#002cb123f767aafdeb0196132ce5c4f9941ccaba" @@ -4565,18 +4141,6 @@ latest-version@^3.0.0: dependencies: package-json "^4.0.0" -lazy-property@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lazy-property/-/lazy-property-1.0.0.tgz#84ddc4b370679ba8bd4cdcfa4c06b43d57111147" - integrity sha1-hN3Es3Bnm6i9TNz6TAa0PVcREUc= - -lcid@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" - integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= - dependencies: - invert-kv "^1.0.0" - lcid@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" @@ -4592,140 +4156,6 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -libcipm@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/libcipm/-/libcipm-3.0.3.tgz#2e764effe0b90d458790dab3165794c804075ed3" - integrity sha512-71V5CpTI+zFydTc5IjJ/tx8JHbXEJvmYF2zaSVW1V3X1rRnRjXqh44iuiyry1xgi3ProUQ1vX1uwFiWs00+2og== - dependencies: - bin-links "^1.1.2" - bluebird "^3.5.1" - figgy-pudding "^3.5.1" - find-npm-prefix "^1.0.2" - graceful-fs "^4.1.11" - ini "^1.3.5" - lock-verify "^2.0.2" - mkdirp "^0.5.1" - npm-lifecycle "^2.0.3" - npm-logical-tree "^1.2.1" - npm-package-arg "^6.1.0" - pacote "^9.1.0" - read-package-json "^2.0.13" - rimraf "^2.6.2" - worker-farm "^1.6.0" - -libnpm@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/libnpm/-/libnpm-2.0.1.tgz#a48fcdee3c25e13c77eb7c60a0efe561d7fb0d8f" - integrity sha512-qTKoxyJvpBxHZQB6k0AhSLajyXq9ZE/lUsZzuHAplr2Bpv9G+k4YuYlExYdUCeVRRGqcJt8hvkPh4tBwKoV98w== - dependencies: - bin-links "^1.1.2" - bluebird "^3.5.3" - find-npm-prefix "^1.0.2" - libnpmaccess "^3.0.1" - libnpmconfig "^1.2.1" - libnpmhook "^5.0.2" - libnpmorg "^1.0.0" - libnpmpublish "^1.1.0" - libnpmsearch "^2.0.0" - libnpmteam "^1.0.1" - lock-verify "^2.0.2" - npm-lifecycle "^2.1.0" - npm-logical-tree "^1.2.1" - npm-package-arg "^6.1.0" - npm-profile "^4.0.1" - npm-registry-fetch "^3.8.0" - npmlog "^4.1.2" - pacote "^9.2.3" - read-package-json "^2.0.13" - stringify-package "^1.0.0" - -libnpmaccess@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-3.0.1.tgz#5b3a9de621f293d425191aa2e779102f84167fa8" - integrity sha512-RlZ7PNarCBt+XbnP7R6PoVgOq9t+kou5rvhaInoNibhPO7eMlRfS0B8yjatgn2yaHIwWNyoJDolC/6Lc5L/IQA== - dependencies: - aproba "^2.0.0" - get-stream "^4.0.0" - npm-package-arg "^6.1.0" - npm-registry-fetch "^3.8.0" - -libnpmconfig@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/libnpmconfig/-/libnpmconfig-1.2.1.tgz#c0c2f793a74e67d4825e5039e7a02a0044dfcbc0" - integrity sha512-9esX8rTQAHqarx6qeZqmGQKBNZR5OIbl/Ayr0qQDy3oXja2iFVQQI81R6GZ2a02bSNZ9p3YOGX1O6HHCb1X7kA== - dependencies: - figgy-pudding "^3.5.1" - find-up "^3.0.0" - ini "^1.3.5" - -libnpmhook@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/libnpmhook/-/libnpmhook-5.0.2.tgz#d12817b0fb893f36f1d5be20017f2aea25825d94" - integrity sha512-vLenmdFWhRfnnZiNFPNMog6CK7Ujofy2TWiM2CrpZUjBRIhHkJeDaAbJdYCT6W4lcHtyrJR8yXW8KFyq6UAp1g== - dependencies: - aproba "^2.0.0" - figgy-pudding "^3.4.1" - get-stream "^4.0.0" - npm-registry-fetch "^3.8.0" - -libnpmorg@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/libnpmorg/-/libnpmorg-1.0.0.tgz#979b868c48ba28c5820e3bb9d9e73c883c16a232" - integrity sha512-o+4eVJBoDGMgRwh2lJY0a8pRV2c/tQM/SxlqXezjcAg26Qe9jigYVs+Xk0vvlYDWCDhP0g74J8UwWeAgsB7gGw== - dependencies: - aproba "^2.0.0" - figgy-pudding "^3.4.1" - get-stream "^4.0.0" - npm-registry-fetch "^3.8.0" - -libnpmpublish@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-1.1.1.tgz#ff0c6bb0b4ad2bda2ad1f5fba6760a4af37125f0" - integrity sha512-nefbvJd/wY38zdt+b9SHL6171vqBrMtZ56Gsgfd0duEKb/pB8rDT4/ObUQLrHz1tOfht1flt2zM+UGaemzAG5g== - dependencies: - aproba "^2.0.0" - figgy-pudding "^3.5.1" - get-stream "^4.0.0" - lodash.clonedeep "^4.5.0" - normalize-package-data "^2.4.0" - npm-package-arg "^6.1.0" - npm-registry-fetch "^3.8.0" - semver "^5.5.1" - ssri "^6.0.1" - -libnpmsearch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/libnpmsearch/-/libnpmsearch-2.0.0.tgz#de05af47ada81554a5f64276a69599070d4a5685" - integrity sha512-vd+JWbTGzOSfiOc+72MU6y7WqmBXn49egCCrIXp27iE/88bX8EpG64ST1blWQI1bSMUr9l1AKPMVsqa2tS5KWA== - dependencies: - figgy-pudding "^3.5.1" - get-stream "^4.0.0" - npm-registry-fetch "^3.8.0" - -libnpmteam@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/libnpmteam/-/libnpmteam-1.0.1.tgz#ff704b1b6c06ea674b3b1101ac3e305f5114f213" - integrity sha512-gDdrflKFCX7TNwOMX1snWojCoDE5LoRWcfOC0C/fqF7mBq8Uz9zWAX4B2RllYETNO7pBupBaSyBDkTAC15cAMg== - dependencies: - aproba "^2.0.0" - figgy-pudding "^3.4.1" - get-stream "^4.0.0" - npm-registry-fetch "^3.8.0" - -libnpx@^10.2.0: - version "10.2.0" - resolved "https://registry.yarnpkg.com/libnpx/-/libnpx-10.2.0.tgz#1bf4a1c9f36081f64935eb014041da10855e3102" - integrity sha512-X28coei8/XRCt15cYStbLBph+KGhFra4VQhRBPuH/HHMkC5dxM8v24RVgUsvODKCrUZ0eTgiTqJp6zbl0sskQQ== - dependencies: - dotenv "^5.0.1" - npm-package-arg "^6.0.0" - rimraf "^2.6.2" - safe-buffer "^5.1.0" - update-notifier "^2.3.0" - which "^1.3.0" - y18n "^4.0.0" - yargs "^11.0.0" - libxmljs@0.19.5: version "0.19.5" resolved "https://registry.yarnpkg.com/libxmljs/-/libxmljs-0.19.5.tgz#b2f34cc12fd6a3e43670c604c42a902f339ea54d" @@ -4736,18 +4166,17 @@ libxmljs@0.19.5: node-pre-gyp "~0.11.0" lint-staged@^8.0.4: - version "8.1.5" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-8.1.5.tgz#372476fe1a58b8834eb562ed4c99126bd60bdd79" - integrity sha512-e5ZavfnSLcBJE1BTzRTqw6ly8OkqVyO3GL2M6teSmTBYQ/2BuueD5GIt2RPsP31u/vjKdexUyDCxSyK75q4BDA== + version "8.2.0" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-8.2.0.tgz#3d4149a229580815c955047a7acd8f09054be5a9" + integrity sha512-DxguyxGOIfb67wZ6EOrqzjAbw6ZH9XK3YS74HO+erJf6+SAQeJJPN//GBOG5xhdt2THeuXjVPaHcCYOWGZwRbA== dependencies: chalk "^2.3.1" commander "^2.14.1" - cosmiconfig "^5.0.2" + cosmiconfig "^5.2.0" debug "^3.1.0" dedent "^0.7.0" del "^3.0.0" execa "^1.0.0" - find-parent-dir "^0.3.0" g-status "^2.0.2" is-glob "^4.0.0" is-windows "^1.0.2" @@ -4764,7 +4193,7 @@ lint-staged@^8.0.4: staged-git-files "1.1.2" string-argv "^0.0.2" stringify-object "^3.2.2" - yup "^0.26.10" + yup "^0.27.0" listr-silent-renderer@^1.1.1: version "1.1.1" @@ -4821,14 +4250,6 @@ load-ip-set@^2.1.0: simple-get "^3.0.0" split "^1.0.0" -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -4837,49 +4258,18 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^3.0.0" -lock-verify@^2.0.2, lock-verify@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/lock-verify/-/lock-verify-2.1.0.tgz#fff4c918b8db9497af0c5fa7f6d71555de3ceb47" - integrity sha512-vcLpxnGvrqisKvLQ2C2v0/u7LVly17ak2YSgoK4PrdsYBXQIax19vhKiLfvKNFx7FRrpTnitrpzF/uuCMuorIg== - dependencies: - npm-package-arg "^6.1.0" - semver "^5.4.1" - -lockfile@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/lockfile/-/lockfile-1.0.4.tgz#07f819d25ae48f87e538e6578b6964a4981a5609" - integrity sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA== - dependencies: - signal-exit "^3.0.2" - -lodash._baseuniq@~4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" - integrity sha1-DrtE5FaBSveQXGIS+iybLVG4Qeg= +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== dependencies: - lodash._createset "~4.0.0" - lodash._root "~3.0.0" - -lodash._createset@~4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26" - integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY= - -lodash._root@~3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" - integrity sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI= + p-locate "^4.1.0" lodash.capitalize@^4.1.0: version "4.2.1" resolved "https://registry.yarnpkg.com/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz#f826c9b4e2a8511d84e3aca29db05e1a4f3b72a9" integrity sha1-+CbJtOKoUR2E46yinbBeGk87cqk= -lodash.clonedeep@^4.5.0, lodash.clonedeep@~4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= - lodash.defaults@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" @@ -4905,21 +4295,6 @@ lodash.kebabcase@^4.0.0: resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" integrity sha1-hImxyw0p/4gZXM7KRI/21swpXDY= -lodash.union@~4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" - integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg= - -lodash.uniq@~4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" - integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= - -lodash.without@~4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac" - integrity sha1-PNRXSgC2e643OpS3SHcmQFB7eqw= - lodash@4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -4974,7 +4349,7 @@ lowercase-keys@^1.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== -lru-cache@4.1.x, lru-cache@^4.0.1, lru-cache@^4.1.2, lru-cache@^4.1.3, lru-cache@^4.1.5: +lru-cache@4.1.x, lru-cache@^4.0.1: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== @@ -4982,13 +4357,6 @@ lru-cache@4.1.x, lru-cache@^4.0.1, lru-cache@^4.1.2, lru-cache@^4.1.3, lru-cache pseudomap "^1.0.2" yallist "^2.1.2" -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - lru-queue@0.1: version "0.1.0" resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" @@ -5049,23 +4417,6 @@ make-error@^1.1.1: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== -make-fetch-happen@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-4.0.1.tgz#141497cb878f243ba93136c83d8aba12c216c083" - integrity sha512-7R5ivfy9ilRJ1EMKIOziwrns9fGeAD4bAha8EB7BIiBBLHm2KeTUGCrICFt2rbHfzheTLynv50GnNTK1zDTrcQ== - dependencies: - agentkeepalive "^3.4.1" - cacache "^11.0.1" - http-cache-semantics "^3.8.1" - http-proxy-agent "^2.1.0" - https-proxy-agent "^2.2.1" - lru-cache "^4.1.2" - mississippi "^3.0.0" - node-fetch-npm "^2.0.2" - promise-retry "^1.1.1" - socks-proxy-agent "^4.0.0" - ssri "^6.0.0" - map-age-cleaner@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" @@ -5085,10 +4436,10 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -marked-man@^0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/marked-man/-/marked-man-0.4.2.tgz#4507ee0bafa04f46f9b48082f62b3593c8d667bb" - integrity sha512-4AMgee2zyjzgybboMBjAfDQ1ZUnBFDmxTKld7xuEc9N5tY3T7CqQYqYfKAkUc73tNLeOdwKcR3N5p1toXLiHAA== +marked-man@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/marked-man/-/marked-man-0.6.0.tgz#ac049f840cf633a0f679af58f321c7de3dd583d8" + integrity sha512-lqzGe2uPo0KPO5J5yyfZ6PbEpZS6VIMHIzltXNzdwTNBysD0pNLtTaGrtT5R4aFT8UaVR3Fuz9rN3SEFYnea5Q== matcher@^1.0.0: version "1.1.1" @@ -5106,11 +4457,6 @@ md5@^2.2.1: crypt "~0.0.1" is-buffer "~1.1.1" -meant@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/meant/-/meant-1.0.1.tgz#66044fea2f23230ec806fb515efea29c44d2115d" - integrity sha512-UakVLFjKkbbUwNWJ2frVLnnAtbb7D7DsloxRd3s/gDpI8rdv8W5Hp3NaDb+POBI1fQdeussER6NB8vpcRURvlg== - media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -5125,13 +4471,6 @@ mediasource@^2.1.0, mediasource@^2.2.2: readable-stream "^3.0.0" to-arraybuffer "^1.0.1" -mem@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" - integrity sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y= - dependencies: - mimic-fn "^1.0.0" - mem@^4.0.0: version "4.3.0" resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" @@ -5175,7 +4514,7 @@ methods@^1.1.1, methods@^1.1.2, methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= -micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8: +micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -5194,37 +4533,32 @@ micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8: snapdragon "^0.8.1" to-regex "^3.0.2" -mime-db@~1.38.0: - version "1.38.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad" - integrity sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg== +mime-db@1.40.0: + version "1.40.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" + integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== -mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.18, mime-types@~2.1.19, mime-types@~2.1.6: - version "2.1.22" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.22.tgz#fe6b355a190926ab7698c9a0556a11199b2199bd" - integrity sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog== +mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.6: + version "2.1.24" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" + integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== dependencies: - mime-db "~1.38.0" + mime-db "1.40.0" mime@1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" integrity sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM= -mime@1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" - integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== - -mime@^1.3.4, mime@^1.4.1: +mime@1.6.0, mime@^1.3.4, mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== mime@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.0.tgz#e051fd881358585f3279df333fe694da0bcffdd6" - integrity sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w== + version "2.4.3" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.3.tgz#229687331e86f68924e6cb59e1cdd937f18275fe" + integrity sha512-QgrPRJfE+riq5TPZMcHZOtm8c6K/yYrMbKIoRfapfiGLxS8OTeIfRhUGW5LU7MlRa52KOAGCfUNruqLrIBvWZw== mimelib@^0.3.0: version "0.3.1" @@ -5276,7 +4610,7 @@ minimist@^1.1.0, minimist@^1.1.1, minimist@^1.2.0: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= -minipass@^2.2.1, minipass@^2.3.4, minipass@^2.3.5: +minipass@^2.2.1, minipass@^2.3.5: version "2.3.5" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA== @@ -5284,29 +4618,13 @@ minipass@^2.2.1, minipass@^2.3.4, minipass@^2.3.5: safe-buffer "^5.1.2" yallist "^3.0.0" -minizlib@^1.1.1: +minizlib@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA== dependencies: minipass "^2.2.1" -mississippi@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" - integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== - dependencies: - concat-stream "^1.5.0" - duplexify "^3.4.2" - end-of-stream "^1.1.0" - flush-write-stream "^1.0.0" - from2 "^2.1.0" - parallel-transform "^1.1.0" - pump "^3.0.0" - pumpify "^1.3.3" - stream-each "^1.1.0" - through2 "^2.0.0" - mixin-deep@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" @@ -5315,46 +4633,56 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@0.5.1, mkdirp@0.x.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: +mkdirp@0.5.1, mkdirp@0.x.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= dependencies: minimist "0.0.8" +mocha-parallel-tests@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/mocha-parallel-tests/-/mocha-parallel-tests-2.1.2.tgz#2f5c24022257e5fc6c63a6bd22d49c1487496b26" + integrity sha512-FatFg3MHLio9ir1oP6J0HNEo6R5344JC1y+We90iALdiT9F9xNPN0KbGXxRNlGlSl0GodfSESKbRzBvT9ctgIw== + dependencies: + circular-json "^0.5.9" + debug "^4.1.1" + uuid "^3.3.2" + yargs "^13.2.2" + mocha@^6.0.0: - version "6.0.2" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.0.2.tgz#cdc1a6fdf66472c079b5605bac59d29807702d2c" - integrity sha512-RtTJsmmToGyeTznSOMoM6TPEk1A84FQaHIciKrRqARZx+B5ccJ5tXlmJzEKGBxZdqk9UjpRsesZTUkZmR5YnuQ== + version "6.1.4" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.1.4.tgz#e35fada242d5434a7e163d555c705f6875951640" + integrity sha512-PN8CIy4RXsIoxoFJzS4QNnCH4psUCPWc4/rPrst/ecSJJbLBkubMiyGCP2Kj/9YnWbotFqAoeXyXMucj7gwCFg== dependencies: ansi-colors "3.2.3" browser-stdout "1.3.1" debug "3.2.6" diff "3.5.0" escape-string-regexp "1.0.5" - findup-sync "2.0.0" + find-up "3.0.0" glob "7.1.3" growl "1.10.5" he "1.2.0" - js-yaml "3.12.0" + js-yaml "3.13.1" log-symbols "2.2.0" minimatch "3.0.4" mkdirp "0.5.1" ms "2.1.1" - node-environment-flags "1.0.4" + node-environment-flags "1.0.5" object.assign "4.1.0" strip-json-comments "2.0.1" supports-color "6.0.0" which "1.3.1" wide-align "1.1.3" - yargs "12.0.5" - yargs-parser "11.1.1" + yargs "13.2.2" + yargs-parser "13.0.0" yargs-unparser "1.5.0" moment-timezone@^0.5.21, moment-timezone@^0.5.23: - version "0.5.23" - resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.23.tgz#7cbb00db2c14c71b19303cb47b0fb0a6d8651463" - integrity sha512-WHFH85DkCfiNMDX5D3X7hpNH3/PUhjTGcD0U1SgfBGZxJ3qUmJh5FdvaFjcClxOvB3rzdfj4oRffbI38jEnC1w== + version "0.5.25" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.25.tgz#a11bfa2f74e088327f2cd4c08b3e7bdf55957810" + integrity sha512-DgEaTyN/z0HFaVcVbSyVCUU6HeFdnNC3vE4c9cgu2dgMTvjBUBdBzWfasTBmAW45u5OIMeCJtU8yNjM22DHucw== dependencies: moment ">= 2.9.0" @@ -5374,18 +4702,6 @@ morgan@^1.5.3: on-finished "~2.3.0" on-headers "~1.0.1" -move-concurrently@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" - integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= - dependencies: - aproba "^1.1.1" - copy-concurrently "^1.0.0" - fs-write-stream-atomic "^1.0.8" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.3" - mp4-box-encoding@^1.1.0, mp4-box-encoding@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/mp4-box-encoding/-/mp4-box-encoding-1.3.0.tgz#2a6f750947ff68c3a498fd76cd6424c53d995d48" @@ -5421,7 +4737,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@2.1.1, ms@^2.0.0, ms@^2.1.1: +ms@2.1.1, ms@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== @@ -5458,16 +4774,16 @@ mute-stream@~0.0.4: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nan@2.13.1: - version "2.13.1" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.1.tgz#a15bee3790bde247e8f38f1d446edcdaeb05f2dd" - integrity sha512-I6YB/YEuDeUZMmhscXKxGgZlFnhsn5y0hgOZBadkzfTRrZBtJDZeg6eQf7PYMIEclwmorTKK8GztsyOUSVBREA== - -nan@^2.10.0, nan@^2.13.1, nan@^2.9.2: +nan@2.13.2: version "2.13.2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7" integrity sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw== +nan@^2.10.0, nan@^2.12.1, nan@^2.13.2: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + nan@~2.10.0: version "2.10.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" @@ -5501,11 +4817,11 @@ ncp@1.0.x: integrity sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY= needle@^2.2.1: - version "2.2.4" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.4.tgz#51931bff82533b1928b7d1d69e01f1b00ffd2a4e" - integrity sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA== + version "2.4.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" + integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== dependencies: - debug "^2.1.2" + debug "^3.2.6" iconv-lite "^0.4.4" sax "^1.2.4" @@ -5519,6 +4835,11 @@ negotiator@0.6.1: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + netmask@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" @@ -5547,38 +4868,25 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -nocache@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.0.0.tgz#202b48021a0c4cbde2df80de15a17443c8b43980" - integrity sha1-ICtIAhoMTL3i34DeFaF0Q8i0OYA= +nocache@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f" + integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q== node-abi@^2.7.0: - version "2.7.1" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.7.1.tgz#a8997ae91176a5fbaa455b194976e32683cda643" - integrity sha512-OV8Bq1OrPh6z+Y4dqwo05HqrRL9YNF7QVMRfq1/pguwKLG+q9UB/Lk0x5qXjO23JjJg+/jqCHSTaG1P3tfKfuw== + version "2.8.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.8.0.tgz#bd2e88dbe6a6871e6dd08553e0605779325737ec" + integrity sha512-1/aa2clS0pue0HjckL62CsbhWWU35HARvBDXcJtYKbYR7LnIutmpxmXbuDMV9kEviD2lP/wACOgWmmwljghHyQ== dependencies: semver "^5.4.1" -node-addon-api@^1.6.0: - version "1.6.2" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.6.2.tgz#d8aad9781a5cfc4132cc2fecdbdd982534265217" - integrity sha512-479Bjw9nTE5DdBSZZWprFryHGjUaQC31y1wHo19We/k0BZlrmhqQitWoUL0cD8+scljCbIUL+E58oRDEakdGGA== - -node-environment-flags@1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.4.tgz#0b784a6551426bfc16d3b2208424dcbc2b2ff038" - integrity sha512-M9rwCnWVLW7PX+NUWe3ejEdiLYinRpsEre9hMkU/6NS4h+EEulYaDH1gCEZ2gyXsmw+RXYDaV2JkkTNcsPDJ0Q== +node-environment-flags@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" + integrity sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ== dependencies: object.getownpropertydescriptors "^2.0.3" - -node-fetch-npm@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz#7258c9046182dca345b4208eda918daf33697ff7" - integrity sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw== - dependencies: - encoding "^0.1.11" - json-parse-better-errors "^1.0.0" - safe-buffer "^5.1.1" + semver "^5.7.0" node-forge@^0.7.1: version "0.7.6" @@ -5590,25 +4898,7 @@ node-gyp-build@~3.7.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-3.7.0.tgz#daa77a4f547b9aed3e2aac779eaf151afd60ec8d" integrity sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w== -node-gyp@^3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c" - integrity sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA== - dependencies: - fstream "^1.0.0" - glob "^7.0.3" - graceful-fs "^4.1.2" - mkdirp "^0.5.0" - nopt "2 || 3" - npmlog "0 || 1 || 2 || 3 || 4" - osenv "0" - request "^2.87.0" - rimraf "2" - semver "~5.3.0" - tar "^2.0.0" - which "1" - -node-pre-gyp@0.12.0: +node-pre-gyp@0.12.0, node-pre-gyp@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149" integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A== @@ -5624,22 +4914,6 @@ node-pre-gyp@0.12.0: semver "^5.3.0" tar "^4" -node-pre-gyp@^0.10.0: - version "0.10.3" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc" - integrity sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A== - dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.1" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4" - node-pre-gyp@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz#db1f33215272f692cd38f03238e3e9b47c5dd054" @@ -5681,16 +4955,16 @@ nodemailer-shared@^1.1.0: nodemailer-fetch "1.6.0" nodemailer@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.0.0.tgz#d9761128771739dc87c1fdd747f569b7f135cb02" - integrity sha512-PMQJyLhoNAMoBU1hEh5aaUkpa/tcDNwzS7s7zow/myKfoEoZewMxUuWZqQ5yjYsAnvE484KSkYH5s6iPvcjhCg== + version "6.2.1" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.2.1.tgz#20d773925eb8f7a06166a0b62c751dc8290429f3" + integrity sha512-TagB7iuIi9uyNgHExo8lUDq3VK5/B0BpbkcjIgNvxbtVrjNqq0DwAOTuzALPVkK76kMhTSzIgHqg8X1uklVs6g== nodemon@^1.18.6: - version "1.18.10" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.18.10.tgz#3ba63f64eb4c283cf3e4f75f30817e9d4f393afe" - integrity sha512-we51yBb1TfEvZamFchRgcfLbVYgg0xlGbyXmOtbBzDwxwgewYS/YbZ5tnlnsH51+AoSTTsT3A2E/FloUbtH8cQ== + version "1.19.1" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.19.1.tgz#576f0aad0f863aabf8c48517f6192ff987cd5071" + integrity sha512-/DXLzd/GhiaDXXbGId5BzxP1GlsqtMGM9zTmkWrgXtSqjKmGSbLicM/oAy4FR0YWm14jCHRwnR31AHS2dYFHrg== dependencies: - chokidar "^2.1.0" + chokidar "^2.1.5" debug "^3.1.0" ignore-by-default "^1.0.1" minimatch "^3.0.4" @@ -5706,14 +4980,7 @@ noop-logger@^0.1.1: resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI= -"nopt@2 || 3": - version "3.0.6" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" - integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= - dependencies: - abbrev "1" - -nopt@^4.0.1, nopt@~4.0.1: +nopt@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= @@ -5728,7 +4995,7 @@ nopt@~1.0.10: dependencies: abbrev "1" -normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package-data@^2.4.0, normalize-package-data@^2.5.0: +normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -5750,61 +5017,12 @@ normalize-path@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -npm-audit-report@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/npm-audit-report/-/npm-audit-report-1.3.2.tgz#303bc78cd9e4c226415076a4f7e528c89fc77018" - integrity sha512-abeqS5ONyXNaZJPGAf6TOUMNdSe1Y6cpc9MLBRn+CuUoYbfdca6AxOyXVlfIv9OgKX+cacblbG5w7A6ccwoTPw== - dependencies: - cli-table3 "^0.5.0" - console-control-strings "^1.1.0" - npm-bundled@^1.0.1: version "1.0.6" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== -npm-cache-filename@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/npm-cache-filename/-/npm-cache-filename-1.0.2.tgz#ded306c5b0bfc870a9e9faf823bc5f283e05ae11" - integrity sha1-3tMGxbC/yHCp6fr4I7xfKD4FrhE= - -npm-install-checks@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-3.0.0.tgz#d4aecdfd51a53e3723b7b2f93b2ee28e307bc0d7" - integrity sha1-1K7N/VGlPjcjt7L5Oy7ijjB7wNc= - dependencies: - semver "^2.3.0 || 3.x || 4 || 5" - -npm-lifecycle@^2.0.3, npm-lifecycle@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-2.1.0.tgz#1eda2eedb82db929e3a0c50341ab0aad140ed569" - integrity sha512-QbBfLlGBKsktwBZLj6AviHC6Q9Y3R/AY4a2PYSIRhSKSS0/CxRyD/PfxEX6tPeOCXQgMSNdwGeECacstgptc+g== - dependencies: - byline "^5.0.0" - graceful-fs "^4.1.11" - node-gyp "^3.8.0" - resolve-from "^4.0.0" - slide "^1.1.6" - uid-number "0.0.6" - umask "^1.1.0" - which "^1.3.1" - -npm-logical-tree@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/npm-logical-tree/-/npm-logical-tree-1.2.1.tgz#44610141ca24664cad35d1e607176193fd8f5b88" - integrity sha512-AJI/qxDB2PWI4LG1CYN579AY1vCiNyWfkiquCsJWqntRu/WwimVrC8yXeILBFHDwxfOejxewlmnvW9XXjMlYIg== - -"npm-package-arg@^4.0.0 || ^5.0.0 || ^6.0.0", npm-package-arg@^6.0.0, npm-package-arg@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-6.1.0.tgz#15ae1e2758a5027efb4c250554b85a737db7fcc1" - integrity sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA== - dependencies: - hosted-git-info "^2.6.0" - osenv "^0.1.5" - semver "^5.5.0" - validate-npm-package-name "^3.0.0" - -npm-packlist@^1.1.12, npm-packlist@^1.1.6, npm-packlist@^1.4.1: +npm-packlist@^1.1.6: version "1.4.1" resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.1.tgz#19064cdf988da80ea3cee45533879d90192bbfbc" integrity sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw== @@ -5819,36 +5037,6 @@ npm-path@^2.0.2: dependencies: which "^1.2.10" -npm-pick-manifest@^2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-2.2.3.tgz#32111d2a9562638bb2c8f2bf27f7f3092c8fae40" - integrity sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA== - dependencies: - figgy-pudding "^3.5.1" - npm-package-arg "^6.0.0" - semver "^5.4.1" - -npm-profile@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-profile/-/npm-profile-4.0.1.tgz#d350f7a5e6b60691c7168fbb8392c3603583f5aa" - integrity sha512-NQ1I/1Q7YRtHZXkcuU1/IyHeLy6pd+ScKg4+DQHdfsm769TGq6HPrkbuNJVJS4zwE+0mvvmeULzQdWn2L2EsVA== - dependencies: - aproba "^1.1.2 || 2" - figgy-pudding "^3.4.1" - npm-registry-fetch "^3.8.0" - -npm-registry-fetch@^3.8.0, npm-registry-fetch@^3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-3.9.0.tgz#44d841780e2833f06accb34488f8c7450d1a6856" - integrity sha512-srwmt8YhNajAoSAaDWndmZgx89lJwIZ1GWxOuckH4Coek4uHv5S+o/l9FLQe/awA+JwTnj4FJHldxhlXdZEBmw== - dependencies: - JSONStream "^1.3.4" - bluebird "^3.5.1" - figgy-pudding "^3.4.1" - lru-cache "^4.1.3" - make-fetch-happen "^4.0.1" - npm-package-arg "^6.1.0" - npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -5856,11 +5044,6 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npm-user-validate@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/npm-user-validate/-/npm-user-validate-1.0.0.tgz#8ceca0f5cea04d4e93519ef72d0557a75122e951" - integrity sha1-jOyg9c6gTU6TUZ73LQVXp1Ei6VE= - npm-which@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/npm-which/-/npm-which-3.0.1.tgz#9225f26ec3a285c209cae67c3b11a6b4ab7140aa" @@ -5870,121 +5053,7 @@ npm-which@^3.0.1: npm-path "^2.0.2" which "^1.2.10" -npm@*: - version "6.9.0" - resolved "https://registry.yarnpkg.com/npm/-/npm-6.9.0.tgz#5296720486814a64a7fb082de00c4b5cfd11211f" - integrity sha512-91V+zB5hDxO+Jyp2sUKS7juHlIM95dGQxTeQtmZI1nAI/7kjWXFipPrtwwKjhyKmV4GsS2LzJhrxRjGWsU9z/w== - dependencies: - JSONStream "^1.3.5" - abbrev "~1.1.1" - ansicolors "~0.3.2" - ansistyles "~0.1.3" - aproba "^2.0.0" - archy "~1.0.0" - bin-links "^1.1.2" - bluebird "^3.5.3" - byte-size "^5.0.1" - cacache "^11.3.2" - call-limit "~1.1.0" - chownr "^1.1.1" - ci-info "^2.0.0" - cli-columns "^3.1.2" - cli-table3 "^0.5.1" - cmd-shim "~2.0.2" - columnify "~1.5.4" - config-chain "^1.1.12" - detect-indent "~5.0.0" - detect-newline "^2.1.0" - dezalgo "~1.0.3" - editor "~1.0.0" - figgy-pudding "^3.5.1" - find-npm-prefix "^1.0.2" - fs-vacuum "~1.2.10" - fs-write-stream-atomic "~1.0.10" - gentle-fs "^2.0.1" - glob "^7.1.3" - graceful-fs "^4.1.15" - has-unicode "~2.0.1" - hosted-git-info "^2.7.1" - iferr "^1.0.2" - inflight "~1.0.6" - inherits "~2.0.3" - ini "^1.3.5" - init-package-json "^1.10.3" - is-cidr "^3.0.0" - json-parse-better-errors "^1.0.2" - lazy-property "~1.0.0" - libcipm "^3.0.3" - libnpm "^2.0.1" - libnpmhook "^5.0.2" - libnpx "^10.2.0" - lock-verify "^2.1.0" - lockfile "^1.0.4" - lodash._baseuniq "~4.6.0" - lodash.clonedeep "~4.5.0" - lodash.union "~4.6.0" - lodash.uniq "~4.5.0" - lodash.without "~4.4.0" - lru-cache "^4.1.5" - meant "~1.0.1" - mississippi "^3.0.0" - mkdirp "~0.5.1" - move-concurrently "^1.0.1" - node-gyp "^3.8.0" - nopt "~4.0.1" - normalize-package-data "^2.5.0" - npm-audit-report "^1.3.2" - npm-cache-filename "~1.0.2" - npm-install-checks "~3.0.0" - npm-lifecycle "^2.1.0" - npm-package-arg "^6.1.0" - npm-packlist "^1.4.1" - npm-pick-manifest "^2.2.3" - npm-registry-fetch "^3.9.0" - npm-user-validate "~1.0.0" - npmlog "~4.1.2" - once "~1.4.0" - opener "^1.5.1" - osenv "^0.1.5" - pacote "^9.5.0" - path-is-inside "~1.0.2" - promise-inflight "~1.0.1" - qrcode-terminal "^0.12.0" - query-string "^6.2.0" - qw "~1.0.1" - read "~1.0.7" - read-cmd-shim "~1.0.1" - read-installed "~4.0.3" - read-package-json "^2.0.13" - read-package-tree "^5.2.2" - readable-stream "^3.1.1" - request "^2.88.0" - retry "^0.12.0" - rimraf "^2.6.3" - safe-buffer "^5.1.2" - semver "^5.6.0" - sha "~2.0.1" - slide "~1.1.6" - sorted-object "~2.0.1" - sorted-union-stream "~2.1.3" - ssri "^6.0.1" - stringify-package "^1.0.0" - tar "^4.4.8" - text-table "~0.2.0" - tiny-relative-date "^1.3.0" - uid-number "0.0.6" - umask "~1.1.0" - unique-filename "^1.1.1" - unpipe "~1.0.0" - update-notifier "^2.5.0" - uuid "^3.3.2" - validate-npm-package-license "^3.0.4" - validate-npm-package-name "~3.0.0" - which "^1.3.1" - worker-farm "^1.6.0" - write-file-atomic "^2.4.2" - -"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.1, npmlog@^4.0.2, npmlog@^4.1.2, npmlog@~4.1.2: +npmlog@^4.0.1, npmlog@^4.0.2, npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -6041,9 +5110,9 @@ object-copy@^0.1.0: kind-of "^3.0.3" object-keys@^1.0.11, object-keys@^1.0.12: - version "1.1.0" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.0.tgz#11bd22348dd2e096a045ab06f6c85bcc340fa032" - integrity sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg== + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== object-visit@^1.0.0: version "1.0.1" @@ -6089,7 +5158,7 @@ on-headers@~1.0.1: resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== -once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0, once@~1.4.0: +once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= @@ -6134,11 +5203,6 @@ openapi-schema-validation@^0.4.2: jsonschema-draft4 "^1.0.0" swagger-schema-official "2.0.0-bab6bed" -opener@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed" - integrity sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA== - optionator@^0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" @@ -6161,16 +5225,7 @@ os-homedir@^1.0.0, os-homedir@^1.0.1: resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= -os-locale@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" - integrity sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA== - dependencies: - execa "^0.7.0" - lcid "^1.0.0" - mem "^1.1.0" - -os-locale@^3.0.0: +os-locale@^3.0.0, os-locale@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== @@ -6184,7 +5239,7 @@ os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= -osenv@0, osenv@^0.1.4, osenv@^0.1.5: +osenv@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== @@ -6203,31 +5258,17 @@ p-finally@^1.0.0: integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= p-is-promise@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.0.0.tgz#7554e3d572109a87e1f3f53f6a7d85d1b194f4c5" - integrity sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg== - -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" + integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== -p-limit@^2.0.0: +p-limit@^2.0.0, p-limit@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.0.tgz#417c9941e6027a9abcba5092dd2904e255b5fbc2" integrity sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ== dependencies: p-try "^2.0.0" -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= - dependencies: - p-limit "^1.1.0" - p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" @@ -6235,28 +5276,30 @@ p-locate@^3.0.0: dependencies: p-limit "^2.0.0" +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + p-map@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA== p-map@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.0.0.tgz#be18c5a5adeb8e156460651421aceca56c213a50" - integrity sha512-GO107XdrSUmtHxVoi60qc9tUl/KkNKm+X2CF4P9amalpGxv5YqVPJNfSb0wcA+syCopkZvYYIzW8OVTQW59x/w== + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== -p-timeout@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" - integrity sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA== +p-timeout@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.1.0.tgz#198c1f503bb973e9b9727177a276c80afd6851f3" + integrity sha512-C27DYI+tCroT8J8cTEyySGydl2B7FlxrGNF5/wmMbl1V+jeehUCzEE/BVgzRebdm2K3ZitKOKx8YbdFumDyYmw== dependencies: p-finally "^1.0.0" -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= - p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -6284,48 +5327,6 @@ packet-reader@1.0.0: resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74" integrity sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ== -pacote@^9.1.0, pacote@^9.2.3, pacote@^9.5.0: - version "9.5.0" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-9.5.0.tgz#85f3013a3f6dd51c108b0ccabd3de8102ddfaeda" - integrity sha512-aUplXozRbzhaJO48FaaeClmN+2Mwt741MC6M3bevIGZwdCaP7frXzbUOfOWa91FPHoLITzG0hYaKY363lxO3bg== - dependencies: - bluebird "^3.5.3" - cacache "^11.3.2" - figgy-pudding "^3.5.1" - get-stream "^4.1.0" - glob "^7.1.3" - lru-cache "^5.1.1" - make-fetch-happen "^4.0.1" - minimatch "^3.0.4" - minipass "^2.3.5" - mississippi "^3.0.0" - mkdirp "^0.5.1" - normalize-package-data "^2.4.0" - npm-package-arg "^6.1.0" - npm-packlist "^1.1.12" - npm-pick-manifest "^2.2.3" - npm-registry-fetch "^3.8.0" - osenv "^0.1.5" - promise-inflight "^1.0.1" - promise-retry "^1.1.1" - protoduck "^5.0.1" - rimraf "^2.6.2" - safe-buffer "^5.1.2" - semver "^5.6.0" - ssri "^6.0.1" - tar "^4.4.8" - unique-filename "^1.1.1" - which "^1.3.1" - -parallel-transform@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" - integrity sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY= - dependencies: - cyclist "~0.2.2" - inherits "^2.0.3" - readable-stream "^2.1.5" - parse-json@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" @@ -6339,11 +5340,6 @@ parse-numeric-range@^0.0.2: resolved "https://registry.yarnpkg.com/parse-numeric-range/-/parse-numeric-range-0.0.2.tgz#b4f09d413c7adbcd987f6e9233c7b4b210c938e4" integrity sha1-tPCdQTx6282Yf26SM8e0shDJOOQ= -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= - parse-torrent@^6.0.0, parse-torrent@^6.1.2: version "6.1.2" resolved "https://registry.yarnpkg.com/parse-torrent/-/parse-torrent-6.1.2.tgz#99da5bdd23435a1cb7e8e7a63847c4efb21b1956" @@ -6378,10 +5374,10 @@ parseuri@0.0.5: dependencies: better-assert "~1.0.0" -parseurl@~1.3.1, parseurl@~1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" - integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M= +parseurl@~1.3.1, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== pascalcase@^0.1.1: version "0.1.1" @@ -6410,7 +5406,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-is-inside@^1.0.1, path-is-inside@^1.0.2, path-is-inside@~1.0.2: +path-is-inside@^1.0.1, path-is-inside@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= @@ -6482,20 +5478,20 @@ pg-pool@^2.0.4: integrity sha512-hod2zYQxM8Gt482q+qONGTYcg/qVcV32VHVPtktbBJs0us3Dj7xibISw0BAAXVMCzt8A/jhfJvpZaxUlqtqs0g== pg-types@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.0.0.tgz#038ddc302a0340efcdb46d0581cc7caa2303cbba" - integrity sha512-THUD7gQll5tys+5eQ8Rvs7DjHiIC3bLqixk3gMN9Hu8UrCBAOjf35FoI39rTGGc3lM2HU/R+Knpxvd11mCwOMA== + version "2.0.1" + resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.0.1.tgz#b8585a37f2a9c7b386747e44574799549e5f4933" + integrity sha512-b7y6QM1VF5nOeX9ukMQ0h8a9z89mojrBHXfJeSug4mhL0YpxNBm83ot2TROyoAmX/ZOX3UbwVO4EbH7i1ZZNiw== dependencies: pg-int8 "1.0.1" postgres-array "~2.0.0" postgres-bytea "~1.0.0" - postgres-date "~1.0.0" + postgres-date "~1.0.4" postgres-interval "^1.1.0" pg@^7.4.1: - version "7.9.0" - resolved "https://registry.yarnpkg.com/pg/-/pg-7.9.0.tgz#04f0024d810544463f47dbb5aada2486aa7dcc36" - integrity sha512-GkzteBFpsIoIBCSuomqik3IGvhqAtTr32jclR24RmUg170Jrn6ypwR97YalFHrsE1iaW8T0aAH13dmij8QUQ0g== + version "7.11.0" + resolved "https://registry.yarnpkg.com/pg/-/pg-7.11.0.tgz#a8b9ae9cf19199b7952b72957573d0a9c5e67c0c" + integrity sha512-YO4V7vCmEMGoF390LJaFaohWNKaA2ayoQOEZmiHVcAUF+YsRThpf/TaKCgSvsSE7cDm37Q/Cy3Gz41xiX/XjTw== dependencies: buffer-writer "2.0.0" packet-reader "1.0.0" @@ -6541,12 +5537,12 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= -pkg-dir@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" - integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== +pkg-dir@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: - find-up "^3.0.0" + find-up "^4.0.0" pkginfo@0.3.x: version "0.3.1" @@ -6590,10 +5586,10 @@ postgres-bytea@~1.0.0: resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" integrity sha1-AntTPAqokOJtFy1Hz5zOzFIazTU= -postgres-date@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.3.tgz#e2d89702efdb258ff9d9cee0fe91bd06975257a8" - integrity sha1-4tiXAu/bJY/52c7g/pG9BpdSV6g= +postgres-date@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.4.tgz#1c2728d62ef1bff49abdd35c1f86d4bdf118a728" + integrity sha512-bESRvKVuTrjoBluEcpv2346+6kgB7UlnqWZsnbnCccTNq/pqfj1j6oBaN5+b/NrDXepYUT/HKadqv3iS9lJuVA== postgres-interval@^1.1.0: version "1.2.0" @@ -6602,10 +5598,10 @@ postgres-interval@^1.1.0: dependencies: xtend "^4.0.0" -prebuild-install@^5.2.5: - version "5.2.5" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.2.5.tgz#c7485911fe98950b7f7cd15bb9daee11b875cc44" - integrity sha512-6uZgMVg7yDfqlP5CPurVhtq3hUKBFNufiar4J5hZrlHTo59DDBEtyxw01xCdFss9j0Zb9+qzFVf/s4niayba3w== +prebuild-install@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.0.tgz#58b4d8344e03590990931ee088dd5401b03004c8" + integrity sha512-aaLVANlj4HgZweKttFNUVNRxDukytuIuxeK2boIMHjagNJCiVKWFsKF4tCE3ql3GbrD2tExPQ7/pwtEJcHNZeg== dependencies: detect-libc "^1.0.3" expand-template "^2.0.3" @@ -6644,19 +5640,6 @@ progress@^1.1.8: resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" integrity sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74= -promise-inflight@^1.0.1, promise-inflight@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" - integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= - -promise-retry@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-1.1.1.tgz#6739e968e3051da20ce6497fb2b50f6911df3d6d" - integrity sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0= - dependencies: - err-code "^1.0.0" - retry "^0.10.0" - promise.prototype.finally@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/promise.prototype.finally/-/promise.prototype.finally-3.1.0.tgz#66f161b1643636e50e7cf201dc1b84a857f3864e" @@ -6687,29 +5670,10 @@ prompt@^1.0.0: utile "0.3.x" winston "2.1.x" -promzard@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee" - integrity sha1-JqXW7ox97kyxIggwWs+5O6OCqe4= - dependencies: - read "1" - -property-expr@^1.5.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-1.5.1.tgz#22e8706894a0c8e28d58735804f6ba3a3673314f" - integrity sha512-CGuc0VUTGthpJXL36ydB6jnbyOf/rAHFvmVrJlH+Rg0DqqLFQGAP6hIaxD/G0OAmBJPhXDHuEJigrp0e0wFV6g== - -proto-list@~1.2.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" - integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= - -protoduck@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/protoduck/-/protoduck-5.0.1.tgz#03c3659ca18007b69a50fd82a7ebcc516261151f" - integrity sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg== - dependencies: - genfun "^5.0.0" +property-expr@^1.5.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-1.5.1.tgz#22e8706894a0c8e28d58735804f6ba3a3673314f" + integrity sha512-CGuc0VUTGthpJXL36ydB6jnbyOf/rAHFvmVrJlH+Rg0DqqLFQGAP6hIaxD/G0OAmBJPhXDHuEJigrp0e0wFV6g== proxy-addr@~1.0.10: version "1.0.10" @@ -6719,18 +5683,13 @@ proxy-addr@~1.0.10: forwarded "~0.1.0" ipaddr.js "1.0.5" -proxy-addr@~2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93" - integrity sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA== +proxy-addr@~2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34" + integrity sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ== dependencies: forwarded "~0.1.2" - ipaddr.js "1.8.0" - -prr@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" - integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + ipaddr.js "1.9.0" pseudomap@^1.0.2: version "1.0.2" @@ -6738,14 +5697,14 @@ pseudomap@^1.0.2: integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= psl@^1.1.24: - version "1.1.31" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184" - integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw== + version "1.1.32" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.32.tgz#3f132717cf2f9c169724b2b6caf373cf694198db" + integrity sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g== pstree.remy@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.6.tgz#73a55aad9e2d95814927131fbf4dc1b62d259f47" - integrity sha512-NdF35+QsqD7EgNEI5mkI/X+UwaxVEbQaz9f4IooEmMUv6ZPmlTQYGjBPJGgrlzNdjSvIy4MWMg6Q6vCgBO2K+w== + version "1.1.7" + resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.7.tgz#c76963a28047ed61542dc361aa26ee55a7fa15f3" + integrity sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A== pump@^1.0.0: version "1.0.3" @@ -6790,44 +5749,25 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -qrcode-terminal@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz#bb5b699ef7f9f0505092a3748be4464fe71b5819" - integrity sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ== - qs@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/qs/-/qs-4.0.0.tgz#c31d9b74ec27df75e543a86c78728ed8d4623607" integrity sha1-wx2bdOwn33XlQ6hseHKO2NRiNgc= -qs@6.5.2, qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - -qs@^6.5.1: +qs@6.7.0, qs@^6.5.1: version "6.7.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== -query-string@^6.2.0: - version "6.4.2" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.4.2.tgz#8be1dbd105306aebf86022144f575a29d516b713" - integrity sha512-DfJqAen17LfLA3rQ+H5S4uXphrF+ANU1lT2ijds4V/Tj4gZxA3gx5/tg1bz7kYCmwna7LyJNCYqO7jNRzo3aLw== - dependencies: - decode-uri-component "^0.2.0" - split-on-first "^1.0.0" - strict-uri-encode "^2.0.0" - -qw@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/qw/-/qw-1.0.1.tgz#efbfdc740f9ad054304426acb183412cc8b996d4" - integrity sha1-77/cdA+a0FQwRCassYNBLMi5ltQ= +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== random-access-file@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/random-access-file/-/random-access-file-2.1.0.tgz#7b3b6623d47e2f89282e77f0c9c9ae6da3cd9039" - integrity sha512-W2hY3DboLETMclybTVzyqCNVKx1MjqUwZPzkpkkMD2t9mbGEtkV2SKWPqAJ/FTrAtnWB7aGwl0NDUS82da0KdQ== + version "2.1.2" + resolved "https://registry.yarnpkg.com/random-access-file/-/random-access-file-2.1.2.tgz#eeb32e50b9831f32060516862381152ae4e05aa6" + integrity sha512-dZo7HEcEPbZ/6XLXC4GXypiWvFbXVkdeMrJTi0B94pBJwddt/AvJh8GaQhso6KGYROGYCI/VWdHbmRDtkwT9pQ== dependencies: mkdirp "^0.5.1" random-access-storage "^1.1.1" @@ -6851,10 +5791,10 @@ randombytes@^2.0.3, randombytes@^2.0.5: dependencies: safe-buffer "^5.1.0" -range-parser@^1.2.0, range-parser@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" - integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= +range-parser@^1.2.0, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== range-parser@~1.0.3: version "1.0.3" @@ -6868,14 +5808,14 @@ range-slice-stream@^2.0.0: dependencies: readable-stream "^3.0.2" -raw-body@2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3" - integrity sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw== +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== dependencies: - bytes "3.0.0" - http-errors "1.6.3" - iconv-lite "0.4.23" + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" unpipe "1.0.0" rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: @@ -6898,50 +5838,6 @@ rdf-canonize@^0.2.1: node-forge "^0.7.1" semver "^5.4.1" -read-cmd-shim@^1.0.1, read-cmd-shim@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.1.tgz#2d5d157786a37c055d22077c32c53f8329e91c7b" - integrity sha1-LV0Vd4ajfAVdIgd8MsU/gynpHHs= - dependencies: - graceful-fs "^4.1.2" - -read-installed@~4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/read-installed/-/read-installed-4.0.3.tgz#ff9b8b67f187d1e4c29b9feb31f6b223acd19067" - integrity sha1-/5uLZ/GH0eTCm5/rMfayI6zRkGc= - dependencies: - debuglog "^1.0.1" - read-package-json "^2.0.0" - readdir-scoped-modules "^1.0.0" - semver "2 || 3 || 4 || 5" - slide "~1.1.3" - util-extend "^1.0.1" - optionalDependencies: - graceful-fs "^4.1.2" - -"read-package-json@1 || 2", read-package-json@^2.0.0, read-package-json@^2.0.13: - version "2.0.13" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.0.13.tgz#2e82ebd9f613baa6d2ebe3aa72cefe3f68e41f4a" - integrity sha512-/1dZ7TRZvGrYqE0UAfN6qQb5GYBsNcqS1C0tNK601CFOJmtHI7NIGXwetEPU/OtoFHZL3hDxm4rolFFVE9Bnmg== - dependencies: - glob "^7.1.1" - json-parse-better-errors "^1.0.1" - normalize-package-data "^2.0.0" - slash "^1.0.0" - optionalDependencies: - graceful-fs "^4.1.2" - -read-package-tree@^5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/read-package-tree/-/read-package-tree-5.2.2.tgz#4b6a0ef2d943c1ea36a578214c9a7f6b7424f7a8" - integrity sha512-rW3XWUUkhdKmN2JKB4FL563YAgtINifso5KShykufR03nJ5loGFlkUMe1g/yxmqX073SoYYTsgXu7XdDinKZuA== - dependencies: - debuglog "^1.0.1" - dezalgo "^1.0.0" - once "^1.3.0" - read-package-json "^2.0.0" - readdir-scoped-modules "^1.0.0" - read-pkg@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-4.0.1.tgz#963625378f3e1c4d48c85872b5a6ec7d5d093237" @@ -6951,27 +5847,24 @@ read-pkg@^4.0.1: parse-json "^4.0.0" pify "^3.0.0" -read@1, read@1.0.x, read@~1.0.1, read@~1.0.7: +read-pkg@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.1.1.tgz#5cf234dde7a405c90c88a519ab73c467e9cb83f5" + integrity sha512-dFcTLQi6BZ+aFUaICg7er+/usEoqFdQxiEBsEMNGoipenihtxxtdrQuBXvyANCEI8VuUIVYFgeHGx9sLLvim4w== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^4.0.0" + type-fest "^0.4.1" + +read@1.0.x: version "1.0.7" resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= dependencies: mute-stream "~0.0.4" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.3, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.2, readable-stream@^2.3.4, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-stream@1.1.x, "readable-stream@>=1.1.13-1 <1.2.0-0", readable-stream@^1.1.13-1, readable-stream@~1.1.10: +readable-stream@1.1.x, "readable-stream@>=1.1.13-1 <1.2.0-0", readable-stream@^1.1.13-1: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= @@ -6991,10 +5884,23 @@ readable-stream@1.1.x, "readable-stream@>=1.1.13-1 <1.2.0-0", readable-stream@^1 isarray "0.0.1" string_decoder "~0.10.x" +readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.3, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.2, readable-stream@^2.3.4, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.3.0.tgz#cb8011aad002eb717bf040291feba8569c986fb9" - integrity sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw== + version "3.4.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" + integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -7007,16 +5913,6 @@ readable-wrap@^1.0.0: dependencies: readable-stream "^1.1.13-1" -readdir-scoped-modules@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz#9fafa37d286be5d92cbaebdee030dc9b5f406747" - integrity sha1-n6+jfShr5dksuuve4DDcm19AZ0c= - dependencies: - debuglog "^1.0.1" - dezalgo "^1.0.0" - graceful-fs "^4.1.2" - once "^1.3.0" - readdirp@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" @@ -7040,11 +5936,16 @@ record-cache@^1.0.2: resolved "https://registry.yarnpkg.com/record-cache/-/record-cache-1.1.0.tgz#f8a467a691a469584b26e88d36b18afdb3932037" integrity sha512-u8rbtLEJV7HRacl/ZYwSBFD8NFyB3PfTTfGLP37IW3hftQCwu6z4Q2RLyxo1YJUNRTEzJfpLpGwVuEYdaIkG9Q== -redis-commands@1.4.0, redis-commands@^1.2.0: +redis-commands@1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.4.0.tgz#52f9cf99153efcce56a8f86af986bd04e988602f" integrity sha512-cu8EF+MtkwI4DLIT0x9P8qNTLFhQD4jLfxLR0cCNkeGzs87FN6879JOJwNQR/1zD7aSYNbU0hgsV9zGY71Itvw== +redis-commands@^1.2.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.5.0.tgz#80d2e20698fe688f227127ff9e5164a7dd17e785" + integrity sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg== + redis-errors@^1.0.0, redis-errors@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" @@ -7071,20 +5972,20 @@ redis@^2.8.0: redis-commands "^1.2.0" redis-parser "^2.6.0" -referrer-policy@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/referrer-policy/-/referrer-policy-1.1.0.tgz#35774eb735bf50fb6c078e83334b472350207d79" - integrity sha1-NXdOtzW/UPtsB46DM0tHI1AgfXk= +referrer-policy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/referrer-policy/-/referrer-policy-1.2.0.tgz#b99cfb8b57090dc454895ef897a4cc35ef67a98e" + integrity sha512-LgQJIuS6nAy1Jd88DCQRemyE3mS+ispwlqMk3b0yjZ257fI1v9c+/p6SD5gP5FGyXUIgrNOAfmyioHwZtYv2VA== reflect-metadata@^0.1.12: version "0.1.13" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== -regenerator-runtime@^0.12.0: - version "0.12.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de" - integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg== +regenerator-runtime@^0.13.2: + version "0.13.2" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447" + integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA== regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" @@ -7135,7 +6036,7 @@ repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -request@^2.81.0, request@^2.83.0, request@^2.87.0, request@^2.88.0, request@~2.88.0: +request@^2.81.0, request@^2.83.0, request@~2.88.0: version "2.88.0" resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== @@ -7171,6 +6072,11 @@ require-main-filename@^1.0.1: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + require-uncached@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" @@ -7179,14 +6085,6 @@ require-uncached@^1.0.2: caller-path "^0.1.0" resolve-from "^1.0.0" -resolve-dir@^1.0.0, resolve-dir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" - integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M= - dependencies: - expand-tilde "^2.0.0" - global-modules "^1.0.0" - resolve-from@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" @@ -7202,11 +6100,6 @@ resolve-from@^3.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" integrity sha1-six699nWiBvItuZTM17rywoYh0g= -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - resolve-pkg@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/resolve-pkg/-/resolve-pkg-1.0.0.tgz#e19a15e78aca2e124461dc92b2e3943ef93494d9" @@ -7220,9 +6113,9 @@ resolve-url@^0.2.1: integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= resolve@^1.10.0, resolve@^1.3.2: - version "1.10.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba" - integrity sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg== + version "1.11.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e" + integrity sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw== dependencies: path-parse "^1.0.6" @@ -7254,22 +6147,12 @@ retry-as-promised@^3.1.0: dependencies: any-promise "^1.3.0" -retry@^0.10.0: - version "0.10.1" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4" - integrity sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q= - -retry@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" - integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= - revalidator@0.1.x: version "0.1.8" resolved "https://registry.yarnpkg.com/revalidator/-/revalidator-0.1.8.tgz#fece61bfa0c1b52a206bd6b18198184bdd523a3b" integrity sha1-/s5hv6DBtSoga9axgZgYS91SOjs= -rimraf@2, rimraf@2.x.x, rimraf@^2.2.8, rimraf@^2.4.2, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@~2.6.2: +rimraf@2.x.x, rimraf@^2.2.8, rimraf@^2.4.2, rimraf@^2.6.1, rimraf@~2.6.2: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== @@ -7298,13 +6181,6 @@ run-parallel@^1.0.0, run-parallel@^1.1.2, run-parallel@^1.1.6: resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== -run-queue@^1.0.0, run-queue@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" - integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= - dependencies: - aproba "^1.1.1" - run-series@^1.0.2: version "1.1.8" resolved "https://registry.yarnpkg.com/run-series/-/run-series-1.1.8.tgz#2c4558f49221e01cd6371ff4e0a1e203e460fc36" @@ -7321,9 +6197,9 @@ rx-lite@^3.1.2: integrity sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI= rxjs@^6.3.3: - version "6.4.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.4.0.tgz#f3bb0fe7bda7fb69deac0c16f17b50b0b8790504" - integrity sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw== + version "6.5.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.2.tgz#2e35ce815cd46d84d02a209fb4e5921e051dbec7" + integrity sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg== dependencies: tslib "^1.9.0" @@ -7345,9 +6221,9 @@ safe-regex@^1.1.0: integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sass-lint@^1.12.1: - version "1.12.1" - resolved "https://registry.yarnpkg.com/sass-lint/-/sass-lint-1.12.1.tgz#630f69c216aa206b8232fb2aa907bdf3336b6d83" - integrity sha1-Yw9pwhaqIGuCMvsqqQe98zNrbYM= + version "1.13.1" + resolved "https://registry.yarnpkg.com/sass-lint/-/sass-lint-1.13.1.tgz#5fd2b2792e9215272335eb0f0dc607f61e8acc8f" + integrity sha512-DSyah8/MyjzW2BWYmQWekYEKir44BpLqrCFsgs9iaWiVTcwZfwXHF586hh3D1n+/9ihUNMfd8iHAyb9KkGgs7Q== dependencies: commander "^2.8.1" eslint "^2.7.0" @@ -7391,7 +6267,7 @@ semver-diff@^2.0.0: dependencies: semver "^5.0.3" -"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", "semver@^2.3.0 || 3.x || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.0: version "5.7.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== @@ -7401,10 +6277,10 @@ semver@4.3.2: resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" integrity sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c= -semver@~5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" - integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= +semver@^6.0.0, semver@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.1.tgz#53f53da9b30b2103cd4f15eab3a18ecbcb210c9b" + integrity sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ== send@0.13.1: version "0.13.1" @@ -7442,10 +6318,10 @@ send@0.13.2: range-parser "~1.0.3" statuses "~1.2.1" -send@0.16.2: - version "0.16.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" - integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw== +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== dependencies: debug "2.6.9" depd "~1.1.2" @@ -7454,12 +6330,12 @@ send@0.16.2: escape-html "~1.0.3" etag "~1.8.1" fresh "0.5.2" - http-errors "~1.6.2" - mime "1.4.1" - ms "2.0.0" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" on-finished "~2.3.0" - range-parser "~1.2.0" - statuses "~1.4.0" + range-parser "~1.2.1" + statuses "~1.5.0" sequelize-pool@^1.0.2: version "1.0.2" @@ -7475,10 +6351,10 @@ sequelize-typescript@1.0.0-beta.2: dependencies: glob "7.1.2" -sequelize@5.7.4: - version "5.7.4" - resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-5.7.4.tgz#1631faadff65f3a345b9757fca60429c65ba8e57" - integrity sha512-CaVYpAgZQEsGDuZ+Oq6uIZy4pxQxscotuh5UGIaFRa0VkTIgV0IiF7vAhSv+1Wn+NvhKCvgJJ85M34BP3AdGNg== +sequelize@5.8.7: + version "5.8.7" + resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-5.8.7.tgz#3af2fd6051277d5fb5b32054c7f8aac4c4a04107" + integrity sha512-1rubZM8fAyCt5ipyS+3HJ3Jbmb8WesLdPJ3jIbTD+78EbuPZILFEA5fK0mliVRBx7oM7oPULeVX0lxSRXBV1jw== dependencies: bluebird "^3.5.0" cls-bluebird "^2.1.0" @@ -7496,15 +6372,15 @@ sequelize@5.7.4: validator "^10.11.0" wkx "^0.4.6" -serve-static@1.13.2: - version "1.13.2" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" - integrity sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw== +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== dependencies: encodeurl "~1.0.2" escape-html "~1.0.3" - parseurl "~1.3.2" - send "0.16.2" + parseurl "~1.3.3" + send "0.17.1" serve-static@~1.10.2: version "1.10.3" @@ -7540,32 +6416,23 @@ set-value@^2.0.0: is-plain-object "^2.0.3" split-string "^3.0.1" -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - -sha@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/sha/-/sha-2.0.1.tgz#6030822fbd2c9823949f8f72ed6411ee5cf25aae" - integrity sha1-YDCCL70smCOUn49y7WQR7lzyWq4= - dependencies: - graceful-fs "^4.1.2" - readable-stream "^2.0.2" +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== sharp@^0.22.0: - version "0.22.0" - resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.22.0.tgz#cf4cfcb019941fd06ac24555d9f5bc84536d29be" - integrity sha512-yInpiWYvVbE0hJylso2Q2A7QaYFBxGdSlVVHGeUf1F9JsQNAUpmaqdnX54TImgKbSCy9mQpEAoGm1pcKCZhCsQ== + version "0.22.1" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.22.1.tgz#a67c0e75567f03dd5a7861b901fec04072c5b0f4" + integrity sha512-lXzSk/FL5b/MpWrT1pQZneKe25stVjEbl6uhhJcTULm7PhmJgKKRbTDM/vtjyUuC/RLqL2PRyC4rpKwbv3soEw== dependencies: - bindings "^1.5.0" - color "^3.1.0" + color "^3.1.1" detect-libc "^1.0.3" fs-copy-file-sync "^1.1.1" - nan "^2.13.1" + nan "^2.13.2" npmlog "^4.1.2" - prebuild-install "^5.2.5" - semver "^5.6.0" + prebuild-install "^5.3.0" + semver "^6.0.0" simple-get "^3.0.3" tar "^4.4.8" tunnel-agent "^0.6.0" @@ -7621,9 +6488,9 @@ simple-get@^3.0.0, simple-get@^3.0.1, simple-get@^3.0.3: simple-concat "^1.0.0" simple-git@^1.85.0: - version "1.110.0" - resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-1.110.0.tgz#54eb179089d055a7783d32399246cebc9d9933e9" - integrity sha512-UYY0rQkknk0P5eb+KW+03F4TevZ9ou0H+LoGaj7iiVgpnZH4wdj/HTViy/1tNNkmIPcmtxuBqXWiYt2YwlRKOQ== + version "1.113.0" + resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-1.113.0.tgz#668989728a1e9cf4ec6c72b69ea2eecc93489bea" + integrity sha512-i9WVsrK2u0G/cASI9nh7voxOk9mhanWY9eGtWBDSYql6m49Yk5/Fan6uZsDr/xmzv8n+eQ8ahKCoEr8cvU3h+g== dependencies: debug "^4.0.1" @@ -7664,39 +6531,24 @@ simple-websocket@^7.0.1: ws "^6.0.0" sitemap@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/sitemap/-/sitemap-2.1.0.tgz#1633cb88c196d755ad94becfb1c1bcacc6d3425a" - integrity sha512-AkfA7RDVCITQo+j5CpXsMJlZ/8ENO2NtgMHYIh+YMvex2Hao/oe3MQgNa03p0aWY6srCfUA1Q02OgiWCAiuccA== + version "2.2.0" + resolved "https://registry.yarnpkg.com/sitemap/-/sitemap-2.2.0.tgz#98b8502762c5d7e8c77c9be5061dce85b326f1b0" + integrity sha512-9Zoi3UBhSIt5jWENDRUbzsqLMJ+Fha3P2aQ2PRghmh0FOivtHsC4FAJdkAEKHvATajd74BWp/57Yh7kz/UA53Q== dependencies: lodash "^4.17.10" url-join "^4.0.0" xmlbuilder "^10.0.0" -slash@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" - integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= - -slash@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" - integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== slice-ansi@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= -slide@^1.1.6, slide@~1.1.3, slide@~1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" - integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= - -smart-buffer@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.0.2.tgz#5207858c3815cc69110703c6b94e46c15634395d" - integrity sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw== - smtp-connection@2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/smtp-connection/-/smtp-connection-2.3.1.tgz#d169c8f1c9a73854134cdabe6fb818237dfc4fba" @@ -7836,35 +6688,6 @@ socket.io@^2.2.0: socket.io-client "2.2.0" socket.io-parser "~3.3.0" -socks-proxy-agent@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz#3c8991f3145b2799e70e11bd5fbc8b1963116386" - integrity sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg== - dependencies: - agent-base "~4.2.1" - socks "~2.3.2" - -socks@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.3.2.tgz#ade388e9e6d87fdb11649c15746c578922a5883e" - integrity sha512-pCpjxQgOByDHLlNqlnh/mNSAxIUkyBBuwwhTcV+enZGbDaClPvHdvm6uvOwZfFJkam7cGhBNbb4JxiP8UZkRvQ== - dependencies: - ip "^1.1.5" - smart-buffer "4.0.2" - -sorted-object@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/sorted-object/-/sorted-object-2.0.1.tgz#7d631f4bd3a798a24af1dffcfbfe83337a5df5fc" - integrity sha1-fWMfS9OnmKJK8d/8+/6DM3pd9fw= - -sorted-union-stream@~2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/sorted-union-stream/-/sorted-union-stream-2.1.3.tgz#c7794c7e077880052ff71a8d4a2dbb4a9a638ac7" - integrity sha1-x3lMfgd4gAUv9xqNSi27Sppjisc= - dependencies: - from2 "^1.3.0" - stream-iterate "^1.1.0" - source-map-resolve@^0.5.0: version "0.5.2" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" @@ -7877,9 +6700,9 @@ source-map-resolve@^0.5.0: urix "^0.1.0" source-map-support@^0.5.0, source-map-support@^0.5.6: - version "0.5.11" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.11.tgz#efac2ce0800355d026326a0ca23e162aeac9a4e2" - integrity sha512-//sajEx/fGL3iw6fltKMdPvy8kL3kJ2O3iuYlRoT3k9Kb4BjOoZ+BZzaNHeuaruSt+Kf3Zk9tnfAQg9/AJqUVQ== + version "0.5.12" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" + integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -7926,20 +6749,15 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz#81c0ce8f21474756148bbb5f3bfc0f36bf15d76e" - integrity sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g== + version "3.0.4" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz#75ecd1a88de8c184ef015eafb51b5b48bfd11bb1" + integrity sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA== speedometer@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/speedometer/-/speedometer-1.1.0.tgz#a30b13abda45687a1a76977012c060f2ac8a7934" integrity sha512-z/wAiTESw2XVPssY2XRcme4niTc4S5FkkJ4gknudtVoc33Zil8TdTxHy5torRcgqMqksJV2Yz8HQcvtbsnw0mQ== -split-on-first@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.0.0.tgz#648af4ce9a28fbcaadd43274455f298b55025fc6" - integrity sha512-mjA57TQtdWztVZ9THAjGNpgbuIrNfsNrGa5IyK94NoPaT4N14M+GI4jD7t4arLjFkYRQWdETC5RxFzLWouoB3A== - split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -7993,13 +6811,6 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -ssri@^6.0.0, ssri@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" - integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== - dependencies: - figgy-pudding "^3.5.1" - stack-trace@0.0.x: version "0.0.10" resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" @@ -8023,7 +6834,7 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -statuses@1, "statuses@>= 1.4.0 < 2": +statuses@1, "statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= @@ -8038,27 +6849,6 @@ statuses@~1.2.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.2.1.tgz#dded45cc18256d51ed40aec142489d5c61026d28" integrity sha1-3e1FzBglbVHtQK7BQkidXGECbSg= -statuses@~1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" - integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew== - -stream-each@^1.1.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" - integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== - dependencies: - end-of-stream "^1.1.0" - stream-shift "^1.0.0" - -stream-iterate@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/stream-iterate/-/stream-iterate-1.2.0.tgz#2bd7c77296c1702a46488b8ad41f79865eecd4e1" - integrity sha1-K9fHcpbBcCpGSIuK1B95hl7s1OE= - dependencies: - readable-stream "^2.1.5" - stream-shift "^1.0.0" - stream-shift@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" @@ -8109,11 +6899,6 @@ streamsearch@0.1.2: resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= -strict-uri-encode@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" - integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= - string-argv@^0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.0.2.tgz#dac30408690c21f3c3630a3ff3a05877bdcbd736" @@ -8136,6 +6921,15 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + string2compact@^1.1.1, string2compact@^1.2.5: version "1.3.0" resolved "https://registry.yarnpkg.com/string2compact/-/string2compact-1.3.0.tgz#22d946127b082d1203c51316af60117a337423c3" @@ -8172,11 +6966,6 @@ stringify-object@^3.2.2: is-obj "^1.0.1" is-regexp "^1.0.0" -stringify-package@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/stringify-package/-/stringify-package-1.0.0.tgz#e02828089333d7d45cd8c287c30aa9a13375081b" - integrity sha512-JIQqiWmLiEozOC0b0BtxZ/AOUtdUZHCBPgqIZ2kSJJqGwgb9neo44XdTHUC4HZSGqi03hOeB7W/E8rAlKnGe9g== - strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -8191,6 +6980,13 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" @@ -8206,15 +7002,6 @@ strip-json-comments@~1.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" integrity sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E= -summon-install@^0.4.3: - version "0.4.6" - resolved "https://registry.yarnpkg.com/summon-install/-/summon-install-0.4.6.tgz#25673446e8b92f8bc0afabc464aa7b73fe946bd5" - integrity sha512-xLiRo8z2srFItquk4VXGfC6AsELPmFCYev5pipARHVCikrgCBdjHMxs2ZGfVcsOOKwTgEL6bVFutf5yF43GBZw== - dependencies: - descrevit "^0.1.1" - dot-json "^1.0.3" - npm "*" - superagent@^3.8.3: version "3.8.3" resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.3.tgz#460ea0dbdb7d5b11bc4f78deba565f86a178e128" @@ -8304,10 +7091,10 @@ symbol-observable@^1.1.0: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== -synchronous-promise@^2.0.5: - version "2.0.7" - resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.7.tgz#3574b3d2fae86b145356a4b89103e1577f646fe3" - integrity sha512-16GbgwTmFMYFyQMLvtQjvNWh30dsFe1cAW5Fg1wm5+dg84L9Pe36mftsIRU95/W2YsISxsz/xq4VB23sqpgb/A== +synchronous-promise@^2.0.6: + version "2.0.9" + resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.9.tgz#b83db98e9e7ae826bf9c8261fd8ac859126c780a" + integrity sha512-LO95GIW16x69LuND1nuuwM4pjgFGupg7pZ/4lU86AmchPKrhk0o2tpMU2unXRrqo81iAFe1YJ0nAGEVwsrZAgg== table@^3.7.8: version "3.8.3" @@ -8344,27 +7131,18 @@ tar-stream@^1.1.2: to-buffer "^1.1.1" xtend "^4.0.0" -tar@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" - integrity sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE= - dependencies: - block-stream "*" - fstream "^1.0.2" - inherits "2" - tar@^4, tar@^4.4.8: - version "4.4.8" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d" - integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ== + version "4.4.10" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1" + integrity sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA== dependencies: chownr "^1.1.1" fs-minipass "^1.2.5" - minipass "^2.3.4" - minizlib "^1.1.1" + minipass "^2.3.5" + minizlib "^1.2.1" mkdirp "^0.5.0" safe-buffer "^5.1.2" - yallist "^3.0.2" + yallist "^3.0.3" term-size@^1.2.0: version "1.2.0" @@ -8404,7 +7182,7 @@ through2@^1.0.0: readable-stream ">=1.1.13-1 <1.2.0-0" xtend ">=4.0.0 <4.1.0-0" -through2@^2.0.0, through2@^2.0.3: +through2@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== @@ -8412,7 +7190,7 @@ through2@^2.0.0, through2@^2.0.3: readable-stream "~2.3.6" xtend "~4.0.1" -through@2, "through@>=2.2.7 <3", through@^2.3.6: +through@2, through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -8435,11 +7213,6 @@ timers-ext@^0.1.5: es5-ext "~0.10.46" next-tick "1" -tiny-relative-date@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz#fa08aad501ed730f31cc043181d995c39a935e07" - integrity sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A== - tmp@0.0.x: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -8498,6 +7271,11 @@ to-utf-8@^1.2.0: peek-stream "^1.1.1" stream-splicer "^1.3.1" +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + toposort-class@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/toposort-class/-/toposort-class-1.0.1.tgz#7ffd1f78c8be28c3ba45cd4e1a3f5ee193bd9988" @@ -8553,13 +7331,13 @@ triple-beam@^1.2.0, triple-beam@^1.3.0: resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== -ts-node@8.0.3: - version "8.0.3" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.0.3.tgz#aa60b836a24dafd8bf21b54766841a232fdbc641" - integrity sha512-2qayBA4vdtVRuDo11DEFSsD/SFsBXQBRZZhbRGSIkmYmVkWjULn/GGMdG10KVqkaGndljfaTD8dKjWgcejO8YA== +ts-node@8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.2.0.tgz#4a89754b00560bb24cd54526e1685fa38c45f240" + integrity sha512-m8XQwUurkbYqXrKqr3WHCW310utRNvV5OnRVeISeea7LoCWVcdfeB/Ntl8JYWFh+WRoUAdBgESrzKochQt7sMw== dependencies: arg "^4.1.0" - diff "^3.1.0" + diff "^4.0.1" make-error "^1.1.1" source-map-support "^0.5.6" yn "^3.0.0" @@ -8591,17 +7369,17 @@ tslint-eslint-rules@^5.3.1: tsutils "^3.0.0" tslint@^5.7.0: - version "5.15.0" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.15.0.tgz#6ffb180986d63afa1e531feb2a134dbf961e27d3" - integrity sha512-6bIEujKR21/3nyeoX2uBnE8s+tMXCQXhqMmaIPJpHmXJoBJPTLcI7/VHRtUwMhnLVdwLqqY3zmd8Dxqa5CVdJA== + version "5.17.0" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.17.0.tgz#f9f0ce2011d8e90debaa6e9b4975f24cd16852b8" + integrity sha512-pflx87WfVoYepTet3xLfDOLDm9Jqi61UXIKePOuca0qoAZyrGWonDG9VTbji58Fy+8gciUn8Bt7y69+KEVjc/w== dependencies: - babel-code-frame "^6.22.0" + "@babel/code-frame" "^7.0.0" builtin-modules "^1.1.1" chalk "^2.3.0" commander "^2.12.1" diff "^3.2.0" glob "^7.1.1" - js-yaml "^3.13.0" + js-yaml "^3.13.1" minimatch "^3.0.4" mkdirp "^0.5.1" resolve "^1.3.2" @@ -8617,9 +7395,9 @@ tsutils@^2.29.0: tslib "^1.8.1" tsutils@^3.0.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.9.1.tgz#2a40dc742943c71eca6d5c1994fcf999956be387" - integrity sha512-hrxVtLtPqQr//p8/msPT1X1UYXUjizqSit5d9AQ5k38TcV38NyecL5xODNxa73cLe/5sdiJ+w1FqzDhRBA/anA== + version "3.14.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.14.0.tgz#bf8d5a7bae5369331fa0f2b0a5a10bd7f7396c77" + integrity sha512-SmzGbB0l+8I0QwsPgjooFRaRvHLBLNYM8SeQ0k6rtNDru5sCGeLJcZdwilNndN+GysuFjF5EIYgN8GfFG6UeUw== dependencies: tslib "^1.8.1" @@ -8630,10 +7408,10 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -tv4@~1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/tv4/-/tv4-1.2.7.tgz#bd29389afc73ade49ae5f48142b5d544bf68d120" - integrity sha1-vSk4mvxzreSa5fSBQrXVRL9o0SA= +tv4@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/tv4/-/tv4-1.3.0.tgz#d020c846fadd50c855abb25ebaecc68fc10f7963" + integrity sha1-0CDIRvrdUMhVq7JeuuzGj8EPeWM= tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" @@ -8657,6 +7435,16 @@ type-detect@^4.0.0, type-detect@^4.0.5: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-fest@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1" + integrity sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ== + +type-fest@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.4.1.tgz#8bdf77743385d8a4f13ba95f610f5ccd68c728f8" + integrity sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw== + type-is@1.6.15: version "1.6.15" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410" @@ -8665,13 +7453,13 @@ type-is@1.6.15: media-typer "0.3.0" mime-types "~2.1.15" -type-is@^1.6.4, type-is@~1.6.16, type-is@~1.6.6: - version "1.6.16" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" - integrity sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q== +type-is@^1.6.4, type-is@~1.6.17, type-is@~1.6.18, type-is@~1.6.6: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== dependencies: media-typer "0.3.0" - mime-types "~2.1.18" + mime-types "~2.1.24" typedarray-to-buffer@^3.0.0: version "3.1.5" @@ -8686,14 +7474,9 @@ typedarray@^0.0.6: integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= typescript@^3.4.3: - version "3.4.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.3.tgz#0eb320e4ace9b10eadf5bc6103286b0f8b7c224f" - integrity sha512-FFgHdPt4T/duxx6Ndf7hwgMZZjZpB+U0nMNGVCYPq0rEzWKjEDobm4J6yb3CS7naZ0yURFqdw9Gwc7UOh/P9oQ== - -uid-number@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" - integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= + version "3.5.1" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.1.tgz#ba72a6a600b2158139c5dd8850f700e231464202" + integrity sha512-64HkdiRv1yYZsSe4xC1WVgamNigVYjlssIoaH2HcZF0+ijsk5YK2g0G34w9wJkze8+5ow4STd22AynfO6ZYYLw== uint64be@^2.0.2: version "2.0.2" @@ -8707,11 +7490,6 @@ ultron@1.0.x: resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" integrity sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po= -umask@^1.1.0, umask@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" - integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0= - undefsafe@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.2.tgz#225f6b9e0337663e0d8e7cfd686fc2836ccace76" @@ -8719,18 +7497,6 @@ undefsafe@^2.0.2: dependencies: debug "^2.2.0" -underscore-keypath@~0.0.22: - version "0.0.22" - resolved "https://registry.yarnpkg.com/underscore-keypath/-/underscore-keypath-0.0.22.tgz#48a528392bb6efc424be1caa56da4b5faccf264d" - integrity sha1-SKUoOSu278QkvhyqVtpLX6zPJk0= - dependencies: - underscore "*" - -underscore@*: - version "1.9.1" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" - integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== - union-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" @@ -8746,20 +7512,6 @@ uniq@^1.0.1: resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= -unique-filename@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" - integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== - dependencies: - unique-slug "^2.0.0" - -unique-slug@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.1.tgz#5e9edc6d1ce8fb264db18a507ef9bd8544451ca6" - integrity sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg== - dependencies: - imurmurhash "^0.1.4" - unique-string@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a" @@ -8800,7 +7552,7 @@ upath@^1.1.1: resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q== -update-notifier@^2.3.0, update-notifier@^2.5.0: +update-notifier@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.5.0.tgz#d0744593e13f161e406acb1d9408b72cad08aff6" integrity sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw== @@ -8892,11 +7644,6 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util-extend@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/util-extend/-/util-extend-1.0.3.tgz#a7c216d267545169637b3b6edc6ca9119e2ff93f" - integrity sha1-p8IW0mdUUWljeztu3GypEZ4v+T8= - util.promisify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" @@ -8947,7 +7694,7 @@ uuid@^3.1.0, uuid@^3.2.1, uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== -validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: +validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== @@ -8955,18 +7702,16 @@ validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -validate-npm-package-name@^3.0.0, validate-npm-package-name@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" - integrity sha1-X6kS2B630MdK/BQN5zF/DKffQ34= - dependencies: - builtins "^1.0.3" - -validator@^10.0.0, validator@^10.11.0, validator@^10.2.0, validator@^10.4.0: +validator@^10.0.0, validator@^10.11.0, validator@^10.4.0: version "10.11.0" resolved "https://registry.yarnpkg.com/validator/-/validator-10.11.0.tgz#003108ea6e9a9874d31ccc9e5006856ccd76b228" integrity sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw== +validator@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-11.0.0.tgz#fb10128bfb1fd14ce4ed36b79fc94289eae70667" + integrity sha512-+wnGLYqaKV2++nUv60uGzUJyJQwYVOin6pn1tgEiFCeCQO60yeu3Og9/yPccbBX574kxIcEJicogkzx6s6eyag== + vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -9000,13 +7745,6 @@ videostream@^2.5.1: pump "^3.0.0" range-slice-stream "^2.0.0" -wcwidth@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" - integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= - dependencies: - defaults "^1.0.3" - webfinger.js@^2.6.6: version "2.7.0" resolved "https://registry.yarnpkg.com/webfinger.js/-/webfinger.js-2.7.0.tgz#403354a14a65aeeba64c1408c18a387487cea106" @@ -9070,7 +7808,7 @@ which-pm-runs@^1.0.0: resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= -which@1, which@1.3.1, which@^1.1.1, which@^1.2.10, which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1: +which@1.3.1, which@^1.1.1, which@^1.2.10, which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -9144,13 +7882,6 @@ wordwrap@~1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= -worker-farm@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.6.0.tgz#aecc405976fab5a95526180846f0dba288f3a4a0" - integrity sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ== - dependencies: - errno "~0.1.7" - wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" @@ -9167,15 +7898,24 @@ wrap-ansi@^3.0.1: string-width "^2.1.1" strip-ansi "^4.0.0" +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write-file-atomic@^2.0.0, write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.2.tgz#a7181706dfba17855d221140a9c06e15fcdd87b9" - integrity sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g== +write-file-atomic@^2.0.0: + version "2.4.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" + integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== dependencies: graceful-fs "^4.1.11" imurmurhash "^0.1.4" @@ -9203,6 +7943,13 @@ ws@^6.0.0: dependencies: async-limiter "~1.0.0" +ws@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.0.0.tgz#79351cbc3f784b3c20d0821baf4b4ff809ffbf51" + integrity sha512-cknCal4k0EAOrh1SHHPPWWh4qm93g1IuGGGwBjWkXmCG7LsDtL8w9w+YVfaF+KSVwiHQKDIMsSLBVftKf9d1pg== + dependencies: + async-limiter "^1.0.0" + ws@~6.1.0: version "6.1.4" resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9" @@ -9231,9 +7978,9 @@ xhr2@^0.1.4: integrity sha1-f4dliEdxbbUCYyOBL4GMras4el8= xliff@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/xliff/-/xliff-4.3.0.tgz#de08098a63ead8afde0bae83f059b8cbf3badaf1" - integrity sha512-YDGHMdgmzDOjDm9ys+CdesxC9NBaxOcARR8fdJKHS8qY+XbW6g0BAaRBj0ssC1H9s5lrY0m2uqLCu0HB58K2ag== + version "4.3.1" + resolved "https://registry.yarnpkg.com/xliff/-/xliff-4.3.1.tgz#ae82d1c6283014aa7506c9957bcb3f95ed1ce505" + integrity sha512-SERrOPuKZ/5XyEiv+cXgIjidQq4vp6HYS0yBS2GwC1TDJiREFaUIin3qg4OBlD6jdFWdPOMwkXGackAvgZ6+LQ== dependencies: xml-js "1.6.11" @@ -9287,11 +8034,6 @@ xmlhttprequest-ssl@~1.5.4: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= -y18n@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" - integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= - "y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" @@ -9302,12 +8044,20 @@ yallist@^2.1.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= -yallist@^3.0.0, yallist@^3.0.2: +yallist@^3.0.0, yallist@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== -yargs-parser@11.1.1, yargs-parser@^11.1.1: +yargs-parser@13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.0.0.tgz#3fc44f3e76a8bdb1cc3602e860108602e5ccde8b" + integrity sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^11.1.1: version "11.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ== @@ -9315,6 +8065,14 @@ yargs-parser@11.1.1, yargs-parser@^11.1.1: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^13.0.0, yargs-parser@^13.1.0: + version "13.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.0.tgz#7016b6dd03e28e1418a510e258be4bff5a31138f" + integrity sha512-Yq+32PrijHRri0vVKQEm+ys8mbqWjLiwQkMFNXEENutzLPP0bE4Lcd4iA3OQY5HF+GD3xXxf0MEHb8E4/SA3AA== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^8.0.0: version "8.1.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950" @@ -9322,13 +8080,6 @@ yargs-parser@^8.0.0: dependencies: camelcase "^4.1.0" -yargs-parser@^9.0.2: - version "9.0.2" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" - integrity sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc= - dependencies: - camelcase "^4.1.0" - yargs-unparser@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.5.0.tgz#f2bb2a7e83cbc87bb95c8e572828a06c9add6e0d" @@ -9338,7 +8089,24 @@ yargs-unparser@1.5.0: lodash "^4.17.11" yargs "^12.0.5" -yargs@12.0.5, yargs@^12.0.1, yargs@^12.0.5: +yargs@13.2.2: + version "13.2.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.2.tgz#0c101f580ae95cea7f39d927e7770e3fdc97f993" + integrity sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA== + dependencies: + cliui "^4.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + os-locale "^3.1.0" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.0.0" + +yargs@^12.0.1, yargs@^12.0.5: version "12.0.5" resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== @@ -9356,23 +8124,22 @@ yargs@12.0.5, yargs@^12.0.1, yargs@^12.0.5: y18n "^3.2.1 || ^4.0.0" yargs-parser "^11.1.1" -yargs@^11.0.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77" - integrity sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A== +yargs@^13.2.2: + version "13.2.4" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83" + integrity sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg== dependencies: - cliui "^4.0.0" - decamelize "^1.1.1" - find-up "^2.1.0" - get-caller-file "^1.0.1" - os-locale "^2.0.0" + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + os-locale "^3.1.0" require-directory "^2.1.1" - require-main-filename "^1.0.1" + require-main-filename "^2.0.0" set-blocking "^2.0.0" - string-width "^2.0.0" + string-width "^3.0.0" which-module "^2.0.0" - y18n "^3.2.1" - yargs-parser "^9.0.2" + y18n "^4.0.0" + yargs-parser "^13.1.0" yeast@0.1.2: version "0.1.2" @@ -9380,30 +8147,30 @@ yeast@0.1.2: integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= yn@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.0.0.tgz#0073c6b56e92aed652fbdfd62431f2d6b9a7a091" - integrity sha512-+Wo/p5VRfxUgBUGy2j/6KX2mj9AYJWOHuhMjMcbBFc3y54o9/4buK1ksBvuiK01C3kby8DH9lSmJdSxw+4G/2Q== + version "3.1.0" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.0.tgz#fcbe2db63610361afcc5eb9e0ac91e976d046114" + integrity sha512-kKfnnYkbTfrAdd0xICNFw7Atm8nKpLcLv9AZGEt+kczL/WQVai4e2V6ZN8U/O+iI6WrNuJjNNOyu4zfhl9D3Hg== -youtube-dl@^1.12.2: - version "1.13.1" - resolved "https://registry.yarnpkg.com/youtube-dl/-/youtube-dl-1.13.1.tgz#2da47c0dad3c5391e2172b7811da46501b82edc4" - integrity sha512-89mUKwOavaojNKQlyzW+A7Dph5G/oPYs6T/PTMcvgdRQ5E2+uDQgYPxWHQDMBhHOkxQaxvxQTiTPKQLdg0OI4w== +youtube-dl@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/youtube-dl/-/youtube-dl-2.0.0.tgz#64f85beff21a5b0b5651437ea51d10effd3dad5a" + integrity sha512-1R5bcnBMVqQIk85wqTJ8pxPq5UbzxZXJy4KSaf3N1dedZG4uAzq5zuoiHcNcsGle5JtoHNpTWDBjTDhHO0SipQ== dependencies: hh-mm-ss "~1.2.0" mkdirp "~0.5.1" request "~2.88.0" streamify "~0.2.9" -yup@^0.26.10: - version "0.26.10" - resolved "https://registry.yarnpkg.com/yup/-/yup-0.26.10.tgz#3545839663289038faf25facfc07e11fd67c0cb1" - integrity sha512-keuNEbNSnsOTOuGCt3UJW69jDE3O4P+UHAakO7vSeFMnjaitcmlbij/a3oNb9g1Y1KvSKH/7O1R2PQ4m4TRylw== +yup@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/yup/-/yup-0.27.0.tgz#f8cb198c8e7dd2124beddc2457571329096b06e7" + integrity sha512-v1yFnE4+u9za42gG/b/081E7uNW9mUj3qtkmelLbW5YPROZzSH/KUUyJu9Wt8vxFJcT9otL/eZopS0YK1L5yPQ== dependencies: - "@babel/runtime" "7.0.0" + "@babel/runtime" "^7.0.0" fn-name "~2.0.1" - lodash "^4.17.10" + lodash "^4.17.11" property-expr "^1.5.0" - synchronous-promise "^2.0.5" + synchronous-promise "^2.0.6" toposort "^2.0.2" z-schema@^3.24.2: