aboutsummaryrefslogtreecommitdiffhomepage
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/package.json39
-rw-r--r--client/src/app/+about/about-instance/contact-admin-modal.component.html2
-rw-r--r--client/src/app/+accounts/accounts.component.ts3
-rw-r--r--client/src/app/+admin/admin.module.ts3
-rw-r--r--client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.html5
-rw-r--r--client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.ts4
-rw-r--r--client/src/app/+admin/users/user-edit/index.ts1
-rw-r--r--client/src/app/+admin/users/user-edit/user-edit.component.html14
-rw-r--r--client/src/app/+admin/users/user-edit/user-edit.component.scss22
-rw-r--r--client/src/app/+admin/users/user-edit/user-edit.ts11
-rw-r--r--client/src/app/+admin/users/user-edit/user-password.component.html21
-rw-r--r--client/src/app/+admin/users/user-edit/user-password.component.scss22
-rw-r--r--client/src/app/+admin/users/user-edit/user-password.component.ts64
-rw-r--r--client/src/app/+admin/users/user-edit/user-update.component.ts14
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.html2
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.scss4
-rw-r--r--client/src/app/+my-account/my-account-history/my-account-history.component.scss4
-rw-r--r--client/src/app/+my-account/my-account-notifications/my-account-notifications.component.html10
-rw-r--r--client/src/app/+my-account/my-account-notifications/my-account-notifications.component.scss4
-rw-r--r--client/src/app/+my-account/my-account-ownership/my-account-accept-ownership/my-account-accept-ownership.component.html3
-rw-r--r--client/src/app/+my-account/my-account-ownership/my-account-ownership.component.html4
-rw-r--r--client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html2
-rw-r--r--client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.scss2
-rw-r--r--client/src/app/+my-account/my-account-videos/my-account-videos.component.html6
-rw-r--r--client/src/app/+my-account/my-account-videos/my-account-videos.component.scss11
-rw-r--r--client/src/app/+my-account/my-account-videos/video-change-ownership/video-change-ownership.component.html3
-rw-r--r--client/src/app/app.component.html4
-rw-r--r--client/src/app/core/confirm/index.ts1
-rw-r--r--client/src/app/core/core.module.ts4
-rw-r--r--client/src/app/core/server/server.service.ts5
-rw-r--r--client/src/app/header/header.component.html2
-rw-r--r--client/src/app/header/header.component.scss11
-rw-r--r--client/src/app/login/login.component.html3
-rw-r--r--client/src/app/menu/avatar-notification.component.scss17
-rw-r--r--client/src/app/menu/language-chooser.component.html2
-rw-r--r--client/src/app/menu/menu.component.scss4
-rw-r--r--client/src/app/search/search.component.scss4
-rw-r--r--client/src/app/shared/actor/actor.model.ts2
-rw-r--r--client/src/app/shared/buttons/action-dropdown.component.html2
-rw-r--r--client/src/app/shared/buttons/action-dropdown.component.scss9
-rw-r--r--client/src/app/shared/buttons/button.component.html2
-rw-r--r--client/src/app/shared/buttons/button.component.scss35
-rw-r--r--client/src/app/shared/buttons/button.component.ts3
-rw-r--r--client/src/app/shared/buttons/delete-button.component.html2
-rw-r--r--client/src/app/shared/buttons/edit-button.component.html2
-rw-r--r--client/src/app/shared/confirm/confirm.component.html (renamed from client/src/app/core/confirm/confirm.component.html)3
-rw-r--r--client/src/app/shared/confirm/confirm.component.scss (renamed from client/src/app/core/confirm/confirm.component.scss)0
-rw-r--r--client/src/app/shared/confirm/confirm.component.ts (renamed from client/src/app/core/confirm/confirm.component.ts)2
-rw-r--r--client/src/app/shared/icons/global-icon.component.html0
-rw-r--r--client/src/app/shared/icons/global-icon.component.scss4
-rw-r--r--client/src/app/shared/icons/global-icon.component.ts48
-rw-r--r--client/src/app/shared/misc/help.component.html4
-rw-r--r--client/src/app/shared/misc/help.component.scss27
-rw-r--r--client/src/app/shared/moderation/user-ban-modal.component.html7
-rw-r--r--client/src/app/shared/moderation/user-ban-modal.component.ts4
-rw-r--r--client/src/app/shared/shared.module.ts8
-rw-r--r--client/src/app/shared/users/user-notification.model.ts42
-rw-r--r--client/src/app/shared/users/user-notification.service.ts2
-rw-r--r--client/src/app/shared/users/user-notifications.component.html82
-rw-r--r--client/src/app/shared/users/user-notifications.component.scss46
-rw-r--r--client/src/app/shared/users/user-notifications.component.ts14
-rw-r--r--client/src/app/shared/video/feed.component.html9
-rw-r--r--client/src/app/shared/video/feed.component.scss17
-rw-r--r--client/src/app/shared/video/video-details.model.ts13
-rw-r--r--client/src/app/shared/video/video-miniature.component.scss4
-rw-r--r--client/src/app/shared/video/video.model.ts4
-rw-r--r--client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.html2
-rw-r--r--client/src/app/videos/+video-edit/shared/video-edit.component.html2
-rw-r--r--client/src/app/videos/+video-edit/shared/video-edit.component.scss28
-rw-r--r--client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.html6
-rw-r--r--client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.scss53
-rw-r--r--client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.ts3
-rw-r--r--client/src/app/videos/+video-edit/video-add-components/video-import-url.component.html6
-rw-r--r--client/src/app/videos/+video-edit/video-add-components/video-import-url.component.ts2
-rw-r--r--client/src/app/videos/+video-edit/video-add-components/video-send.scss (renamed from client/src/app/videos/+video-edit/video-add-components/video-import-url.component.scss)29
-rw-r--r--client/src/app/videos/+video-edit/video-add-components/video-upload.component.html6
-rw-r--r--client/src/app/videos/+video-edit/video-add-components/video-upload.component.scss44
-rw-r--r--client/src/app/videos/+video-edit/video-add-components/video-upload.component.ts3
-rw-r--r--client/src/app/videos/+video-edit/video-update.component.html2
-rw-r--r--client/src/app/videos/+video-watch/comment/video-comment.component.scss6
-rw-r--r--client/src/app/videos/+video-watch/modal/video-blacklist.component.html2
-rw-r--r--client/src/app/videos/+video-watch/modal/video-download.component.html2
-rw-r--r--client/src/app/videos/+video-watch/modal/video-report.component.html2
-rw-r--r--client/src/app/videos/+video-watch/modal/video-share.component.html2
-rw-r--r--client/src/app/videos/+video-watch/modal/video-support.component.html2
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.html24
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.scss80
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.ts113
-rw-r--r--client/src/app/videos/video-list/video-trending.component.ts25
-rw-r--r--client/src/assets/images/global/add.html (renamed from client/src/assets/images/global/add.svg)6
-rw-r--r--client/src/assets/images/global/alert.html (renamed from client/src/assets/images/video/alert.svg)9
-rw-r--r--client/src/assets/images/global/circle-tick.html12
-rw-r--r--client/src/assets/images/global/cloud-download.html11
-rw-r--r--client/src/assets/images/global/cloud-error.html11
-rw-r--r--client/src/assets/images/global/cog.html9
-rw-r--r--client/src/assets/images/global/cross.html (renamed from client/src/assets/images/global/cross.svg)6
-rw-r--r--client/src/assets/images/global/delete-black.svg14
-rw-r--r--client/src/assets/images/global/delete-grey.svg14
-rw-r--r--client/src/assets/images/global/delete.html (renamed from client/src/assets/images/global/delete-white.svg)14
-rw-r--r--client/src/assets/images/global/download.html (renamed from client/src/assets/images/video/download-black.svg)9
-rw-r--r--client/src/assets/images/global/edit-black.svg15
-rw-r--r--client/src/assets/images/global/edit.html (renamed from client/src/assets/images/global/edit-grey.svg)9
-rw-r--r--client/src/assets/images/global/help.html (renamed from client/src/assets/images/global/help.svg)12
-rw-r--r--client/src/assets/images/global/im-with-her.html (renamed from client/src/assets/images/global/im-with-her.svg)13
-rw-r--r--client/src/assets/images/global/no.html10
-rw-r--r--client/src/assets/images/global/sparkle.html11
-rw-r--r--client/src/assets/images/global/syndication.html (renamed from client/src/assets/images/global/syndication.svg)4
-rw-r--r--client/src/assets/images/global/tick.html (renamed from client/src/assets/images/global/tick.svg)6
-rw-r--r--client/src/assets/images/global/undo.html9
-rw-r--r--client/src/assets/images/global/undo.svg11
-rw-r--r--client/src/assets/images/global/user-add.html11
-rw-r--r--client/src/assets/images/global/validate.html (renamed from client/src/assets/images/global/validate.svg)6
-rw-r--r--client/src/assets/images/video/blacklist.svg15
-rw-r--r--client/src/assets/images/video/dislike-white.svg14
-rw-r--r--client/src/assets/images/video/dislike.html (renamed from client/src/assets/images/video/dislike-grey.svg)6
-rw-r--r--client/src/assets/images/video/download-grey.svg16
-rw-r--r--client/src/assets/images/video/download-white.svg16
-rw-r--r--client/src/assets/images/video/heart.html (renamed from client/src/assets/images/video/heart.svg)8
-rw-r--r--client/src/assets/images/video/like-white.svg15
-rw-r--r--client/src/assets/images/video/like.html (renamed from client/src/assets/images/video/like-grey.svg)9
-rw-r--r--client/src/assets/images/video/more.html (renamed from client/src/assets/images/video/more.svg)6
-rw-r--r--client/src/assets/images/video/share.html (renamed from client/src/assets/images/video/share.svg)9
-rw-r--r--client/src/assets/images/video/upload.html (renamed from client/src/assets/images/header/upload-white.svg)9
-rw-r--r--client/src/assets/images/video/upload.svg16
-rw-r--r--client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts143
-rw-r--r--client/src/assets/player/p2p-media-loader/segment-url-builder.ts28
-rw-r--r--client/src/assets/player/p2p-media-loader/segment-validator.ts63
-rw-r--r--client/src/assets/player/peertube-player-manager.ts466
-rw-r--r--client/src/assets/player/peertube-player.ts300
-rw-r--r--client/src/assets/player/peertube-plugin.ts262
-rw-r--r--client/src/assets/player/peertube-videojs-typings.ts92
-rw-r--r--client/src/assets/player/resolution-menu-button.ts88
-rw-r--r--client/src/assets/player/resolution-menu-item.ts67
-rw-r--r--client/src/assets/player/utils.ts14
-rw-r--r--client/src/assets/player/videojs-components/p2p-info-button.ts (renamed from client/src/assets/player/webtorrent-info-button.ts)25
-rw-r--r--client/src/assets/player/videojs-components/peertube-link-button.ts (renamed from client/src/assets/player/peertube-link-button.ts)4
-rw-r--r--client/src/assets/player/videojs-components/peertube-load-progress-bar.ts (renamed from client/src/assets/player/peertube-load-progress-bar.ts)4
-rw-r--r--client/src/assets/player/videojs-components/resolution-menu-button.ts109
-rw-r--r--client/src/assets/player/videojs-components/resolution-menu-item.ts83
-rw-r--r--client/src/assets/player/videojs-components/settings-menu-button.ts (renamed from client/src/assets/player/settings-menu-button.ts)4
-rw-r--r--client/src/assets/player/videojs-components/settings-menu-item.ts (renamed from client/src/assets/player/settings-menu-item.ts)21
-rw-r--r--client/src/assets/player/videojs-components/theater-button.ts (renamed from client/src/assets/player/theater-button.ts)4
-rw-r--r--client/src/assets/player/webtorrent/peertube-chunk-store.ts (renamed from client/src/assets/player/peertube-chunk-store.ts)0
-rw-r--r--client/src/assets/player/webtorrent/video-renderer.ts (renamed from client/src/assets/player/video-renderer.ts)0
-rw-r--r--client/src/assets/player/webtorrent/webtorrent-plugin.ts (renamed from client/src/assets/player/peertube-videojs-plugin.ts)305
-rw-r--r--client/src/index.html2
-rw-r--r--client/src/main.ts2
-rw-r--r--client/src/sass/application.scss13
-rw-r--r--client/src/sass/include/_mixins.scss54
-rw-r--r--client/src/sass/include/_variables.scss9
-rw-r--r--client/src/sass/primeng-custom.scss71
-rw-r--r--client/src/standalone/videos/embed.html1
-rw-r--r--client/src/standalone/videos/embed.ts128
-rw-r--r--client/src/tsconfig.app.json2
-rw-r--r--client/tsconfig.json1
-rw-r--r--client/yarn.lock1180
156 files changed, 2937 insertions, 2157 deletions
diff --git a/client/package.json b/client/package.json
index 5fe1f3d5f..3eea661f1 100644
--- a/client/package.json
+++ b/client/package.json
@@ -1,6 +1,6 @@
1{ 1{
2 "name": "peertube-client", 2 "name": "peertube-client",
3 "version": "1.1.0", 3 "version": "1.2.0",
4 "private": true, 4 "private": true,
5 "licence": "GPLv3", 5 "licence": "GPLv3",
6 "author": { 6 "author": {
@@ -28,7 +28,8 @@
28 "resolutions": { 28 "resolutions": {
29 "video.js": "^7", 29 "video.js": "^7",
30 "webtorrent/create-torrent/junk": "^1", 30 "webtorrent/create-torrent/junk": "^1",
31 "simple-get": "^2.8.1" 31 "simple-get": "^2.8.1",
32 "punycode": "^1.4.1"
32 }, 33 },
33 "jest": { 34 "jest": {
34 "globals": { 35 "globals": {
@@ -63,20 +64,20 @@
63 "setupTestFrameworkScriptFile": "<rootDir>/src/setupJest.ts" 64 "setupTestFrameworkScriptFile": "<rootDir>/src/setupJest.ts"
64 }, 65 },
65 "devDependencies": { 66 "devDependencies": {
66 "@angular-devkit/build-angular": "~0.11.1", 67 "@angular-devkit/build-angular": "~0.13.1",
67 "@angular/animations": "~7.1.1", 68 "@angular/animations": "~7.2.4",
68 "@angular/cli": "~7.1.1", 69 "@angular/cli": "~7.3.1",
69 "@angular/common": "~7.1.1", 70 "@angular/common": "~7.2.4",
70 "@angular/compiler": "~7.1.1", 71 "@angular/compiler": "~7.2.4",
71 "@angular/compiler-cli": "~7.1.1", 72 "@angular/compiler-cli": "~7.2.4",
72 "@angular/core": "~7.1.1", 73 "@angular/core": "~7.2.4",
73 "@angular/forms": "~7.1.1", 74 "@angular/forms": "~7.2.4",
74 "@angular/http": "~7.1.1", 75 "@angular/http": "~7.2.4",
75 "@angular/language-service": "~7.1.1", 76 "@angular/language-service": "~7.2.4",
76 "@angular/platform-browser": "~7.1.1", 77 "@angular/platform-browser": "~7.2.4",
77 "@angular/platform-browser-dynamic": "~7.1.1", 78 "@angular/platform-browser-dynamic": "~7.2.4",
78 "@angular/router": "~7.1.1", 79 "@angular/router": "~7.2.4",
79 "@angular/service-worker": "~7.1.1", 80 "@angular/service-worker": "~7.2.4",
80 "@angularclass/hmr": "^2.1.3", 81 "@angularclass/hmr": "^2.1.3",
81 "@neos21/bootstrap3-glyphicons": "^1.0.1", 82 "@neos21/bootstrap3-glyphicons": "^1.0.1",
82 "@ng-bootstrap/ng-bootstrap": "^4.0.0", 83 "@ng-bootstrap/ng-bootstrap": "^4.0.0",
@@ -85,7 +86,9 @@
85 "@ngx-loading-bar/router": "^3.0.0", 86 "@ngx-loading-bar/router": "^3.0.0",
86 "@ngx-meta/core": "^6.0.0-rc.1", 87 "@ngx-meta/core": "^6.0.0-rc.1",
87 "@ngx-translate/i18n-polyfill": "^1.0.0", 88 "@ngx-translate/i18n-polyfill": "^1.0.0",
89 "@streamroot/videojs-hlsjs-plugin": "^1.0.7",
88 "@types/core-js": "^2.5.0", 90 "@types/core-js": "^2.5.0",
91 "@types/hls.js": "^0.12.0",
89 "@types/jasmine": "^2.8.7", 92 "@types/jasmine": "^2.8.7",
90 "@types/jasminewd2": "^2.0.3", 93 "@types/jasminewd2": "^2.0.3",
91 "@types/jest": "^23.3.1", 94 "@types/jest": "^23.3.1",
@@ -109,6 +112,7 @@
109 "extract-text-webpack-plugin": "4.0.0-beta.0", 112 "extract-text-webpack-plugin": "4.0.0-beta.0",
110 "file-loader": "^2.0.0", 113 "file-loader": "^2.0.0",
111 "focus-visible": "^4.1.5", 114 "focus-visible": "^4.1.5",
115 "hls.js": "^0.12.2",
112 "html-loader": "^0.5.5", 116 "html-loader": "^0.5.5",
113 "html-webpack-plugin": "^3.2.0", 117 "html-webpack-plugin": "^3.2.0",
114 "https-browserify": "^1.0.0", 118 "https-browserify": "^1.0.0",
@@ -131,6 +135,7 @@
131 "ngx-qrcode2": "^0.0.9", 135 "ngx-qrcode2": "^0.0.9",
132 "node-sass": "^4.9.3", 136 "node-sass": "^4.9.3",
133 "npm-font-source-sans-pro": "^1.0.2", 137 "npm-font-source-sans-pro": "^1.0.2",
138 "p2p-media-loader-hlsjs": "^0.4.0",
134 "path-browserify": "^1.0.0", 139 "path-browserify": "^1.0.0",
135 "primeng": "^7.0.0", 140 "primeng": "^7.0.0",
136 "process": "^0.11.10", 141 "process": "^0.11.10",
@@ -152,9 +157,9 @@
152 "typescript": "3.1.6", 157 "typescript": "3.1.6",
153 "video.js": "^7", 158 "video.js": "^7",
154 "videojs-contextmenu-ui": "^5.0.0", 159 "videojs-contextmenu-ui": "^5.0.0",
160 "videojs-contrib-quality-levels": "^2.0.9",
155 "videojs-dock": "^2.0.2", 161 "videojs-dock": "^2.0.2",
156 "videojs-hotkeys": "^0.2.21", 162 "videojs-hotkeys": "^0.2.21",
157 "webpack": "^4.17.1",
158 "webpack-bundle-analyzer": "^3.0.2", 163 "webpack-bundle-analyzer": "^3.0.2",
159 "webpack-cli": "^3.0.8", 164 "webpack-cli": "^3.0.8",
160 "webtorrent": "https://github.com/webtorrent/webtorrent#e9b209c7970816fc29e0cc871157a4918d66001d", 165 "webtorrent": "https://github.com/webtorrent/webtorrent#e9b209c7970816fc29e0cc871157a4918d66001d",
diff --git a/client/src/app/+about/about-instance/contact-admin-modal.component.html b/client/src/app/+about/about-instance/contact-admin-modal.component.html
index 2b3fb32f3..b2cbd0873 100644
--- a/client/src/app/+about/about-instance/contact-admin-modal.component.html
+++ b/client/src/app/+about/about-instance/contact-admin-modal.component.html
@@ -1,7 +1,7 @@
1<ng-template #modal> 1<ng-template #modal>
2 <div class="modal-header"> 2 <div class="modal-header">
3 <h4 i18n class="modal-title">Contact {{ instanceName }} administrator</h4> 3 <h4 i18n class="modal-title">Contact {{ instanceName }} administrator</h4>
4 <span class="close" aria-label="Close" role="button" (click)="hide()"></span> 4 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
5 </div> 5 </div>
6 6
7 <div class="modal-body"> 7 <div class="modal-body">
diff --git a/client/src/app/+accounts/accounts.component.ts b/client/src/app/+accounts/accounts.component.ts
index 036264602..e8339b78b 100644
--- a/client/src/app/+accounts/accounts.component.ts
+++ b/client/src/app/+accounts/accounts.component.ts
@@ -26,8 +26,7 @@ export class AccountsComponent implements OnInit, OnDestroy {
26 private notifier: Notifier, 26 private notifier: Notifier,
27 private restExtractor: RestExtractor, 27 private restExtractor: RestExtractor,
28 private redirectService: RedirectService, 28 private redirectService: RedirectService,
29 private authService: AuthService, 29 private authService: AuthService
30 private i18n: I18n
31 ) {} 30 ) {}
32 31
33 ngOnInit () { 32 ngOnInit () {
diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts
index c06ae1d60..f7f347105 100644
--- a/client/src/app/+admin/admin.module.ts
+++ b/client/src/app/+admin/admin.module.ts
@@ -10,7 +10,7 @@ import { FollowingListComponent } from './follows/following-list/following-list.
10import { JobsComponent } from './jobs/job.component' 10import { JobsComponent } from './jobs/job.component'
11import { JobsListComponent } from './jobs/jobs-list/jobs-list.component' 11import { JobsListComponent } from './jobs/jobs-list/jobs-list.component'
12import { JobService } from './jobs/shared/job.service' 12import { JobService } from './jobs/shared/job.service'
13import { UserCreateComponent, UserListComponent, UsersComponent, UserUpdateComponent } from './users' 13import { UserCreateComponent, UserListComponent, UsersComponent, UserUpdateComponent, UserPasswordComponent } from './users'
14import { ModerationCommentModalComponent, VideoAbuseListComponent, VideoBlacklistListComponent } from './moderation' 14import { ModerationCommentModalComponent, VideoAbuseListComponent, VideoBlacklistListComponent } from './moderation'
15import { ModerationComponent } from '@app/+admin/moderation/moderation.component' 15import { ModerationComponent } from '@app/+admin/moderation/moderation.component'
16import { RedundancyCheckboxComponent } from '@app/+admin/follows/shared/redundancy-checkbox.component' 16import { RedundancyCheckboxComponent } from '@app/+admin/follows/shared/redundancy-checkbox.component'
@@ -36,6 +36,7 @@ import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } f
36 UsersComponent, 36 UsersComponent,
37 UserCreateComponent, 37 UserCreateComponent,
38 UserUpdateComponent, 38 UserUpdateComponent,
39 UserPasswordComponent,
39 UserListComponent, 40 UserListComponent,
40 41
41 ModerationComponent, 42 ModerationComponent,
diff --git a/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.html b/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.html
index 952235c55..303a788d2 100644
--- a/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.html
+++ b/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.html
@@ -1,7 +1,8 @@
1<ng-template #modal> 1<ng-template #modal>
2 <div class="modal-header"> 2 <div class="modal-header">
3 <h4 i18n class="modal-title">Moderation comment</h4> 3 <h4 i18n class="modal-title">Moderation comment</h4>
4 <span class="close" aria-hidden="true" (click)="hideModerationCommentModal()"></span> 4
5 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
5 </div> 6 </div>
6 7
7 <div class="modal-body"> 8 <div class="modal-body">
@@ -19,7 +20,7 @@
19 </div> 20 </div>
20 21
21 <div class="form-group inputs"> 22 <div class="form-group inputs">
22 <span i18n class="action-button action-button-cancel" (click)="hideModerationCommentModal()">Cancel</span> 23 <span i18n class="action-button action-button-cancel" (click)="hide()">Cancel</span>
23 24
24 <input 25 <input
25 type="submit" i18n-value value="Update this comment" class="action-button-submit" 26 type="submit" i18n-value value="Update this comment" class="action-button-submit"
diff --git a/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.ts b/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.ts
index bebcb4207..f915978ee 100644
--- a/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.ts
+++ b/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.ts
@@ -45,7 +45,7 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI
45 }) 45 })
46 } 46 }
47 47
48 hideModerationCommentModal () { 48 hide () {
49 this.abuseToComment = undefined 49 this.abuseToComment = undefined
50 this.openedModal.close() 50 this.openedModal.close()
51 this.form.reset() 51 this.form.reset()
@@ -60,7 +60,7 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI
60 this.notifier.success(this.i18n('Comment updated.')) 60 this.notifier.success(this.i18n('Comment updated.'))
61 61
62 this.commentUpdated.emit(moderationComment) 62 this.commentUpdated.emit(moderationComment)
63 this.hideModerationCommentModal() 63 this.hide()
64 }, 64 },
65 65
66 err => this.notifier.error(err.message) 66 err => this.notifier.error(err.message)
diff --git a/client/src/app/+admin/users/user-edit/index.ts b/client/src/app/+admin/users/user-edit/index.ts
index fd80a02e0..ec734ef92 100644
--- a/client/src/app/+admin/users/user-edit/index.ts
+++ b/client/src/app/+admin/users/user-edit/index.ts
@@ -1,2 +1,3 @@
1export * from './user-create.component' 1export * from './user-create.component'
2export * from './user-update.component' 2export * from './user-update.component'
3export * from './user-password.component'
diff --git a/client/src/app/+admin/users/user-edit/user-edit.component.html b/client/src/app/+admin/users/user-edit/user-edit.component.html
index 56cf7d17d..c6566da24 100644
--- a/client/src/app/+admin/users/user-edit/user-edit.component.html
+++ b/client/src/app/+admin/users/user-edit/user-edit.component.html
@@ -81,3 +81,17 @@
81 81
82 <input type="submit" value="{{ getFormButtonTitle() }}" [disabled]="!form.valid"> 82 <input type="submit" value="{{ getFormButtonTitle() }}" [disabled]="!form.valid">
83</form> 83</form>
84
85<div *ngIf="!isCreation()" class="danger-zone">
86 <div class="account-title" i18n>Danger Zone</div>
87
88 <div class="form-group reset-password-email">
89 <label i18n>Send a link to reset the password by email to the user</label>
90 <button (click)="resetPassword()" i18n>Ask for new password</button>
91 </div>
92
93 <div class="form-group">
94 <label i18n>Manually set the user password</label>
95 <my-user-password [userId]="userId"></my-user-password>
96 </div>
97</div>
diff --git a/client/src/app/+admin/users/user-edit/user-edit.component.scss b/client/src/app/+admin/users/user-edit/user-edit.component.scss
index 6675f65cc..c1cc4ca45 100644
--- a/client/src/app/+admin/users/user-edit/user-edit.component.scss
+++ b/client/src/app/+admin/users/user-edit/user-edit.component.scss
@@ -14,7 +14,7 @@ input:not([type=submit]) {
14 @include peertube-select-container(340px); 14 @include peertube-select-container(340px);
15} 15}
16 16
17input[type=submit] { 17input[type=submit], button {
18 @include peertube-button; 18 @include peertube-button;
19 @include orange-button; 19 @include orange-button;
20 20
@@ -25,3 +25,23 @@ input[type=submit] {
25 margin-top: 5px; 25 margin-top: 5px;
26 font-size: 11px; 26 font-size: 11px;
27} 27}
28
29.account-title {
30 @include in-content-small-title;
31
32 margin-top: 55px;
33 margin-bottom: 30px;
34}
35
36.danger-zone {
37 .reset-password-email {
38 margin-bottom: 30px;
39 padding-bottom: 30px;
40 border-bottom: 1px solid rgba(0, 0, 0, 0.1);
41
42 button {
43 display: block;
44 margin-top: 0;
45 }
46 }
47}
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 0b3511e8e..649b35b0c 100644
--- a/client/src/app/+admin/users/user-edit/user-edit.ts
+++ b/client/src/app/+admin/users/user-edit/user-edit.ts
@@ -8,6 +8,7 @@ export abstract class UserEdit extends FormReactive {
8 videoQuotaDailyOptions: { value: string, label: string }[] = [] 8 videoQuotaDailyOptions: { value: string, label: string }[] = []
9 roles = Object.keys(USER_ROLE_LABELS).map(key => ({ value: key.toString(), label: USER_ROLE_LABELS[key] })) 9 roles = Object.keys(USER_ROLE_LABELS).map(key => ({ value: key.toString(), label: USER_ROLE_LABELS[key] }))
10 username: string 10 username: string
11 userId: number
11 12
12 protected abstract serverService: ServerService 13 protected abstract serverService: ServerService
13 protected abstract configService: ConfigService 14 protected abstract configService: ConfigService
@@ -22,7 +23,9 @@ export abstract class UserEdit extends FormReactive {
22 } 23 }
23 24
24 computeQuotaWithTranscoding () { 25 computeQuotaWithTranscoding () {
25 const resolutions = this.serverService.getConfig().transcoding.enabledResolutions 26 const transcodingConfig = this.serverService.getConfig().transcoding
27
28 const resolutions = transcodingConfig.enabledResolutions
26 const higherResolution = VideoResolution.H_1080P 29 const higherResolution = VideoResolution.H_1080P
27 let multiplier = 0 30 let multiplier = 0
28 31
@@ -30,9 +33,15 @@ export abstract class UserEdit extends FormReactive {
30 multiplier += resolution / higherResolution 33 multiplier += resolution / higherResolution
31 } 34 }
32 35
36 if (transcodingConfig.hls.enabled) multiplier *= 2
37
33 return multiplier * parseInt(this.form.value['videoQuota'], 10) 38 return multiplier * parseInt(this.form.value['videoQuota'], 10)
34 } 39 }
35 40
41 resetPassword () {
42 return
43 }
44
36 protected buildQuotaOptions () { 45 protected buildQuotaOptions () {
37 // These are used by a HTML select, so convert key into strings 46 // These are used by a HTML select, so convert key into strings
38 this.videoQuotaOptions = this.configService 47 this.videoQuotaOptions = this.configService
diff --git a/client/src/app/+admin/users/user-edit/user-password.component.html b/client/src/app/+admin/users/user-edit/user-password.component.html
new file mode 100644
index 000000000..a1e1f6216
--- /dev/null
+++ b/client/src/app/+admin/users/user-edit/user-password.component.html
@@ -0,0 +1,21 @@
1<form role="form" (ngSubmit)="formValidated()" [formGroup]="form">
2 <div class="form-group">
3
4 <div class="input-group">
5 <input id="password" [attr.type]="showPassword ? 'text' : 'password'"
6 formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }"
7 >
8 <div class="input-group-append">
9 <button class="btn btn-sm btn-outline-secondary" (click)="togglePasswordVisibility()" type="button">
10 <ng-container *ngIf="!showPassword" i18n>Show</ng-container>
11 <ng-container *ngIf="!!showPassword" i18n>Hide</ng-container>
12 </button>
13 </div>
14 </div>
15 <div *ngIf="formErrors.password" class="form-error">
16 {{ formErrors.password }}
17 </div>
18 </div>
19
20 <input type="submit" value="{{ getFormButtonTitle() }}" [disabled]="!form.valid">
21</form>
diff --git a/client/src/app/+admin/users/user-edit/user-password.component.scss b/client/src/app/+admin/users/user-edit/user-password.component.scss
new file mode 100644
index 000000000..217d585af
--- /dev/null
+++ b/client/src/app/+admin/users/user-edit/user-password.component.scss
@@ -0,0 +1,22 @@
1@import '_variables';
2@import '_mixins';
3
4input:not([type=submit]):not([type=checkbox]) {
5 @include peertube-input-text(340px);
6
7 display: block;
8 border-top-right-radius: 0;
9 border-bottom-right-radius: 0;
10 border-right: none;
11}
12
13input[type=submit] {
14 @include peertube-button;
15 @include orange-button;
16
17 margin-top: 10px;
18}
19
20.input-group-append {
21 height: 30px;
22}
diff --git a/client/src/app/+admin/users/user-edit/user-password.component.ts b/client/src/app/+admin/users/user-edit/user-password.component.ts
new file mode 100644
index 000000000..5b3040440
--- /dev/null
+++ b/client/src/app/+admin/users/user-edit/user-password.component.ts
@@ -0,0 +1,64 @@
1import { Component, Input, OnInit } from '@angular/core'
2import { ActivatedRoute, Router } from '@angular/router'
3import { UserService } from '@app/shared/users/user.service'
4import { Notifier } from '../../../core'
5import { User, UserUpdate } from '../../../../../../shared'
6import { I18n } from '@ngx-translate/i18n-polyfill'
7import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
8import { UserValidatorsService } from '@app/shared/forms/form-validators/user-validators.service'
9import { FormReactive } from '../../../shared'
10
11@Component({
12 selector: 'my-user-password',
13 templateUrl: './user-password.component.html',
14 styleUrls: [ './user-password.component.scss' ]
15})
16export class UserPasswordComponent extends FormReactive implements OnInit {
17 error: string
18 username: string
19 showPassword = false
20
21 @Input() userId: number
22
23 constructor (
24 protected formValidatorService: FormValidatorService,
25 private userValidatorsService: UserValidatorsService,
26 private route: ActivatedRoute,
27 private router: Router,
28 private notifier: Notifier,
29 private userService: UserService,
30 private i18n: I18n
31 ) {
32 super()
33 }
34
35 ngOnInit () {
36 this.buildForm({
37 password: this.userValidatorsService.USER_PASSWORD
38 })
39 }
40
41 formValidated () {
42 this.error = undefined
43
44 const userUpdate: UserUpdate = this.form.value
45
46 this.userService.updateUser(this.userId, userUpdate).subscribe(
47 () => {
48 this.notifier.success(
49 this.i18n('Password changed for user {{username}}.', { username: this.username })
50 )
51 },
52
53 err => this.error = err.message
54 )
55 }
56
57 togglePasswordVisibility () {
58 this.showPassword = !this.showPassword
59 }
60
61 getFormButtonTitle () {
62 return this.i18n('Update user password')
63 }
64}
diff --git a/client/src/app/+admin/users/user-edit/user-update.component.ts b/client/src/app/+admin/users/user-edit/user-update.component.ts
index 61e641823..94ef87b08 100644
--- a/client/src/app/+admin/users/user-edit/user-update.component.ts
+++ b/client/src/app/+admin/users/user-edit/user-update.component.ts
@@ -19,6 +19,7 @@ import { UserService } from '@app/shared'
19export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy { 19export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
20 error: string 20 error: string
21 userId: number 21 userId: number
22 userEmail: string
22 username: string 23 username: string
23 24
24 private paramsSub: Subscription 25 private paramsSub: Subscription
@@ -89,9 +90,22 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
89 return this.i18n('Update user') 90 return this.i18n('Update user')
90 } 91 }
91 92
93 resetPassword () {
94 this.userService.askResetPassword(this.userEmail).subscribe(
95 () => {
96 this.notifier.success(
97 this.i18n('An email asking for password reset has been sent to {{username}}.', { username: this.username })
98 )
99 },
100
101 err => this.error = err.message
102 )
103 }
104
92 private onUserFetched (userJson: User) { 105 private onUserFetched (userJson: User) {
93 this.userId = userJson.id 106 this.userId = userJson.id
94 this.username = userJson.username 107 this.username = userJson.username
108 this.userEmail = userJson.email
95 109
96 this.form.patchValue({ 110 this.form.patchValue({
97 email: userJson.email, 111 email: userJson.email,
diff --git a/client/src/app/+admin/users/user-list/user-list.component.html b/client/src/app/+admin/users/user-list/user-list.component.html
index 8c03a924b..69a4616a3 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.html
+++ b/client/src/app/+admin/users/user-list/user-list.component.html
@@ -2,7 +2,7 @@
2 <div i18n class="form-sub-title">Users list</div> 2 <div i18n class="form-sub-title">Users list</div>
3 3
4 <a class="add-button" routerLink="/admin/users/create"> 4 <a class="add-button" routerLink="/admin/users/create">
5 <span class="icon icon-add"></span> 5 <my-global-icon iconName="add"></my-global-icon>
6 <ng-container i18n>Create user</ng-container> 6 <ng-container i18n>Create user</ng-container>
7 </a> 7 </a>
8</div> 8</div>
diff --git a/client/src/app/+admin/users/user-list/user-list.component.scss b/client/src/app/+admin/users/user-list/user-list.component.scss
index f235769f0..5274be01c 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.scss
+++ b/client/src/app/+admin/users/user-list/user-list.component.scss
@@ -2,7 +2,7 @@
2@import '_mixins'; 2@import '_mixins';
3 3
4.add-button { 4.add-button {
5 @include create-button('../../../../assets/images/global/add.svg'); 5 @include create-button;
6} 6}
7 7
8tr.banned { 8tr.banned {
@@ -23,4 +23,4 @@ tr.banned {
23 input { 23 input {
24 @include peertube-input-text(250px); 24 @include peertube-input-text(250px);
25 } 25 }
26} \ No newline at end of file 26}
diff --git a/client/src/app/+my-account/my-account-history/my-account-history.component.scss b/client/src/app/+my-account/my-account-history/my-account-history.component.scss
index 82150cbe3..e7c6863f1 100644
--- a/client/src/app/+my-account/my-account-history/my-account-history.component.scss
+++ b/client/src/app/+my-account/my-account-history/my-account-history.component.scss
@@ -65,10 +65,10 @@
65 text-overflow: ellipsis; 65 text-overflow: ellipsis;
66 white-space: nowrap; 66 white-space: nowrap;
67 font-size: 14px; 67 font-size: 14px;
68 color: #585858; 68 color: $grey-foreground-color;
69 69
70 &:hover { 70 &:hover {
71 color: #303030; 71 color: $grey-foreground-hover-color;
72 } 72 }
73 } 73 }
74 } 74 }
diff --git a/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.html b/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.html
index b98a1087e..d518b22ec 100644
--- a/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.html
+++ b/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.html
@@ -1,7 +1,13 @@
1<div class="header"> 1<div class="header">
2 <a routerLink="/my-account/settings" fragment="notifications" i18n>Notification preferences</a> 2 <a routerLink="/my-account/settings" fragment="notifications" i18n>
3 <my-global-icon iconName="cog"></my-global-icon>
4 Notification preferences
5 </a>
3 6
4 <button (click)="markAllAsRead()" i18n>Mark all as read</button> 7 <button (click)="markAllAsRead()" i18n>
8 <my-global-icon iconName="circle-tick"></my-global-icon>
9 Mark all as read
10 </button>
5</div> 11</div>
6 12
7<my-user-notifications #userNotification></my-user-notifications> 13<my-user-notifications #userNotification></my-user-notifications>
diff --git a/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.scss b/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.scss
index 86ac094c5..43d1f82ab 100644
--- a/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.scss
+++ b/client/src/app/+my-account/my-account-notifications/my-account-notifications.component.scss
@@ -5,16 +5,18 @@
5 display: flex; 5 display: flex;
6 justify-content: space-between; 6 justify-content: space-between;
7 font-size: 15px; 7 font-size: 15px;
8 margin-bottom: 10px; 8 margin-bottom: 20px;
9 9
10 a { 10 a {
11 @include peertube-button-link; 11 @include peertube-button-link;
12 @include grey-button; 12 @include grey-button;
13 @include button-with-icon(18px, 3px, -1px);
13 } 14 }
14 15
15 button { 16 button {
16 @include peertube-button; 17 @include peertube-button;
17 @include grey-button; 18 @include grey-button;
19 @include button-with-icon(20px, 3px, -1px);
18 } 20 }
19} 21}
20 22
diff --git a/client/src/app/+my-account/my-account-ownership/my-account-accept-ownership/my-account-accept-ownership.component.html b/client/src/app/+my-account/my-account-ownership/my-account-accept-ownership/my-account-accept-ownership.component.html
index fd7d7d23b..674a4e8a2 100644
--- a/client/src/app/+my-account/my-account-ownership/my-account-accept-ownership/my-account-accept-ownership.component.html
+++ b/client/src/app/+my-account/my-account-ownership/my-account-accept-ownership/my-account-accept-ownership.component.html
@@ -1,7 +1,8 @@
1<ng-template #modal let-close="close" let-dismiss="dismiss"> 1<ng-template #modal let-close="close" let-dismiss="dismiss">
2 <div class="modal-header"> 2 <div class="modal-header">
3 <h4 i18n class="modal-title">Accept ownership</h4> 3 <h4 i18n class="modal-title">Accept ownership</h4>
4 <span class="close" aria-label="Close" role="button" (click)="dismiss()"></span> 4
5 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="dismiss()"></my-global-icon>
5 </div> 6 </div>
6 7
7 <div class="modal-body" [formGroup]="form"> 8 <div class="modal-body" [formGroup]="form">
diff --git a/client/src/app/+my-account/my-account-ownership/my-account-ownership.component.html b/client/src/app/+my-account/my-account-ownership/my-account-ownership.component.html
index 379fd8bb1..5709e9f54 100644
--- a/client/src/app/+my-account/my-account-ownership/my-account-ownership.component.html
+++ b/client/src/app/+my-account/my-account-ownership/my-account-ownership.component.html
@@ -40,10 +40,10 @@
40 <td class="action-cell"> 40 <td class="action-cell">
41 <ng-container *ngIf="videoChangeOwnership.status === 'WAITING'"> 41 <ng-container *ngIf="videoChangeOwnership.status === 'WAITING'">
42 <my-button i18n label="Accept" 42 <my-button i18n label="Accept"
43 icon="icon-tick" 43 icon="tick"
44 (click)="openAcceptModal(videoChangeOwnership)"></my-button> 44 (click)="openAcceptModal(videoChangeOwnership)"></my-button>
45 <my-button i18n label="Refuse" 45 <my-button i18n label="Refuse"
46 icon="icon-cross" 46 icon="cross"
47 (click)="refuse(videoChangeOwnership)">Refuse</my-button> 47 (click)="refuse(videoChangeOwnership)">Refuse</my-button>
48 </ng-container> 48 </ng-container>
49 </td> 49 </td>
diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html b/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html
index df74b19b6..51db2e75d 100644
--- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html
+++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html
@@ -1,6 +1,6 @@
1<div class="video-channels-header"> 1<div class="video-channels-header">
2 <a class="create-button" routerLink="create"> 2 <a class="create-button" routerLink="create">
3 <span class="icon icon-add"></span> 3 <my-global-icon iconName="add"></my-global-icon>
4 <ng-container i18n>Create another video channel</ng-container> 4 <ng-container i18n>Create another video channel</ng-container>
5 </a> 5 </a>
6</div> 6</div>
diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.scss b/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.scss
index 472cbb723..77fce138b 100644
--- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.scss
+++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.scss
@@ -2,7 +2,7 @@
2@import '_mixins'; 2@import '_mixins';
3 3
4.create-button { 4.create-button {
5 @include create-button('../../../assets/images/global/add.svg'); 5 @include create-button;
6} 6}
7 7
8/deep/ .action-button { 8/deep/ .action-button {
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 a6911e4bf..69748ef37 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
@@ -32,7 +32,7 @@
32 </span> 32 </span>
33 33
34 <span class="action-button action-button-delete-selection" (click)="deleteSelectedVideos()"> 34 <span class="action-button action-button-delete-selection" (click)="deleteSelectedVideos()">
35 <span class="icon icon-delete-white"></span> 35 <my-global-icon iconName="delete"></my-global-icon>
36 <ng-container i18n>Delete</ng-container> 36 <ng-container i18n>Delete</ng-container>
37 </span> 37 </span>
38 </div> 38 </div>
@@ -45,7 +45,7 @@
45 45
46 <my-button i18n-label label="Change ownership" 46 <my-button i18n-label label="Change ownership"
47 className="action-button-change-ownership" 47 className="action-button-change-ownership"
48 icon="icon-im-with-her" 48 icon="im-with-her"
49 (click)="changeOwnership($event, video)" 49 (click)="changeOwnership($event, video)"
50 ></my-button> 50 ></my-button>
51 </div> 51 </div>
@@ -53,4 +53,4 @@
53 </div> 53 </div>
54</div> 54</div>
55 55
56<my-video-change-ownership #videoChangeOwnershipModal></my-video-change-ownership> \ No newline at end of file 56<my-video-change-ownership #videoChangeOwnershipModal></my-video-change-ownership>
diff --git a/client/src/app/+my-account/my-account-videos/my-account-videos.component.scss b/client/src/app/+my-account/my-account-videos/my-account-videos.component.scss
index a735562f8..39d0cf2f7 100644
--- a/client/src/app/+my-account/my-account-videos/my-account-videos.component.scss
+++ b/client/src/app/+my-account/my-account-videos/my-account-videos.component.scss
@@ -23,14 +23,11 @@
23 .action-button-delete-selection { 23 .action-button-delete-selection {
24 @include peertube-button; 24 @include peertube-button;
25 @include orange-button; 25 @include orange-button;
26 } 26 @include button-with-icon(21px);
27
28 .icon.icon-delete-white {
29 @include icon(21px);
30 27
31 position: relative; 28 my-global-icon {
32 top: -2px; 29 @include apply-svg-color(#fff);
33 background-image: url('../../../assets/images/global/delete-white.svg'); 30 }
34 } 31 }
35 } 32 }
36} 33}
diff --git a/client/src/app/+my-account/my-account-videos/video-change-ownership/video-change-ownership.component.html b/client/src/app/+my-account/my-account-videos/video-change-ownership/video-change-ownership.component.html
index 7c0df850d..22f127904 100644
--- a/client/src/app/+my-account/my-account-videos/video-change-ownership/video-change-ownership.component.html
+++ b/client/src/app/+my-account/my-account-videos/video-change-ownership/video-change-ownership.component.html
@@ -1,7 +1,8 @@
1<ng-template #modal let-close="close" let-dismiss="dismiss"> 1<ng-template #modal let-close="close" let-dismiss="dismiss">
2 <div class="modal-header"> 2 <div class="modal-header">
3 <h4 i18n class="modal-title">Change ownership</h4> 3 <h4 i18n class="modal-title">Change ownership</h4>
4 <span class="close" aria-label="Close" role="button" (click)="dismiss()"></span> 4
5 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="dismiss()"></my-global-icon>
5 </div> 6 </div>
6 7
7 <div class="modal-body" [formGroup]="form"> 8 <div class="modal-body" [formGroup]="form">
diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html
index 3a60139e1..d398d4f35 100644
--- a/client/src/app/app.component.html
+++ b/client/src/app/app.component.html
@@ -30,14 +30,16 @@
30 30
31 <footer class="row"> 31 <footer class="row">
32 <a href="https://joinpeertube.org" title="PeerTube website" target="_blank" rel="noopener noreferrer">PeerTube v{{ serverVersion }}{{ serverCommit }}</a>&nbsp;-&nbsp; 32 <a href="https://joinpeertube.org" title="PeerTube website" target="_blank" rel="noopener noreferrer">PeerTube v{{ serverVersion }}{{ serverCommit }}</a>&nbsp;-&nbsp;
33 <a href="https://github.com/Chocobozzz/PeerTube/blob/develop/LICENSE" title="PeerTube license" target="_blank" rel="noopener noreferrer">CopyLeft 2015-2018</a> 33 <a href="https://github.com/Chocobozzz/PeerTube/blob/develop/LICENSE" title="PeerTube license" target="_blank" rel="noopener noreferrer">CopyLeft 2015-2019</a>
34 </footer> 34 </footer>
35 </div> 35 </div>
36 </div> 36 </div>
37</div> 37</div>
38 38
39<ngx-loading-bar [includeSpinner]="false"></ngx-loading-bar> 39<ngx-loading-bar [includeSpinner]="false"></ngx-loading-bar>
40
40<my-confirm></my-confirm> 41<my-confirm></my-confirm>
42
41<p-toast position="bottom-right"> 43<p-toast position="bottom-right">
42 <ng-template let-message pTemplate="message"> 44 <ng-template let-message pTemplate="message">
43 <div class="notification-block"> 45 <div class="notification-block">
diff --git a/client/src/app/core/confirm/index.ts b/client/src/app/core/confirm/index.ts
index 44aabfc13..aca591e1a 100644
--- a/client/src/app/core/confirm/index.ts
+++ b/client/src/app/core/confirm/index.ts
@@ -1,2 +1 @@
1export * from './confirm.component'
2export * from './confirm.service' export * from './confirm.service'
diff --git a/client/src/app/core/core.module.ts b/client/src/app/core/core.module.ts
index 3bc0e2885..4ef3b1e73 100644
--- a/client/src/app/core/core.module.ts
+++ b/client/src/app/core/core.module.ts
@@ -8,7 +8,7 @@ import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client'
8import { LoadingBarRouterModule } from '@ngx-loading-bar/router' 8import { LoadingBarRouterModule } from '@ngx-loading-bar/router'
9 9
10import { AuthService } from './auth' 10import { AuthService } from './auth'
11import { ConfirmComponent, ConfirmService } from './confirm' 11import { ConfirmService } from './confirm'
12import { throwIfAlreadyLoaded } from './module-import-guard' 12import { throwIfAlreadyLoaded } from './module-import-guard'
13import { LoginGuard, RedirectService, UserRightGuard } from './routing' 13import { LoginGuard, RedirectService, UserRightGuard } from './routing'
14import { ServerService } from './server' 14import { ServerService } from './server'
@@ -38,7 +38,6 @@ import { UserNotificationSocket } from '@app/core/notification/user-notification
38 ], 38 ],
39 39
40 declarations: [ 40 declarations: [
41 ConfirmComponent,
42 CheatSheetComponent 41 CheatSheetComponent
43 ], 42 ],
44 43
@@ -48,7 +47,6 @@ import { UserNotificationSocket } from '@app/core/notification/user-notification
48 47
49 ToastModule, 48 ToastModule,
50 49
51 ConfirmComponent,
52 CheatSheetComponent 50 CheatSheetComponent
53 ], 51 ],
54 52
diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts
index 4ae72427b..c868ccdcc 100644
--- a/client/src/app/core/server/server.service.ts
+++ b/client/src/app/core/server/server.service.ts
@@ -51,7 +51,10 @@ export class ServerService {
51 requiresEmailVerification: false 51 requiresEmailVerification: false
52 }, 52 },
53 transcoding: { 53 transcoding: {
54 enabledResolutions: [] 54 enabledResolutions: [],
55 hls: {
56 enabled: false
57 }
55 }, 58 },
56 avatar: { 59 avatar: {
57 file: { 60 file: {
diff --git a/client/src/app/header/header.component.html b/client/src/app/header/header.component.html
index c23e0c55d..46a87c79c 100644
--- a/client/src/app/header/header.component.html
+++ b/client/src/app/header/header.component.html
@@ -5,6 +5,6 @@
5<span (click)="doSearch()" class="icon icon-search"></span> 5<span (click)="doSearch()" class="icon icon-search"></span>
6 6
7<a class="upload-button" routerLink="/videos/upload"> 7<a class="upload-button" routerLink="/videos/upload">
8 <span class="icon icon-upload"></span> 8 <my-global-icon iconName="upload"></my-global-icon>
9 <span i18n class="upload-button-label">Upload</span> 9 <span i18n class="upload-button-label">Upload</span>
10</a> 10</a>
diff --git a/client/src/app/header/header.component.scss b/client/src/app/header/header.component.scss
index 2f9820665..cea415d9b 100644
--- a/client/src/app/header/header.component.scss
+++ b/client/src/app/header/header.component.scss
@@ -6,6 +6,7 @@
6 padding-left: 10px; 6 padding-left: 10px;
7 margin-right: 15px; 7 margin-right: 15px;
8 padding-right: 40px; // For the search icon 8 padding-right: 40px; // For the search icon
9 font-size: 14px;
9 10
10 &::placeholder { 11 &::placeholder {
11 color: var(--inputPlaceholderColor); 12 color: var(--inputPlaceholderColor);
@@ -40,6 +41,7 @@
40.upload-button { 41.upload-button {
41 @include peertube-button-link; 42 @include peertube-button-link;
42 @include orange-button; 43 @include orange-button;
44 @include button-with-icon(22px, 3px, -1px);
43 45
44 margin-right: 25px; 46 margin-right: 25px;
45 47
@@ -47,15 +49,6 @@
47 margin-right: 0; 49 margin-right: 0;
48 } 50 }
49 51
50 .icon.icon-upload {
51 @include icon(22px);
52
53 background-image: url('../../assets/images/header/upload-white.svg');
54 height: 24px;
55 vertical-align: middle;
56 margin-right: 6px;
57 }
58
59 @media screen and (max-width: 600px) { 52 @media screen and (max-width: 600px) {
60 margin-right: 10px; 53 margin-right: 10px;
61 padding: 0 10px; 54 padding: 0 10px;
diff --git a/client/src/app/login/login.component.html b/client/src/app/login/login.component.html
index 9b8146624..4efe3fb22 100644
--- a/client/src/app/login/login.component.html
+++ b/client/src/app/login/login.component.html
@@ -55,7 +55,8 @@
55<ng-template #forgotPasswordModal> 55<ng-template #forgotPasswordModal>
56 <div class="modal-header"> 56 <div class="modal-header">
57 <h4 i18n class="modal-title">Forgot your password</h4> 57 <h4 i18n class="modal-title">Forgot your password</h4>
58 <span class="close" aria-hidden="true" (click)="hideForgotPasswordModal()"></span> 58
59 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hideForgotPasswordModal()"></my-global-icon>
59 </div> 60 </div>
60 61
61 <div class="modal-body"> 62 <div class="modal-body">
diff --git a/client/src/app/menu/avatar-notification.component.scss b/client/src/app/menu/avatar-notification.component.scss
index 807385022..e785db788 100644
--- a/client/src/app/menu/avatar-notification.component.scss
+++ b/client/src/app/menu/avatar-notification.component.scss
@@ -3,7 +3,7 @@
3 3
4/deep/ { 4/deep/ {
5 .popover-notifications.popover { 5 .popover-notifications.popover {
6 max-width: 400px; 6 max-width: none;
7 7
8 .popover-body { 8 .popover-body {
9 padding: 0; 9 padding: 0;
@@ -11,9 +11,8 @@
11 font-family: $main-fonts; 11 font-family: $main-fonts;
12 overflow-y: auto; 12 overflow-y: auto;
13 max-height: 500px; 13 max-height: 500px;
14 min-width: 200px; 14 width: 400px;
15 box-shadow: 0 6px 14px rgba(0, 0, 0, 0.30); 15 box-shadow: 0 6px 14px rgba(0, 0, 0, 0.30);
16 overflow-y: auto;
17 16
18 .notifications-header { 17 .notifications-header {
19 display: flex; 18 display: flex;
@@ -42,7 +41,7 @@
42 justify-content: center; 41 justify-content: center;
43 font-weight: $font-semibold; 42 font-weight: $font-semibold;
44 color: var(--mainForegroundColor); 43 color: var(--mainForegroundColor);
45 height: 30px; 44 padding: 7px 0;
46 } 45 }
47 } 46 }
48 } 47 }
@@ -73,7 +72,7 @@
73 justify-content: center; 72 justify-content: center;
74 73
75 background-color: var(--mainColor); 74 background-color: var(--mainColor);
76 color: var(--mainBackgroundColor); 75 color: var(#fff);
77 font-size: 10px; 76 font-size: 10px;
78 font-weight: $font-semibold; 77 font-weight: $font-semibold;
79 78
@@ -82,3 +81,11 @@
82 height: 15px; 81 height: 15px;
83 } 82 }
84} 83}
84
85@media screen and (max-width: $mobile-view) {
86 /deep/ {
87 .popover-notifications.popover .popover-body {
88 width: 400px;
89 }
90 }
91}
diff --git a/client/src/app/menu/language-chooser.component.html b/client/src/app/menu/language-chooser.component.html
index c79609898..a62b33dda 100644
--- a/client/src/app/menu/language-chooser.component.html
+++ b/client/src/app/menu/language-chooser.component.html
@@ -1,7 +1,7 @@
1<ng-template #modal let-hide="close"> 1<ng-template #modal let-hide="close">
2 <div class="modal-header"> 2 <div class="modal-header">
3 <h4 i18n class="modal-title">Change the language</h4> 3 <h4 i18n class="modal-title">Change the language</h4>
4 <span class="close" aria-label="Close" role="button" (click)="hide()"></span> 4 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
5 </div> 5 </div>
6 6
7 7
diff --git a/client/src/app/menu/menu.component.scss b/client/src/app/menu/menu.component.scss
index a4aaadc7f..f30b89413 100644
--- a/client/src/app/menu/menu.component.scss
+++ b/client/src/app/menu/menu.component.scss
@@ -16,7 +16,7 @@ menu {
16 height: 100%; 16 height: 100%;
17 white-space: nowrap; 17 white-space: nowrap;
18 text-overflow: ellipsis; 18 text-overflow: ellipsis;
19 overflow: hidden; 19 overflow: auto;
20 color: var(--menuForegroundColor); 20 color: var(--menuForegroundColor);
21 display: flex; 21 display: flex;
22 flex-direction: column; 22 flex-direction: column;
@@ -243,7 +243,7 @@ menu {
243 } 243 }
244} 244}
245 245
246@media screen and (max-width: 400px) { 246@media screen and (max-width: $mobile-view) {
247 .menu-wrapper { 247 .menu-wrapper {
248 width: 100% !important; 248 width: 100% !important;
249 } 249 }
diff --git a/client/src/app/search/search.component.scss b/client/src/app/search/search.component.scss
index 3e074621b..6de13d276 100644
--- a/client/src/app/search/search.component.scss
+++ b/client/src/app/search/search.component.scss
@@ -87,10 +87,10 @@
87 text-overflow: ellipsis; 87 text-overflow: ellipsis;
88 white-space: nowrap; 88 white-space: nowrap;
89 font-size: 14px; 89 font-size: 14px;
90 color: #585858; 90 color: $grey-foreground-color;
91 91
92 &:hover { 92 &:hover {
93 color: #303030; 93 color: $grey-foreground-hover-color;
94 } 94 }
95 } 95 }
96 } 96 }
diff --git a/client/src/app/shared/actor/actor.model.ts b/client/src/app/shared/actor/actor.model.ts
index 811afb449..adecec1fc 100644
--- a/client/src/app/shared/actor/actor.model.ts
+++ b/client/src/app/shared/actor/actor.model.ts
@@ -16,7 +16,7 @@ export abstract class Actor implements ActorServer {
16 16
17 avatarUrl: string 17 avatarUrl: string
18 18
19 static GET_ACTOR_AVATAR_URL (actor: { avatar: Avatar }) { 19 static GET_ACTOR_AVATAR_URL (actor: { avatar?: { path: string } }) {
20 const absoluteAPIUrl = getAbsoluteAPIUrl() 20 const absoluteAPIUrl = getAbsoluteAPIUrl()
21 21
22 if (actor && actor.avatar) return absoluteAPIUrl + actor.avatar.path 22 if (actor && actor.avatar) return absoluteAPIUrl + actor.avatar.path
diff --git a/client/src/app/shared/buttons/action-dropdown.component.html b/client/src/app/shared/buttons/action-dropdown.component.html
index 90651f217..114b1d71f 100644
--- a/client/src/app/shared/buttons/action-dropdown.component.html
+++ b/client/src/app/shared/buttons/action-dropdown.component.html
@@ -3,7 +3,7 @@
3 class="action-button" [ngClass]="{ small: buttonSize === 'small', grey: theme === 'grey', orange: theme === 'orange' }" 3 class="action-button" [ngClass]="{ small: buttonSize === 'small', grey: theme === 'grey', orange: theme === 'orange' }"
4 ngbDropdownToggle role="button" 4 ngbDropdownToggle role="button"
5 > 5 >
6 <span *ngIf="!label" class="icon icon-action"></span> 6 <my-global-icon *ngIf="!label" class="more-icon" iconName="more"></my-global-icon>
7 <span *ngIf="label" class="dropdown-toggle">{{ label }}</span> 7 <span *ngIf="label" class="dropdown-toggle">{{ label }}</span>
8 </div> 8 </div>
9 9
diff --git a/client/src/app/shared/buttons/action-dropdown.component.scss b/client/src/app/shared/buttons/action-dropdown.component.scss
index a4fcceeee..985b2ca88 100644
--- a/client/src/app/shared/buttons/action-dropdown.component.scss
+++ b/client/src/app/shared/buttons/action-dropdown.component.scss
@@ -24,14 +24,11 @@
24 } 24 }
25 25
26 &:hover, &:active, &:focus { 26 &:hover, &:active, &:focus {
27 background-color: $grey-color; 27 background-color: $grey-background-color;
28 } 28 }
29 29
30 .icon-action { 30 .more-icon {
31 @include icon(21px); 31 width: 21px;
32
33 background-image: url('../../../assets/images/video/more.svg');
34 top: -1px;
35 } 32 }
36 33
37 &.small { 34 &.small {
diff --git a/client/src/app/shared/buttons/button.component.html b/client/src/app/shared/buttons/button.component.html
index 87a8daccf..b6df67102 100644
--- a/client/src/app/shared/buttons/button.component.html
+++ b/client/src/app/shared/buttons/button.component.html
@@ -1,4 +1,4 @@
1<span class="action-button" [ngClass]="className" [title]="getTitle()"> 1<span class="action-button" [ngClass]="className" [title]="getTitle()">
2 <span class="icon" [ngClass]="icon"></span> 2 <my-global-icon [iconName]="icon"></my-global-icon>
3 <span class="button-label">{{ label }}</span> 3 <span class="button-label">{{ label }}</span>
4</span> 4</span>
diff --git a/client/src/app/shared/buttons/button.component.scss b/client/src/app/shared/buttons/button.component.scss
index 168102f09..04199a2a9 100644
--- a/client/src/app/shared/buttons/button.component.scss
+++ b/client/src/app/shared/buttons/button.component.scss
@@ -3,41 +3,18 @@
3 3
4.action-button { 4.action-button {
5 @include peertube-button-link; 5 @include peertube-button-link;
6 @include button-with-icon(21px, 0, -2px);
6 7
7 font-size: 15px;
8 font-weight: $font-semibold; 8 font-weight: $font-semibold;
9 color: #585858; 9 color: $grey-foreground-color;
10 background-color: #E5E5E5; 10 background-color: $grey-background-color;
11 11
12 &:hover { 12 &:hover {
13 background-color: #EFEFEF; 13 background-color: $grey-background-hover-color;
14 } 14 }
15 15
16 .icon { 16 my-global-icon {
17 @include icon(21px); 17 @include apply-svg-color($grey-foreground-color);
18
19 position: relative;
20 top: -2px;
21
22 &.icon-edit {
23 background-image: url('../../../assets/images/global/edit-grey.svg');
24 }
25
26 &.icon-delete-grey {
27 background-image: url('../../../assets/images/global/delete-grey.svg');
28 }
29
30 &.icon-im-with-her {
31 background-image: url('../../../assets/images/global/im-with-her.svg');
32 }
33
34 &.icon-tick {
35 background-image: url('../../../assets/images/global/tick.svg');
36 }
37
38 &.icon-cross {
39 background-image: url('../../../assets/images/global/cross.svg');
40 }
41 } 18 }
42} 19}
43 20
diff --git a/client/src/app/shared/buttons/button.component.ts b/client/src/app/shared/buttons/button.component.ts
index 1a1162f09..a91e9c7eb 100644
--- a/client/src/app/shared/buttons/button.component.ts
+++ b/client/src/app/shared/buttons/button.component.ts
@@ -1,4 +1,5 @@
1import { Component, Input } from '@angular/core' 1import { Component, Input } from '@angular/core'
2import { GlobalIconName } from '@app/shared/icons/global-icon.component'
2 3
3@Component({ 4@Component({
4 selector: 'my-button', 5 selector: 'my-button',
@@ -9,7 +10,7 @@ import { Component, Input } from '@angular/core'
9export class ButtonComponent { 10export class ButtonComponent {
10 @Input() label = '' 11 @Input() label = ''
11 @Input() className: string = undefined 12 @Input() className: string = undefined
12 @Input() icon: string = undefined 13 @Input() icon: GlobalIconName = undefined
13 @Input() title: string = undefined 14 @Input() title: string = undefined
14 15
15 getTitle () { 16 getTitle () {
diff --git a/client/src/app/shared/buttons/delete-button.component.html b/client/src/app/shared/buttons/delete-button.component.html
index 6c55d8104..4d12a84c0 100644
--- a/client/src/app/shared/buttons/delete-button.component.html
+++ b/client/src/app/shared/buttons/delete-button.component.html
@@ -1,5 +1,5 @@
1<span class="action-button action-button-delete" [title]="getTitle()" role="button"> 1<span class="action-button action-button-delete" [title]="getTitle()" role="button">
2 <span class="icon icon-delete-grey"></span> 2 <my-global-icon iconName="delete"></my-global-icon>
3 3
4 <span class="button-label" *ngIf="label">{{ label }}</span> 4 <span class="button-label" *ngIf="label">{{ label }}</span>
5 <span class="button-label" i18n *ngIf="!label">Delete</span> 5 <span class="button-label" i18n *ngIf="!label">Delete</span>
diff --git a/client/src/app/shared/buttons/edit-button.component.html b/client/src/app/shared/buttons/edit-button.component.html
index cecb780f3..da3addbae 100644
--- a/client/src/app/shared/buttons/edit-button.component.html
+++ b/client/src/app/shared/buttons/edit-button.component.html
@@ -1,5 +1,5 @@
1<a class="action-button action-button-edit" [routerLink]="routerLink" i18n-title title="Edit"> 1<a class="action-button action-button-edit" [routerLink]="routerLink" i18n-title title="Edit">
2 <span class="icon icon-edit"></span> 2 <my-global-icon iconName="edit"></my-global-icon>
3 3
4 <span class="button-label" *ngIf="label">{{ label }}</span> 4 <span class="button-label" *ngIf="label">{{ label }}</span>
5 <span i18n class="button-label" *ngIf="!label">Edit</span> 5 <span i18n class="button-label" *ngIf="!label">Edit</span>
diff --git a/client/src/app/core/confirm/confirm.component.html b/client/src/app/shared/confirm/confirm.component.html
index 43f0c6190..65df1cd4d 100644
--- a/client/src/app/core/confirm/confirm.component.html
+++ b/client/src/app/shared/confirm/confirm.component.html
@@ -2,7 +2,8 @@
2 2
3 <div class="modal-header"> 3 <div class="modal-header">
4 <h4 class="modal-title">{{ title }}</h4> 4 <h4 class="modal-title">{{ title }}</h4>
5 <span class="close" aria-label="Close" role="button" (click)="dismiss()"></span> 5
6 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="dismiss()"></my-global-icon>
6 </div> 7 </div>
7 8
8 <div class="modal-body" > 9 <div class="modal-body" >
diff --git a/client/src/app/core/confirm/confirm.component.scss b/client/src/app/shared/confirm/confirm.component.scss
index 93dd7926b..93dd7926b 100644
--- a/client/src/app/core/confirm/confirm.component.scss
+++ b/client/src/app/shared/confirm/confirm.component.scss
diff --git a/client/src/app/core/confirm/confirm.component.ts b/client/src/app/shared/confirm/confirm.component.ts
index 5138b7848..63c163da6 100644
--- a/client/src/app/core/confirm/confirm.component.ts
+++ b/client/src/app/shared/confirm/confirm.component.ts
@@ -1,5 +1,5 @@
1import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core' 1import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core'
2import { ConfirmService } from './confirm.service' 2import { ConfirmService } from '@app/core/confirm/confirm.service'
3import { I18n } from '@ngx-translate/i18n-polyfill' 3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { NgbModal } from '@ng-bootstrap/ng-bootstrap' 4import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
5import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 5import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
diff --git a/client/src/app/shared/icons/global-icon.component.html b/client/src/app/shared/icons/global-icon.component.html
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/client/src/app/shared/icons/global-icon.component.html
diff --git a/client/src/app/shared/icons/global-icon.component.scss b/client/src/app/shared/icons/global-icon.component.scss
new file mode 100644
index 000000000..6805fb6f7
--- /dev/null
+++ b/client/src/app/shared/icons/global-icon.component.scss
@@ -0,0 +1,4 @@
1/deep/ svg {
2 width: inherit;
3 height: inherit;
4}
diff --git a/client/src/app/shared/icons/global-icon.component.ts b/client/src/app/shared/icons/global-icon.component.ts
new file mode 100644
index 000000000..e8ada0324
--- /dev/null
+++ b/client/src/app/shared/icons/global-icon.component.ts
@@ -0,0 +1,48 @@
1import { Component, ElementRef, Input, OnInit } from '@angular/core'
2
3const icons = {
4 'add': require('../../../assets/images/global/add.html'),
5 'syndication': require('../../../assets/images/global/syndication.html'),
6 'help': require('../../../assets/images/global/help.html'),
7 'sparkle': require('../../../assets/images/global/sparkle.html'),
8 'alert': require('../../../assets/images/global/alert.html'),
9 'cloud-error': require('../../../assets/images/global/cloud-error.html'),
10 'user-add': require('../../../assets/images/global/user-add.html'),
11 'no': require('../../../assets/images/global/no.html'),
12 'cloud-download': require('../../../assets/images/global/cloud-download.html'),
13 'undo': require('../../../assets/images/global/undo.html'),
14 'circle-tick': require('../../../assets/images/global/circle-tick.html'),
15 'cog': require('../../../assets/images/global/cog.html'),
16 'download': require('../../../assets/images/global/download.html'),
17 'edit': require('../../../assets/images/global/edit.html'),
18 'im-with-her': require('../../../assets/images/global/im-with-her.html'),
19 'delete': require('../../../assets/images/global/delete.html'),
20 'cross': require('../../../assets/images/global/cross.html'),
21 'validate': require('../../../assets/images/global/validate.html'),
22 'tick': require('../../../assets/images/global/tick.html'),
23 'dislike': require('../../../assets/images/video/dislike.html'),
24 'heart': require('../../../assets/images/video/heart.html'),
25 'like': require('../../../assets/images/video/like.html'),
26 'more': require('../../../assets/images/video/more.html'),
27 'share': require('../../../assets/images/video/share.html'),
28 'upload': require('../../../assets/images/video/upload.html')
29}
30
31export type GlobalIconName = keyof typeof icons
32
33@Component({
34 selector: 'my-global-icon',
35 template: '',
36 styleUrls: [ './global-icon.component.scss' ]
37})
38export class GlobalIconComponent implements OnInit {
39 @Input() iconName: GlobalIconName
40
41 constructor (private el: ElementRef) {}
42
43 ngOnInit () {
44 const nativeElement = this.el.nativeElement
45
46 nativeElement.innerHTML = icons[this.iconName]
47 }
48}
diff --git a/client/src/app/shared/misc/help.component.html b/client/src/app/shared/misc/help.component.html
index 08a2fc367..444425c9f 100644
--- a/client/src/app/shared/misc/help.component.html
+++ b/client/src/app/shared/misc/help.component.html
@@ -25,4 +25,6 @@
25 [autoClose]="true" 25 [autoClose]="true"
26 (onHidden)="onPopoverHidden()" 26 (onHidden)="onPopoverHidden()"
27 (onShown)="onPopoverShown()" 27 (onShown)="onPopoverShown()"
28></span> 28>
29 <my-global-icon iconName="help"></my-global-icon>
30</span>
diff --git a/client/src/app/shared/misc/help.component.scss b/client/src/app/shared/misc/help.component.scss
index 047e53fab..3898f3cda 100644
--- a/client/src/app/shared/misc/help.component.scss
+++ b/client/src/app/shared/misc/help.component.scss
@@ -2,13 +2,17 @@
2@import '_mixins'; 2@import '_mixins';
3 3
4.help-tooltip-button { 4.help-tooltip-button {
5 @include icon(17px); 5 cursor: pointer;
6
7 position: relative;
8 top: -2px;
9 background-image: url('../../../assets/images/global/help.svg');
10 border: none; 6 border: none;
11 margin: 5px; 7
8 my-global-icon {
9 width: 17px;
10 position: relative;
11 top: -2px;
12 margin: 5px;
13
14 @include apply-svg-color(var(--mainForegroundColor))
15 }
12} 16}
13 17
14/deep/ { 18/deep/ {
@@ -16,16 +20,21 @@
16 max-width: 300px; 20 max-width: 300px;
17 21
18 .popover-body { 22 .popover-body {
23 font-family: $main-fonts;
19 text-align: left; 24 text-align: left;
20 padding: 10px; 25 padding: 10px;
21 font-size: 13px; 26 font-size: 13px;
22 font-family: $main-fonts; 27 background-color: var(--mainBackgroundColor);
23 background-color: #fff; 28 color: var(--mainForegroundColor);
24 color: #000;
25 box-shadow: 0 0 6px rgba(0, 0, 0, 0.5); 29 box-shadow: 0 0 6px rgba(0, 0, 0, 0.5);
26 30
31 p {
32 margin-bottom: 0;
33 }
34
27 ul { 35 ul {
28 padding-left: 20px; 36 padding-left: 20px;
37 margin-bottom: 0;
29 } 38 }
30 } 39 }
31 } 40 }
diff --git a/client/src/app/shared/moderation/user-ban-modal.component.html b/client/src/app/shared/moderation/user-ban-modal.component.html
index fa5cb7404..f38ea543d 100644
--- a/client/src/app/shared/moderation/user-ban-modal.component.html
+++ b/client/src/app/shared/moderation/user-ban-modal.component.html
@@ -1,7 +1,8 @@
1<ng-template #modal> 1<ng-template #modal>
2 <div class="modal-header"> 2 <div class="modal-header">
3 <h4 i18n class="modal-title">Ban</h4> 3 <h4 i18n class="modal-title">Ban</h4>
4 <span class="close" aria-hidden="true" (click)="hideBanUserModal()"></span> 4
5 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
5 </div> 6 </div>
6 7
7 <div class="modal-body"> 8 <div class="modal-body">
@@ -19,7 +20,7 @@
19 </div> 20 </div>
20 21
21 <div class="form-group inputs"> 22 <div class="form-group inputs">
22 <span i18n class="action-button action-button-cancel" (click)="hideBanUserModal()">Cancel</span> 23 <span i18n class="action-button action-button-cancel" (click)="hide()">Cancel</span>
23 24
24 <input 25 <input
25 type="submit" i18n-value value="Ban this user" class="action-button-submit" 26 type="submit" i18n-value value="Ban this user" class="action-button-submit"
@@ -29,4 +30,4 @@
29 </form> 30 </form>
30 </div> 31 </div>
31 32
32</ng-template> \ No newline at end of file 33</ng-template>
diff --git a/client/src/app/shared/moderation/user-ban-modal.component.ts b/client/src/app/shared/moderation/user-ban-modal.component.ts
index f755ba0e8..942765301 100644
--- a/client/src/app/shared/moderation/user-ban-modal.component.ts
+++ b/client/src/app/shared/moderation/user-ban-modal.component.ts
@@ -42,7 +42,7 @@ export class UserBanModalComponent extends FormReactive implements OnInit {
42 this.openedModal = this.modalService.open(this.modal) 42 this.openedModal = this.modalService.open(this.modal)
43 } 43 }
44 44
45 hideBanUserModal () { 45 hide () {
46 this.usersToBan = undefined 46 this.usersToBan = undefined
47 this.openedModal.close() 47 this.openedModal.close()
48 } 48 }
@@ -60,7 +60,7 @@ export class UserBanModalComponent extends FormReactive implements OnInit {
60 this.notifier.success(message) 60 this.notifier.success(message)
61 61
62 this.userBanned.emit(this.usersToBan) 62 this.userBanned.emit(this.usersToBan)
63 this.hideBanUserModal() 63 this.hide()
64 }, 64 },
65 65
66 err => this.notifier.error(err.message) 66 err => this.notifier.error(err.message)
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts
index 384f5d722..6f8625c7e 100644
--- a/client/src/app/shared/shared.module.ts
+++ b/client/src/app/shared/shared.module.ts
@@ -67,6 +67,8 @@ import { UserNotificationService } from '@app/shared/users/user-notification.ser
67import { UserNotificationsComponent } from '@app/shared/users/user-notifications.component' 67import { UserNotificationsComponent } from '@app/shared/users/user-notifications.component'
68import { InstanceService } from '@app/shared/instance/instance.service' 68import { InstanceService } from '@app/shared/instance/instance.service'
69import { HtmlRendererService, LinkifierService, MarkdownService } from '@app/shared/renderer' 69import { HtmlRendererService, LinkifierService, MarkdownService } from '@app/shared/renderer'
70import { ConfirmComponent } from '@app/shared/confirm/confirm.component'
71import { GlobalIconComponent } from '@app/shared/icons/global-icon.component'
70 72
71@NgModule({ 73@NgModule({
72 imports: [ 74 imports: [
@@ -110,7 +112,9 @@ import { HtmlRendererService, LinkifierService, MarkdownService } from '@app/sha
110 UserBanModalComponent, 112 UserBanModalComponent,
111 UserModerationDropdownComponent, 113 UserModerationDropdownComponent,
112 TopMenuDropdownComponent, 114 TopMenuDropdownComponent,
113 UserNotificationsComponent 115 UserNotificationsComponent,
116 ConfirmComponent,
117 GlobalIconComponent
114 ], 118 ],
115 119
116 exports: [ 120 exports: [
@@ -151,6 +155,8 @@ import { HtmlRendererService, LinkifierService, MarkdownService } from '@app/sha
151 UserModerationDropdownComponent, 155 UserModerationDropdownComponent,
152 TopMenuDropdownComponent, 156 TopMenuDropdownComponent,
153 UserNotificationsComponent, 157 UserNotificationsComponent,
158 ConfirmComponent,
159 GlobalIconComponent,
154 160
155 NumberFormatterPipe, 161 NumberFormatterPipe,
156 ObjectLengthPipe, 162 ObjectLengthPipe,
diff --git a/client/src/app/shared/users/user-notification.model.ts b/client/src/app/shared/users/user-notification.model.ts
index 5ff816fb8..125d2120c 100644
--- a/client/src/app/shared/users/user-notification.model.ts
+++ b/client/src/app/shared/users/user-notification.model.ts
@@ -1,4 +1,5 @@
1import { UserNotification as UserNotificationServer, UserNotificationType, VideoInfo } from '../../../../../shared' 1import { UserNotification as UserNotificationServer, UserNotificationType, VideoInfo, ActorInfo } from '../../../../../shared'
2import { Actor } from '@app/shared/actor/actor.model'
2 3
3export class UserNotification implements UserNotificationServer { 4export class UserNotification implements UserNotificationServer {
4 id: number 5 id: number
@@ -6,10 +7,7 @@ export class UserNotification implements UserNotificationServer {
6 read: boolean 7 read: boolean
7 8
8 video?: VideoInfo & { 9 video?: VideoInfo & {
9 channel: { 10 channel: ActorInfo & { avatarUrl?: string }
10 id: number
11 displayName: string
12 }
13 } 11 }
14 12
15 videoImport?: { 13 videoImport?: {
@@ -23,10 +21,7 @@ export class UserNotification implements UserNotificationServer {
23 comment?: { 21 comment?: {
24 id: number 22 id: number
25 threadId: number 23 threadId: number
26 account: { 24 account: ActorInfo & { avatarUrl?: string }
27 id: number
28 displayName: string
29 }
30 video: VideoInfo 25 video: VideoInfo
31 } 26 }
32 27
@@ -40,18 +35,11 @@ export class UserNotification implements UserNotificationServer {
40 video: VideoInfo 35 video: VideoInfo
41 } 36 }
42 37
43 account?: { 38 account?: ActorInfo & { avatarUrl?: string }
44 id: number
45 displayName: string
46 name: string
47 }
48 39
49 actorFollow?: { 40 actorFollow?: {
50 id: number 41 id: number
51 follower: { 42 follower: ActorInfo & { avatarUrl?: string }
52 name: string
53 displayName: string
54 }
55 following: { 43 following: {
56 type: 'account' | 'channel' 44 type: 'account' | 'channel'
57 name: string 45 name: string
@@ -76,12 +64,22 @@ export class UserNotification implements UserNotificationServer {
76 this.read = hash.read 64 this.read = hash.read
77 65
78 this.video = hash.video 66 this.video = hash.video
67 if (this.video) this.setAvatarUrl(this.video.channel)
68
79 this.videoImport = hash.videoImport 69 this.videoImport = hash.videoImport
70
80 this.comment = hash.comment 71 this.comment = hash.comment
72 if (this.comment) this.setAvatarUrl(this.comment.account)
73
81 this.videoAbuse = hash.videoAbuse 74 this.videoAbuse = hash.videoAbuse
75
82 this.videoBlacklist = hash.videoBlacklist 76 this.videoBlacklist = hash.videoBlacklist
77
83 this.account = hash.account 78 this.account = hash.account
79 if (this.account) this.setAvatarUrl(this.account)
80
84 this.actorFollow = hash.actorFollow 81 this.actorFollow = hash.actorFollow
82 if (this.actorFollow) this.setAvatarUrl(this.actorFollow.follower)
85 83
86 this.createdAt = hash.createdAt 84 this.createdAt = hash.createdAt
87 this.updatedAt = hash.updatedAt 85 this.updatedAt = hash.updatedAt
@@ -97,6 +95,7 @@ export class UserNotification implements UserNotificationServer {
97 95
98 case UserNotificationType.NEW_COMMENT_ON_MY_VIDEO: 96 case UserNotificationType.NEW_COMMENT_ON_MY_VIDEO:
99 case UserNotificationType.COMMENT_MENTION: 97 case UserNotificationType.COMMENT_MENTION:
98 this.accountUrl = this.buildAccountUrl(this.comment.account)
100 this.commentUrl = [ this.buildVideoUrl(this.comment.video), { threadId: this.comment.threadId } ] 99 this.commentUrl = [ this.buildVideoUrl(this.comment.video), { threadId: this.comment.threadId } ]
101 break 100 break
102 101
@@ -138,8 +137,8 @@ export class UserNotification implements UserNotificationServer {
138 return '/videos/watch/' + video.uuid 137 return '/videos/watch/' + video.uuid
139 } 138 }
140 139
141 private buildAccountUrl (account: { name: string }) { 140 private buildAccountUrl (account: { name: string, host: string }) {
142 return '/accounts/' + account.name 141 return '/accounts/' + Actor.CREATE_BY_STRING(account.name, account.host)
143 } 142 }
144 143
145 private buildVideoImportUrl () { 144 private buildVideoImportUrl () {
@@ -150,4 +149,7 @@ export class UserNotification implements UserNotificationServer {
150 return videoImport.targetUrl || videoImport.magnetUri || videoImport.torrentName 149 return videoImport.targetUrl || videoImport.magnetUri || videoImport.torrentName
151 } 150 }
152 151
152 private setAvatarUrl (actor: { avatarUrl?: string, avatar?: { path: string } }) {
153 actor.avatarUrl = Actor.GET_ACTOR_AVATAR_URL(actor)
154 }
153} 155}
diff --git a/client/src/app/shared/users/user-notification.service.ts b/client/src/app/shared/users/user-notification.service.ts
index 67ed8f74e..f8a30955d 100644
--- a/client/src/app/shared/users/user-notification.service.ts
+++ b/client/src/app/shared/users/user-notification.service.ts
@@ -15,8 +15,6 @@ export class UserNotificationService {
15 static BASE_NOTIFICATIONS_URL = environment.apiUrl + '/api/v1/users/me/notifications' 15 static BASE_NOTIFICATIONS_URL = environment.apiUrl + '/api/v1/users/me/notifications'
16 static BASE_NOTIFICATION_SETTINGS = environment.apiUrl + '/api/v1/users/me/notification-settings' 16 static BASE_NOTIFICATION_SETTINGS = environment.apiUrl + '/api/v1/users/me/notification-settings'
17 17
18 private socket: SocketIOClient.Socket
19
20 constructor ( 18 constructor (
21 private auth: AuthService, 19 private auth: AuthService,
22 private authHttp: HttpClient, 20 private authHttp: HttpClient,
diff --git a/client/src/app/shared/users/user-notifications.component.html b/client/src/app/shared/users/user-notifications.component.html
index 86379d941..0d69e0feb 100644
--- a/client/src/app/shared/users/user-notifications.component.html
+++ b/client/src/app/shared/users/user-notifications.component.html
@@ -1,61 +1,101 @@
1<div *ngIf="componentPagination.totalItems === 0" class="no-notification" i18n>You don't have notifications.</div> 1<div *ngIf="componentPagination.totalItems === 0" class="no-notification" i18n>You don't have notifications.</div>
2 2
3<div class="notifications" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()"> 3<div class="notifications" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()">
4 <div *ngFor="let notification of notifications" class="notification" [ngClass]="{ unread: !notification.read }"> 4 <div *ngFor="let notification of notifications" class="notification" [ngClass]="{ unread: !notification.read }" (click)="markAsRead(notification)">
5 5
6 <div [ngSwitch]="notification.type"> 6 <ng-container [ngSwitch]="notification.type">
7 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION"> 7 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION">
8 {{ notification.video.channel.displayName }} published a <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">new video</a> 8 <img alt="" aria-labelledby="avatar" class="avatar" [src]="notification.video.channel.avatarUrl" />
9
10 <div class="message">
11 {{ notification.video.channel.displayName }} published a <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">new video</a>
12 </div>
9 </ng-container> 13 </ng-container>
10 14
11 <ng-container i18n *ngSwitchCase="UserNotificationType.UNBLACKLIST_ON_MY_VIDEO"> 15 <ng-container i18n *ngSwitchCase="UserNotificationType.UNBLACKLIST_ON_MY_VIDEO">
12 Your video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.video.name }}</a> has been unblacklisted 16 <my-global-icon iconName="undo"></my-global-icon>
17
18 <div class="message">
19 Your video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.video.name }}</a> has been unblacklisted
20 </div>
13 </ng-container> 21 </ng-container>
14 22
15 <ng-container i18n *ngSwitchCase="UserNotificationType.BLACKLIST_ON_MY_VIDEO"> 23 <ng-container i18n *ngSwitchCase="UserNotificationType.BLACKLIST_ON_MY_VIDEO">
16 Your video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.videoBlacklist.video.name }}</a> has been blacklisted 24 <my-global-icon iconName="no"></my-global-icon>
25
26 <div class="message">
27 Your video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.videoBlacklist.video.name }}</a> has been blacklisted
28 </div>
17 </ng-container> 29 </ng-container>
18 30
19 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS"> 31 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS">
20 <a (click)="markAsRead(notification)" [routerLink]="notification.videoAbuseUrl">A new video abuse</a> has been created on video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.videoAbuse.video.name }}</a> 32 <my-global-icon iconName="alert"></my-global-icon>
33
34 <div class="message">
35 <a (click)="markAsRead(notification)" [routerLink]="notification.videoAbuseUrl">A new video abuse</a> has been created on video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.videoAbuse.video.name }}</a>
36 </div>
21 </ng-container> 37 </ng-container>
22 38
23 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_COMMENT_ON_MY_VIDEO"> 39 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_COMMENT_ON_MY_VIDEO">
24 {{ notification.comment.account.displayName }} commented your video <a (click)="markAsRead(notification)" [routerLink]="notification.commentUrl">{{ notification.comment.video.name }}</a> 40 <img alt="" aria-labelledby="avatar" class="avatar" [src]="notification.comment.account.avatarUrl" />
41
42 <div class="message">
43 <a (click)="markAsRead(notification)" [routerLink]="notification.accountUrl">{{ notification.comment.account.displayName }}</a> commented your video <a (click)="markAsRead(notification)" [routerLink]="notification.commentUrl">{{ notification.comment.video.name }}</a>
44 </div>
25 </ng-container> 45 </ng-container>
26 46
27 <ng-container i18n *ngSwitchCase="UserNotificationType.MY_VIDEO_PUBLISHED"> 47 <ng-container i18n *ngSwitchCase="UserNotificationType.MY_VIDEO_PUBLISHED">
28 Your video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.video.name }}</a> has been published 48 <my-global-icon iconName="sparkle"></my-global-icon>
49
50 <div class="message">
51 Your video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.video.name }}</a> has been published
52 </div>
29 </ng-container> 53 </ng-container>
30 54
31 <ng-container i18n *ngSwitchCase="UserNotificationType.MY_VIDEO_IMPORT_SUCCESS"> 55 <ng-container i18n *ngSwitchCase="UserNotificationType.MY_VIDEO_IMPORT_SUCCESS">
32 <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">Your video import</a> {{ notification.videoImportIdentifier }} succeeded 56 <my-global-icon iconName="cloud-download"></my-global-icon>
57
58 <div class="message">
59 <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">Your video import</a> {{ notification.videoImportIdentifier }} succeeded
60 </div>
33 </ng-container> 61 </ng-container>
34 62
35 <ng-container i18n *ngSwitchCase="UserNotificationType.MY_VIDEO_IMPORT_ERROR"> 63 <ng-container i18n *ngSwitchCase="UserNotificationType.MY_VIDEO_IMPORT_ERROR">
36 <a (click)="markAsRead(notification)" [routerLink]="notification.videoImportUrl">Your video import</a> {{ notification.videoImportIdentifier }} failed 64 <my-global-icon iconName="cloud-error"></my-global-icon>
65
66 <div class="message">
67 <a (click)="markAsRead(notification)" [routerLink]="notification.videoImportUrl">Your video import</a> {{ notification.videoImportIdentifier }} failed
68 </div>
37 </ng-container> 69 </ng-container>
38 70
39 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_USER_REGISTRATION"> 71 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_USER_REGISTRATION">
40 User <a (click)="markAsRead(notification)" [routerLink]="notification.accountUrl">{{ notification.account.name }} registered</a> on your instance 72 <my-global-icon iconName="user-add"></my-global-icon>
73
74 <div class="message">
75 User <a (click)="markAsRead(notification)" [routerLink]="notification.accountUrl">{{ notification.account.name }} registered</a> on your instance
76 </div>
41 </ng-container> 77 </ng-container>
42 78
43 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_FOLLOW"> 79 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_FOLLOW">
44 <a (click)="markAsRead(notification)" [routerLink]="notification.accountUrl">{{ notification.actorFollow.follower.displayName }}</a> is following 80 <img alt="" aria-labelledby="avatar" class="avatar" [src]="notification.actorFollow.follower.avatarUrl" />
45 81
46 <ng-container *ngIf="notification.actorFollow.following.type === 'channel'"> 82 <div class="message">
47 your channel {{ notification.actorFollow.following.displayName }} 83 <a (click)="markAsRead(notification)" [routerLink]="notification.accountUrl">{{ notification.actorFollow.follower.displayName }}</a> is following
48 </ng-container> 84
49 <ng-container *ngIf="notification.actorFollow.following.type === 'account'">your account</ng-container> 85 <ng-container *ngIf="notification.actorFollow.following.type === 'channel'">your channel {{ notification.actorFollow.following.displayName }}</ng-container>
86 <ng-container *ngIf="notification.actorFollow.following.type === 'account'">your account</ng-container>
87 </div>
50 </ng-container> 88 </ng-container>
51 89
52 <ng-container i18n *ngSwitchCase="UserNotificationType.COMMENT_MENTION"> 90 <ng-container i18n *ngSwitchCase="UserNotificationType.COMMENT_MENTION">
53 {{ notification.comment.account.displayName }} mentioned you on <a (click)="markAsRead(notification)" [routerLink]="notification.commentUrl">video {{ notification.comment.video.name }}</a> 91 <img alt="" aria-labelledby="avatar" class="avatar" [src]="notification.comment.account.avatarUrl" />
92
93 <div class="message">
94 <a (click)="markAsRead(notification)" [routerLink]="notification.accountUrl">{{ notification.comment.account.displayName }}</a> mentioned you on <a (click)="markAsRead(notification)" [routerLink]="notification.commentUrl">video {{ notification.comment.video.name }}</a>
95 </div>
54 </ng-container> 96 </ng-container>
55 </div> 97 </ng-container>
56 98
57 <div i18n title="Mark as read" class="mark-as-read"> 99 <div class="from-date">{{ notification.createdAt | myFromNow }}</div>
58 <div class="glyphicon glyphicon-ok" (click)="markAsRead(notification)"></div>
59 </div>
60 </div> 100 </div>
61</div> 101</div>
diff --git a/client/src/app/shared/users/user-notifications.component.scss b/client/src/app/shared/users/user-notifications.component.scss
index 0ae26ea39..315d504c9 100644
--- a/client/src/app/shared/users/user-notifications.component.scss
+++ b/client/src/app/shared/users/user-notifications.component.scss
@@ -1,3 +1,6 @@
1@import '_variables';
2@import '_mixins';
3
1.no-notification { 4.no-notification {
2 display: flex; 5 display: flex;
3 justify-content: center; 6 justify-content: center;
@@ -7,31 +10,42 @@
7 10
8.notification { 11.notification {
9 display: flex; 12 display: flex;
10 justify-content: space-between;
11 align-items: center; 13 align-items: center;
12 font-size: inherit; 14 font-size: inherit;
13 padding: 15px 10px; 15 padding: 15px 5px 15px 10px;
14 border-bottom: 1px solid rgba(0, 0, 0, 0.10); 16 border-bottom: 1px solid rgba(0, 0, 0, 0.10);
15 17
16 .mark-as-read { 18 &.unread {
17 min-width: 35px; 19 background-color: rgba(0, 0, 0, 0.05);
20 }
21
22 my-global-icon {
23 width: 24px;
24 margin-right: 11px;
25 margin-left: 3px;
18 26
19 .glyphicon { 27 @include apply-svg-color(#333);
20 display: none;
21 cursor: pointer;
22 color: rgba(20, 20, 20, 0.5)
23 }
24 } 28 }
25 29
26 &.unread { 30 .avatar {
27 background-color: rgba(0, 0, 0, 0.05); 31 @include avatar(30px);
32
33 margin-right: 10px;
34 }
28 35
29 &:hover .mark-as-read .glyphicon { 36 .message {
30 display: block; 37 flex-grow: 1;
31 38
32 &:hover { 39 a {
33 color: rgba(20, 20, 20, 0.8); 40 font-weight: $font-semibold;
34 }
35 } 41 }
36 } 42 }
43
44 .from-date {
45 font-size: 0.85em;
46 color: $grey-foreground-color;
47 padding-left: 5px;
48 min-width: 70px;
49 text-align: right;
50 }
37} 51}
diff --git a/client/src/app/shared/users/user-notifications.component.ts b/client/src/app/shared/users/user-notifications.component.ts
index e3913ba56..b5f9fd399 100644
--- a/client/src/app/shared/users/user-notifications.component.ts
+++ b/client/src/app/shared/users/user-notifications.component.ts
@@ -20,11 +20,7 @@ export class UserNotificationsComponent implements OnInit {
20 // So we can access it in the template 20 // So we can access it in the template
21 UserNotificationType = UserNotificationType 21 UserNotificationType = UserNotificationType
22 22
23 componentPagination: ComponentPagination = { 23 componentPagination: ComponentPagination
24 currentPage: 1,
25 itemsPerPage: this.itemsPerPage,
26 totalItems: null
27 }
28 24
29 constructor ( 25 constructor (
30 private userNotificationService: UserNotificationService, 26 private userNotificationService: UserNotificationService,
@@ -32,6 +28,12 @@ export class UserNotificationsComponent implements OnInit {
32 ) { } 28 ) { }
33 29
34 ngOnInit () { 30 ngOnInit () {
31 this.componentPagination = {
32 currentPage: 1,
33 itemsPerPage: this.itemsPerPage, // Reset items per page, because of the @Input() variable
34 totalItems: null
35 }
36
35 this.loadMoreNotifications() 37 this.loadMoreNotifications()
36 } 38 }
37 39
@@ -58,6 +60,8 @@ export class UserNotificationsComponent implements OnInit {
58 } 60 }
59 61
60 markAsRead (notification: UserNotification) { 62 markAsRead (notification: UserNotification) {
63 if (notification.read) return
64
61 this.userNotificationService.markAsRead(notification) 65 this.userNotificationService.markAsRead(notification)
62 .subscribe( 66 .subscribe(
63 () => { 67 () => {
diff --git a/client/src/app/shared/video/feed.component.html b/client/src/app/shared/video/feed.component.html
index 16116ba88..f7624ec01 100644
--- a/client/src/app/shared/video/feed.component.html
+++ b/client/src/app/shared/video/feed.component.html
@@ -1,10 +1,11 @@
1<div class="video-feed"> 1<div class="video-feed">
2 <span 2 <my-global-icon
3 *ngIf="syndicationItems.length !== 0" [ngbPopover]="feedsList" [autoClose]="true" placement="bottom" 3 *ngIf="syndicationItems.length !== 0" [ngbPopover]="feedsList" [autoClose]="true" placement="bottom"
4 class="icon icon-syndication" role="button" 4 class="icon-syndication" role="button" iconName="syndication"
5 ></span> 5 >
6 </my-global-icon>
6 7
7 <ng-template #feedsList> 8 <ng-template #feedsList>
8 <a *ngFor="let item of syndicationItems" [href]="item.url" target="_blank" rel="noopener noreferrer">{{ item.label }}</a> 9 <a *ngFor="let item of syndicationItems" [href]="item.url" target="_blank" rel="noopener noreferrer">{{ item.label }}</a>
9 </ng-template> 10 </ng-template>
10</div> \ No newline at end of file 11</div>
diff --git a/client/src/app/shared/video/feed.component.scss b/client/src/app/shared/video/feed.component.scss
index 385764be0..ed1dc17d3 100644
--- a/client/src/app/shared/video/feed.component.scss
+++ b/client/src/app/shared/video/feed.component.scss
@@ -1,3 +1,4 @@
1@import '_variables';
1@import '_mixins'; 2@import '_mixins';
2 3
3.video-feed { 4.video-feed {
@@ -6,14 +7,12 @@
6 display: block; 7 display: block;
7 } 8 }
8 9
9 .icon { 10 my-global-icon {
10 @include icon(12px); 11 cursor: pointer;
12 width: 12px;
13 position: relative;
14 top: -2px;
11 15
12 &.icon-syndication { 16 @include apply-svg-color(var(--mainForegroundColor))
13 position: relative;
14 top: -2px;
15 background-color: var(--mainForegroundColor);
16 mask-image: url('../../../assets/images/global/syndication.svg');
17 }
18 } 17 }
19} \ No newline at end of file 18}
diff --git a/client/src/app/shared/video/video-details.model.ts b/client/src/app/shared/video/video-details.model.ts
index fa4ca7f93..f44b4138b 100644
--- a/client/src/app/shared/video/video-details.model.ts
+++ b/client/src/app/shared/video/video-details.model.ts
@@ -3,6 +3,8 @@ import { AuthUser } from '../../core'
3import { Video } from '../../shared/video/video.model' 3import { Video } from '../../shared/video/video.model'
4import { Account } from '@app/shared/account/account.model' 4import { Account } from '@app/shared/account/account.model'
5import { VideoChannel } from '@app/shared/video-channel/video-channel.model' 5import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
6import { VideoStreamingPlaylist } from '../../../../../shared/models/videos/video-streaming-playlist.model'
7import { VideoStreamingPlaylistType } from '../../../../../shared/models/videos/video-streaming-playlist.type'
6 8
7export class VideoDetails extends Video implements VideoDetailsServerModel { 9export class VideoDetails extends Video implements VideoDetailsServerModel {
8 descriptionPath: string 10 descriptionPath: string
@@ -19,6 +21,10 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
19 likesPercent: number 21 likesPercent: number
20 dislikesPercent: number 22 dislikesPercent: number
21 23
24 trackerUrls: string[]
25
26 streamingPlaylists: VideoStreamingPlaylist[]
27
22 constructor (hash: VideoDetailsServerModel, translations = {}) { 28 constructor (hash: VideoDetailsServerModel, translations = {}) {
23 super(hash, translations) 29 super(hash, translations)
24 30
@@ -30,6 +36,9 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
30 this.support = hash.support 36 this.support = hash.support
31 this.commentsEnabled = hash.commentsEnabled 37 this.commentsEnabled = hash.commentsEnabled
32 38
39 this.trackerUrls = hash.trackerUrls
40 this.streamingPlaylists = hash.streamingPlaylists
41
33 this.buildLikeAndDislikePercents() 42 this.buildLikeAndDislikePercents()
34 } 43 }
35 44
@@ -53,4 +62,8 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
53 this.likesPercent = (this.likes / (this.likes + this.dislikes)) * 100 62 this.likesPercent = (this.likes / (this.likes + this.dislikes)) * 100
54 this.dislikesPercent = (this.dislikes / (this.likes + this.dislikes)) * 100 63 this.dislikesPercent = (this.dislikes / (this.likes + this.dislikes)) * 100
55 } 64 }
65
66 getHlsPlaylist () {
67 return this.streamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS)
68 }
56} 69}
diff --git a/client/src/app/shared/video/video-miniature.component.scss b/client/src/app/shared/video/video-miniature.component.scss
index 895879adc..f44bdf9a9 100644
--- a/client/src/app/shared/video/video-miniature.component.scss
+++ b/client/src/app/shared/video/video-miniature.component.scss
@@ -50,10 +50,10 @@
50 text-overflow: ellipsis; 50 text-overflow: ellipsis;
51 white-space: nowrap; 51 white-space: nowrap;
52 font-size: 13px; 52 font-size: 13px;
53 color: #585858; 53 color: $grey-foreground-color;
54 54
55 &:hover { 55 &:hover {
56 color: #303030; 56 color: $grey-foreground-hover-color;
57 } 57 }
58 } 58 }
59 } 59 }
diff --git a/client/src/app/shared/video/video.model.ts b/client/src/app/shared/video/video.model.ts
index b92c96450..6ea83d13b 100644
--- a/client/src/app/shared/video/video.model.ts
+++ b/client/src/app/shared/video/video.model.ts
@@ -53,7 +53,7 @@ export class Video implements VideoServerModel {
53 displayName: string 53 displayName: string
54 url: string 54 url: string
55 host: string 55 host: string
56 avatar: Avatar 56 avatar?: Avatar
57 } 57 }
58 58
59 channel: { 59 channel: {
@@ -63,7 +63,7 @@ export class Video implements VideoServerModel {
63 displayName: string 63 displayName: string
64 url: string 64 url: string
65 host: string 65 host: string
66 avatar: Avatar 66 avatar?: Avatar
67 } 67 }
68 68
69 userHistory?: { 69 userHistory?: {
diff --git a/client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.html b/client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.html
index 30aefdbfc..19043eee6 100644
--- a/client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.html
+++ b/client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.html
@@ -3,7 +3,7 @@
3 3
4 <div class="modal-header"> 4 <div class="modal-header">
5 <h4 i18n class="modal-title">Add caption</h4> 5 <h4 i18n class="modal-title">Add caption</h4>
6 <span class="close" aria-label="Close" role="button" (click)="hide()"></span> 6 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
7 </div> 7 </div>
8 8
9 <div class="modal-body"> 9 <div class="modal-body">
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 bd52d686a..092c0e862 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
@@ -143,7 +143,7 @@
143 143
144 <div class="captions-header"> 144 <div class="captions-header">
145 <a (click)="openAddCaptionModal()" class="create-caption"> 145 <a (click)="openAddCaptionModal()" class="create-caption">
146 <span class="icon icon-add"></span> 146 <my-global-icon iconName="add"></my-global-icon>
147 <ng-container i18n>Add another caption</ng-container> 147 <ng-container i18n>Add another caption</ng-container>
148 </a> 148 </a>
149 </div> 149 </div>
diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.scss b/client/src/app/videos/+video-edit/shared/video-edit.component.scss
index 25db8e8ed..bb775cb0a 100644
--- a/client/src/app/videos/+video-edit/shared/video-edit.component.scss
+++ b/client/src/app/videos/+video-edit/shared/video-edit.component.scss
@@ -23,10 +23,6 @@ my-peertube-checkbox {
23 display: block; 23 display: block;
24 } 24 }
25 25
26 input, select {
27 font-size: 15px
28 }
29
30 .label-tags + span { 26 .label-tags + span {
31 font-size: 15px; 27 font-size: 15px;
32 } 28 }
@@ -42,7 +38,7 @@ my-peertube-checkbox {
42 text-align: right; 38 text-align: right;
43 39
44 .create-caption { 40 .create-caption {
45 @include create-button('../../../../assets/images/global/add.svg'); 41 @include create-button;
46 } 42 }
47 } 43 }
48 44
@@ -100,13 +96,14 @@ my-peertube-checkbox {
100 display: inline-block; 96 display: inline-block;
101 margin-right: 25px; 97 margin-right: 25px;
102 98
103 color: #585858; 99 color: $grey-foreground-color;
104 font-size: 15px; 100 font-size: 15px;
105 } 101 }
106 102
107 .submit-button { 103 .submit-button {
108 @include peertube-button; 104 @include peertube-button;
109 @include orange-button; 105 @include orange-button;
106 @include button-with-icon(20px, 1px);
110 107
111 display: inline-block; 108 display: inline-block;
112 109
@@ -119,16 +116,6 @@ my-peertube-checkbox {
119 color: inherit; 116 color: inherit;
120 font-weight: $font-semibold; 117 font-weight: $font-semibold;
121 } 118 }
122
123 .icon.icon-validate {
124 @include icon(20px);
125
126 cursor: inherit;
127 position: relative;
128 top: -1px;
129 margin-right: 4px;
130 background-image: url('../../../../assets/images/global/validate.svg');
131 }
132 } 119 }
133} 120}
134 121
@@ -176,10 +163,10 @@ p-calendar {
176 } 163 }
177 164
178 tag { 165 tag {
179 background-color: var(--inputColor) !important; 166 background-color: $grey-background-color !important;
167 color: #000 !important;
180 border-radius: 3px !important; 168 border-radius: 3px !important;
181 font-size: 15px !important; 169 font-size: 15px !important;
182 color: var(--mainForegroundColor) !important;
183 height: 30px !important; 170 height: 30px !important;
184 line-height: 30px !important; 171 line-height: 30px !important;
185 margin: 0 5px 0 0 !important; 172 margin: 0 5px 0 0 !important;
@@ -202,7 +189,10 @@ p-calendar {
202 top: -1px; 189 top: -1px;
203 height: auto !important; 190 height: auto !important;
204 vertical-align: middle !important; 191 vertical-align: middle !important;
205 fill: #585858 !important; 192
193 path {
194 fill: $grey-foreground-color !important;
195 }
206 } 196 }
207 197
208 &:hover { 198 &:hover {
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 11a81ad66..28eb143c9 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
@@ -1,6 +1,6 @@
1<div *ngIf="!hasImportedVideo" class="upload-video-container"> 1<div *ngIf="!hasImportedVideo" class="upload-video-container">
2 <div class="import-video-torrent"> 2 <div class="first-step-block">
3 <div class="icon icon-upload"></div> 3 <my-global-icon class="upload-icon" iconName="upload"></my-global-icon>
4 4
5 <div class="button-file"> 5 <div class="button-file">
6 <span i18n>Select the torrent to import</span> 6 <span i18n>Select the torrent to import</span>
@@ -66,7 +66,7 @@
66 (click)="updateSecondStep()" 66 (click)="updateSecondStep()"
67 [ngClass]="{ disabled: !form.valid || isUpdatingVideo === true }" 67 [ngClass]="{ disabled: !form.valid || isUpdatingVideo === true }"
68 > 68 >
69 <span class="icon icon-validate"></span> 69 <my-global-icon iconName="validate"></my-global-icon>
70 <input type="button" i18n-value value="Update" /> 70 <input type="button" i18n-value value="Update" />
71 </div> 71 </div>
72 </div> 72 </div>
diff --git a/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.scss b/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.scss
index 00626cd7b..6d59ed834 100644
--- a/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.scss
+++ b/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.scss
@@ -1,45 +1,7 @@
1@import 'variables'; 1@import 'variables';
2@import 'mixins'; 2@import 'mixins';
3 3
4$width-size: 190px; 4.first-step-block {
5
6.peertube-select-container {
7 @include peertube-select-container($width-size);
8}
9
10.alert.alert-danger {
11 text-align: center;
12
13 & > div {
14 font-weight: $font-semibold;
15 }
16}
17
18.import-video-torrent {
19 display: flex;
20 flex-direction: column;
21 align-items: center;
22
23 .icon.icon-upload {
24 @include icon(90px);
25 margin-bottom: 25px;
26 cursor: default;
27
28 background-image: url('../../../../assets/images/video/upload.svg');
29 }
30
31 .button-file {
32 @include peertube-button-file(auto);
33
34 min-width: 190px;
35 }
36
37 .button-file-extension {
38 display: block;
39 font-size: 12px;
40 margin-top: 5px;
41 }
42
43 .torrent-or-magnet { 5 .torrent-or-magnet {
44 margin: 10px 0; 6 margin: 10px 0;
45 } 7 }
@@ -47,19 +9,6 @@ $width-size: 190px;
47 .form-group-magnet-uri { 9 .form-group-magnet-uri {
48 margin-bottom: 40px; 10 margin-bottom: 40px;
49 } 11 }
50
51 input[type=text] {
52 @include peertube-input-text($width-size);
53 display: block;
54 }
55
56 input[type=button] {
57 @include peertube-button;
58 @include orange-button;
59
60 width: $width-size;
61 margin-top: 30px;
62 }
63} 12}
64 13
65 14
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 63db06919..307806bb9 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
@@ -18,7 +18,8 @@ import { scrollToTop } from '@app/shared/misc/utils'
18 templateUrl: './video-import-torrent.component.html', 18 templateUrl: './video-import-torrent.component.html',
19 styleUrls: [ 19 styleUrls: [
20 '../shared/video-edit.component.scss', 20 '../shared/video-edit.component.scss',
21 './video-import-torrent.component.scss' 21 './video-import-torrent.component.scss',
22 './video-send.scss'
22 ] 23 ]
23}) 24})
24export class VideoImportTorrentComponent extends VideoSend implements OnInit, CanComponentDeactivate { 25export class VideoImportTorrentComponent extends VideoSend implements OnInit, CanComponentDeactivate {
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 533446672..3550c3585 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
@@ -1,6 +1,6 @@
1<div *ngIf="!hasImportedVideo" class="upload-video-container"> 1<div *ngIf="!hasImportedVideo" class="upload-video-container">
2 <div class="import-video-url"> 2 <div class="first-step-block">
3 <div class="icon icon-upload"></div> 3 <my-global-icon class="upload-icon" iconName="upload"></my-global-icon>
4 4
5 <div class="form-group"> 5 <div class="form-group">
6 <label i18n for="targetUrl">URL</label> 6 <label i18n for="targetUrl">URL</label>
@@ -59,7 +59,7 @@
59 (click)="updateSecondStep()" 59 (click)="updateSecondStep()"
60 [ngClass]="{ disabled: !form.valid || isUpdatingVideo === true }" 60 [ngClass]="{ disabled: !form.valid || isUpdatingVideo === true }"
61 > 61 >
62 <span class="icon icon-validate"></span> 62 <my-global-icon iconName="validate"></my-global-icon>
63 <input type="button" i18n-value value="Update" /> 63 <input type="button" i18n-value value="Update" />
64 </div> 64 </div>
65 </div> 65 </div>
diff --git a/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.ts b/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.ts
index a1810b7a0..257c6e5db 100644
--- a/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.ts
+++ b/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.ts
@@ -18,7 +18,7 @@ import { scrollToTop } from '@app/shared/misc/utils'
18 templateUrl: './video-import-url.component.html', 18 templateUrl: './video-import-url.component.html',
19 styleUrls: [ 19 styleUrls: [
20 '../shared/video-edit.component.scss', 20 '../shared/video-edit.component.scss',
21 './video-import-url.component.scss' 21 './video-send.scss'
22 ] 22 ]
23}) 23})
24export class VideoImportUrlComponent extends VideoSend implements OnInit, CanComponentDeactivate { 24export class VideoImportUrlComponent extends VideoSend implements OnInit, CanComponentDeactivate {
diff --git a/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.scss b/client/src/app/videos/+video-edit/video-add-components/video-send.scss
index e907edc70..8769dd302 100644
--- a/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.scss
+++ b/client/src/app/videos/+video-edit/video-add-components/video-send.scss
@@ -3,10 +3,6 @@
3 3
4$width-size: 190px; 4$width-size: 190px;
5 5
6.peertube-select-container {
7 @include peertube-select-container($width-size);
8}
9
10.alert.alert-danger { 6.alert.alert-danger {
11 text-align: center; 7 text-align: center;
12 8
@@ -15,17 +11,20 @@ $width-size: 190px;
15 } 11 }
16} 12}
17 13
18.import-video-url { 14.first-step-block {
19 display: flex; 15 display: flex;
20 flex-direction: column; 16 flex-direction: column;
21 align-items: center; 17 align-items: center;
22 18
23 .icon.icon-upload { 19 .upload-icon {
24 @include icon(90px); 20 width: 90px;
25 margin-bottom: 25px; 21 margin-bottom: 25px;
26 cursor: default;
27 22
28 background-image: url('../../../../assets/images/video/upload.svg'); 23 @include apply-svg-color(#C6C6C6);
24 }
25
26 .peertube-select-container {
27 @include peertube-select-container($width-size);
29 } 28 }
30 29
31 input[type=text] { 30 input[type=text] {
@@ -40,6 +39,16 @@ $width-size: 190px;
40 width: $width-size; 39 width: $width-size;
41 margin-top: 30px; 40 margin-top: 30px;
42 } 41 }
43}
44 42
43 .button-file {
44 @include peertube-button-file(auto);
45 45
46 min-width: 190px;
47 }
48
49 .button-file-extension {
50 display: block;
51 font-size: 12px;
52 margin-top: 5px;
53 }
54}
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 826e54d25..b252cd60a 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
@@ -1,6 +1,6 @@
1<div *ngIf="!isUploadingVideo" class="upload-video-container"> 1<div *ngIf="!isUploadingVideo" class="upload-video-container">
2 <div class="upload-video"> 2 <div class="first-step-block">
3 <div class="icon icon-upload"></div> 3 <my-global-icon class="upload-icon" iconName="upload"></my-global-icon>
4 4
5 <div class="button-file"> 5 <div class="button-file">
6 <span i18n>Select the file to upload</span> 6 <span i18n>Select the file to upload</span>
@@ -61,7 +61,7 @@
61 (click)="updateSecondStep()" 61 (click)="updateSecondStep()"
62 [ngClass]="{ disabled: isPublishingButtonDisabled() }" 62 [ngClass]="{ disabled: isPublishingButtonDisabled() }"
63 > 63 >
64 <span class="icon icon-validate"></span> 64 <my-global-icon iconName="validate"></my-global-icon>
65 <input [disabled]="isPublishingButtonDisabled()" type="button" i18n-value value="Publish" /> 65 <input [disabled]="isPublishingButtonDisabled()" type="button" i18n-value value="Publish" />
66 </div> 66 </div>
67 </div> 67 </div>
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 4b2c86ae9..8adf8f169 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,47 +1,9 @@
1@import 'variables'; 1@import 'variables';
2@import 'mixins'; 2@import 'mixins';
3 3
4.peertube-select-container { 4.first-step-block .form-group-channel {
5 @include peertube-select-container(190px); 5 margin-bottom: 20px;
6} 6 margin-top: 35px;
7
8.alert.alert-danger {
9 text-align: center;
10
11 & > div {
12 font-weight: $font-semibold;
13 }
14}
15
16.upload-video {
17 display: flex;
18 flex-direction: column;
19 align-items: center;
20
21 .form-group-channel {
22 margin-bottom: 20px;
23 margin-top: 35px;
24 }
25
26 .icon.icon-upload {
27 @include icon(90px);
28 margin-bottom: 25px;
29 cursor: default;
30
31 background-image: url('../../../../assets/images/video/upload.svg');
32 }
33
34 .button-file {
35 @include peertube-button-file(auto);
36
37 min-width: 190px;
38 }
39
40 .button-file-extension {
41 display: block;
42 font-size: 12px;
43 margin-top: 5px;
44 }
45} 7}
46 8
47.upload-progress-cancel { 9.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 aa40f8781..e4d54b654 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
@@ -20,7 +20,8 @@ import { scrollToTop } from '@app/shared/misc/utils'
20 templateUrl: './video-upload.component.html', 20 templateUrl: './video-upload.component.html',
21 styleUrls: [ 21 styleUrls: [
22 '../shared/video-edit.component.scss', 22 '../shared/video-edit.component.scss',
23 './video-upload.component.scss' 23 './video-upload.component.scss',
24 './video-send.scss'
24 ] 25 ]
25}) 26})
26export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy, CanComponentDeactivate { 27export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy, CanComponentDeactivate {
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 0457778c0..4992bb369 100644
--- a/client/src/app/videos/+video-edit/video-update.component.html
+++ b/client/src/app/videos/+video-edit/video-update.component.html
@@ -13,7 +13,7 @@
13 13
14 <div class="submit-container"> 14 <div class="submit-container">
15 <div class="submit-button" (click)="update()" [ngClass]="{ disabled: !form.valid || isUpdatingVideo === true }"> 15 <div class="submit-button" (click)="update()" [ngClass]="{ disabled: !form.valid || isUpdatingVideo === true }">
16 <span class="icon icon-validate"></span> 16 <my-global-icon iconName="validate"></my-global-icon>
17 <input type="button" i18n-value value="Update" /> 17 <input type="button" i18n-value value="Update" />
18 </div> 18 </div>
19 </div> 19 </div>
diff --git a/client/src/app/videos/+video-watch/comment/video-comment.component.scss b/client/src/app/videos/+video-watch/comment/video-comment.component.scss
index 84da5727e..731ecbf8f 100644
--- a/client/src/app/videos/+video-watch/comment/video-comment.component.scss
+++ b/client/src/app/videos/+video-watch/comment/video-comment.component.scss
@@ -41,7 +41,7 @@
41 } 41 }
42 42
43 .comment-date { 43 .comment-date {
44 color: #585858; 44 color: $grey-foreground-color;
45 margin-left: 10px; 45 margin-left: 10px;
46 } 46 }
47 } 47 }
@@ -69,7 +69,7 @@
69 69
70 .comment-action-reply, 70 .comment-action-reply,
71 .comment-action-delete { 71 .comment-action-delete {
72 color: #585858; 72 color: $grey-foreground-color;
73 cursor: pointer; 73 cursor: pointer;
74 margin-right: 10px; 74 margin-right: 10px;
75 75
@@ -108,4 +108,4 @@
108 .root-comment { 108 .root-comment {
109 font-size: 14px; 109 font-size: 14px;
110 } 110 }
111} \ No newline at end of file 111}
diff --git a/client/src/app/videos/+video-watch/modal/video-blacklist.component.html b/client/src/app/videos/+video-watch/modal/video-blacklist.component.html
index 83600fa81..1a87bdcd4 100644
--- a/client/src/app/videos/+video-watch/modal/video-blacklist.component.html
+++ b/client/src/app/videos/+video-watch/modal/video-blacklist.component.html
@@ -1,7 +1,7 @@
1<ng-template #modal> 1<ng-template #modal>
2 <div class="modal-header"> 2 <div class="modal-header">
3 <h4 i18n class="modal-title">Blacklist video</h4> 3 <h4 i18n class="modal-title">Blacklist video</h4>
4 <span class="close" aria-label="Close" role="button" (click)="hide()"></span> 4 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
5 </div> 5 </div>
6 6
7 <div class="modal-body"> 7 <div class="modal-body">
diff --git a/client/src/app/videos/+video-watch/modal/video-download.component.html b/client/src/app/videos/+video-watch/modal/video-download.component.html
index f46f92a17..2bb5d6d37 100644
--- a/client/src/app/videos/+video-watch/modal/video-download.component.html
+++ b/client/src/app/videos/+video-watch/modal/video-download.component.html
@@ -1,7 +1,7 @@
1<ng-template #modal let-hide="close"> 1<ng-template #modal let-hide="close">
2 <div class="modal-header"> 2 <div class="modal-header">
3 <h4 i18n class="modal-title">Download video</h4> 3 <h4 i18n class="modal-title">Download video</h4>
4 <span class="close" aria-hidden="true" (click)="hide()"></span> 4 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
5 </div> 5 </div>
6 6
7 <div class="modal-body"> 7 <div class="modal-body">
diff --git a/client/src/app/videos/+video-watch/modal/video-report.component.html b/client/src/app/videos/+video-watch/modal/video-report.component.html
index 733c01be0..b9434da26 100644
--- a/client/src/app/videos/+video-watch/modal/video-report.component.html
+++ b/client/src/app/videos/+video-watch/modal/video-report.component.html
@@ -1,7 +1,7 @@
1<ng-template #modal> 1<ng-template #modal>
2 <div class="modal-header"> 2 <div class="modal-header">
3 <h4 i18n class="modal-title">Report video</h4> 3 <h4 i18n class="modal-title">Report video</h4>
4 <span class="close" aria-label="Close" role="button" (click)="hide()"></span> 4 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
5 </div> 5 </div>
6 6
7 <div class="modal-body"> 7 <div class="modal-body">
diff --git a/client/src/app/videos/+video-watch/modal/video-share.component.html b/client/src/app/videos/+video-watch/modal/video-share.component.html
index 301f67f2d..9f3c37fe8 100644
--- a/client/src/app/videos/+video-watch/modal/video-share.component.html
+++ b/client/src/app/videos/+video-watch/modal/video-share.component.html
@@ -1,7 +1,7 @@
1<ng-template #modal let-hide="close"> 1<ng-template #modal let-hide="close">
2 <div class="modal-header"> 2 <div class="modal-header">
3 <h4 i18n class="modal-title">Share</h4> 3 <h4 i18n class="modal-title">Share</h4>
4 <span class="close" aria-hidden="true" (click)="hide()"></span> 4 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
5 </div> 5 </div>
6 6
7 <div class="modal-body"> 7 <div class="modal-body">
diff --git a/client/src/app/videos/+video-watch/modal/video-support.component.html b/client/src/app/videos/+video-watch/modal/video-support.component.html
index 00c304709..2a05224a8 100644
--- a/client/src/app/videos/+video-watch/modal/video-support.component.html
+++ b/client/src/app/videos/+video-watch/modal/video-support.component.html
@@ -1,7 +1,7 @@
1<ng-template #modal let-hide="close"> 1<ng-template #modal let-hide="close">
2 <div class="modal-header"> 2 <div class="modal-header">
3 <h4 i18n class="modal-title">Support</h4> 3 <h4 i18n class="modal-title">Support</h4>
4 <span class="close" aria-label="Close" role="button" (click)="hide()"></span> 4 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
5 </div> 5 </div>
6 6
7 <div class="modal-body" [innerHTML]="videoHTMLSupport"></div> 7 <div class="modal-body" [innerHTML]="videoHTMLSupport"></div>
diff --git a/client/src/app/videos/+video-watch/video-watch.component.html b/client/src/app/videos/+video-watch/video-watch.component.html
index 770785d02..709eb91a8 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.html
+++ b/client/src/app/videos/+video-watch/video-watch.component.html
@@ -53,55 +53,57 @@
53 <div 53 <div
54 *ngIf="isUserLoggedIn()" [ngClass]="{ 'activated': userRating === 'like' }" (click)="setLike()" 54 *ngIf="isUserLoggedIn()" [ngClass]="{ 'activated': userRating === 'like' }" (click)="setLike()"
55 class="action-button action-button-like" role="button" [attr.aria-pressed]="userRating === 'like'" 55 class="action-button action-button-like" role="button" [attr.aria-pressed]="userRating === 'like'"
56 i18n-title title="Like this video"
56 > 57 >
57 <span class="icon icon-like" i18n-title title="Like this video" ></span> 58 <my-global-icon iconName="like"></my-global-icon>
58 </div> 59 </div>
59 60
60 <div 61 <div
61 *ngIf="isUserLoggedIn()" [ngClass]="{ 'activated': userRating === 'dislike' }" (click)="setDislike()" 62 *ngIf="isUserLoggedIn()" [ngClass]="{ 'activated': userRating === 'dislike' }" (click)="setDislike()"
62 class="action-button action-button-dislike" role="button" [attr.aria-pressed]="userRating === 'dislike'" 63 class="action-button action-button-dislike" role="button" [attr.aria-pressed]="userRating === 'dislike'"
64 i18n-title title="Dislike this video"
63 > 65 >
64 <span class="icon icon-dislike" i18n-title title="Dislike this video"></span> 66 <my-global-icon iconName="dislike"></my-global-icon>
65 </div> 67 </div>
66 68
67 <div *ngIf="video.support" (click)="showSupportModal()" class="action-button action-button-support"> 69 <div *ngIf="video.support" (click)="showSupportModal()" class="action-button action-button-support">
68 <span class="icon icon-support"></span> 70 <my-global-icon iconName="heart"></my-global-icon>
69 <span class="icon-text" i18n>Support</span> 71 <span class="icon-text" i18n>Support</span>
70 </div> 72 </div>
71 73
72 <div (click)="showShareModal()" class="action-button action-button-share" role="button"> 74 <div (click)="showShareModal()" class="action-button action-button-share" role="button">
73 <span class="icon icon-share"></span> 75 <my-global-icon iconName="share"></my-global-icon>
74 <span class="icon-text" i18n>Share</span> 76 <span class="icon-text" i18n>Share</span>
75 </div> 77 </div>
76 78
77 <div class="action-more" ngbDropdown placement="top" role="button"> 79 <div class="action-more" ngbDropdown placement="top" role="button">
78 <div class="action-button" ngbDropdownToggle role="button"> 80 <div class="action-button" ngbDropdownToggle role="button">
79 <span class="icon icon-more"></span> 81 <my-global-icon class="more-icon" iconName="more"></my-global-icon>
80 </div> 82 </div>
81 83
82 <div ngbDropdownMenu> 84 <div ngbDropdownMenu>
83 <a class="dropdown-item" i18n-title title="Download the video" href="#" (click)="showDownloadModal($event)"> 85 <a class="dropdown-item" i18n-title title="Download the video" href="#" (click)="showDownloadModal($event)">
84 <span class="icon icon-download"></span> <ng-container i18n>Download</ng-container> 86 <my-global-icon iconName="download"></my-global-icon> <ng-container i18n>Download</ng-container>
85 </a> 87 </a>
86 88
87 <a *ngIf="isUserLoggedIn()" class="dropdown-item" i18n-title title="Report this video" href="#" (click)="showReportModal($event)"> 89 <a *ngIf="isUserLoggedIn()" class="dropdown-item" i18n-title title="Report this video" href="#" (click)="showReportModal($event)">
88 <span class="icon icon-alert"></span> <ng-container i18n>Report</ng-container> 90 <my-global-icon iconName="alert"></my-global-icon> <ng-container i18n>Report</ng-container>
89 </a> 91 </a>
90 92
91 <a *ngIf="isVideoUpdatable()" class="dropdown-item" i18n-title title="Update this video" href="#" [routerLink]="[ '/videos/update', video.uuid ]"> 93 <a *ngIf="isVideoUpdatable()" class="dropdown-item" i18n-title title="Update this video" href="#" [routerLink]="[ '/videos/update', video.uuid ]">
92 <span class="icon icon-edit"></span> <ng-container i18n>Update</ng-container> 94 <my-global-icon iconName="edit"></my-global-icon> <ng-container i18n>Update</ng-container>
93 </a> 95 </a>
94 96
95 <a *ngIf="isVideoBlacklistable()" class="dropdown-item" i18n-title title="Blacklist this video" href="#" (click)="showBlacklistModal($event)"> 97 <a *ngIf="isVideoBlacklistable()" class="dropdown-item" i18n-title title="Blacklist this video" href="#" (click)="showBlacklistModal($event)">
96 <span class="icon icon-blacklist"></span> <ng-container i18n>Blacklist</ng-container> 98 <my-global-icon iconName="no"></my-global-icon> <ng-container i18n>Blacklist</ng-container>
97 </a> 99 </a>
98 100
99 <a *ngIf="isVideoUnblacklistable()" class="dropdown-item" i18n-title title="Unblacklist this video" href="#" (click)="unblacklistVideo($event)"> 101 <a *ngIf="isVideoUnblacklistable()" class="dropdown-item" i18n-title title="Unblacklist this video" href="#" (click)="unblacklistVideo($event)">
100 <span class="icon icon-unblacklist"></span> <ng-container i18n>Unblacklist</ng-container> 102 <my-global-icon iconName="undo"></my-global-icon> <ng-container i18n>Unblacklist</ng-container>
101 </a> 103 </a>
102 104
103 <a *ngIf="isVideoRemovable()" class="dropdown-item" i18n-title title="Delete this video" href="#" (click)="removeVideo($event)"> 105 <a *ngIf="isVideoRemovable()" class="dropdown-item" i18n-title title="Delete this video" href="#" (click)="removeVideo($event)">
104 <span class="icon icon-delete"></span> <ng-container i18n>Delete</ng-container> 106 <my-global-icon iconName="delete"></my-global-icon> <ng-container i18n>Delete</ng-container>
105 </a> 107 </a>
106 </div> 108 </div>
107 </div> 109 </div>
diff --git a/client/src/app/videos/+video-watch/video-watch.component.scss b/client/src/app/videos/+video-watch/video-watch.component.scss
index 2586a2204..b03ed197d 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.scss
+++ b/client/src/app/videos/+video-watch/video-watch.component.scss
@@ -183,6 +183,8 @@ $other-videos-width: 260px;
183 .action-button { 183 .action-button {
184 @include peertube-button; 184 @include peertube-button;
185 @include grey-button; 185 @include grey-button;
186 @include button-with-icon(21px, 0, -1px);
187 @include apply-svg-color($grey-foreground-color);
186 188
187 font-size: 15px; 189 font-size: 15px;
188 font-weight: $font-semibold; 190 font-weight: $font-semibold;
@@ -194,53 +196,25 @@ $other-videos-width: 260px;
194 display: none; 196 display: none;
195 } 197 }
196 198
197 .icon {
198 @include icon(21px);
199
200 position: relative;
201 top: -2px;
202
203 &.icon-like {
204 background-image: url('../../../assets/images/video/like-grey.svg');
205 }
206
207 &.icon-dislike {
208 background-image: url('../../../assets/images/video/dislike-grey.svg');
209 }
210
211 &.icon-support {
212 background-image: url('../../../assets/images/video/heart.svg');
213 }
214
215 &.icon-share {
216 background-image: url('../../../assets/images/video/share.svg');
217 }
218
219 &.icon-more {
220 background-image: url('../../../assets/images/video/more.svg');
221 top: -1px;
222 }
223 }
224
225 .icon-text {
226 margin-left: 3px;
227 }
228
229 &.action-button-like.activated { 199 &.action-button-like.activated {
230 background-color: $green; 200 background-color: $green;
231 201
232 .icon-like { 202 my-global-icon {
233 background-image: url('../../../assets/images/video/like-white.svg'); 203 @include apply-svg-color(#fff);
234 } 204 }
235 } 205 }
236 206
237 &.action-button-dislike.activated { 207 &.action-button-dislike.activated {
238 background-color: $red; 208 background-color: $red;
239 209
240 .icon-dislike { 210 my-global-icon {
241 background-image: url('../../../assets/images/video/dislike-white.svg'); 211 @include apply-svg-color(#fff);
242 } 212 }
243 } 213 }
214
215 .icon-text {
216 margin-left: 3px;
217 }
244 } 218 }
245 219
246 .action-more { 220 .action-more {
@@ -249,36 +223,12 @@ $other-videos-width: 260px;
249 .dropdown-menu .dropdown-item { 223 .dropdown-menu .dropdown-item {
250 padding: 6px 24px; 224 padding: 6px 24px;
251 225
252 .icon { 226 my-global-icon {
253 @include icon(24px); 227 width: 24px;
254 228
255 margin-right: 10px; 229 margin-right: 10px;
256 position: relative; 230 position: relative;
257 top: -1px; 231 top: -2px;
258
259 &.icon-download {
260 background-image: url('../../../assets/images/video/download-black.svg');
261 }
262
263 &.icon-edit {
264 background-image: url('../../../assets/images/global/edit-black.svg');
265 }
266
267 &.icon-alert {
268 background-image: url('../../../assets/images/video/alert.svg');
269 }
270
271 &.icon-blacklist {
272 background-image: url('../../../assets/images/video/blacklist.svg');
273 }
274
275 &.icon-unblacklist {
276 background-image: url('../../../assets/images/global/undo.svg');
277 }
278
279 &.icon-delete {
280 background-image: url('../../../assets/images/global/delete-black.svg');
281 }
282 } 232 }
283 } 233 }
284 } 234 }
@@ -320,7 +270,7 @@ $other-videos-width: 260px;
320 .video-info-description-more { 270 .video-info-description-more {
321 cursor: pointer; 271 cursor: pointer;
322 font-weight: $font-semibold; 272 font-weight: $font-semibold;
323 color: #585858; 273 color: $grey-foreground-color;
324 font-size: 14px; 274 font-size: 14px;
325 275
326 .glyphicon { 276 .glyphicon {
@@ -339,7 +289,7 @@ $other-videos-width: 260px;
339 min-width: 91px; 289 min-width: 91px;
340 padding-right: 5px; 290 padding-right: 5px;
341 display: inline-block; 291 display: inline-block;
342 color: #585858; 292 color: $grey-foreground-color;
343 font-weight: $font-bold; 293 font-weight: $font-bold;
344 } 294 }
345 295
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 ee504bc58..e801f03ad 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/videos/+video-watch/video-watch.component.ts
@@ -7,14 +7,8 @@ import { VideoSupportComponent } from '@app/videos/+video-watch/modal/video-supp
7import { MetaService } from '@ngx-meta/core' 7import { MetaService } from '@ngx-meta/core'
8import { Notifier, ServerService } from '@app/core' 8import { Notifier, ServerService } from '@app/core'
9import { forkJoin, Subscription } from 'rxjs' 9import { forkJoin, Subscription } from 'rxjs'
10// FIXME: something weird with our path definition in tsconfig and typings
11// @ts-ignore
12import videojs from 'video.js'
13import 'videojs-hotkeys'
14import { Hotkey, HotkeysService } from 'angular2-hotkeys' 10import { Hotkey, HotkeysService } from 'angular2-hotkeys'
15import * as WebTorrent from 'webtorrent'
16import { UserVideoRateType, VideoCaption, VideoPrivacy, VideoState } from '../../../../../shared' 11import { UserVideoRateType, VideoCaption, VideoPrivacy, VideoState } from '../../../../../shared'
17import '../../../assets/player/peertube-videojs-plugin'
18import { AuthService, ConfirmService } from '../../core' 12import { AuthService, ConfirmService } from '../../core'
19import { RestExtractor, VideoBlacklistService } from '../../shared' 13import { RestExtractor, VideoBlacklistService } from '../../shared'
20import { VideoDetails } from '../../shared/video/video-details.model' 14import { VideoDetails } from '../../shared/video/video-details.model'
@@ -24,12 +18,16 @@ import { VideoReportComponent } from './modal/video-report.component'
24import { VideoShareComponent } from './modal/video-share.component' 18import { VideoShareComponent } from './modal/video-share.component'
25import { VideoBlacklistComponent } from './modal/video-blacklist.component' 19import { VideoBlacklistComponent } from './modal/video-blacklist.component'
26import { SubscribeButtonComponent } from '@app/shared/user-subscription/subscribe-button.component' 20import { SubscribeButtonComponent } from '@app/shared/user-subscription/subscribe-button.component'
27import { addContextMenu, getVideojsOptions, loadLocaleInVideoJS } from '../../../assets/player/peertube-player'
28import { I18n } from '@ngx-translate/i18n-polyfill' 21import { I18n } from '@ngx-translate/i18n-polyfill'
29import { environment } from '../../../environments/environment' 22import { environment } from '../../../environments/environment'
30import { getDevLocale, isOnDevLocale } from '@app/shared/i18n/i18n-utils'
31import { VideoCaptionService } from '@app/shared/video-caption' 23import { VideoCaptionService } from '@app/shared/video-caption'
32import { MarkdownService } from '@app/shared/renderer' 24import { MarkdownService } from '@app/shared/renderer'
25import {
26 P2PMediaLoaderOptions,
27 PeertubePlayerManager,
28 PeertubePlayerManagerOptions,
29 PlayerMode
30} from '../../../assets/player/peertube-player-manager'
33 31
34@Component({ 32@Component({
35 selector: 'my-video-watch', 33 selector: 'my-video-watch',
@@ -46,7 +44,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
46 @ViewChild('videoBlacklistModal') videoBlacklistModal: VideoBlacklistComponent 44 @ViewChild('videoBlacklistModal') videoBlacklistModal: VideoBlacklistComponent
47 @ViewChild('subscribeButton') subscribeButton: SubscribeButtonComponent 45 @ViewChild('subscribeButton') subscribeButton: SubscribeButtonComponent
48 46
49 player: videojs.Player 47 player: any
50 playerElement: HTMLVideoElement 48 playerElement: HTMLVideoElement
51 userRating: UserVideoRateType = null 49 userRating: UserVideoRateType = null
52 video: VideoDetails = null 50 video: VideoDetails = null
@@ -61,7 +59,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
61 remoteServerDown = false 59 remoteServerDown = false
62 hotkeys: Hotkey[] 60 hotkeys: Hotkey[]
63 61
64 private videojsLocaleLoaded = false
65 private paramsSub: Subscription 62 private paramsSub: Subscription
66 63
67 constructor ( 64 constructor (
@@ -92,7 +89,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
92 89
93 ngOnInit () { 90 ngOnInit () {
94 if ( 91 if (
95 WebTorrent.WEBRTC_SUPPORT === false || 92 !!((window as any).RTCPeerConnection || (window as any).mozRTCPeerConnection || (window as any).webkitRTCPeerConnection) === false ||
96 peertubeLocalStorage.getItem(VideoWatchComponent.LOCAL_STORAGE_PRIVACY_CONCERN_KEY) === 'true' 93 peertubeLocalStorage.getItem(VideoWatchComponent.LOCAL_STORAGE_PRIVACY_CONCERN_KEY) === 'true'
97 ) { 94 ) {
98 this.hasAlreadyAcceptedPrivacyConcern = true 95 this.hasAlreadyAcceptedPrivacyConcern = true
@@ -118,8 +115,9 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
118 .subscribe(([ video, captionsResult ]) => { 115 .subscribe(([ video, captionsResult ]) => {
119 const startTime = this.route.snapshot.queryParams.start 116 const startTime = this.route.snapshot.queryParams.start
120 const subtitle = this.route.snapshot.queryParams.subtitle 117 const subtitle = this.route.snapshot.queryParams.subtitle
118 const playerMode = this.route.snapshot.queryParams.mode
121 119
122 this.onVideoFetched(video, captionsResult.data, { startTime, subtitle }) 120 this.onVideoFetched(video, captionsResult.data, { startTime, subtitle, playerMode })
123 .catch(err => this.handleError(err)) 121 .catch(err => this.handleError(err))
124 }) 122 })
125 }) 123 })
@@ -366,7 +364,11 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
366 ) 364 )
367 } 365 }
368 366
369 private async onVideoFetched (video: VideoDetails, videoCaptions: VideoCaption[], urlOptions: { startTime: number, subtitle: string }) { 367 private async onVideoFetched (
368 video: VideoDetails,
369 videoCaptions: VideoCaption[],
370 urlOptions: { startTime?: number, subtitle?: string, playerMode?: string }
371 ) {
370 this.video = video 372 this.video = video
371 373
372 // Re init attributes 374 // Re init attributes
@@ -402,41 +404,64 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
402 src: environment.apiUrl + c.captionPath 404 src: environment.apiUrl + c.captionPath
403 })) 405 }))
404 406
405 const videojsOptions = getVideojsOptions({ 407 const options: PeertubePlayerManagerOptions = {
406 autoplay: this.isAutoplay(), 408 common: {
407 inactivityTimeout: 2500, 409 autoplay: this.isAutoplay(),
408 videoFiles: this.video.files, 410
409 videoCaptions: playerCaptions, 411 playerElement: this.playerElement,
410 playerElement: this.playerElement, 412 onPlayerElementChange: (element: HTMLVideoElement) => this.playerElement = element,
411 videoViewUrl: this.video.privacy.id !== VideoPrivacy.PRIVATE ? this.videoService.getVideoViewUrl(this.video.uuid) : null, 413
412 videoDuration: this.video.duration, 414 videoDuration: this.video.duration,
413 enableHotkeys: true, 415 enableHotkeys: true,
414 peertubeLink: false, 416 inactivityTimeout: 2500,
415 poster: this.video.previewUrl, 417 poster: this.video.previewUrl,
416 startTime, 418 startTime,
417 subtitle: urlOptions.subtitle, 419
418 theaterMode: true, 420 theaterMode: true,
419 language: this.localeId, 421 captions: videoCaptions.length !== 0,
420 422 peertubeLink: false,
421 userWatching: this.user && this.user.videosHistoryEnabled === true ? { 423
422 url: this.videoService.getUserWatchingVideoUrl(this.video.uuid), 424 videoViewUrl: this.video.privacy.id !== VideoPrivacy.PRIVATE ? this.videoService.getVideoViewUrl(this.video.uuid) : null,
423 authorizationHeader: this.authService.getRequestHeaderValue() 425 embedUrl: this.video.embedUrl,
424 } : undefined 426
425 }) 427 language: this.localeId,
428
429 subtitle: urlOptions.subtitle,
430
431 userWatching: this.user && this.user.videosHistoryEnabled === true ? {
432 url: this.videoService.getUserWatchingVideoUrl(this.video.uuid),
433 authorizationHeader: this.authService.getRequestHeaderValue()
434 } : undefined,
435
436 serverUrl: environment.apiUrl,
426 437
427 if (this.videojsLocaleLoaded === false) { 438 videoCaptions: playerCaptions
428 await loadLocaleInVideoJS(environment.apiUrl, videojs, isOnDevLocale() ? getDevLocale() : this.localeId) 439 },
429 this.videojsLocaleLoaded = true 440
441 webtorrent: {
442 videoFiles: this.video.files
443 }
430 } 444 }
431 445
432 const self = this 446 const mode: PlayerMode = urlOptions.playerMode === 'p2p-media-loader' ? 'p2p-media-loader' : 'webtorrent'
433 this.zone.runOutsideAngular(async () => { 447
434 videojs(this.playerElement, videojsOptions, function (this: videojs.Player) { 448 if (mode === 'p2p-media-loader') {
435 self.player = this 449 const hlsPlaylist = this.video.getHlsPlaylist()
436 this.on('customError', ({ err }: { err: any }) => self.handleError(err))
437 450
438 addContextMenu(self.player, self.video.embedUrl) 451 const p2pMediaLoader = {
439 }) 452 playlistUrl: hlsPlaylist.playlistUrl,
453 segmentsSha256Url: hlsPlaylist.segmentsSha256Url,
454 redundancyBaseUrls: hlsPlaylist.redundancies.map(r => r.baseUrl),
455 trackerAnnounce: this.video.trackerUrls,
456 videoFiles: this.video.files
457 } as P2PMediaLoaderOptions
458
459 Object.assign(options, { p2pMediaLoader })
460 }
461
462 this.zone.runOutsideAngular(async () => {
463 this.player = await PeertubePlayerManager.initialize(mode, options)
464 this.player.on('customError', ({ err }: { err: any }) => this.handleError(err))
440 }) 465 })
441 466
442 this.setVideoDescriptionHTML() 467 this.setVideoDescriptionHTML()
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 881ab2174..6fd74e67a 100644
--- a/client/src/app/videos/video-list/video-trending.component.ts
+++ b/client/src/app/videos/video-list/video-trending.component.ts
@@ -39,18 +39,21 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit,
39 39
40 this.generateSyndicationList() 40 this.generateSyndicationList()
41 41
42 const trendingDays = this.serverService.getConfig().trending.videos.intervalDays 42 this.serverService.configLoaded.subscribe(
43 () => {
44 const trendingDays = this.serverService.getConfig().trending.videos.intervalDays
43 45
44 if (trendingDays === 1) { 46 if (trendingDays === 1) {
45 this.titlePage = this.i18n('Trending for the last 24 hours') 47 this.titlePage = this.i18n('Trending for the last 24 hours')
46 this.titleTooltip = this.i18n('Trending videos are those totalizing the greatest number of views during the last 24 hours.') 48 this.titleTooltip = this.i18n('Trending videos are those totalizing the greatest number of views during the last 24 hours.')
47 } else { 49 } else {
48 this.titlePage = this.i18n('Trending for the last {{days}} days', { days: trendingDays }) 50 this.titlePage = this.i18n('Trending for the last {{days}} days', { days: trendingDays })
49 this.titleTooltip = this.i18n( 51 this.titleTooltip = this.i18n(
50 'Trending videos are those totalizing the greatest number of views during the last {{days}} days.', 52 'Trending videos are those totalizing the greatest number of views during the last {{days}} days.',
51 { days: trendingDays } 53 { days: trendingDays }
52 ) 54 )
53 } 55 }
56 })
54 } 57 }
55 58
56 ngOnDestroy () { 59 ngOnDestroy () {
diff --git a/client/src/assets/images/global/add.svg b/client/src/assets/images/global/add.html
index 42b269c43..bfb0a52bc 100644
--- a/client/src/assets/images/global/add.svg
+++ b/client/src/assets/images/global/add.html
@@ -1,8 +1,6 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <defs></defs> 2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
4 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> 3 <g transform="translate(-92.000000, -115.000000)">
5 <g id="Artboard-4" transform="translate(-92.000000, -115.000000)">
6 <g id="2" transform="translate(92.000000, 115.000000)"> 4 <g id="2" transform="translate(92.000000, 115.000000)">
7 <circle id="Oval-1" stroke="#ffffff" stroke-width="2" cx="12" cy="12" r="10"></circle> 5 <circle id="Oval-1" stroke="#ffffff" stroke-width="2" cx="12" cy="12" r="10"></circle>
8 <rect id="Rectangle-1" fill="#ffffff" x="11" y="7" width="2" height="10" rx="1"></rect> 6 <rect id="Rectangle-1" fill="#ffffff" x="11" y="7" width="2" height="10" rx="1"></rect>
diff --git a/client/src/assets/images/video/alert.svg b/client/src/assets/images/global/alert.html
index 5b43534ad..7c8c02074 100644
--- a/client/src/assets/images/video/alert.svg
+++ b/client/src/assets/images/global/alert.html
@@ -1,11 +1,6 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch --> 2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
4 <title>alert</title> 3 <g transform="translate(-48.000000, -467.000000)">
5 <desc>Created with Sketch.</desc>
6 <defs></defs>
7 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
8 <g id="Artboard-4" transform="translate(-48.000000, -467.000000)">
9 <g id="161" transform="translate(48.000000, 467.000000)"> 4 <g id="161" transform="translate(48.000000, 467.000000)">
10 <path d="M12.8715755,3.50973876 L12,1.96027114 L11.1284245,3.50973876 L2.12842446,19.5097388 L1.29015252,21 L3,21 L21,21 L22.7098475,21 L21.8715755,19.5097388 L12.8715755,3.50973876 Z" id="Triangle-2" stroke="#000000" stroke-width="2" stroke-linejoin="round"></path> 5 <path d="M12.8715755,3.50973876 L12,1.96027114 L11.1284245,3.50973876 L2.12842446,19.5097388 L1.29015252,21 L3,21 L21,21 L22.7098475,21 L21.8715755,19.5097388 L12.8715755,3.50973876 Z" id="Triangle-2" stroke="#000000" stroke-width="2" stroke-linejoin="round"></path>
11 <path d="M12,17.75 C12.6903559,17.75 13.25,17.1903559 13.25,16.5 C13.25,15.8096441 12.6903559,15.25 12,15.25 C11.3096441,15.25 10.75,15.8096441 10.75,16.5 C10.75,17.1903559 11.3096441,17.75 12,17.75 Z" id="Oval-8" fill="#000000"></path> 6 <path d="M12,17.75 C12.6903559,17.75 13.25,17.1903559 13.25,16.5 C13.25,15.8096441 12.6903559,15.25 12,15.25 C11.3096441,15.25 10.75,15.8096441 10.75,16.5 C10.75,17.1903559 11.3096441,17.75 12,17.75 Z" id="Oval-8" fill="#000000"></path>
diff --git a/client/src/assets/images/global/circle-tick.html b/client/src/assets/images/global/circle-tick.html
new file mode 100644
index 000000000..2327de6be
--- /dev/null
+++ b/client/src/assets/images/global/circle-tick.html
@@ -0,0 +1,12 @@
1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
3 <g transform="translate(-400.000000, -1134.000000)" stroke="#000000" stroke-width="2">
4 <g id="Extras" transform="translate(48.000000, 1046.000000)">
5 <g id="yes" transform="translate(352.000000, 88.000000)">
6 <circle id="Oval-1" cx="12" cy="12" r="10"/>
7 <polyline id="Path-288" stroke-linecap="round" stroke-linejoin="round" points="8.5 12.5 10.5 14.5 15.5 9.5"/>
8 </g>
9 </g>
10 </g>
11 </g>
12</svg>
diff --git a/client/src/assets/images/global/cloud-download.html b/client/src/assets/images/global/cloud-download.html
new file mode 100644
index 000000000..b2634fd1f
--- /dev/null
+++ b/client/src/assets/images/global/cloud-download.html
@@ -0,0 +1,11 @@
1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
3 <g transform="translate(-356.000000, -775.000000)" stroke="#000000" stroke-width="2">
4 <g id="308" transform="translate(356.000000, 775.000000)">
5 <path d="M8,17 L5,17 L5,17 C2.790861,17 1,15.209139 1,13 C1,10.790861 2.790861,9 5,9 C5.35840468,9 5.70579988,9.04713713 6.03632437,9.13555013 C6.01233106,8.92702603 6,8.71495305 6,8.5 C6,5.46243388 8.46243388,3 11.5,3 C14.0673313,3 16.2238156,4.7590449 16.8299648,7.1376465 C17.2052921,7.04765874 17.5970804,7 18,7 C20.7614237,7 23,9.23857625 23,12 C23,14.7614237 20.7614237,17 18,17 L16,17" id="Combined-Shape" stroke-linejoin="round"></path>
6 <path d="M12,13 L12,21" id="Path-58"></path>
7 <polyline id="Path-59" stroke-linejoin="round" points="15 20 12 23 9 20"></polyline>
8 </g>
9 </g>
10 </g>
11</svg>
diff --git a/client/src/assets/images/global/cloud-error.html b/client/src/assets/images/global/cloud-error.html
new file mode 100644
index 000000000..1a3483805
--- /dev/null
+++ b/client/src/assets/images/global/cloud-error.html
@@ -0,0 +1,11 @@
1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
3 <g transform="translate(-400.000000, -775.000000)" stroke="#000000" stroke-width="2">
4 <g id="309" transform="translate(400.000000, 775.000000)">
5 <path d="M7,18 L5,18 C2.790861,18 1,16.209139 1,14 C1,11.790861 2.790861,10 5,10 C5.35840468,10 5.70579988,10.0471371 6.03632437,10.1355501 C6.01233106,9.92702603 6,9.71495305 6,9.5 C6,6.46243388 8.46243388,4 11.5,4 C14.0673313,4 16.2238156,5.7590449 16.8299648,8.1376465 C17.2052921,8.04765874 17.5970804,8 18,8 C20.7614237,8 23,10.2385763 23,13 C23,15.7614237 20.7614237,18 18,18 L17,18" id="Combined-Shape"></path>
6 <path d="M9,21 L15,15" id="Path-238"></path>
7 <path d="M9,21 L15,15" id="Path-238" transform="translate(12.000000, 18.000000) scale(-1, 1) translate(-12.000000, -18.000000) "></path>
8 </g>
9 </g>
10 </g>
11</svg>
diff --git a/client/src/assets/images/global/cog.html b/client/src/assets/images/global/cog.html
new file mode 100644
index 000000000..b74a180e7
--- /dev/null
+++ b/client/src/assets/images/global/cog.html
@@ -0,0 +1,9 @@
1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linejoin="round">
3 <g transform="translate(-796.000000, -159.000000)" stroke="#000000" stroke-width="2">
4 <g id="38" transform="translate(796.000000, 159.000000)">
5 <path d="M7.20852293,4.3800958 C8.05442158,3.84706631 8.99528987,3.45099725 10,3.22301642 L10,1.99980749 C10,1.44762906 10.4433532,1 11.0093689,1 L12.9906311,1 C13.5480902,1 14,1.44371665 14,1.99980749 L14,3.22301642 C15.0047101,3.45099725 15.9455784,3.84706631 16.7914771,4.3800958 L17.6569904,3.5145825 C18.0474395,3.12413339 18.6774591,3.12110988 19.0776926,3.52134344 L20.4786566,4.92230738 C20.8728396,5.31649045 20.8786331,5.94979402 20.4854175,6.34300963 L19.6199042,7.20852293 C20.1529337,8.05442158 20.5490027,8.99528987 20.7769836,10 L22.0001925,10 C22.5523709,10 23,10.4433532 23,11.0093689 L23,12.9906311 C23,13.5480902 22.5562834,14 22.0001925,14 L20.7769836,14 C20.5490027,15.0047101 20.1529337,15.9455784 19.6199042,16.7914771 L20.4854175,17.6569904 C20.8758666,18.0474395 20.8788901,18.6774591 20.4786566,19.0776926 L19.0776926,20.4786566 C18.6835095,20.8728396 18.050206,20.8786331 17.6569904,20.4854175 L16.7914771,19.6199042 C15.9455784,20.1529337 15.0047101,20.5490027 14,20.7769836 L14,22.0001925 C14,22.5523709 13.5566468,23 12.9906311,23 L11.0093689,23 C10.4519098,23 10,22.5562834 10,22.0001925 L10,20.7769836 C8.99528987,20.5490027 8.05442158,20.1529337 7.20852293,19.6199042 L6.34300963,20.4854175 C5.95256051,20.8758666 5.32254093,20.8788901 4.92230738,20.4786566 L3.52134344,19.0776926 C3.12716036,18.6835095 3.12136689,18.050206 3.5145825,17.6569904 L4.3800958,16.7914771 C3.84706631,15.9455784 3.45099725,15.0047101 3.22301642,14 L1.99980749,14 C1.44762906,14 1,13.5566468 1,12.9906311 L1,11.0093689 C1,10.4519098 1.44371665,10 1.99980749,10 L3.22301642,10 C3.45099725,8.99528987 3.84706631,8.05442158 4.3800958,7.20852293 L3.5145825,6.34300963 C3.12413339,5.95256051 3.12110988,5.32254093 3.52134344,4.92230738 L4.92230738,3.52134344 C5.31649045,3.12716036 5.94979402,3.12136689 6.34300963,3.5145825 L7.20852293,4.3800958 Z M12,16 C14.209139,16 16,14.209139 16,12 C16,9.790861 14.209139,8 12,8 C9.790861,8 8,9.790861 8,12 C8,14.209139 9.790861,16 12,16 Z" id="Combined-Shape"/>
6 </g>
7 </g>
8 </g>
9</svg>
diff --git a/client/src/assets/images/global/cross.svg b/client/src/assets/images/global/cross.html
index d47a75996..962578487 100644
--- a/client/src/assets/images/global/cross.svg
+++ b/client/src/assets/images/global/cross.html
@@ -1,8 +1,6 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <defs></defs> 2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
4 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round"> 3 <g transform="translate(-312.000000, -115.000000)" stroke="#000000" stroke-width="2">
5 <g id="Artboard-4" transform="translate(-312.000000, -115.000000)" stroke="#585858" stroke-width="2">
6 <g id="7" transform="translate(312.000000, 115.000000)"> 4 <g id="7" transform="translate(312.000000, 115.000000)">
7 <path d="M19,5 L5,19" id="Path-14"></path> 5 <path d="M19,5 L5,19" id="Path-14"></path>
8 <path d="M19,5 L5,19" id="Path-14" transform="translate(12.000000, 12.000000) scale(-1, 1) translate(-12.000000, -12.000000) "></path> 6 <path d="M19,5 L5,19" id="Path-14" transform="translate(12.000000, 12.000000) scale(-1, 1) translate(-12.000000, -12.000000) "></path>
diff --git a/client/src/assets/images/global/delete-black.svg b/client/src/assets/images/global/delete-black.svg
deleted file mode 100644
index 04ddc23aa..000000000
--- a/client/src/assets/images/global/delete-black.svg
+++ /dev/null
@@ -1,14 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <defs></defs>
4 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
5 <g id="Artboard-4" transform="translate(-224.000000, -159.000000)">
6 <g id="25" transform="translate(224.000000, 159.000000)">
7 <path d="M5,7 L5,20.0081158 C5,21.1082031 5.89706013,22 7.00585866,22 L16.9941413,22 C18.1019465,22 19,21.1066027 19,20.0081158 L19,7" id="Path-296" stroke="#000" stroke-width="2"></path>
8 <rect id="Rectangle-424" fill="#000" x="2" y="4" width="20" height="2" rx="1"></rect>
9 <path d="M9,10.9970301 C9,10.4463856 9.44386482,10 10,10 C10.5522847,10 11,10.4530363 11,10.9970301 L11,17.0029699 C11,17.5536144 10.5561352,18 10,18 C9.44771525,18 9,17.5469637 9,17.0029699 L9,10.9970301 Z M13,10.9970301 C13,10.4463856 13.4438648,10 14,10 C14.5522847,10 15,10.4530363 15,10.9970301 L15,17.0029699 C15,17.5536144 14.5561352,18 14,18 C13.4477153,18 13,17.5469637 13,17.0029699 L13,10.9970301 Z" id="Combined-Shape" fill="#000"></path>
10 <path d="M9,5 L9,2.99895656 C9,2.44724809 9.45097518,2 9.99077797,2 L14.009222,2 C14.5564136,2 15,2.44266033 15,2.99895656 L15,5" id="Path-33" stroke="#000" stroke-width="2" stroke-linejoin="round"></path>
11 </g>
12 </g>
13 </g>
14</svg>
diff --git a/client/src/assets/images/global/delete-grey.svg b/client/src/assets/images/global/delete-grey.svg
deleted file mode 100644
index 67e9e2ce7..000000000
--- a/client/src/assets/images/global/delete-grey.svg
+++ /dev/null
@@ -1,14 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <defs></defs>
4 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
5 <g id="Artboard-4" transform="translate(-224.000000, -159.000000)">
6 <g id="25" transform="translate(224.000000, 159.000000)">
7 <path d="M5,7 L5,20.0081158 C5,21.1082031 5.89706013,22 7.00585866,22 L16.9941413,22 C18.1019465,22 19,21.1066027 19,20.0081158 L19,7" id="Path-296" stroke="#585858" stroke-width="2"></path>
8 <rect id="Rectangle-424" fill="#585858" x="2" y="4" width="20" height="2" rx="1"></rect>
9 <path d="M9,10.9970301 C9,10.4463856 9.44386482,10 10,10 C10.5522847,10 11,10.4530363 11,10.9970301 L11,17.0029699 C11,17.5536144 10.5561352,18 10,18 C9.44771525,18 9,17.5469637 9,17.0029699 L9,10.9970301 Z M13,10.9970301 C13,10.4463856 13.4438648,10 14,10 C14.5522847,10 15,10.4530363 15,10.9970301 L15,17.0029699 C15,17.5536144 14.5561352,18 14,18 C13.4477153,18 13,17.5469637 13,17.0029699 L13,10.9970301 Z" id="Combined-Shape" fill="#585858"></path>
10 <path d="M9,5 L9,2.99895656 C9,2.44724809 9.45097518,2 9.99077797,2 L14.009222,2 C14.5564136,2 15,2.44266033 15,2.99895656 L15,5" id="Path-33" stroke="#585858" stroke-width="2" stroke-linejoin="round"></path>
11 </g>
12 </g>
13 </g>
14</svg>
diff --git a/client/src/assets/images/global/delete-white.svg b/client/src/assets/images/global/delete.html
index 9c52de557..a0d9a0cac 100644
--- a/client/src/assets/images/global/delete-white.svg
+++ b/client/src/assets/images/global/delete.html
@@ -1,13 +1,11 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <defs></defs> 2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
4 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> 3 <g transform="translate(-224.000000, -159.000000)">
5 <g id="Artboard-4" transform="translate(-224.000000, -159.000000)">
6 <g id="25" transform="translate(224.000000, 159.000000)"> 4 <g id="25" transform="translate(224.000000, 159.000000)">
7 <path d="M5,7 L5,20.0081158 C5,21.1082031 5.89706013,22 7.00585866,22 L16.9941413,22 C18.1019465,22 19,21.1066027 19,20.0081158 L19,7" id="Path-296" stroke="#ffffff" stroke-width="2"></path> 5 <path d="M5,7 L5,20.0081158 C5,21.1082031 5.89706013,22 7.00585866,22 L16.9941413,22 C18.1019465,22 19,21.1066027 19,20.0081158 L19,7" id="Path-296" stroke="#000000" stroke-width="2"></path>
8 <rect id="Rectangle-424" fill="#ffffff" x="2" y="4" width="20" height="2" rx="1"></rect> 6 <rect id="Rectangle-424" fill="#000000" x="2" y="4" width="20" height="2" rx="1"></rect>
9 <path d="M9,10.9970301 C9,10.4463856 9.44386482,10 10,10 C10.5522847,10 11,10.4530363 11,10.9970301 L11,17.0029699 C11,17.5536144 10.5561352,18 10,18 C9.44771525,18 9,17.5469637 9,17.0029699 L9,10.9970301 Z M13,10.9970301 C13,10.4463856 13.4438648,10 14,10 C14.5522847,10 15,10.4530363 15,10.9970301 L15,17.0029699 C15,17.5536144 14.5561352,18 14,18 C13.4477153,18 13,17.5469637 13,17.0029699 L13,10.9970301 Z" id="Combined-Shape" fill="#ffffff"></path> 7 <path d="M9,10.9970301 C9,10.4463856 9.44386482,10 10,10 C10.5522847,10 11,10.4530363 11,10.9970301 L11,17.0029699 C11,17.5536144 10.5561352,18 10,18 C9.44771525,18 9,17.5469637 9,17.0029699 L9,10.9970301 Z M13,10.9970301 C13,10.4463856 13.4438648,10 14,10 C14.5522847,10 15,10.4530363 15,10.9970301 L15,17.0029699 C15,17.5536144 14.5561352,18 14,18 C13.4477153,18 13,17.5469637 13,17.0029699 L13,10.9970301 Z" id="Combined-Shape" fill="#000000"></path>
10 <path d="M9,5 L9,2.99895656 C9,2.44724809 9.45097518,2 9.99077797,2 L14.009222,2 C14.5564136,2 15,2.44266033 15,2.99895656 L15,5" id="Path-33" stroke="#ffffff" stroke-width="2" stroke-linejoin="round"></path> 8 <path d="M9,5 L9,2.99895656 C9,2.44724809 9.45097518,2 9.99077797,2 L14.009222,2 C14.5564136,2 15,2.44266033 15,2.99895656 L15,5" id="Path-33" stroke="#000000" stroke-width="2" stroke-linejoin="round"></path>
11 </g> 9 </g>
12 </g> 10 </g>
13 </g> 11 </g>
diff --git a/client/src/assets/images/video/download-black.svg b/client/src/assets/images/global/download.html
index 501836746..259506f31 100644
--- a/client/src/assets/images/video/download-black.svg
+++ b/client/src/assets/images/global/download.html
@@ -1,11 +1,6 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch --> 2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
4 <title>download</title> 3 <g transform="translate(-180.000000, -291.000000)" stroke="#000000" stroke-width="2">
5 <desc>Created with Sketch.</desc>
6 <defs></defs>
7 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
8 <g id="Artboard-4" transform="translate(-180.000000, -291.000000)" stroke="#000000" stroke-width="2">
9 <g id="84" transform="translate(180.000000, 291.000000)"> 4 <g id="84" transform="translate(180.000000, 291.000000)">
10 <path d="M12,3 L12,15" id="Path-58"></path> 5 <path d="M12,3 L12,15" id="Path-58"></path>
11 <polyline id="Path-59" stroke-linejoin="round" transform="translate(12.000000, 14.000000) rotate(-270.000000) translate(-12.000000, -14.000000) " points="9 8 15 14 9 20"></polyline> 6 <polyline id="Path-59" stroke-linejoin="round" transform="translate(12.000000, 14.000000) rotate(-270.000000) translate(-12.000000, -14.000000) " points="9 8 15 14 9 20"></polyline>
diff --git a/client/src/assets/images/global/edit-black.svg b/client/src/assets/images/global/edit-black.svg
deleted file mode 100644
index 0176b0f37..000000000
--- a/client/src/assets/images/global/edit-black.svg
+++ /dev/null
@@ -1,15 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
4 <title>edit</title>
5 <desc>Created with Sketch.</desc>
6 <defs></defs>
7 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
8 <g id="Artboard-4" transform="translate(-48.000000, -203.000000)" stroke="#000000" stroke-width="2">
9 <g id="41" transform="translate(48.000000, 203.000000)">
10 <path d="M3,21.0000003 L3,17 L15.8898356,4.11016442 C17.0598483,2.9401517 18.9638992,2.94723715 20.1306896,4.11402752 L19.9181432,3.90148112 C21.0902894,5.07362738 21.0882407,6.97202708 19.9174652,8.1377941 L7,21.0000003 L3,21.0000003 Z" id="Path-74" stroke-linecap="round" stroke-linejoin="round"></path>
11 <path d="M14.5,5.5 L18.5,9.5" id="Path-75"></path>
12 </g>
13 </g>
14 </g>
15</svg>
diff --git a/client/src/assets/images/global/edit-grey.svg b/client/src/assets/images/global/edit.html
index 23ece68f1..f04183c2d 100644
--- a/client/src/assets/images/global/edit-grey.svg
+++ b/client/src/assets/images/global/edit.html
@@ -1,11 +1,6 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch --> 2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
4 <title>edit</title> 3 <g transform="translate(-48.000000, -203.000000)" stroke="#000000" stroke-width="2">
5 <desc>Created with Sketch.</desc>
6 <defs></defs>
7 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
8 <g id="Artboard-4" transform="translate(-48.000000, -203.000000)" stroke="#585858" stroke-width="2">
9 <g id="41" transform="translate(48.000000, 203.000000)"> 4 <g id="41" transform="translate(48.000000, 203.000000)">
10 <path d="M3,21.0000003 L3,17 L15.8898356,4.11016442 C17.0598483,2.9401517 18.9638992,2.94723715 20.1306896,4.11402752 L19.9181432,3.90148112 C21.0902894,5.07362738 21.0882407,6.97202708 19.9174652,8.1377941 L7,21.0000003 L3,21.0000003 Z" id="Path-74" stroke-linecap="round" stroke-linejoin="round"></path> 5 <path d="M3,21.0000003 L3,17 L15.8898356,4.11016442 C17.0598483,2.9401517 18.9638992,2.94723715 20.1306896,4.11402752 L19.9181432,3.90148112 C21.0902894,5.07362738 21.0882407,6.97202708 19.9174652,8.1377941 L7,21.0000003 L3,21.0000003 Z" id="Path-74" stroke-linecap="round" stroke-linejoin="round"></path>
11 <path d="M14.5,5.5 L18.5,9.5" id="Path-75"></path> 6 <path d="M14.5,5.5 L18.5,9.5" id="Path-75"></path>
diff --git a/client/src/assets/images/global/help.svg b/client/src/assets/images/global/help.html
index 48252febe..80cd40321 100644
--- a/client/src/assets/images/global/help.svg
+++ b/client/src/assets/images/global/help.html
@@ -1,12 +1,10 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <defs></defs> 2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
4 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> 3 <g transform="translate(-400.000000, -247.000000)">
5 <g id="Artboard-4" transform="translate(-400.000000, -247.000000)">
6 <g id="69" transform="translate(400.000000, 247.000000)"> 4 <g id="69" transform="translate(400.000000, 247.000000)">
7 <circle id="Oval-7" stroke="#333333" stroke-width="2" cx="12" cy="12" r="10"></circle> 5 <circle id="Oval-7" stroke="#000000" stroke-width="2" cx="12" cy="12" r="10"></circle>
8 <path d="M12.016,14.544 C12.384,14.544 12.64,14.256 12.704,13.904 L12.768,13.168 C14.544,12.864 16,11.952 16,9.936 L16,9.904 C16,7.904 14.48,6.656 12.24,6.656 C10.768,6.656 9.696,7.184 8.848,7.984 C8.624,8.176 8.528,8.432 8.528,8.672 C8.528,9.152 8.928,9.552 9.424,9.552 C9.648,9.552 9.856,9.456 10.016,9.328 C10.656,8.752 11.344,8.448 12.192,8.448 C13.344,8.448 14.032,9.072 14.032,9.968 L14.032,10 C14.032,11.008 13.2,11.584 11.696,11.728 C11.264,11.776 11.008,12.096 11.072,12.528 L11.232,13.904 C11.28,14.272 11.552,14.544 11.92,14.544 L12.016,14.544 Z M10.784,16.816 L10.784,16.976 C10.784,17.6 11.264,18.08 11.92,18.08 C12.576,18.08 13.056,17.6 13.056,16.976 L13.056,16.816 C13.056,16.192 12.576,15.712 11.92,15.712 C11.264,15.712 10.784,16.192 10.784,16.816 Z" id="?" fill="#333333"></path> 6 <path d="M12.016,14.544 C12.384,14.544 12.64,14.256 12.704,13.904 L12.768,13.168 C14.544,12.864 16,11.952 16,9.936 L16,9.904 C16,7.904 14.48,6.656 12.24,6.656 C10.768,6.656 9.696,7.184 8.848,7.984 C8.624,8.176 8.528,8.432 8.528,8.672 C8.528,9.152 8.928,9.552 9.424,9.552 C9.648,9.552 9.856,9.456 10.016,9.328 C10.656,8.752 11.344,8.448 12.192,8.448 C13.344,8.448 14.032,9.072 14.032,9.968 L14.032,10 C14.032,11.008 13.2,11.584 11.696,11.728 C11.264,11.776 11.008,12.096 11.072,12.528 L11.232,13.904 C11.28,14.272 11.552,14.544 11.92,14.544 L12.016,14.544 Z M10.784,16.816 L10.784,16.976 C10.784,17.6 11.264,18.08 11.92,18.08 C12.576,18.08 13.056,17.6 13.056,16.976 L13.056,16.816 C13.056,16.192 12.576,15.712 11.92,15.712 C11.264,15.712 10.784,16.192 10.784,16.816 Z" id="?" fill="#000000"></path>
9 </g> 7 </g>
10 </g> 8 </g>
11 </g> 9 </g>
12</svg> \ No newline at end of file 10</svg>
diff --git a/client/src/assets/images/global/im-with-her.svg b/client/src/assets/images/global/im-with-her.html
index 31d4754fd..de2c62e96 100644
--- a/client/src/assets/images/global/im-with-her.svg
+++ b/client/src/assets/images/global/im-with-her.html
@@ -1,15 +1,10 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch --> 2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
4 <title>im-with-her</title> 3 <g transform="translate(-708.000000, -467.000000)">
5 <desc>Created with Sketch.</desc>
6 <defs></defs>
7 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
8 <g id="Artboard-4" transform="translate(-708.000000, -467.000000)">
9 <g id="176" transform="translate(708.000000, 467.000000)"> 4 <g id="176" transform="translate(708.000000, 467.000000)">
10 <path d="M8,9 L8,3.99339768 C8,3.44494629 7.55641359,3 7.00922203,3 L2.99077797,3 C2.45097518,3 2,3.44475929 2,3.99339768 L2,20.0066023 C2,20.5550537 2.44358641,21 2.99077797,21 L7.00922203,21 C7.54902482,21 8,20.5552407 8,20.0066023 L8,15 L14,15 L14,20.0066023 C14,20.5550537 14.4435864,21 14.990778,21 L19.009222,21 C19.5490248,21 20,20.5564587 20,20.0093228 L20,15.0006104 L23,12 L20,8.99267578 L20,4.00303919 C20,3.45042467 19.5564136,3 19.009222,3 L14.990778,3 C14.4509752,3 14,3.44475929 14,3.99339768 L14,9 L8,9 Z" id="Combined-Shape" fill="#333333" opacity="0.5"></path> 5 <path d="M8,9 L8,3.99339768 C8,3.44494629 7.55641359,3 7.00922203,3 L2.99077797,3 C2.45097518,3 2,3.44475929 2,3.99339768 L2,20.0066023 C2,20.5550537 2.44358641,21 2.99077797,21 L7.00922203,21 C7.54902482,21 8,20.5552407 8,20.0066023 L8,15 L14,15 L14,20.0066023 C14,20.5550537 14.4435864,21 14.990778,21 L19.009222,21 C19.5490248,21 20,20.5564587 20,20.0093228 L20,15.0006104 L23,12 L20,8.99267578 L20,4.00303919 C20,3.45042467 19.5564136,3 19.009222,3 L14.990778,3 C14.4509752,3 14,3.44475929 14,3.99339768 L14,9 L8,9 Z" id="Combined-Shape" fill="#000000" opacity="0.5"></path>
11 <path d="M2,9 L14,9 L14,3.99077797 C14,3.44358641 14.3203148,3.32031476 14.7062149,3.7062149 L23,12 L14.7062149,20.2937851 C14.3161832,20.6838168 14,20.5490248 14,20.009222 L14,15 L2,15 L2,9 Z" id="Rectangle-121" fill-opacity="0.5" fill="#000000"></path> 6 <path d="M2,9 L14,9 L14,3.99077797 C14,3.44358641 14.3203148,3.32031476 14.7062149,3.7062149 L23,12 L14.7062149,20.2937851 C14.3161832,20.6838168 14,20.5490248 14,20.009222 L14,15 L2,15 L2,9 Z" id="Rectangle-121" fill-opacity="0.5" fill="#000000"></path>
12 </g> 7 </g>
13 </g> 8 </g>
14 </g> 9 </g>
15</svg> \ No newline at end of file 10</svg>
diff --git a/client/src/assets/images/global/no.html b/client/src/assets/images/global/no.html
new file mode 100644
index 000000000..bb7b28514
--- /dev/null
+++ b/client/src/assets/images/global/no.html
@@ -0,0 +1,10 @@
1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
3 <g transform="translate(-312.000000, -863.000000)" stroke="#000000" stroke-width="2">
4 <g id="347" transform="translate(312.000000, 863.000000)">
5 <circle id="Oval-196" cx="12" cy="12" r="9"></circle>
6 <path d="M18,18 L6,6" id="Path-275"></path>
7 </g>
8 </g>
9 </g>
10</svg>
diff --git a/client/src/assets/images/global/sparkle.html b/client/src/assets/images/global/sparkle.html
new file mode 100644
index 000000000..3b29fefb9
--- /dev/null
+++ b/client/src/assets/images/global/sparkle.html
@@ -0,0 +1,11 @@
1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
3 <g transform="translate(-488.000000, -731.000000)" stroke="#000000" stroke-width="2">
4 <g id="291" transform="translate(488.000000, 731.000000)">
5 <path d="M10,9 C8.5,7.5 8,3 8,3 C8,3 7.5,7.5 6,9 C4.5,10.5 2,11 2,11 C2,11 4.5,11.5 6,13 C7.5,14.5 8,19 8,19 C8,19 8.5,14.5 10,13 C11.5,11.5 14,11 14,11 C14,11 11.5,10.5 10,9 Z" id="Combined-Shape"></path>
6 <path d="M19.6666667,4.75 C18.7916667,3.8125 18.5,1 18.5,1 C18.5,1 18.2083333,3.8125 17.3333333,4.75 C16.4583333,5.6875 15,6 15,6 C15,6 16.4583333,6.3125 17.3333333,7.25 C18.2083333,8.1875 18.5,11 18.5,11 C18.5,11 18.7916667,8.1875 19.6666667,7.25 C20.5416667,6.3125 22,6 22,6 C22,6 20.5416667,5.6875 19.6666667,4.75 Z" id="Combined-Shape"></path>
7 <path d="M17,17 C16.25,16.25 16,14 16,14 C16,14 15.75,16.25 15,17 C14.25,17.75 13,18 13,18 C13,18 14.25,18.25 15,19 C15.75,19.75 16,22 16,22 C16,22 16.25,19.75 17,19 C17.75,18.25 19,18 19,18 C19,18 17.75,17.75 17,17 Z" id="Combined-Shape"></path>
8 </g>
9 </g>
10 </g>
11</svg>
diff --git a/client/src/assets/images/global/syndication.svg b/client/src/assets/images/global/syndication.html
index cb74cf81b..e6c88a4db 100644
--- a/client/src/assets/images/global/syndication.svg
+++ b/client/src/assets/images/global/syndication.html
@@ -1,10 +1,8 @@
1<?xml version="1.0" encoding="iso-8859-1"?>
2<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" 1<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4 viewBox="0 0 559.372 559.372" style="enable-background:new 0 0 559.372 559.372;" xml:space="preserve"> 2 viewBox="0 0 559.372 559.372" style="enable-background:new 0 0 559.372 559.372;" xml:space="preserve">
5<g> 3<g>
6 <g> 4 <g>
7 <path style="fill:#010002;" d="M53.244,0.002c46.512,0,91.29,6.018,134.334,18.054s83.334,29.07,120.869,51.102 5 <path fill="#000000" d="M53.244,0.002c46.512,0,91.29,6.018,134.334,18.054s83.334,29.07,120.869,51.102
8 c37.537,22.032,71.707,48.45,102.514,79.254c30.803,30.804,57.221,64.974,79.254,102.51 6 c37.537,22.032,71.707,48.45,102.514,79.254c30.803,30.804,57.221,64.974,79.254,102.51
9 c22.029,37.539,39.063,77.828,51.102,120.873c12.037,43.043,18.055,87.818,18.055,134.334c0,14.688-5.201,27.23-15.605,37.637 7 c22.029,37.539,39.063,77.828,51.102,120.873c12.037,43.043,18.055,87.818,18.055,134.334c0,14.688-5.201,27.23-15.605,37.637
10 c-10.404,10.407-22.949,15.604-37.637,15.604c-14.689,0-27.234-5.199-37.641-15.604c-10.402-10.404-15.604-22.949-15.604-37.637 8 c-10.404,10.407-22.949,15.604-37.637,15.604c-14.689,0-27.234-5.199-37.641-15.604c-10.402-10.404-15.604-22.949-15.604-37.637
diff --git a/client/src/assets/images/global/tick.svg b/client/src/assets/images/global/tick.html
index 230caa111..4784b4807 100644
--- a/client/src/assets/images/global/tick.svg
+++ b/client/src/assets/images/global/tick.html
@@ -1,8 +1,6 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <defs></defs> 2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
4 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round"> 3 <g transform="translate(-356.000000, -115.000000)" stroke="#000000" stroke-width="2">
5 <g id="Artboard-4" transform="translate(-356.000000, -115.000000)" stroke="#585858" stroke-width="2">
6 <g id="8" transform="translate(356.000000, 115.000000)"> 4 <g id="8" transform="translate(356.000000, 115.000000)">
7 <path d="M21,6 L9,18" id="Path-14"></path> 5 <path d="M21,6 L9,18" id="Path-14"></path>
8 <path d="M9,13 L4,18" id="Path-14" transform="translate(6.500000, 15.500000) scale(-1, 1) translate(-6.500000, -15.500000) "></path> 6 <path d="M9,13 L4,18" id="Path-14" transform="translate(6.500000, 15.500000) scale(-1, 1) translate(-6.500000, -15.500000) "></path>
diff --git a/client/src/assets/images/global/undo.html b/client/src/assets/images/global/undo.html
new file mode 100644
index 000000000..228245c86
--- /dev/null
+++ b/client/src/assets/images/global/undo.html
@@ -0,0 +1,9 @@
1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
3 <g transform="translate(-180.000000, -115.000000)" fill="#000000">
4 <g id="4" transform="translate(180.000000, 115.000000)">
5 <path d="M10,19 C10.5522847,19 11,19.4477153 11,20 C11,20.5522847 10.5522847,21 10,21 C9.99404288,21 9.98809793,20.9999479 9.98216558,20.9998442 C5.01980239,20.990358 1,16.9646166 1,12 C1,7.02943725 5.02943725,3 10,3 C14.9705627,3 19,7.02943725 19,12 L17,12 C17,8.13400675 13.8659932,5 10,5 C6.13400675,5 3,8.13400675 3,12 C3,15.8659932 6.13400675,19 10,19 Z M14,12 L22,12 L18,16 L14,12 Z" id="Combined-Shape" transform="translate(11.500000, 12.000000) scale(-1, 1) translate(-11.500000, -12.000000) "/>
6 </g>
7 </g>
8 </g>
9</svg>
diff --git a/client/src/assets/images/global/undo.svg b/client/src/assets/images/global/undo.svg
deleted file mode 100644
index f1cca03f7..000000000
--- a/client/src/assets/images/global/undo.svg
+++ /dev/null
@@ -1,11 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <defs></defs>
4 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
5 <g id="Artboard-4" transform="translate(-180.000000, -115.000000)" fill="#000">
6 <g id="4" transform="translate(180.000000, 115.000000)">
7 <path d="M10,19 C10.5522847,19 11,19.4477153 11,20 C11,20.5522847 10.5522847,21 10,21 C9.99404288,21 9.98809793,20.9999479 9.98216558,20.9998442 C5.01980239,20.990358 1,16.9646166 1,12 C1,7.02943725 5.02943725,3 10,3 C14.9705627,3 19,7.02943725 19,12 L17,12 C17,8.13400675 13.8659932,5 10,5 C6.13400675,5 3,8.13400675 3,12 C3,15.8659932 6.13400675,19 10,19 Z M14,12 L22,12 L18,16 L14,12 Z" id="Combined-Shape" transform="translate(11.500000, 12.000000) scale(-1, 1) translate(-11.500000, -12.000000) "></path>
8 </g>
9 </g>
10 </g>
11</svg>
diff --git a/client/src/assets/images/global/user-add.html b/client/src/assets/images/global/user-add.html
new file mode 100644
index 000000000..57df23c74
--- /dev/null
+++ b/client/src/assets/images/global/user-add.html
@@ -0,0 +1,11 @@
1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
3 <g transform="translate(-136.000000, -863.000000)">
4 <g id="343" transform="translate(136.000000, 863.000000)">
5 <path d="M14.2571621,15 L7,15 C4.20063223,15 2.390348,16.1679253 1.5255785,18.0896353 C1.07423388,19.0926234 0.949016905,20.1108713 0.995546634,20.9698816 C0.998604759,21.0263393 1.0014872,21.0632937 1.00496281,21.0995037 C1.0599172,21.6490476 1.54995985,22.0499916 2.09950372,21.9950372 C2.64904758,21.9400828 3.04999158,21.4500401 2.99503719,20.9004963 C2.99555422,20.9071205 2.99399879,20.8871791 2.99261905,20.8617069 C2.96185588,20.2937714 3.05021139,19.575276 3.34942151,18.9103647 C3.890902,17.7070747 4.98686778,17 7,17 L12.0070975,17 L13.2070325,17 C13.4170071,16.2576107 13.7789623,15.5790321 14.2571621,15 Z" id="Path-41" fill="#000000" fill-rule="nonzero"></path>
6 <path d="M19,18 L19,16.4976988 C19,16.2228273 18.7680664,16 18.5,16 C18.2238576,16 18,16.2148438 18,16.4976988 L18,18 L16.4976988,18 C16.2148438,18 16,18.2238576 16,18.5 C16,18.7680664 16.2228273,19 16.4976988,19 L18,19 L18,20.5023012 C18,20.7771727 18.2319336,21 18.5,21 C18.7761424,21 19,20.7851562 19,20.5023012 L19,19 L20.5023012,19 C20.7851562,19 21,18.7761424 21,18.5 C21,18.2319336 20.7771727,18 20.5023012,18 L19,18 Z M18.5,23 C16.0147186,23 14,20.9852814 14,18.5 C14,16.0147186 16.0147186,14 18.5,14 C20.9852814,14 23,16.0147186 23,18.5 C23,20.9852814 20.9852814,23 18.5,23 Z" id="Combined-Shape" fill="#000000"></path>
7 <circle id="Oval-40" stroke="#000000" stroke-width="2" cx="12" cy="8" r="5"></circle>
8 </g>
9 </g>
10 </g>
11</svg>
diff --git a/client/src/assets/images/global/validate.svg b/client/src/assets/images/global/validate.html
index 5c7ee9d14..520624ff6 100644
--- a/client/src/assets/images/global/validate.svg
+++ b/client/src/assets/images/global/validate.html
@@ -1,8 +1,6 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <defs></defs> 2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
4 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> 3 <g transform="translate(-400.000000, -1134.000000)" stroke="#000000" stroke-width="2">
5 <g id="Artboard-4" transform="translate(-400.000000, -1134.000000)" stroke="#ffffff" stroke-width="2">
6 <g id="Extras" transform="translate(48.000000, 1046.000000)"> 4 <g id="Extras" transform="translate(48.000000, 1046.000000)">
7 <g id="yes" transform="translate(352.000000, 88.000000)"> 5 <g id="yes" transform="translate(352.000000, 88.000000)">
8 <circle id="Oval-1" cx="12" cy="12" r="10"></circle> 6 <circle id="Oval-1" cx="12" cy="12" r="10"></circle>
diff --git a/client/src/assets/images/video/blacklist.svg b/client/src/assets/images/video/blacklist.svg
deleted file mode 100644
index 431c73816..000000000
--- a/client/src/assets/images/video/blacklist.svg
+++ /dev/null
@@ -1,15 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
4 <title>no</title>
5 <desc>Created with Sketch.</desc>
6 <defs></defs>
7 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
8 <g id="Artboard-4" transform="translate(-312.000000, -863.000000)" stroke="#000000" stroke-width="2">
9 <g id="347" transform="translate(312.000000, 863.000000)">
10 <circle id="Oval-196" cx="12" cy="12" r="9"></circle>
11 <path d="M18,18 L6,6" id="Path-275"></path>
12 </g>
13 </g>
14 </g>
15</svg>
diff --git a/client/src/assets/images/video/dislike-white.svg b/client/src/assets/images/video/dislike-white.svg
deleted file mode 100644
index cfc6eaa1f..000000000
--- a/client/src/assets/images/video/dislike-white.svg
+++ /dev/null
@@ -1,14 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <defs></defs>
4 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
5 <g id="Artboard-4" transform="translate(-752.000000, -1090.000000)" stroke="#ffffff" stroke-width="2">
6 <g id="Extras" transform="translate(48.000000, 1046.000000)">
7 <g id="thumbs-down" transform="translate(704.000000, 44.000000)">
8 <path d="M6,16 C6,18.5 6.5,21 8,21 L16.9938335,21 C17.5495239,21 18.1819788,20.5956028 18.4072817,20.0949295 L20.8562951,14.6526776 C21.7640882,12.6353595 20.7154925,11 18.5092545,11 L15.5,11 C15.5,11 18.5,5 15,5 C12.5,5 11.5,11 8,11 C6.5,11 6,13.5 6,16 Z" id="Path-188" stroke-linejoin="round" transform="translate(13.591488, 13.000000) scale(1, -1) translate(-13.591488, -13.000000) "></path>
9 <path d="M4,4.5 C4,4.5 3,7 3,10 C3,13 4,15.5 4,15.5" id="Path-189" transform="translate(3.500000, 10.000000) scale(1, -1) translate(-3.500000, -10.000000) "></path>
10 </g>
11 </g>
12 </g>
13 </g>
14</svg>
diff --git a/client/src/assets/images/video/dislike-grey.svg b/client/src/assets/images/video/dislike.html
index 56a7908fb..acde951e2 100644
--- a/client/src/assets/images/video/dislike-grey.svg
+++ b/client/src/assets/images/video/dislike.html
@@ -1,8 +1,6 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <defs></defs> 2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
4 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round"> 3 <g transform="translate(-752.000000, -1090.000000)" stroke="#000000" stroke-width="2">
5 <g id="Artboard-4" transform="translate(-752.000000, -1090.000000)" stroke="#585858" stroke-width="2">
6 <g id="Extras" transform="translate(48.000000, 1046.000000)"> 4 <g id="Extras" transform="translate(48.000000, 1046.000000)">
7 <g id="thumbs-down" transform="translate(704.000000, 44.000000)"> 5 <g id="thumbs-down" transform="translate(704.000000, 44.000000)">
8 <path d="M6,16 C6,18.5 6.5,21 8,21 L16.9938335,21 C17.5495239,21 18.1819788,20.5956028 18.4072817,20.0949295 L20.8562951,14.6526776 C21.7640882,12.6353595 20.7154925,11 18.5092545,11 L15.5,11 C15.5,11 18.5,5 15,5 C12.5,5 11.5,11 8,11 C6.5,11 6,13.5 6,16 Z" id="Path-188" stroke-linejoin="round" transform="translate(13.591488, 13.000000) scale(1, -1) translate(-13.591488, -13.000000) "></path> 6 <path d="M6,16 C6,18.5 6.5,21 8,21 L16.9938335,21 C17.5495239,21 18.1819788,20.5956028 18.4072817,20.0949295 L20.8562951,14.6526776 C21.7640882,12.6353595 20.7154925,11 18.5092545,11 L15.5,11 C15.5,11 18.5,5 15,5 C12.5,5 11.5,11 8,11 C6.5,11 6,13.5 6,16 Z" id="Path-188" stroke-linejoin="round" transform="translate(13.591488, 13.000000) scale(1, -1) translate(-13.591488, -13.000000) "></path>
diff --git a/client/src/assets/images/video/download-grey.svg b/client/src/assets/images/video/download-grey.svg
deleted file mode 100644
index 5b0cca5ef..000000000
--- a/client/src/assets/images/video/download-grey.svg
+++ /dev/null
@@ -1,16 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
4 <title>download</title>
5 <desc>Created with Sketch.</desc>
6 <defs></defs>
7 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
8 <g id="Artboard-4" transform="translate(-180.000000, -291.000000)" stroke="#585858" stroke-width="2">
9 <g id="84" transform="translate(180.000000, 291.000000)">
10 <path d="M12,3 L12,15" id="Path-58"></path>
11 <polyline id="Path-59" stroke-linejoin="round" transform="translate(12.000000, 14.000000) rotate(-270.000000) translate(-12.000000, -14.000000) " points="9 8 15 14 9 20"></polyline>
12 <path d="M3,18 L3,20.0590859 C3,20.6127331 3.44494889,21.0615528 3.99340349,21.0615528 L20.0067018,21.0615528 C20.5553434,21.0615528 21.0001052,20.6098102 21.0001051,20.0590859 L21.0001049,18" id="Path-12" stroke-linejoin="round"></path>
13 </g>
14 </g>
15 </g>
16</svg>
diff --git a/client/src/assets/images/video/download-white.svg b/client/src/assets/images/video/download-white.svg
deleted file mode 100644
index 0e66e06e8..000000000
--- a/client/src/assets/images/video/download-white.svg
+++ /dev/null
@@ -1,16 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
4 <title>download</title>
5 <desc>Created with Sketch.</desc>
6 <defs></defs>
7 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
8 <g id="Artboard-4" transform="translate(-180.000000, -291.000000)" stroke="#ffffff" stroke-width="2">
9 <g id="84" transform="translate(180.000000, 291.000000)">
10 <path d="M12,3 L12,15" id="Path-58"></path>
11 <polyline id="Path-59" stroke-linejoin="round" transform="translate(12.000000, 14.000000) rotate(-270.000000) translate(-12.000000, -14.000000) " points="9 8 15 14 9 20"></polyline>
12 <path d="M3,18 L3,20.0590859 C3,20.6127331 3.44494889,21.0615528 3.99340349,21.0615528 L20.0067018,21.0615528 C20.5553434,21.0615528 21.0001052,20.6098102 21.0001051,20.0590859 L21.0001049,18" id="Path-12" stroke-linejoin="round"></path>
13 </g>
14 </g>
15 </g>
16</svg>
diff --git a/client/src/assets/images/video/heart.svg b/client/src/assets/images/video/heart.html
index 5d64aee0f..618f64f10 100644
--- a/client/src/assets/images/video/heart.svg
+++ b/client/src/assets/images/video/heart.html
@@ -1,9 +1,7 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <defs></defs> 2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
4 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> 3 <g transform="translate(-48.000000, -1046.000000)" fill-rule="nonzero" fill="#000000">
5 <g id="Artboard-4" transform="translate(-48.000000, -1046.000000)" fill-rule="nonzero" fill="#585858"> 4 <g transform="translate(48.000000, 1046.000000)">
6 <g id="Extras" transform="translate(48.000000, 1046.000000)">
7 <g id="heart"> 5 <g id="heart">
8 <path d="M12.0174466,21 L20.9041801,11.3556763 C22.6291961,9.13778099 22.2795957,5.90145416 20.1233257,4.12713796 C17.9670557,2.35282175 14.8206518,2.71241362 13.0956358,4.93030888 L12.0174465,6.5 L10.9043642,4.93030888 C9.17934824,2.71241362 6.0329443,2.35282175 3.87667432,4.12713796 C1.72040435,5.90145416 1.37080391,9.13778099 3.09581989,11.3556763 L12.0174466,21 Z"></path> 6 <path d="M12.0174466,21 L20.9041801,11.3556763 C22.6291961,9.13778099 22.2795957,5.90145416 20.1233257,4.12713796 C17.9670557,2.35282175 14.8206518,2.71241362 13.0956358,4.93030888 L12.0174465,6.5 L10.9043642,4.93030888 C9.17934824,2.71241362 6.0329443,2.35282175 3.87667432,4.12713796 C1.72040435,5.90145416 1.37080391,9.13778099 3.09581989,11.3556763 L12.0174466,21 Z"></path>
9 </g> 7 </g>
diff --git a/client/src/assets/images/video/like-white.svg b/client/src/assets/images/video/like-white.svg
deleted file mode 100644
index 88e5f6a9a..000000000
--- a/client/src/assets/images/video/like-white.svg
+++ /dev/null
@@ -1,15 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
4 <title>thumbs-up</title>
5 <desc>Created with Sketch.</desc>
6 <defs></defs>
7 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
8 <g id="Artboard-4" transform="translate(-708.000000, -643.000000)" stroke="#ffffff" stroke-width="2">
9 <g id="256" transform="translate(708.000000, 643.000000)">
10 <path d="M6,14 C6,16.5 6.5,19 8,19 L16.9938335,19 C17.5495239,19 18.1819788,18.5956028 18.4072817,18.0949295 L20.8562951,12.6526776 C21.7640882,10.6353595 20.7154925,9 18.5092545,9 L15.5,9 C15.5,9 18.5,3 15,3 C12.5,3 11.5,9 8,9 C6.5,9 6,11.5 6,14 Z" id="Path-188" stroke-linejoin="round"></path>
11 <path d="M4,8.5 C4,8.5 3,11 3,14 C3,17 4,19.5 4,19.5" id="Path-189"></path>
12 </g>
13 </g>
14 </g>
15</svg>
diff --git a/client/src/assets/images/video/like-grey.svg b/client/src/assets/images/video/like.html
index 5ef6c7b31..d0e71763b 100644
--- a/client/src/assets/images/video/like-grey.svg
+++ b/client/src/assets/images/video/like.html
@@ -1,11 +1,6 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch --> 2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
4 <title>thumbs-up</title> 3 <g transform="translate(-708.000000, -643.000000)" stroke="#000000" stroke-width="2">
5 <desc>Created with Sketch.</desc>
6 <defs></defs>
7 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
8 <g id="Artboard-4" transform="translate(-708.000000, -643.000000)" stroke="#585858" stroke-width="2">
9 <g id="256" transform="translate(708.000000, 643.000000)"> 4 <g id="256" transform="translate(708.000000, 643.000000)">
10 <path d="M6,14 C6,16.5 6.5,19 8,19 L16.9938335,19 C17.5495239,19 18.1819788,18.5956028 18.4072817,18.0949295 L20.8562951,12.6526776 C21.7640882,10.6353595 20.7154925,9 18.5092545,9 L15.5,9 C15.5,9 18.5,3 15,3 C12.5,3 11.5,9 8,9 C6.5,9 6,11.5 6,14 Z" id="Path-188" stroke-linejoin="round"></path> 5 <path d="M6,14 C6,16.5 6.5,19 8,19 L16.9938335,19 C17.5495239,19 18.1819788,18.5956028 18.4072817,18.0949295 L20.8562951,12.6526776 C21.7640882,10.6353595 20.7154925,9 18.5092545,9 L15.5,9 C15.5,9 18.5,3 15,3 C12.5,3 11.5,9 8,9 C6.5,9 6,11.5 6,14 Z" id="Path-188" stroke-linejoin="round"></path>
11 <path d="M4,8.5 C4,8.5 3,11 3,14 C3,17 4,19.5 4,19.5" id="Path-189"></path> 6 <path d="M4,8.5 C4,8.5 3,11 3,14 C3,17 4,19.5 4,19.5" id="Path-189"></path>
diff --git a/client/src/assets/images/video/more.svg b/client/src/assets/images/video/more.html
index dea392136..39dcad10e 100644
--- a/client/src/assets/images/video/more.svg
+++ b/client/src/assets/images/video/more.html
@@ -1,8 +1,6 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <defs></defs> 2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
4 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> 3 <g transform="translate(-444.000000, -115.000000)" fill="#000000">
5 <g id="Artboard-4" transform="translate(-444.000000, -115.000000)" fill="#585858">
6 <g id="10" transform="translate(444.000000, 115.000000)"> 4 <g id="10" transform="translate(444.000000, 115.000000)">
7 <path d="M10,12 C10,10.8954305 10.8877296,10 12,10 C13.1045695,10 14,10.8877296 14,12 C14,13.1045695 13.1122704,14 12,14 C10.8954305,14 10,13.1122704 10,12 Z M17,12 C17,10.8954305 17.8877296,10 19,10 C20.1045695,10 21,10.8877296 21,12 C21,13.1045695 20.1122704,14 19,14 C17.8954305,14 17,13.1122704 17,12 Z M3,12 C3,10.8954305 3.88772964,10 5,10 C6.1045695,10 7,10.8877296 7,12 C7,13.1045695 6.11227036,14 5,14 C3.8954305,14 3,13.1122704 3,12 Z" id="Combined-Shape"></path> 5 <path d="M10,12 C10,10.8954305 10.8877296,10 12,10 C13.1045695,10 14,10.8877296 14,12 C14,13.1045695 13.1122704,14 12,14 C10.8954305,14 10,13.1122704 10,12 Z M17,12 C17,10.8954305 17.8877296,10 19,10 C20.1045695,10 21,10.8877296 21,12 C21,13.1045695 20.1122704,14 19,14 C17.8954305,14 17,13.1122704 17,12 Z M3,12 C3,10.8954305 3.88772964,10 5,10 C6.1045695,10 7,10.8877296 7,12 C7,13.1045695 6.11227036,14 5,14 C3.8954305,14 3,13.1122704 3,12 Z" id="Combined-Shape"></path>
8 </g> 6 </g>
diff --git a/client/src/assets/images/video/share.svg b/client/src/assets/images/video/share.html
index da0f43e81..7759b37af 100644
--- a/client/src/assets/images/video/share.svg
+++ b/client/src/assets/images/video/share.html
@@ -1,11 +1,6 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch --> 2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
4 <title>share</title> 3 <g transform="translate(-312.000000, -203.000000)" stroke="#000000" stroke-width="2">
5 <desc>Created with Sketch.</desc>
6 <defs></defs>
7 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
8 <g id="Artboard-4" transform="translate(-312.000000, -203.000000)" stroke="#585858" stroke-width="2">
9 <g id="47" transform="translate(312.000000, 203.000000)"> 4 <g id="47" transform="translate(312.000000, 203.000000)">
10 <path d="M20,15 L20,18.0026083 C20,19.1057373 19.1073772,20 18.0049107,20 L5.99508929,20 C4.8932319,20 4,19.1073772 4,18.0049107 L4,5.99508929 C4,4.8932319 4.89585781,4 5.9973917,4 L9,4" id="Rectangle-460"></path> 5 <path d="M20,15 L20,18.0026083 C20,19.1057373 19.1073772,20 18.0049107,20 L5.99508929,20 C4.8932319,20 4,19.1073772 4,18.0049107 L4,5.99508929 C4,4.8932319 4.89585781,4 5.9973917,4 L9,4" id="Rectangle-460"></path>
11 <polyline id="Path-93" stroke-linejoin="round" points="13 4 20.0207973 4 20.0207973 11.0191059"></polyline> 6 <polyline id="Path-93" stroke-linejoin="round" points="13 4 20.0207973 4 20.0207973 11.0191059"></polyline>
diff --git a/client/src/assets/images/header/upload-white.svg b/client/src/assets/images/video/upload.html
index 2b07caf76..3bc0d3a8a 100644
--- a/client/src/assets/images/header/upload-white.svg
+++ b/client/src/assets/images/video/upload.html
@@ -1,11 +1,6 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> 1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch --> 2 <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
4 <title>cloud-upload</title> 3 <g transform="translate(-312.000000, -775.000000)" stroke="#000000" stroke-width="2">
5 <desc>Created with Sketch.</desc>
6 <defs></defs>
7 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
8 <g id="Artboard-4" transform="translate(-312.000000, -775.000000)" stroke="#fff" stroke-width="2">
9 <g id="307" transform="translate(312.000000, 775.000000)"> 4 <g id="307" transform="translate(312.000000, 775.000000)">
10 <path d="M8,18 L5,18 L5,18 C2.790861,18 1,16.209139 1,14 C1,11.790861 2.790861,10 5,10 C5.35840468,10 5.70579988,10.0471371 6.03632437,10.1355501 C6.01233106,9.92702603 6,9.71495305 6,9.5 C6,6.46243388 8.46243388,4 11.5,4 C14.0673313,4 16.2238156,5.7590449 16.8299648,8.1376465 C17.2052921,8.04765874 17.5970804,8 18,8 C20.7614237,8 23,10.2385763 23,13 C23,15.7614237 20.7614237,18 18,18 L16,18" id="Combined-Shape" stroke-linejoin="round"></path> 5 <path d="M8,18 L5,18 L5,18 C2.790861,18 1,16.209139 1,14 C1,11.790861 2.790861,10 5,10 C5.35840468,10 5.70579988,10.0471371 6.03632437,10.1355501 C6.01233106,9.92702603 6,9.71495305 6,9.5 C6,6.46243388 8.46243388,4 11.5,4 C14.0673313,4 16.2238156,5.7590449 16.8299648,8.1376465 C17.2052921,8.04765874 17.5970804,8 18,8 C20.7614237,8 23,10.2385763 23,13 C23,15.7614237 20.7614237,18 18,18 L16,18" id="Combined-Shape" stroke-linejoin="round"></path>
11 <path d="M12,13 L12,21" id="Path-58"></path> 6 <path d="M12,13 L12,21" id="Path-58"></path>
diff --git a/client/src/assets/images/video/upload.svg b/client/src/assets/images/video/upload.svg
deleted file mode 100644
index c5b7cb443..000000000
--- a/client/src/assets/images/video/upload.svg
+++ /dev/null
@@ -1,16 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3 <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
4 <title>cloud-upload</title>
5 <desc>Created with Sketch.</desc>
6 <defs></defs>
7 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
8 <g id="Artboard-4" transform="translate(-312.000000, -775.000000)" stroke="#C6C6C6" stroke-width="2">
9 <g id="307" transform="translate(312.000000, 775.000000)">
10 <path d="M8,18 L5,18 L5,18 C2.790861,18 1,16.209139 1,14 C1,11.790861 2.790861,10 5,10 C5.35840468,10 5.70579988,10.0471371 6.03632437,10.1355501 C6.01233106,9.92702603 6,9.71495305 6,9.5 C6,6.46243388 8.46243388,4 11.5,4 C14.0673313,4 16.2238156,5.7590449 16.8299648,8.1376465 C17.2052921,8.04765874 17.5970804,8 18,8 C20.7614237,8 23,10.2385763 23,13 C23,15.7614237 20.7614237,18 18,18 L16,18" id="Combined-Shape" stroke-linejoin="round"></path>
11 <path d="M12,13 L12,21" id="Path-58"></path>
12 <polyline id="Path-59" stroke-linejoin="round" transform="translate(12.000000, 12.500000) scale(1, -1) translate(-12.000000, -12.500000) " points="15 11 12 14 9 11"></polyline>
13 </g>
14 </g>
15 </g>
16</svg>
diff --git a/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts b/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts
new file mode 100644
index 000000000..022a9c16f
--- /dev/null
+++ b/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts
@@ -0,0 +1,143 @@
1// FIXME: something weird with our path definition in tsconfig and typings
2// @ts-ignore
3import * as videojs from 'video.js'
4import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo, VideoJSComponentInterface } from '../peertube-videojs-typings'
5import { Engine, initHlsJsPlayer, initVideoJsContribHlsJsPlayer } from 'p2p-media-loader-hlsjs'
6import { Events } from 'p2p-media-loader-core'
7
8// videojs-hlsjs-plugin needs videojs in window
9window['videojs'] = videojs
10require('@streamroot/videojs-hlsjs-plugin')
11
12const Plugin: VideoJSComponentInterface = videojs.getPlugin('plugin')
13class P2pMediaLoaderPlugin extends Plugin {
14
15 private readonly CONSTANTS = {
16 INFO_SCHEDULER: 1000 // Don't change this
17 }
18 private readonly options: P2PMediaLoaderPluginOptions
19
20 private hlsjs: any // Don't type hlsjs to not bundle the module
21 private p2pEngine: Engine
22 private statsP2PBytes = {
23 pendingDownload: [] as number[],
24 pendingUpload: [] as number[],
25 numPeers: 0,
26 totalDownload: 0,
27 totalUpload: 0
28 }
29 private statsHTTPBytes = {
30 pendingDownload: [] as number[],
31 pendingUpload: [] as number[],
32 totalDownload: 0,
33 totalUpload: 0
34 }
35
36 private networkInfoInterval: any
37
38 constructor (player: videojs.Player, options: P2PMediaLoaderPluginOptions) {
39 super(player, options)
40
41 this.options = options
42
43 videojs.Html5Hlsjs.addHook('beforeinitialize', (videojsPlayer: any, hlsjs: any) => {
44 this.hlsjs = hlsjs
45 })
46
47 initVideoJsContribHlsJsPlayer(player)
48
49 player.src({
50 type: options.type,
51 src: options.src
52 })
53
54 player.on('play', () => {
55 player.addClass('vjs-has-big-play-button-clicked')
56 })
57
58 player.ready(() => this.initialize())
59 }
60
61 dispose () {
62 if (this.hlsjs) this.hlsjs.destroy()
63 if (this.p2pEngine) this.p2pEngine.destroy()
64
65 clearInterval(this.networkInfoInterval)
66 }
67
68 private initialize () {
69 initHlsJsPlayer(this.hlsjs)
70
71 const tech = this.player.tech_
72 this.p2pEngine = tech.options_.hlsjsConfig.loader.getEngine()
73
74 // Avoid using constants to not import hls.hs
75 // https://github.com/video-dev/hls.js/blob/master/src/events.js#L37
76 this.hlsjs.on('hlsLevelSwitching', (_: any, data: any) => {
77 this.trigger('resolutionChange', { auto: this.hlsjs.autoLevelEnabled, resolutionId: data.height })
78 })
79
80 this.p2pEngine.on(Events.SegmentError, (segment, err) => {
81 console.error('Segment error.', segment, err)
82 })
83
84 this.statsP2PBytes.numPeers = 1 + this.options.redundancyBaseUrls.length
85
86 this.runStats()
87 }
88
89 private runStats () {
90 this.p2pEngine.on(Events.PieceBytesDownloaded, (method: string, size: number) => {
91 const elem = method === 'p2p' ? this.statsP2PBytes : this.statsHTTPBytes
92
93 elem.pendingDownload.push(size)
94 elem.totalDownload += size
95 })
96
97 this.p2pEngine.on(Events.PieceBytesUploaded, (method: string, size: number) => {
98 const elem = method === 'p2p' ? this.statsP2PBytes : this.statsHTTPBytes
99
100 elem.pendingUpload.push(size)
101 elem.totalUpload += size
102 })
103
104 this.p2pEngine.on(Events.PeerConnect, () => this.statsP2PBytes.numPeers++)
105 this.p2pEngine.on(Events.PeerClose, () => this.statsP2PBytes.numPeers--)
106
107 this.networkInfoInterval = setInterval(() => {
108 const p2pDownloadSpeed = this.arraySum(this.statsP2PBytes.pendingDownload)
109 const p2pUploadSpeed = this.arraySum(this.statsP2PBytes.pendingUpload)
110
111 const httpDownloadSpeed = this.arraySum(this.statsHTTPBytes.pendingDownload)
112 const httpUploadSpeed = this.arraySum(this.statsHTTPBytes.pendingUpload)
113
114 this.statsP2PBytes.pendingDownload = []
115 this.statsP2PBytes.pendingUpload = []
116 this.statsHTTPBytes.pendingDownload = []
117 this.statsHTTPBytes.pendingUpload = []
118
119 return this.player.trigger('p2pInfo', {
120 http: {
121 downloadSpeed: httpDownloadSpeed,
122 uploadSpeed: httpUploadSpeed,
123 downloaded: this.statsHTTPBytes.totalDownload,
124 uploaded: this.statsHTTPBytes.totalUpload
125 },
126 p2p: {
127 downloadSpeed: p2pDownloadSpeed,
128 uploadSpeed: p2pUploadSpeed,
129 numPeers: this.statsP2PBytes.numPeers,
130 downloaded: this.statsP2PBytes.totalDownload,
131 uploaded: this.statsP2PBytes.totalUpload
132 }
133 } as PlayerNetworkInfo)
134 }, this.CONSTANTS.INFO_SCHEDULER)
135 }
136
137 private arraySum (data: number[]) {
138 return data.reduce((a: number, b: number) => a + b, 0)
139 }
140}
141
142videojs.registerPlugin('p2pMediaLoader', P2pMediaLoaderPlugin)
143export { P2pMediaLoaderPlugin }
diff --git a/client/src/assets/player/p2p-media-loader/segment-url-builder.ts b/client/src/assets/player/p2p-media-loader/segment-url-builder.ts
new file mode 100644
index 000000000..32e7ce4f2
--- /dev/null
+++ b/client/src/assets/player/p2p-media-loader/segment-url-builder.ts
@@ -0,0 +1,28 @@
1import { basename } from 'path'
2import { Segment } from 'p2p-media-loader-core'
3
4function segmentUrlBuilderFactory (baseUrls: string[]) {
5 return function segmentBuilder (segment: Segment) {
6 const max = baseUrls.length + 1
7 const i = getRandomInt(max)
8
9 if (i === max - 1) return segment.url
10
11 let newBaseUrl = baseUrls[i]
12 let middlePart = newBaseUrl.endsWith('/') ? '' : '/'
13
14 return newBaseUrl + middlePart + basename(segment.url)
15 }
16}
17
18// ---------------------------------------------------------------------------
19
20export {
21 segmentUrlBuilderFactory
22}
23
24// ---------------------------------------------------------------------------
25
26function getRandomInt (max: number) {
27 return Math.floor(Math.random() * Math.floor(max))
28}
diff --git a/client/src/assets/player/p2p-media-loader/segment-validator.ts b/client/src/assets/player/p2p-media-loader/segment-validator.ts
new file mode 100644
index 000000000..72c32f9e0
--- /dev/null
+++ b/client/src/assets/player/p2p-media-loader/segment-validator.ts
@@ -0,0 +1,63 @@
1import { Segment } from 'p2p-media-loader-core'
2import { basename } from 'path'
3
4function segmentValidatorFactory (segmentsSha256Url: string) {
5 const segmentsJSON = fetchSha256Segments(segmentsSha256Url)
6 const regex = /bytes=(\d+)-(\d+)/
7
8 return async function segmentValidator (segment: Segment) {
9 const filename = basename(segment.url)
10 const captured = regex.exec(segment.range)
11
12 const range = captured[1] + '-' + captured[2]
13
14 const hashShouldBe = (await segmentsJSON)[filename][range]
15 if (hashShouldBe === undefined) {
16 throw new Error(`Unknown segment name ${filename}/${range} in segment validator`)
17 }
18
19 const calculatedSha = bufferToEx(await sha256(segment.data))
20 if (calculatedSha !== hashShouldBe) {
21 throw new Error(
22 `Hashes does not correspond for segment ${filename}/${range}` +
23 `(expected: ${hashShouldBe} instead of ${calculatedSha})`
24 )
25 }
26 }
27}
28
29// ---------------------------------------------------------------------------
30
31export {
32 segmentValidatorFactory
33}
34
35// ---------------------------------------------------------------------------
36
37function fetchSha256Segments (url: string) {
38 return fetch(url)
39 .then(res => res.json())
40 .catch(err => {
41 console.error('Cannot get sha256 segments', err)
42 return {}
43 })
44}
45
46function sha256 (data?: ArrayBuffer) {
47 if (!data) return undefined
48
49 return window.crypto.subtle.digest('SHA-256', data)
50}
51
52// Thanks: https://stackoverflow.com/a/53307879
53function bufferToEx (buffer?: ArrayBuffer) {
54 if (!buffer) return ''
55
56 let s = ''
57 const h = '0123456789abcdef'
58 const o = new Uint8Array(buffer)
59
60 o.forEach((v: any) => s += h[ v >> 4 ] + h[ v & 15 ])
61
62 return s
63}
diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts
new file mode 100644
index 000000000..0ba9bcb11
--- /dev/null
+++ b/client/src/assets/player/peertube-player-manager.ts
@@ -0,0 +1,466 @@
1import { VideoFile } from '../../../../shared/models/videos'
2// @ts-ignore
3import * as videojs from 'video.js'
4import 'videojs-hotkeys'
5import 'videojs-dock'
6import 'videojs-contextmenu-ui'
7import 'videojs-contrib-quality-levels'
8import './peertube-plugin'
9import './videojs-components/peertube-link-button'
10import './videojs-components/resolution-menu-button'
11import './videojs-components/settings-menu-button'
12import './videojs-components/p2p-info-button'
13import './videojs-components/peertube-load-progress-bar'
14import './videojs-components/theater-button'
15import { P2PMediaLoaderPluginOptions, UserWatching, VideoJSCaption, VideoJSPluginOptions, videojsUntyped } from './peertube-videojs-typings'
16import { buildVideoEmbed, buildVideoLink, copyToClipboard, getRtcConfig } from './utils'
17import { getCompleteLocale, getShortLocale, is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n'
18import { segmentValidatorFactory } from './p2p-media-loader/segment-validator'
19import { segmentUrlBuilderFactory } from './p2p-media-loader/segment-url-builder'
20
21// Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
22videojsUntyped.getComponent('PlaybackRateMenuButton').prototype.controlText_ = 'Speed'
23// Change Captions to Subtitles/CC
24videojsUntyped.getComponent('CaptionsButton').prototype.controlText_ = 'Subtitles/CC'
25// We just want to display 'Off' instead of 'captions off', keep a space so the variable == true (hacky I know)
26videojsUntyped.getComponent('CaptionsButton').prototype.label_ = ' '
27
28export type PlayerMode = 'webtorrent' | 'p2p-media-loader'
29
30export type WebtorrentOptions = {
31 videoFiles: VideoFile[]
32}
33
34export type P2PMediaLoaderOptions = {
35 playlistUrl: string
36 segmentsSha256Url: string
37 trackerAnnounce: string[]
38 redundancyBaseUrls: string[]
39 videoFiles: VideoFile[]
40}
41
42export type CommonOptions = {
43 playerElement: HTMLVideoElement
44 onPlayerElementChange: (element: HTMLVideoElement) => void
45
46 autoplay: boolean
47 videoDuration: number
48 enableHotkeys: boolean
49 inactivityTimeout: number
50 poster: string
51 startTime: number | string
52
53 theaterMode: boolean
54 captions: boolean
55 peertubeLink: boolean
56
57 videoViewUrl: string
58 embedUrl: string
59
60 language?: string
61 controls?: boolean
62 muted?: boolean
63 loop?: boolean
64 subtitle?: string
65
66 videoCaptions: VideoJSCaption[]
67
68 userWatching?: UserWatching
69
70 serverUrl: string
71}
72
73export type PeertubePlayerManagerOptions = {
74 common: CommonOptions,
75 webtorrent: WebtorrentOptions,
76 p2pMediaLoader?: P2PMediaLoaderOptions
77}
78
79export class PeertubePlayerManager {
80
81 private static videojsLocaleCache: { [ path: string ]: any } = {}
82 private static playerElementClassName: string
83
84 static getServerTranslations (serverUrl: string, locale: string) {
85 const path = PeertubePlayerManager.getLocalePath(serverUrl, locale)
86 // It is the default locale, nothing to translate
87 if (!path) return Promise.resolve(undefined)
88
89 return fetch(path + '/server.json')
90 .then(res => res.json())
91 .catch(err => {
92 console.error('Cannot get server translations', err)
93 return undefined
94 })
95 }
96
97 static async initialize (mode: PlayerMode, options: PeertubePlayerManagerOptions) {
98 let p2pMediaLoader: any
99
100 this.playerElementClassName = options.common.playerElement.className
101
102 if (mode === 'webtorrent') await import('./webtorrent/webtorrent-plugin')
103 if (mode === 'p2p-media-loader') {
104 [ p2pMediaLoader ] = await Promise.all([
105 import('p2p-media-loader-hlsjs'),
106 import('./p2p-media-loader/p2p-media-loader-plugin')
107 ])
108 }
109
110 const videojsOptions = this.getVideojsOptions(mode, options, p2pMediaLoader)
111
112 await this.loadLocaleInVideoJS(options.common.serverUrl, options.common.language)
113
114 const self = this
115 return new Promise(res => {
116 videojs(options.common.playerElement, videojsOptions, function (this: any) {
117 const player = this
118
119 player.tech_.on('error', () => {
120 // Fallback to webtorrent?
121 if (mode === 'p2p-media-loader') {
122 self.fallbackToWebTorrent(player, options)
123 }
124 })
125
126 self.addContextMenu(mode, player, options.common.embedUrl)
127
128 return res(player)
129 })
130 })
131 }
132
133 private static async fallbackToWebTorrent (player: any, options: PeertubePlayerManagerOptions) {
134 const newVideoElement = document.createElement('video')
135 newVideoElement.className = this.playerElementClassName
136
137 // VideoJS wraps our video element inside a div
138 const currentParentPlayerElement = options.common.playerElement.parentNode
139 currentParentPlayerElement.parentNode.insertBefore(newVideoElement, currentParentPlayerElement)
140
141 options.common.playerElement = newVideoElement
142 options.common.onPlayerElementChange(newVideoElement)
143
144 player.dispose()
145
146 await import('./webtorrent/webtorrent-plugin')
147
148 const mode = 'webtorrent'
149 const videojsOptions = this.getVideojsOptions(mode, options)
150
151 const self = this
152 videojs(newVideoElement, videojsOptions, function (this: any) {
153 const player = this
154
155 self.addContextMenu(mode, player, options.common.embedUrl)
156 })
157 }
158
159 private static loadLocaleInVideoJS (serverUrl: string, locale: string) {
160 const path = PeertubePlayerManager.getLocalePath(serverUrl, locale)
161 // It is the default locale, nothing to translate
162 if (!path) return Promise.resolve(undefined)
163
164 let p: Promise<any>
165
166 if (PeertubePlayerManager.videojsLocaleCache[path]) {
167 p = Promise.resolve(PeertubePlayerManager.videojsLocaleCache[path])
168 } else {
169 p = fetch(path + '/player.json')
170 .then(res => res.json())
171 .then(json => {
172 PeertubePlayerManager.videojsLocaleCache[path] = json
173 return json
174 })
175 .catch(err => {
176 console.error('Cannot get player translations', err)
177 return undefined
178 })
179 }
180
181 const completeLocale = getCompleteLocale(locale)
182 return p.then(json => videojs.addLanguage(getShortLocale(completeLocale), json))
183 }
184
185 private static getVideojsOptions (mode: PlayerMode, options: PeertubePlayerManagerOptions, p2pMediaLoaderModule?: any) {
186 const commonOptions = options.common
187 const webtorrentOptions = options.webtorrent
188 const p2pMediaLoaderOptions = options.p2pMediaLoader
189
190 let autoplay = options.common.autoplay
191 let html5 = {}
192
193 const plugins: VideoJSPluginOptions = {
194 peertube: {
195 mode,
196 autoplay, // Use peertube plugin autoplay because we get the file by webtorrent
197 videoViewUrl: commonOptions.videoViewUrl,
198 videoDuration: commonOptions.videoDuration,
199 startTime: commonOptions.startTime,
200 userWatching: commonOptions.userWatching,
201 subtitle: commonOptions.subtitle,
202 videoCaptions: commonOptions.videoCaptions
203 }
204 }
205
206 if (mode === 'p2p-media-loader') {
207 const p2pMediaLoader: P2PMediaLoaderPluginOptions = {
208 redundancyBaseUrls: options.p2pMediaLoader.redundancyBaseUrls,
209 type: 'application/x-mpegURL',
210 src: p2pMediaLoaderOptions.playlistUrl
211 }
212
213 const trackerAnnounce = p2pMediaLoaderOptions.trackerAnnounce
214 .filter(t => t.startsWith('ws'))
215
216 const p2pMediaLoaderConfig = {
217 loader: {
218 trackerAnnounce,
219 segmentValidator: segmentValidatorFactory(options.p2pMediaLoader.segmentsSha256Url),
220 rtcConfig: getRtcConfig(),
221 requiredSegmentsPriority: 5,
222 segmentUrlBuilder: segmentUrlBuilderFactory(options.p2pMediaLoader.redundancyBaseUrls)
223 },
224 segments: {
225 swarmId: p2pMediaLoaderOptions.playlistUrl
226 }
227 }
228 const streamrootHls = {
229 levelLabelHandler: (level: { height: number, width: number }) => {
230 const file = p2pMediaLoaderOptions.videoFiles.find(f => f.resolution.id === level.height)
231
232 let label = file.resolution.label
233 if (file.fps >= 50) label += file.fps
234
235 return label
236 },
237 html5: {
238 hlsjsConfig: {
239 liveSyncDurationCount: 7,
240 loader: new p2pMediaLoaderModule.Engine(p2pMediaLoaderConfig).createLoaderClass()
241 }
242 }
243 }
244
245 Object.assign(plugins, { p2pMediaLoader, streamrootHls })
246 html5 = streamrootHls.html5
247 }
248
249 if (mode === 'webtorrent') {
250 const webtorrent = {
251 autoplay,
252 videoDuration: commonOptions.videoDuration,
253 playerElement: commonOptions.playerElement,
254 videoFiles: webtorrentOptions.videoFiles
255 }
256 Object.assign(plugins, { webtorrent })
257
258 // WebTorrent plugin handles autoplay, because we do some hackish stuff in there
259 autoplay = false
260 }
261
262 const videojsOptions = {
263 html5,
264
265 // We don't use text track settings for now
266 textTrackSettings: false,
267 controls: commonOptions.controls !== undefined ? commonOptions.controls : true,
268 loop: commonOptions.loop !== undefined ? commonOptions.loop : false,
269
270 muted: commonOptions.muted !== undefined
271 ? commonOptions.muted
272 : undefined, // Undefined so the player knows it has to check the local storage
273
274 poster: commonOptions.poster,
275 autoplay: autoplay === true ? 'any' : autoplay, // Use 'any' instead of true to get notifier by videojs if autoplay fails
276 inactivityTimeout: commonOptions.inactivityTimeout,
277 playbackRates: [ 0.5, 0.75, 1, 1.25, 1.5, 2 ],
278 plugins,
279 controlBar: {
280 children: this.getControlBarChildren(mode, {
281 captions: commonOptions.captions,
282 peertubeLink: commonOptions.peertubeLink,
283 theaterMode: commonOptions.theaterMode
284 })
285 }
286 }
287
288 if (commonOptions.enableHotkeys === true) {
289 Object.assign(videojsOptions.plugins, {
290 hotkeys: {
291 enableVolumeScroll: false,
292 enableModifiersForNumbers: false,
293
294 fullscreenKey: function (event: KeyboardEvent) {
295 // fullscreen with the f key or Ctrl+Enter
296 return event.key === 'f' || (event.ctrlKey && event.key === 'Enter')
297 },
298
299 seekStep: function (event: KeyboardEvent) {
300 // mimic VLC seek behavior, and default to 5 (original value is 5).
301 if (event.ctrlKey && event.altKey) {
302 return 5 * 60
303 } else if (event.ctrlKey) {
304 return 60
305 } else if (event.altKey) {
306 return 10
307 } else {
308 return 5
309 }
310 },
311
312 customKeys: {
313 increasePlaybackRateKey: {
314 key: function (event: KeyboardEvent) {
315 return event.key === '>'
316 },
317 handler: function (player: videojs.Player) {
318 player.playbackRate((player.playbackRate() + 0.1).toFixed(2))
319 }
320 },
321 decreasePlaybackRateKey: {
322 key: function (event: KeyboardEvent) {
323 return event.key === '<'
324 },
325 handler: function (player: videojs.Player) {
326 player.playbackRate((player.playbackRate() - 0.1).toFixed(2))
327 }
328 },
329 frameByFrame: {
330 key: function (event: KeyboardEvent) {
331 return event.key === '.'
332 },
333 handler: function (player: videojs.Player) {
334 player.pause()
335 // Calculate movement distance (assuming 30 fps)
336 const dist = 1 / 30
337 player.currentTime(player.currentTime() + dist)
338 }
339 }
340 }
341 }
342 })
343 }
344
345 if (commonOptions.language && !isDefaultLocale(commonOptions.language)) {
346 Object.assign(videojsOptions, { language: commonOptions.language })
347 }
348
349 return videojsOptions
350 }
351
352 private static getControlBarChildren (mode: PlayerMode, options: {
353 peertubeLink: boolean
354 theaterMode: boolean,
355 captions: boolean
356 }) {
357 const settingEntries = []
358 const loadProgressBar = mode === 'webtorrent' ? 'peerTubeLoadProgressBar' : 'loadProgressBar'
359
360 // Keep an order
361 settingEntries.push('playbackRateMenuButton')
362 if (options.captions === true) settingEntries.push('captionsButton')
363 settingEntries.push('resolutionMenuButton')
364
365 const children = {
366 'playToggle': {},
367 'currentTimeDisplay': {},
368 'timeDivider': {},
369 'durationDisplay': {},
370 'liveDisplay': {},
371
372 'flexibleWidthSpacer': {},
373 'progressControl': {
374 children: {
375 'seekBar': {
376 children: {
377 [loadProgressBar]: {},
378 'mouseTimeDisplay': {},
379 'playProgressBar': {}
380 }
381 }
382 }
383 },
384
385 'p2PInfoButton': {},
386
387 'muteToggle': {},
388 'volumeControl': {},
389
390 'settingsButton': {
391 setup: {
392 maxHeightOffset: 40
393 },
394 entries: settingEntries
395 }
396 }
397
398 if (options.peertubeLink === true) {
399 Object.assign(children, {
400 'peerTubeLinkButton': {}
401 })
402 }
403
404 if (options.theaterMode === true) {
405 Object.assign(children, {
406 'theaterButton': {}
407 })
408 }
409
410 Object.assign(children, {
411 'fullscreenToggle': {}
412 })
413
414 return children
415 }
416
417 private static addContextMenu (mode: PlayerMode, player: any, videoEmbedUrl: string) {
418 const content = [
419 {
420 label: player.localize('Copy the video URL'),
421 listener: function () {
422 copyToClipboard(buildVideoLink())
423 }
424 },
425 {
426 label: player.localize('Copy the video URL at the current time'),
427 listener: function () {
428 const player = this as videojs.Player
429 copyToClipboard(buildVideoLink(player.currentTime()))
430 }
431 },
432 {
433 label: player.localize('Copy embed code'),
434 listener: () => {
435 copyToClipboard(buildVideoEmbed(videoEmbedUrl))
436 }
437 }
438 ]
439
440 if (mode === 'webtorrent') {
441 content.push({
442 label: player.localize('Copy magnet URI'),
443 listener: function () {
444 const player = this as videojs.Player
445 copyToClipboard(player.webtorrent().getCurrentVideoFile().magnetUri)
446 }
447 })
448 }
449
450 player.contextmenuUI({ content })
451 }
452
453 private static getLocalePath (serverUrl: string, locale: string) {
454 const completeLocale = getCompleteLocale(locale)
455
456 if (!is18nLocale(completeLocale) || isDefaultLocale(completeLocale)) return undefined
457
458 return serverUrl + '/client/locales/' + completeLocale
459 }
460}
461
462// ############################################################################
463
464export {
465 videojs
466}
diff --git a/client/src/assets/player/peertube-player.ts b/client/src/assets/player/peertube-player.ts
deleted file mode 100644
index 2de6d7fef..000000000
--- a/client/src/assets/player/peertube-player.ts
+++ /dev/null
@@ -1,300 +0,0 @@
1import { VideoFile } from '../../../../shared/models/videos'
2
3import 'videojs-hotkeys'
4import 'videojs-dock'
5import 'videojs-contextmenu-ui'
6import './peertube-link-button'
7import './resolution-menu-button'
8import './settings-menu-button'
9import './webtorrent-info-button'
10import './peertube-videojs-plugin'
11import './peertube-load-progress-bar'
12import './theater-button'
13import { UserWatching, VideoJSCaption, videojsUntyped } from './peertube-videojs-typings'
14import { buildVideoEmbed, buildVideoLink, copyToClipboard } from './utils'
15import { getCompleteLocale, getShortLocale, is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n'
16
17// FIXME: something weird with our path definition in tsconfig and typings
18// @ts-ignore
19import { Player } from 'video.js'
20
21// Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
22videojsUntyped.getComponent('PlaybackRateMenuButton').prototype.controlText_ = 'Speed'
23// Change Captions to Subtitles/CC
24videojsUntyped.getComponent('CaptionsButton').prototype.controlText_ = 'Subtitles/CC'
25// We just want to display 'Off' instead of 'captions off', keep a space so the variable == true (hacky I know)
26videojsUntyped.getComponent('CaptionsButton').prototype.label_ = ' '
27
28function getVideojsOptions (options: {
29 autoplay: boolean
30 playerElement: HTMLVideoElement
31 videoViewUrl: string
32 videoDuration: number
33 videoFiles: VideoFile[]
34 enableHotkeys: boolean
35 inactivityTimeout: number
36 peertubeLink: boolean
37 poster: string
38 startTime: number | string
39 theaterMode: boolean
40 videoCaptions: VideoJSCaption[]
41
42 language?: string
43 controls?: boolean
44 muted?: boolean
45 loop?: boolean
46 subtitle?: string
47
48 userWatching?: UserWatching
49}) {
50 const videojsOptions = {
51 // We don't use text track settings for now
52 textTrackSettings: false,
53 controls: options.controls !== undefined ? options.controls : true,
54 loop: options.loop !== undefined ? options.loop : false,
55
56 muted: options.muted !== undefined ? options.muted : undefined, // Undefined so the player knows it has to check the local storage
57
58 poster: options.poster,
59 autoplay: false,
60 inactivityTimeout: options.inactivityTimeout,
61 playbackRates: [ 0.5, 0.75, 1, 1.25, 1.5, 2 ],
62 plugins: {
63 peertube: {
64 autoplay: options.autoplay, // Use peertube plugin autoplay because we get the file by webtorrent
65 videoCaptions: options.videoCaptions,
66 videoFiles: options.videoFiles,
67 playerElement: options.playerElement,
68 videoViewUrl: options.videoViewUrl,
69 videoDuration: options.videoDuration,
70 startTime: options.startTime,
71 userWatching: options.userWatching,
72 subtitle: options.subtitle
73 }
74 },
75 controlBar: {
76 children: getControlBarChildren(options)
77 }
78 }
79
80 if (options.enableHotkeys === true) {
81 Object.assign(videojsOptions.plugins, {
82 hotkeys: {
83 enableVolumeScroll: false,
84 enableModifiersForNumbers: false,
85
86 fullscreenKey: function (event: KeyboardEvent) {
87 // fullscreen with the f key or Ctrl+Enter
88 return event.key === 'f' || (event.ctrlKey && event.key === 'Enter')
89 },
90
91 seekStep: function (event: KeyboardEvent) {
92 // mimic VLC seek behavior, and default to 5 (original value is 5).
93 if (event.ctrlKey && event.altKey) {
94 return 5 * 60
95 } else if (event.ctrlKey) {
96 return 60
97 } else if (event.altKey) {
98 return 10
99 } else {
100 return 5
101 }
102 },
103
104 customKeys: {
105 increasePlaybackRateKey: {
106 key: function (event: KeyboardEvent) {
107 return event.key === '>'
108 },
109 handler: function (player: Player) {
110 player.playbackRate((player.playbackRate() + 0.1).toFixed(2))
111 }
112 },
113 decreasePlaybackRateKey: {
114 key: function (event: KeyboardEvent) {
115 return event.key === '<'
116 },
117 handler: function (player: Player) {
118 player.playbackRate((player.playbackRate() - 0.1).toFixed(2))
119 }
120 },
121 frameByFrame: {
122 key: function (event: KeyboardEvent) {
123 return event.key === '.'
124 },
125 handler: function (player: Player) {
126 player.pause()
127 // Calculate movement distance (assuming 30 fps)
128 const dist = 1 / 30
129 player.currentTime(player.currentTime() + dist)
130 }
131 }
132 }
133 }
134 })
135 }
136
137 if (options.language && !isDefaultLocale(options.language)) {
138 Object.assign(videojsOptions, { language: options.language })
139 }
140
141 return videojsOptions
142}
143
144function getControlBarChildren (options: {
145 peertubeLink: boolean
146 theaterMode: boolean,
147 videoCaptions: VideoJSCaption[]
148}) {
149 const settingEntries = []
150
151 // Keep an order
152 settingEntries.push('playbackRateMenuButton')
153 if (options.videoCaptions.length !== 0) settingEntries.push('captionsButton')
154 settingEntries.push('resolutionMenuButton')
155
156 const children = {
157 'playToggle': {},
158 'currentTimeDisplay': {},
159 'timeDivider': {},
160 'durationDisplay': {},
161 'liveDisplay': {},
162
163 'flexibleWidthSpacer': {},
164 'progressControl': {
165 children: {
166 'seekBar': {
167 children: {
168 'peerTubeLoadProgressBar': {},
169 'mouseTimeDisplay': {},
170 'playProgressBar': {}
171 }
172 }
173 }
174 },
175
176 'webTorrentButton': {},
177
178 'muteToggle': {},
179 'volumeControl': {},
180
181 'settingsButton': {
182 setup: {
183 maxHeightOffset: 40
184 },
185 entries: settingEntries
186 }
187 }
188
189 if (options.peertubeLink === true) {
190 Object.assign(children, {
191 'peerTubeLinkButton': {}
192 })
193 }
194
195 if (options.theaterMode === true) {
196 Object.assign(children, {
197 'theaterButton': {}
198 })
199 }
200
201 Object.assign(children, {
202 'fullscreenToggle': {}
203 })
204
205 return children
206}
207
208function addContextMenu (player: any, videoEmbedUrl: string) {
209 player.contextmenuUI({
210 content: [
211 {
212 label: player.localize('Copy the video URL'),
213 listener: function () {
214 copyToClipboard(buildVideoLink())
215 }
216 },
217 {
218 label: player.localize('Copy the video URL at the current time'),
219 listener: function () {
220 const player = this as Player
221 copyToClipboard(buildVideoLink(player.currentTime()))
222 }
223 },
224 {
225 label: player.localize('Copy embed code'),
226 listener: () => {
227 copyToClipboard(buildVideoEmbed(videoEmbedUrl))
228 }
229 },
230 {
231 label: player.localize('Copy magnet URI'),
232 listener: function () {
233 const player = this as Player
234 copyToClipboard(player.peertube().getCurrentVideoFile().magnetUri)
235 }
236 }
237 ]
238 })
239}
240
241function loadLocaleInVideoJS (serverUrl: string, videojs: any, locale: string) {
242 const path = getLocalePath(serverUrl, locale)
243 // It is the default locale, nothing to translate
244 if (!path) return Promise.resolve(undefined)
245
246 let p: Promise<any>
247
248 if (loadLocaleInVideoJS.cache[path]) {
249 p = Promise.resolve(loadLocaleInVideoJS.cache[path])
250 } else {
251 p = fetch(path + '/player.json')
252 .then(res => res.json())
253 .then(json => {
254 loadLocaleInVideoJS.cache[path] = json
255 return json
256 })
257 .catch(err => {
258 console.error('Cannot get player translations', err)
259 return undefined
260 })
261 }
262
263 const completeLocale = getCompleteLocale(locale)
264 return p.then(json => videojs.addLanguage(getShortLocale(completeLocale), json))
265}
266namespace loadLocaleInVideoJS {
267 export const cache: { [ path: string ]: any } = {}
268}
269
270function getServerTranslations (serverUrl: string, locale: string) {
271 const path = getLocalePath(serverUrl, locale)
272 // It is the default locale, nothing to translate
273 if (!path) return Promise.resolve(undefined)
274
275 return fetch(path + '/server.json')
276 .then(res => res.json())
277 .catch(err => {
278 console.error('Cannot get server translations', err)
279 return undefined
280 })
281}
282
283// ############################################################################
284
285export {
286 getServerTranslations,
287 loadLocaleInVideoJS,
288 getVideojsOptions,
289 addContextMenu
290}
291
292// ############################################################################
293
294function getLocalePath (serverUrl: string, locale: string) {
295 const completeLocale = getCompleteLocale(locale)
296
297 if (!is18nLocale(completeLocale) || isDefaultLocale(completeLocale)) return undefined
298
299 return serverUrl + '/client/locales/' + completeLocale
300}
diff --git a/client/src/assets/player/peertube-plugin.ts b/client/src/assets/player/peertube-plugin.ts
new file mode 100644
index 000000000..7ea4a06d4
--- /dev/null
+++ b/client/src/assets/player/peertube-plugin.ts
@@ -0,0 +1,262 @@
1// FIXME: something weird with our path definition in tsconfig and typings
2// @ts-ignore
3import * as videojs from 'video.js'
4import './videojs-components/settings-menu-button'
5import {
6 PeerTubePluginOptions,
7 ResolutionUpdateData,
8 UserWatching,
9 VideoJSCaption,
10 VideoJSComponentInterface,
11 videojsUntyped
12} from './peertube-videojs-typings'
13import { isMobile, timeToInt } from './utils'
14import {
15 getStoredLastSubtitle,
16 getStoredMute,
17 getStoredVolume,
18 saveLastSubtitle,
19 saveMuteInStore,
20 saveVolumeInStore
21} from './peertube-player-local-storage'
22
23const Plugin: VideoJSComponentInterface = videojs.getPlugin('plugin')
24class PeerTubePlugin extends Plugin {
25 private readonly autoplay: boolean = false
26 private readonly startTime: number = 0
27 private readonly videoViewUrl: string
28 private readonly videoDuration: number
29 private readonly CONSTANTS = {
30 USER_WATCHING_VIDEO_INTERVAL: 5000 // Every 5 seconds, notify the user is watching the video
31 }
32
33 private player: any
34 private videoCaptions: VideoJSCaption[]
35 private defaultSubtitle: string
36
37 private videoViewInterval: any
38 private userWatchingVideoInterval: any
39 private qualityObservationTimer: any
40 private lastResolutionChange: ResolutionUpdateData
41
42 constructor (player: videojs.Player, options: PeerTubePluginOptions) {
43 super(player, options)
44
45 this.startTime = timeToInt(options.startTime)
46 this.videoViewUrl = options.videoViewUrl
47 this.videoDuration = options.videoDuration
48 this.videoCaptions = options.videoCaptions
49
50 if (options.autoplay === true) this.player.addClass('vjs-has-autoplay')
51
52 this.player.on('autoplay-failure', () => {
53 this.player.removeClass('vjs-has-autoplay')
54 })
55
56 this.player.ready(() => {
57 const playerOptions = this.player.options_
58
59 if (options.mode === 'webtorrent') {
60 this.player.webtorrent().on('resolutionChange', (_: any, d: any) => this.handleResolutionChange(d))
61 this.player.webtorrent().on('autoResolutionChange', (_: any, d: any) => this.trigger('autoResolutionChange', d))
62 }
63
64 if (options.mode === 'p2p-media-loader') {
65 this.player.p2pMediaLoader().on('resolutionChange', (_: any, d: any) => this.handleResolutionChange(d))
66 }
67
68 this.player.tech_.on('loadedqualitydata', () => {
69 setTimeout(() => {
70 // Replay a resolution change, now we loaded all quality data
71 if (this.lastResolutionChange) this.handleResolutionChange(this.lastResolutionChange)
72 }, 0)
73 })
74
75 const volume = getStoredVolume()
76 if (volume !== undefined) this.player.volume(volume)
77
78 const muted = playerOptions.muted !== undefined ? playerOptions.muted : getStoredMute()
79 if (muted !== undefined) this.player.muted(muted)
80
81 this.defaultSubtitle = options.subtitle || getStoredLastSubtitle()
82
83 this.player.on('volumechange', () => {
84 saveVolumeInStore(this.player.volume())
85 saveMuteInStore(this.player.muted())
86 })
87
88 this.player.textTracks().on('change', () => {
89 const showing = this.player.textTracks().tracks_.find((t: { kind: string, mode: string }) => {
90 return t.kind === 'captions' && t.mode === 'showing'
91 })
92
93 if (!showing) {
94 saveLastSubtitle('off')
95 return
96 }
97
98 saveLastSubtitle(showing.language)
99 })
100
101 this.player.on('sourcechange', () => this.initCaptions())
102
103 this.player.duration(options.videoDuration)
104
105 this.initializePlayer()
106 this.runViewAdd()
107
108 if (options.userWatching) this.runUserWatchVideo(options.userWatching)
109 })
110 }
111
112 dispose () {
113 clearTimeout(this.qualityObservationTimer)
114
115 clearInterval(this.videoViewInterval)
116
117 if (this.userWatchingVideoInterval) clearInterval(this.userWatchingVideoInterval)
118 }
119
120 private initializePlayer () {
121 if (isMobile()) this.player.addClass('vjs-is-mobile')
122
123 this.initSmoothProgressBar()
124
125 this.initCaptions()
126
127 this.alterInactivity()
128 }
129
130 private runViewAdd () {
131 this.clearVideoViewInterval()
132
133 // After 30 seconds (or 3/4 of the video), add a view to the video
134 let minSecondsToView = 30
135
136 if (this.videoDuration < minSecondsToView) minSecondsToView = (this.videoDuration * 3) / 4
137
138 let secondsViewed = 0
139 this.videoViewInterval = setInterval(() => {
140 if (this.player && !this.player.paused()) {
141 secondsViewed += 1
142
143 if (secondsViewed > minSecondsToView) {
144 this.clearVideoViewInterval()
145
146 this.addViewToVideo().catch(err => console.error(err))
147 }
148 }
149 }, 1000)
150 }
151
152 private runUserWatchVideo (options: UserWatching) {
153 let lastCurrentTime = 0
154
155 this.userWatchingVideoInterval = setInterval(() => {
156 const currentTime = Math.floor(this.player.currentTime())
157
158 if (currentTime - lastCurrentTime >= 1) {
159 lastCurrentTime = currentTime
160
161 this.notifyUserIsWatching(currentTime, options.url, options.authorizationHeader)
162 .catch(err => console.error('Cannot notify user is watching.', err))
163 }
164 }, this.CONSTANTS.USER_WATCHING_VIDEO_INTERVAL)
165 }
166
167 private clearVideoViewInterval () {
168 if (this.videoViewInterval !== undefined) {
169 clearInterval(this.videoViewInterval)
170 this.videoViewInterval = undefined
171 }
172 }
173
174 private addViewToVideo () {
175 if (!this.videoViewUrl) return Promise.resolve(undefined)
176
177 return fetch(this.videoViewUrl, { method: 'POST' })
178 }
179
180 private notifyUserIsWatching (currentTime: number, url: string, authorizationHeader: string) {
181 const body = new URLSearchParams()
182 body.append('currentTime', currentTime.toString())
183
184 const headers = new Headers({ 'Authorization': authorizationHeader })
185
186 return fetch(url, { method: 'PUT', body, headers })
187 }
188
189 private handleResolutionChange (data: ResolutionUpdateData) {
190 this.lastResolutionChange = data
191
192 const qualityLevels = this.player.qualityLevels()
193
194 for (let i = 0; i < qualityLevels.length; i++) {
195 if (qualityLevels[i].height === data.resolutionId) {
196 data.id = qualityLevels[i].id
197 break
198 }
199 }
200
201 this.trigger('resolutionChange', data)
202 }
203
204 private alterInactivity () {
205 let saveInactivityTimeout: number
206
207 const disableInactivity = () => {
208 saveInactivityTimeout = this.player.options_.inactivityTimeout
209 this.player.options_.inactivityTimeout = 0
210 }
211 const enableInactivity = () => {
212 this.player.options_.inactivityTimeout = saveInactivityTimeout
213 }
214
215 const settingsDialog = this.player.children_.find((c: any) => c.name_ === 'SettingsDialog')
216
217 this.player.controlBar.on('mouseenter', () => disableInactivity())
218 settingsDialog.on('mouseenter', () => disableInactivity())
219 this.player.controlBar.on('mouseleave', () => enableInactivity())
220 settingsDialog.on('mouseleave', () => enableInactivity())
221 }
222
223 private initCaptions () {
224 for (const caption of this.videoCaptions) {
225 this.player.addRemoteTextTrack({
226 kind: 'captions',
227 label: caption.label,
228 language: caption.language,
229 id: caption.language,
230 src: caption.src,
231 default: this.defaultSubtitle === caption.language
232 }, false)
233 }
234
235 this.player.trigger('captionsChanged')
236 }
237
238 // Thanks: https://github.com/videojs/video.js/issues/4460#issuecomment-312861657
239 private initSmoothProgressBar () {
240 const SeekBar = videojsUntyped.getComponent('SeekBar')
241 SeekBar.prototype.getPercent = function getPercent () {
242 // Allows for smooth scrubbing, when player can't keep up.
243 // const time = (this.player_.scrubbing()) ?
244 // this.player_.getCache().currentTime :
245 // this.player_.currentTime()
246 const time = this.player_.currentTime()
247 const percent = time / this.player_.duration()
248 return percent >= 1 ? 1 : percent
249 }
250 SeekBar.prototype.handleMouseMove = function handleMouseMove (event: any) {
251 let newTime = this.calculateDistance(event) * this.player_.duration()
252 if (newTime === this.player_.duration()) {
253 newTime = newTime - 0.1
254 }
255 this.player_.currentTime(newTime)
256 this.update()
257 }
258 }
259}
260
261videojs.registerPlugin('peertube', PeerTubePlugin)
262export { PeerTubePlugin }
diff --git a/client/src/assets/player/peertube-videojs-typings.ts b/client/src/assets/player/peertube-videojs-typings.ts
index 634c7fdc9..79a5a6c4d 100644
--- a/client/src/assets/player/peertube-videojs-typings.ts
+++ b/client/src/assets/player/peertube-videojs-typings.ts
@@ -3,11 +3,16 @@
3import * as videojs from 'video.js' 3import * as videojs from 'video.js'
4 4
5import { VideoFile } from '../../../../shared/models/videos/video.model' 5import { VideoFile } from '../../../../shared/models/videos/video.model'
6import { PeerTubePlugin } from './peertube-videojs-plugin' 6import { PeerTubePlugin } from './peertube-plugin'
7import { WebTorrentPlugin } from './webtorrent/webtorrent-plugin'
8import { P2pMediaLoaderPlugin } from './p2p-media-loader/p2p-media-loader-plugin'
9import { PlayerMode } from './peertube-player-manager'
7 10
8declare namespace videojs { 11declare namespace videojs {
9 interface Player { 12 interface Player {
10 peertube (): PeerTubePlugin 13 peertube (): PeerTubePlugin
14 webtorrent (): WebTorrentPlugin
15 p2pMediaLoader (): P2pMediaLoaderPlugin
11 } 16 }
12} 17}
13 18
@@ -30,26 +35,95 @@ type UserWatching = {
30 authorizationHeader: string 35 authorizationHeader: string
31} 36}
32 37
33type PeertubePluginOptions = { 38type PeerTubePluginOptions = {
34 videoFiles: VideoFile[] 39 mode: PlayerMode
35 playerElement: HTMLVideoElement 40
41 autoplay: boolean
36 videoViewUrl: string 42 videoViewUrl: string
37 videoDuration: number 43 videoDuration: number
38 startTime: number | string 44 startTime: number | string
39 autoplay: boolean,
40 videoCaptions: VideoJSCaption[]
41 45
42 subtitle?: string
43 userWatching?: UserWatching 46 userWatching?: UserWatching
47 subtitle?: string
48
49 videoCaptions: VideoJSCaption[]
50}
51
52type WebtorrentPluginOptions = {
53 playerElement: HTMLVideoElement
54
55 autoplay: boolean
56 videoDuration: number
57
58 videoFiles: VideoFile[]
59}
60
61type P2PMediaLoaderPluginOptions = {
62 redundancyBaseUrls: string[]
63 type: string
64 src: string
65}
66
67type VideoJSPluginOptions = {
68 peertube: PeerTubePluginOptions
69
70 webtorrent?: WebtorrentPluginOptions
71
72 p2pMediaLoader?: P2PMediaLoaderPluginOptions
44} 73}
45 74
46// videojs typings don't have some method we need 75// videojs typings don't have some method we need
47const videojsUntyped = videojs as any 76const videojsUntyped = videojs as any
48 77
78type LoadedQualityData = {
79 qualitySwitchCallback: Function,
80 qualityData: {
81 video: {
82 id: number
83 label: string
84 selected: boolean
85 }[]
86 }
87}
88
89type ResolutionUpdateData = {
90 auto: boolean,
91 resolutionId: number
92 id?: number
93}
94
95type AutoResolutionUpdateData = {
96 possible: boolean
97}
98
99type PlayerNetworkInfo = {
100 http: {
101 downloadSpeed: number
102 uploadSpeed: number
103 downloaded: number
104 uploaded: number
105 }
106
107 p2p: {
108 downloadSpeed: number
109 uploadSpeed: number
110 downloaded: number
111 uploaded: number
112 numPeers: number
113 }
114}
115
49export { 116export {
117 PlayerNetworkInfo,
118 ResolutionUpdateData,
119 AutoResolutionUpdateData,
50 VideoJSComponentInterface, 120 VideoJSComponentInterface,
51 PeertubePluginOptions,
52 videojsUntyped, 121 videojsUntyped,
53 VideoJSCaption, 122 VideoJSCaption,
54 UserWatching 123 UserWatching,
124 PeerTubePluginOptions,
125 WebtorrentPluginOptions,
126 P2PMediaLoaderPluginOptions,
127 VideoJSPluginOptions,
128 LoadedQualityData
55} 129}
diff --git a/client/src/assets/player/resolution-menu-button.ts b/client/src/assets/player/resolution-menu-button.ts
deleted file mode 100644
index a3c1108ca..000000000
--- a/client/src/assets/player/resolution-menu-button.ts
+++ /dev/null
@@ -1,88 +0,0 @@
1// FIXME: something weird with our path definition in tsconfig and typings
2// @ts-ignore
3import { Player } from 'video.js'
4
5import { VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings'
6import { ResolutionMenuItem } from './resolution-menu-item'
7
8const Menu: VideoJSComponentInterface = videojsUntyped.getComponent('Menu')
9const MenuButton: VideoJSComponentInterface = videojsUntyped.getComponent('MenuButton')
10class ResolutionMenuButton extends MenuButton {
11 label: HTMLElement
12
13 constructor (player: Player, options: any) {
14 super(player, options)
15 this.player = player
16
17 player.peertube().on('videoFileUpdate', () => this.updateLabel())
18 player.peertube().on('autoResolutionUpdate', () => this.updateLabel())
19 }
20
21 createEl () {
22 const el = super.createEl()
23
24 this.labelEl_ = videojsUntyped.dom.createEl('div', {
25 className: 'vjs-resolution-value',
26 innerHTML: this.buildLabelHTML()
27 })
28
29 el.appendChild(this.labelEl_)
30
31 return el
32 }
33
34 updateARIAAttributes () {
35 this.el().setAttribute('aria-label', 'Quality')
36 }
37
38 createMenu () {
39 const menu = new Menu(this.player_)
40 for (const videoFile of this.player_.peertube().videoFiles) {
41 let label = videoFile.resolution.label
42 if (videoFile.fps && videoFile.fps >= 50) {
43 label += videoFile.fps
44 }
45
46 menu.addChild(new ResolutionMenuItem(
47 this.player_,
48 {
49 id: videoFile.resolution.id,
50 label,
51 src: videoFile.magnetUri
52 })
53 )
54 }
55
56 menu.addChild(new ResolutionMenuItem(
57 this.player_,
58 {
59 id: -1,
60 label: this.player_.localize('Auto'),
61 src: null
62 }
63 ))
64
65 return menu
66 }
67
68 updateLabel () {
69 if (!this.labelEl_) return
70
71 this.labelEl_.innerHTML = this.buildLabelHTML()
72 }
73
74 buildCSSClass () {
75 return super.buildCSSClass() + ' vjs-resolution-button'
76 }
77
78 buildWrapperCSSClass () {
79 return 'vjs-resolution-control ' + super.buildWrapperCSSClass()
80 }
81
82 private buildLabelHTML () {
83 return this.player_.peertube().getCurrentResolutionLabel()
84 }
85}
86ResolutionMenuButton.prototype.controlText_ = 'Quality'
87
88MenuButton.registerComponent('ResolutionMenuButton', ResolutionMenuButton)
diff --git a/client/src/assets/player/resolution-menu-item.ts b/client/src/assets/player/resolution-menu-item.ts
deleted file mode 100644
index b54fd91ef..000000000
--- a/client/src/assets/player/resolution-menu-item.ts
+++ /dev/null
@@ -1,67 +0,0 @@
1// FIXME: something weird with our path definition in tsconfig and typings
2// @ts-ignore
3import { Player } from 'video.js'
4
5import { VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings'
6
7const MenuItem: VideoJSComponentInterface = videojsUntyped.getComponent('MenuItem')
8class ResolutionMenuItem extends MenuItem {
9
10 constructor (player: Player, options: any) {
11 const currentResolutionId = player.peertube().getCurrentResolutionId()
12 options.selectable = true
13 options.selected = options.id === currentResolutionId
14
15 super(player, options)
16
17 this.label = options.label
18 this.id = options.id
19
20 player.peertube().on('videoFileUpdate', () => this.updateSelection())
21 player.peertube().on('autoResolutionUpdate', () => this.updateSelection())
22 }
23
24 handleClick (event: any) {
25 if (this.id === -1 && this.player_.peertube().isAutoResolutionForbidden()) return
26
27 super.handleClick(event)
28
29 // Auto resolution
30 if (this.id === -1) {
31 this.player_.peertube().enableAutoResolution()
32 return
33 }
34
35 this.player_.peertube().disableAutoResolution()
36 this.player_.peertube().updateResolution(this.id)
37 }
38
39 updateSelection () {
40 // Check if auto resolution is forbidden or not
41 if (this.id === -1) {
42 if (this.player_.peertube().isAutoResolutionForbidden()) {
43 this.addClass('disabled')
44 } else {
45 this.removeClass('disabled')
46 }
47 }
48
49 if (this.player_.peertube().isAutoResolutionOn()) {
50 this.selected(this.id === -1)
51 return
52 }
53
54 this.selected(this.player_.peertube().getCurrentResolutionId() === this.id)
55 }
56
57 getLabel () {
58 if (this.id === -1) {
59 return this.label + ' <small>' + this.player_.peertube().getCurrentResolutionLabel() + '</small>'
60 }
61
62 return this.label
63 }
64}
65MenuItem.registerComponent('ResolutionMenuItem', ResolutionMenuItem)
66
67export { ResolutionMenuItem }
diff --git a/client/src/assets/player/utils.ts b/client/src/assets/player/utils.ts
index 8b9f34b99..8d87567c2 100644
--- a/client/src/assets/player/utils.ts
+++ b/client/src/assets/player/utils.ts
@@ -112,9 +112,23 @@ function videoFileMinByResolution (files: VideoFile[]) {
112 return min 112 return min
113} 113}
114 114
115function getRtcConfig () {
116 return {
117 iceServers: [
118 {
119 urls: 'stun:stun.stunprotocol.org'
120 },
121 {
122 urls: 'stun:stun.framasoft.org'
123 }
124 ]
125 }
126}
127
115// --------------------------------------------------------------------------- 128// ---------------------------------------------------------------------------
116 129
117export { 130export {
131 getRtcConfig,
118 toTitleCase, 132 toTitleCase,
119 timeToInt, 133 timeToInt,
120 buildVideoLink, 134 buildVideoLink,
diff --git a/client/src/assets/player/webtorrent-info-button.ts b/client/src/assets/player/videojs-components/p2p-info-button.ts
index c3c1af951..6424787b2 100644
--- a/client/src/assets/player/webtorrent-info-button.ts
+++ b/client/src/assets/player/videojs-components/p2p-info-button.ts
@@ -1,8 +1,8 @@
1import { VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings' 1import { PlayerNetworkInfo, VideoJSComponentInterface, videojsUntyped } from '../peertube-videojs-typings'
2import { bytes } from './utils' 2import { bytes } from '../utils'
3 3
4const Button: VideoJSComponentInterface = videojsUntyped.getComponent('Button') 4const Button: VideoJSComponentInterface = videojsUntyped.getComponent('Button')
5class WebtorrentInfoButton extends Button { 5class P2pInfoButton extends Button {
6 6
7 createEl () { 7 createEl () {
8 const div = videojsUntyped.dom.createEl('div', { 8 const div = videojsUntyped.dom.createEl('div', {
@@ -65,7 +65,7 @@ class WebtorrentInfoButton extends Button {
65 subDivHttp.appendChild(subDivHttpText) 65 subDivHttp.appendChild(subDivHttpText)
66 div.appendChild(subDivHttp) 66 div.appendChild(subDivHttp)
67 67
68 this.player_.peertube().on('torrentInfo', (event: any, data: any) => { 68 this.player_.on('p2pInfo', (event: any, data: PlayerNetworkInfo) => {
69 // We are in HTTP fallback 69 // We are in HTTP fallback
70 if (!data) { 70 if (!data) {
71 subDivHttp.className = 'vjs-peertube-displayed' 71 subDivHttp.className = 'vjs-peertube-displayed'
@@ -74,11 +74,14 @@ class WebtorrentInfoButton extends Button {
74 return 74 return
75 } 75 }
76 76
77 const downloadSpeed = bytes(data.downloadSpeed) 77 const p2pStats = data.p2p
78 const uploadSpeed = bytes(data.uploadSpeed) 78 const httpStats = data.http
79 const totalDownloaded = bytes(data.downloaded) 79
80 const totalUploaded = bytes(data.uploaded) 80 const downloadSpeed = bytes(p2pStats.downloadSpeed + httpStats.downloadSpeed)
81 const numPeers = data.numPeers 81 const uploadSpeed = bytes(p2pStats.uploadSpeed + httpStats.uploadSpeed)
82 const totalDownloaded = bytes(p2pStats.downloaded + httpStats.downloaded)
83 const totalUploaded = bytes(p2pStats.uploaded + httpStats.uploaded)
84 const numPeers = p2pStats.numPeers
82 85
83 subDivWebtorrent.title = this.player_.localize('Total downloaded: ') + totalDownloaded.join(' ') + '\n' + 86 subDivWebtorrent.title = this.player_.localize('Total downloaded: ') + totalDownloaded.join(' ') + '\n' +
84 this.player_.localize('Total uploaded: ' + totalUploaded.join(' ')) 87 this.player_.localize('Total uploaded: ' + totalUploaded.join(' '))
@@ -90,7 +93,7 @@ class WebtorrentInfoButton extends Button {
90 uploadSpeedUnit.textContent = ' ' + uploadSpeed[ 1 ] 93 uploadSpeedUnit.textContent = ' ' + uploadSpeed[ 1 ]
91 94
92 peersNumber.textContent = numPeers 95 peersNumber.textContent = numPeers
93 peersText.textContent = ' ' + this.player_.localize('peers') 96 peersText.textContent = ' ' + (numPeers > 1 ? this.player_.localize('peers') : this.player_.localize('peer'))
94 97
95 subDivHttp.className = 'vjs-peertube-hidden' 98 subDivHttp.className = 'vjs-peertube-hidden'
96 subDivWebtorrent.className = 'vjs-peertube-displayed' 99 subDivWebtorrent.className = 'vjs-peertube-displayed'
@@ -99,4 +102,4 @@ class WebtorrentInfoButton extends Button {
99 return div 102 return div
100 } 103 }
101} 104}
102Button.registerComponent('WebTorrentButton', WebtorrentInfoButton) 105Button.registerComponent('P2PInfoButton', P2pInfoButton)
diff --git a/client/src/assets/player/peertube-link-button.ts b/client/src/assets/player/videojs-components/peertube-link-button.ts
index de9a49de9..fed8ea33e 100644
--- a/client/src/assets/player/peertube-link-button.ts
+++ b/client/src/assets/player/videojs-components/peertube-link-button.ts
@@ -1,5 +1,5 @@
1import { VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings' 1import { VideoJSComponentInterface, videojsUntyped } from '../peertube-videojs-typings'
2import { buildVideoLink } from './utils' 2import { buildVideoLink } from '../utils'
3// FIXME: something weird with our path definition in tsconfig and typings 3// FIXME: something weird with our path definition in tsconfig and typings
4// @ts-ignore 4// @ts-ignore
5import { Player } from 'video.js' 5import { Player } from 'video.js'
diff --git a/client/src/assets/player/peertube-load-progress-bar.ts b/client/src/assets/player/videojs-components/peertube-load-progress-bar.ts
index af276d1b2..9a0e3b550 100644
--- a/client/src/assets/player/peertube-load-progress-bar.ts
+++ b/client/src/assets/player/videojs-components/peertube-load-progress-bar.ts
@@ -1,4 +1,4 @@
1import { VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings' 1import { VideoJSComponentInterface, videojsUntyped } from '../peertube-videojs-typings'
2// FIXME: something weird with our path definition in tsconfig and typings 2// FIXME: something weird with our path definition in tsconfig and typings
3// @ts-ignore 3// @ts-ignore
4import { Player } from 'video.js' 4import { Player } from 'video.js'
@@ -27,7 +27,7 @@ class PeerTubeLoadProgressBar extends Component {
27 } 27 }
28 28
29 update () { 29 update () {
30 const torrent = this.player().peertube().getTorrent() 30 const torrent = this.player().webtorrent().getTorrent()
31 if (!torrent) return 31 if (!torrent) return
32 32
33 this.el_.style.width = (torrent.progress * 100) + '%' 33 this.el_.style.width = (torrent.progress * 100) + '%'
diff --git a/client/src/assets/player/videojs-components/resolution-menu-button.ts b/client/src/assets/player/videojs-components/resolution-menu-button.ts
new file mode 100644
index 000000000..abcc16411
--- /dev/null
+++ b/client/src/assets/player/videojs-components/resolution-menu-button.ts
@@ -0,0 +1,109 @@
1// FIXME: something weird with our path definition in tsconfig and typings
2// @ts-ignore
3import { Player } from 'video.js'
4
5import { LoadedQualityData, VideoJSComponentInterface, videojsUntyped } from '../peertube-videojs-typings'
6import { ResolutionMenuItem } from './resolution-menu-item'
7
8const Menu: VideoJSComponentInterface = videojsUntyped.getComponent('Menu')
9const MenuButton: VideoJSComponentInterface = videojsUntyped.getComponent('MenuButton')
10class ResolutionMenuButton extends MenuButton {
11 label: HTMLElement
12
13 constructor (player: Player, options: any) {
14 super(player, options)
15 this.player = player
16
17 player.tech_.on('loadedqualitydata', (e: any, data: any) => this.buildQualities(data))
18
19 player.peertube().on('resolutionChange', () => setTimeout(() => this.trigger('updateLabel'), 0))
20 }
21
22 createEl () {
23 const el = super.createEl()
24
25 this.labelEl_ = videojsUntyped.dom.createEl('div', {
26 className: 'vjs-resolution-value'
27 })
28
29 el.appendChild(this.labelEl_)
30
31 return el
32 }
33
34 updateARIAAttributes () {
35 this.el().setAttribute('aria-label', 'Quality')
36 }
37
38 createMenu () {
39 return new Menu(this.player_)
40 }
41
42 buildCSSClass () {
43 return super.buildCSSClass() + ' vjs-resolution-button'
44 }
45
46 buildWrapperCSSClass () {
47 return 'vjs-resolution-control ' + super.buildWrapperCSSClass()
48 }
49
50 private addClickListener (component: any) {
51 component.on('click', () => {
52 let children = this.menu.children()
53
54 for (const child of children) {
55 if (component !== child) {
56 child.selected(false)
57 }
58 }
59 })
60 }
61
62 private buildQualities (data: LoadedQualityData) {
63 // The automatic resolution item will need other labels
64 const labels: { [ id: number ]: string } = {}
65
66 data.qualityData.video.sort((a, b) => {
67 if (a.id > b.id) return -1
68 if (a.id === b.id) return 0
69 return 1
70 })
71
72 for (const d of data.qualityData.video) {
73 // Skip auto resolution, we'll add it ourselves
74 if (d.id === -1) continue
75
76 this.menu.addChild(new ResolutionMenuItem(
77 this.player_,
78 {
79 id: d.id,
80 label: d.label,
81 selected: d.selected,
82 callback: data.qualitySwitchCallback
83 })
84 )
85
86 labels[d.id] = d.label
87 }
88
89 this.menu.addChild(new ResolutionMenuItem(
90 this.player_,
91 {
92 id: -1,
93 label: this.player_.localize('Auto'),
94 labels,
95 callback: data.qualitySwitchCallback,
96 selected: true // By default, in auto mode
97 }
98 ))
99
100 for (const m of this.menu.children()) {
101 this.addClickListener(m)
102 }
103
104 this.trigger('menuChanged')
105 }
106}
107ResolutionMenuButton.prototype.controlText_ = 'Quality'
108
109MenuButton.registerComponent('ResolutionMenuButton', ResolutionMenuButton)
diff --git a/client/src/assets/player/videojs-components/resolution-menu-item.ts b/client/src/assets/player/videojs-components/resolution-menu-item.ts
new file mode 100644
index 000000000..6c42fefd2
--- /dev/null
+++ b/client/src/assets/player/videojs-components/resolution-menu-item.ts
@@ -0,0 +1,83 @@
1// FIXME: something weird with our path definition in tsconfig and typings
2// @ts-ignore
3import { Player } from 'video.js'
4
5import { AutoResolutionUpdateData, ResolutionUpdateData, VideoJSComponentInterface, videojsUntyped } from '../peertube-videojs-typings'
6
7const MenuItem: VideoJSComponentInterface = videojsUntyped.getComponent('MenuItem')
8class ResolutionMenuItem extends MenuItem {
9 private readonly id: number
10 private readonly label: string
11 // Only used for the automatic item
12 private readonly labels: { [id: number]: string }
13 private readonly callback: Function
14
15 private autoResolutionPossible: boolean
16 private currentResolutionLabel: string
17
18 constructor (player: Player, options: any) {
19 options.selectable = true
20
21 super(player, options)
22
23 this.autoResolutionPossible = true
24 this.currentResolutionLabel = ''
25
26 this.label = options.label
27 this.labels = options.labels
28 this.id = options.id
29 this.callback = options.callback
30
31 player.peertube().on('resolutionChange', (_: any, data: ResolutionUpdateData) => this.updateSelection(data))
32
33 // We only want to disable the "Auto" item
34 if (this.id === -1) {
35 player.peertube().on('autoResolutionChange', (_: any, data: AutoResolutionUpdateData) => this.updateAutoResolution(data))
36 }
37 }
38
39 handleClick (event: any) {
40 // Auto button disabled?
41 if (this.autoResolutionPossible === false && this.id === -1) return
42
43 super.handleClick(event)
44
45 this.callback(this.id, 'video')
46 }
47
48 updateSelection (data: ResolutionUpdateData) {
49 if (this.id === -1) {
50 this.currentResolutionLabel = this.labels[data.id]
51 }
52
53 // Automatic resolution only
54 if (data.auto === true) {
55 this.selected(this.id === -1)
56 return
57 }
58
59 this.selected(this.id === data.id)
60 }
61
62 updateAutoResolution (data: AutoResolutionUpdateData) {
63 // Check if the auto resolution is enabled or not
64 if (data.possible === false) {
65 this.addClass('disabled')
66 } else {
67 this.removeClass('disabled')
68 }
69
70 this.autoResolutionPossible = data.possible
71 }
72
73 getLabel () {
74 if (this.id === -1) {
75 return this.label + ' <small>' + this.currentResolutionLabel + '</small>'
76 }
77
78 return this.label
79 }
80}
81MenuItem.registerComponent('ResolutionMenuItem', ResolutionMenuItem)
82
83export { ResolutionMenuItem }
diff --git a/client/src/assets/player/settings-menu-button.ts b/client/src/assets/player/videojs-components/settings-menu-button.ts
index a7aefdcc3..14cb8ba43 100644
--- a/client/src/assets/player/settings-menu-button.ts
+++ b/client/src/assets/player/videojs-components/settings-menu-button.ts
@@ -6,8 +6,8 @@
6import * as videojs from 'video.js' 6import * as videojs from 'video.js'
7 7
8import { SettingsMenuItem } from './settings-menu-item' 8import { SettingsMenuItem } from './settings-menu-item'
9import { VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings' 9import { VideoJSComponentInterface, videojsUntyped } from '../peertube-videojs-typings'
10import { toTitleCase } from './utils' 10import { toTitleCase } from '../utils'
11 11
12const Button: VideoJSComponentInterface = videojsUntyped.getComponent('Button') 12const Button: VideoJSComponentInterface = videojsUntyped.getComponent('Button')
13const Menu: VideoJSComponentInterface = videojsUntyped.getComponent('Menu') 13const Menu: VideoJSComponentInterface = videojsUntyped.getComponent('Menu')
diff --git a/client/src/assets/player/settings-menu-item.ts b/client/src/assets/player/videojs-components/settings-menu-item.ts
index 2a3460ae5..f14959f9c 100644
--- a/client/src/assets/player/settings-menu-item.ts
+++ b/client/src/assets/player/videojs-components/settings-menu-item.ts
@@ -5,8 +5,8 @@
5// @ts-ignore 5// @ts-ignore
6import * as videojs from 'video.js' 6import * as videojs from 'video.js'
7 7
8import { toTitleCase } from './utils' 8import { toTitleCase } from '../utils'
9import { VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings' 9import { VideoJSComponentInterface, videojsUntyped } from '../peertube-videojs-typings'
10 10
11const MenuItem: VideoJSComponentInterface = videojsUntyped.getComponent('MenuItem') 11const MenuItem: VideoJSComponentInterface = videojsUntyped.getComponent('MenuItem')
12const component: VideoJSComponentInterface = videojsUntyped.getComponent('Component') 12const component: VideoJSComponentInterface = videojsUntyped.getComponent('Component')
@@ -220,12 +220,14 @@ class SettingsMenuItem extends MenuItem {
220 } 220 }
221 221
222 build () { 222 build () {
223 const saveUpdateLabel = this.subMenu.updateLabel 223 this.subMenu.on('updateLabel', () => {
224 this.subMenu.updateLabel = () => {
225 this.update() 224 this.update()
226 225 })
227 saveUpdateLabel.call(this.subMenu) 226 this.subMenu.on('menuChanged', () => {
228 } 227 this.bindClickEvents()
228 this.setSize()
229 this.update()
230 })
229 231
230 this.settingsSubMenuTitleEl_.innerHTML = this.player_.localize(this.subMenu.controlText_) 232 this.settingsSubMenuTitleEl_.innerHTML = this.player_.localize(this.subMenu.controlText_)
231 this.settingsSubMenuEl_.appendChild(this.subMenu.menu.el_) 233 this.settingsSubMenuEl_.appendChild(this.subMenu.menu.el_)
@@ -233,7 +235,7 @@ class SettingsMenuItem extends MenuItem {
233 this.update() 235 this.update()
234 236
235 this.createBackButton() 237 this.createBackButton()
236 this.getSize() 238 this.setSize()
237 this.bindClickEvents() 239 this.bindClickEvents()
238 240
239 // prefixed event listeners for CSS TransitionEnd 241 // prefixed event listeners for CSS TransitionEnd
@@ -295,8 +297,9 @@ class SettingsMenuItem extends MenuItem {
295 297
296 // save size of submenus on first init 298 // save size of submenus on first init
297 // if number of submenu items change dynamically more logic will be needed 299 // if number of submenu items change dynamically more logic will be needed
298 getSize () { 300 setSize () {
299 this.dialog.removeClass('vjs-hidden') 301 this.dialog.removeClass('vjs-hidden')
302 videojsUntyped.dom.removeClass(this.settingsSubMenuEl_, 'vjs-hidden')
300 this.size = this.settingsButton.getComponentSize(this.settingsSubMenuEl_) 303 this.size = this.settingsButton.getComponentSize(this.settingsSubMenuEl_)
301 this.setMargin() 304 this.setMargin()
302 this.dialog.addClass('vjs-hidden') 305 this.dialog.addClass('vjs-hidden')
diff --git a/client/src/assets/player/theater-button.ts b/client/src/assets/player/videojs-components/theater-button.ts
index 4f8fede3d..1e11a9546 100644
--- a/client/src/assets/player/theater-button.ts
+++ b/client/src/assets/player/videojs-components/theater-button.ts
@@ -2,8 +2,8 @@
2// @ts-ignore 2// @ts-ignore
3import * as videojs from 'video.js' 3import * as videojs from 'video.js'
4 4
5import { VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings' 5import { VideoJSComponentInterface, videojsUntyped } from '../peertube-videojs-typings'
6import { saveTheaterInStore, getStoredTheater } from './peertube-player-local-storage' 6import { saveTheaterInStore, getStoredTheater } from '../peertube-player-local-storage'
7 7
8const Button: VideoJSComponentInterface = videojsUntyped.getComponent('Button') 8const Button: VideoJSComponentInterface = videojsUntyped.getComponent('Button')
9class TheaterButton extends Button { 9class TheaterButton extends Button {
diff --git a/client/src/assets/player/peertube-chunk-store.ts b/client/src/assets/player/webtorrent/peertube-chunk-store.ts
index 54cc0ea64..54cc0ea64 100644
--- a/client/src/assets/player/peertube-chunk-store.ts
+++ b/client/src/assets/player/webtorrent/peertube-chunk-store.ts
diff --git a/client/src/assets/player/video-renderer.ts b/client/src/assets/player/webtorrent/video-renderer.ts
index a3415937b..a3415937b 100644
--- a/client/src/assets/player/video-renderer.ts
+++ b/client/src/assets/player/webtorrent/video-renderer.ts
diff --git a/client/src/assets/player/peertube-videojs-plugin.ts b/client/src/assets/player/webtorrent/webtorrent-plugin.ts
index e9fb90c61..c69bf31fa 100644
--- a/client/src/assets/player/peertube-videojs-plugin.ts
+++ b/client/src/assets/player/webtorrent/webtorrent-plugin.ts
@@ -3,23 +3,18 @@
3import * as videojs from 'video.js' 3import * as videojs from 'video.js'
4 4
5import * as WebTorrent from 'webtorrent' 5import * as WebTorrent from 'webtorrent'
6import { VideoFile } from '../../../../shared/models/videos/video.model' 6import { VideoFile } from '../../../../../shared/models/videos/video.model'
7import { renderVideo } from './video-renderer' 7import { renderVideo } from './video-renderer'
8import './settings-menu-button' 8import { LoadedQualityData, PlayerNetworkInfo, VideoJSComponentInterface, WebtorrentPluginOptions } from '../peertube-videojs-typings'
9import { PeertubePluginOptions, UserWatching, VideoJSCaption, VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings' 9import { getRtcConfig, videoFileMaxByResolution, videoFileMinByResolution } from '../utils'
10import { isMobile, timeToInt, videoFileMaxByResolution, videoFileMinByResolution } from './utils'
11import { PeertubeChunkStore } from './peertube-chunk-store' 10import { PeertubeChunkStore } from './peertube-chunk-store'
12import { 11import {
13 getAverageBandwidthInStore, 12 getAverageBandwidthInStore,
14 getStoredLastSubtitle,
15 getStoredMute, 13 getStoredMute,
16 getStoredVolume, 14 getStoredVolume,
17 getStoredWebTorrentEnabled, 15 getStoredWebTorrentEnabled,
18 saveAverageBandwidth, 16 saveAverageBandwidth
19 saveLastSubtitle, 17} from '../peertube-player-local-storage'
20 saveMuteInStore,
21 saveVolumeInStore
22} from './peertube-player-local-storage'
23 18
24const CacheChunkStore = require('cache-chunk-store') 19const CacheChunkStore = require('cache-chunk-store')
25 20
@@ -30,14 +25,13 @@ type PlayOptions = {
30} 25}
31 26
32const Plugin: VideoJSComponentInterface = videojs.getPlugin('plugin') 27const Plugin: VideoJSComponentInterface = videojs.getPlugin('plugin')
33class PeerTubePlugin extends Plugin { 28class WebTorrentPlugin extends Plugin {
34 private readonly playerElement: HTMLVideoElement 29 private readonly playerElement: HTMLVideoElement
35 30
36 private readonly autoplay: boolean = false 31 private readonly autoplay: boolean = false
37 private readonly startTime: number = 0 32 private readonly startTime: number = 0
38 private readonly savePlayerSrcFunction: Function 33 private readonly savePlayerSrcFunction: Function
39 private readonly videoFiles: VideoFile[] 34 private readonly videoFiles: VideoFile[]
40 private readonly videoViewUrl: string
41 private readonly videoDuration: number 35 private readonly videoDuration: number
42 private readonly CONSTANTS = { 36 private readonly CONSTANTS = {
43 INFO_SCHEDULER: 1000, // Don't change this 37 INFO_SCHEDULER: 1000, // Don't change this
@@ -45,22 +39,12 @@ class PeerTubePlugin extends Plugin {
45 AUTO_QUALITY_THRESHOLD_PERCENT: 30, // Bandwidth should be 30% more important than a resolution bitrate to change to it 39 AUTO_QUALITY_THRESHOLD_PERCENT: 30, // Bandwidth should be 30% more important than a resolution bitrate to change to it
46 AUTO_QUALITY_OBSERVATION_TIME: 10000, // Wait 10 seconds after having change the resolution before another check 40 AUTO_QUALITY_OBSERVATION_TIME: 10000, // Wait 10 seconds after having change the resolution before another check
47 AUTO_QUALITY_HIGHER_RESOLUTION_DELAY: 5000, // Buffering higher resolution during 5 seconds 41 AUTO_QUALITY_HIGHER_RESOLUTION_DELAY: 5000, // Buffering higher resolution during 5 seconds
48 BANDWIDTH_AVERAGE_NUMBER_OF_VALUES: 5, // Last 5 seconds to build average bandwidth 42 BANDWIDTH_AVERAGE_NUMBER_OF_VALUES: 5 // Last 5 seconds to build average bandwidth
49 USER_WATCHING_VIDEO_INTERVAL: 5000 // Every 5 seconds, notify the user is watching the video
50 } 43 }
51 44
52 private readonly webtorrent = new WebTorrent({ 45 private readonly webtorrent = new WebTorrent({
53 tracker: { 46 tracker: {
54 rtcConfig: { 47 rtcConfig: getRtcConfig()
55 iceServers: [
56 {
57 urls: 'stun:stun.stunprotocol.org'
58 },
59 {
60 urls: 'stun:stun.framasoft.org'
61 }
62 ]
63 }
64 }, 48 },
65 dht: false 49 dht: false
66 }) 50 })
@@ -68,46 +52,37 @@ class PeerTubePlugin extends Plugin {
68 private player: any 52 private player: any
69 private currentVideoFile: VideoFile 53 private currentVideoFile: VideoFile
70 private torrent: WebTorrent.Torrent 54 private torrent: WebTorrent.Torrent
71 private videoCaptions: VideoJSCaption[]
72 private defaultSubtitle: string
73 55
74 private renderer: any 56 private renderer: any
75 private fakeRenderer: any 57 private fakeRenderer: any
76 private destroyingFakeRenderer = false 58 private destroyingFakeRenderer = false
77 59
78 private autoResolution = true 60 private autoResolution = true
79 private forbidAutoResolution = false 61 private autoResolutionPossible = true
80 private isAutoResolutionObservation = false 62 private isAutoResolutionObservation = false
81 private playerRefusedP2P = false 63 private playerRefusedP2P = false
82 64
83 private videoViewInterval: any
84 private torrentInfoInterval: any 65 private torrentInfoInterval: any
85 private autoQualityInterval: any 66 private autoQualityInterval: any
86 private userWatchingVideoInterval: any
87 private addTorrentDelay: any 67 private addTorrentDelay: any
88 private qualityObservationTimer: any 68 private qualityObservationTimer: any
89 private runAutoQualitySchedulerTimer: any 69 private runAutoQualitySchedulerTimer: any
90 70
91 private downloadSpeeds: number[] = [] 71 private downloadSpeeds: number[] = []
92 72
93 constructor (player: videojs.Player, options: PeertubePluginOptions) { 73 constructor (player: videojs.Player, options: WebtorrentPluginOptions) {
94 super(player, options) 74 super(player, options)
95 75
96 // Disable auto play on iOS 76 // Disable auto play on iOS
97 this.autoplay = options.autoplay && this.isIOS() === false 77 this.autoplay = options.autoplay && this.isIOS() === false
98 this.playerRefusedP2P = !getStoredWebTorrentEnabled() 78 this.playerRefusedP2P = !getStoredWebTorrentEnabled()
99 79
100 this.startTime = timeToInt(options.startTime)
101 this.videoFiles = options.videoFiles 80 this.videoFiles = options.videoFiles
102 this.videoViewUrl = options.videoViewUrl
103 this.videoDuration = options.videoDuration 81 this.videoDuration = options.videoDuration
104 this.videoCaptions = options.videoCaptions
105 82
106 this.savePlayerSrcFunction = this.player.src 83 this.savePlayerSrcFunction = this.player.src
107 this.playerElement = options.playerElement 84 this.playerElement = options.playerElement
108 85
109 if (this.autoplay === true) this.player.addClass('vjs-has-autoplay')
110
111 this.player.ready(() => { 86 this.player.ready(() => {
112 const playerOptions = this.player.options_ 87 const playerOptions = this.player.options_
113 88
@@ -117,33 +92,10 @@ class PeerTubePlugin extends Plugin {
117 const muted = playerOptions.muted !== undefined ? playerOptions.muted : getStoredMute() 92 const muted = playerOptions.muted !== undefined ? playerOptions.muted : getStoredMute()
118 if (muted !== undefined) this.player.muted(muted) 93 if (muted !== undefined) this.player.muted(muted)
119 94
120 this.defaultSubtitle = options.subtitle || getStoredLastSubtitle()
121
122 this.player.on('volumechange', () => {
123 saveVolumeInStore(this.player.volume())
124 saveMuteInStore(this.player.muted())
125 })
126
127 this.player.textTracks().on('change', () => {
128 const showing = this.player.textTracks().tracks_.find((t: { kind: string, mode: string }) => {
129 return t.kind === 'captions' && t.mode === 'showing'
130 })
131
132 if (!showing) {
133 saveLastSubtitle('off')
134 return
135 }
136
137 saveLastSubtitle(showing.language)
138 })
139
140 this.player.duration(options.videoDuration) 95 this.player.duration(options.videoDuration)
141 96
142 this.initializePlayer() 97 this.initializePlayer()
143 this.runTorrentInfoScheduler() 98 this.runTorrentInfoScheduler()
144 this.runViewAdd()
145
146 if (options.userWatching) this.runUserWatchVideo(options.userWatching)
147 99
148 this.player.one('play', () => { 100 this.player.one('play', () => {
149 // Don't run immediately scheduler, wait some seconds the TCP connections are made 101 // Don't run immediately scheduler, wait some seconds the TCP connections are made
@@ -157,12 +109,9 @@ class PeerTubePlugin extends Plugin {
157 clearTimeout(this.qualityObservationTimer) 109 clearTimeout(this.qualityObservationTimer)
158 clearTimeout(this.runAutoQualitySchedulerTimer) 110 clearTimeout(this.runAutoQualitySchedulerTimer)
159 111
160 clearInterval(this.videoViewInterval)
161 clearInterval(this.torrentInfoInterval) 112 clearInterval(this.torrentInfoInterval)
162 clearInterval(this.autoQualityInterval) 113 clearInterval(this.autoQualityInterval)
163 114
164 if (this.userWatchingVideoInterval) clearInterval(this.userWatchingVideoInterval)
165
166 // Don't need to destroy renderer, video player will be destroyed 115 // Don't need to destroy renderer, video player will be destroyed
167 this.flushVideoFile(this.currentVideoFile, false) 116 this.flushVideoFile(this.currentVideoFile, false)
168 117
@@ -173,13 +122,6 @@ class PeerTubePlugin extends Plugin {
173 return this.currentVideoFile ? this.currentVideoFile.resolution.id : -1 122 return this.currentVideoFile ? this.currentVideoFile.resolution.id : -1
174 } 123 }
175 124
176 getCurrentResolutionLabel () {
177 if (!this.currentVideoFile) return ''
178
179 const fps = this.currentVideoFile.fps >= 50 ? this.currentVideoFile.fps : ''
180 return this.currentVideoFile.resolution.label + fps
181 }
182
183 updateVideoFile ( 125 updateVideoFile (
184 videoFile?: VideoFile, 126 videoFile?: VideoFile,
185 options: { 127 options: {
@@ -228,7 +170,8 @@ class PeerTubePlugin extends Plugin {
228 return done() 170 return done()
229 }) 171 })
230 172
231 this.trigger('videoFileUpdate') 173 this.changeQuality()
174 this.trigger('resolutionChange', { auto: this.autoResolution, resolutionId: this.currentVideoFile.resolution.id })
232 } 175 }
233 176
234 updateResolution (resolutionId: number, delay = 0) { 177 updateResolution (resolutionId: number, delay = 0) {
@@ -262,28 +205,17 @@ class PeerTubePlugin extends Plugin {
262 } 205 }
263 } 206 }
264 207
265 isAutoResolutionOn () {
266 return this.autoResolution
267 }
268
269 enableAutoResolution () { 208 enableAutoResolution () {
270 this.autoResolution = true 209 this.autoResolution = true
271 this.trigger('autoResolutionUpdate') 210 this.trigger('resolutionChange', { auto: this.autoResolution, resolutionId: this.getCurrentResolutionId() })
272 } 211 }
273 212
274 disableAutoResolution (forbid = false) { 213 disableAutoResolution (forbid = false) {
275 if (forbid === true) this.forbidAutoResolution = true 214 if (forbid === true) this.autoResolutionPossible = false
276 215
277 this.autoResolution = false 216 this.autoResolution = false
278 this.trigger('autoResolutionUpdate') 217 this.trigger('autoResolutionChange', { possible: this.autoResolutionPossible })
279 } 218 this.trigger('resolutionChange', { auto: this.autoResolution, resolutionId: this.getCurrentResolutionId() })
280
281 isAutoResolutionForbidden () {
282 return this.forbidAutoResolution === true
283 }
284
285 getCurrentVideoFile () {
286 return this.currentVideoFile
287 } 219 }
288 220
289 getTorrent () { 221 getTorrent () {
@@ -462,13 +394,7 @@ class PeerTubePlugin extends Plugin {
462 } 394 }
463 395
464 private initializePlayer () { 396 private initializePlayer () {
465 if (isMobile()) this.player.addClass('vjs-is-mobile') 397 this.buildQualities()
466
467 this.initSmoothProgressBar()
468
469 this.initCaptions()
470
471 this.alterInactivity()
472 398
473 if (this.autoplay === true) { 399 if (this.autoplay === true) {
474 this.player.posterImage.hide() 400 this.player.posterImage.hide()
@@ -491,7 +417,7 @@ class PeerTubePlugin extends Plugin {
491 417
492 // Not initialized or in HTTP fallback 418 // Not initialized or in HTTP fallback
493 if (this.torrent === undefined || this.torrent === null) return 419 if (this.torrent === undefined || this.torrent === null) return
494 if (this.isAutoResolutionOn() === false) return 420 if (this.autoResolution === false) return
495 if (this.isAutoResolutionObservation === true) return 421 if (this.isAutoResolutionObservation === true) return
496 422
497 const file = this.getAppropriateFile() 423 const file = this.getAppropriateFile()
@@ -531,78 +457,27 @@ class PeerTubePlugin extends Plugin {
531 if (this.torrent === undefined) return 457 if (this.torrent === undefined) return
532 458
533 // Http fallback 459 // Http fallback
534 if (this.torrent === null) return this.trigger('torrentInfo', false) 460 if (this.torrent === null) return this.player.trigger('p2pInfo', false)
535 461
536 // this.webtorrent.downloadSpeed because we need to take into account the potential old torrent too 462 // this.webtorrent.downloadSpeed because we need to take into account the potential old torrent too
537 if (this.webtorrent.downloadSpeed !== 0) this.downloadSpeeds.push(this.webtorrent.downloadSpeed) 463 if (this.webtorrent.downloadSpeed !== 0) this.downloadSpeeds.push(this.webtorrent.downloadSpeed)
538 464
539 return this.trigger('torrentInfo', { 465 return this.player.trigger('p2pInfo', {
540 downloadSpeed: this.torrent.downloadSpeed, 466 http: {
541 numPeers: this.torrent.numPeers, 467 downloadSpeed: 0,
542 uploadSpeed: this.torrent.uploadSpeed, 468 uploadSpeed: 0,
543 downloaded: this.torrent.downloaded, 469 downloaded: 0,
544 uploaded: this.torrent.uploaded 470 uploaded: 0
545 }) 471 },
546 }, this.CONSTANTS.INFO_SCHEDULER) 472 p2p: {
547 } 473 downloadSpeed: this.torrent.downloadSpeed,
548 474 numPeers: this.torrent.numPeers,
549 private runViewAdd () { 475 uploadSpeed: this.torrent.uploadSpeed,
550 this.clearVideoViewInterval() 476 downloaded: this.torrent.downloaded,
551 477 uploaded: this.torrent.uploaded
552 // After 30 seconds (or 3/4 of the video), add a view to the video
553 let minSecondsToView = 30
554
555 if (this.videoDuration < minSecondsToView) minSecondsToView = (this.videoDuration * 3) / 4
556
557 let secondsViewed = 0
558 this.videoViewInterval = setInterval(() => {
559 if (this.player && !this.player.paused()) {
560 secondsViewed += 1
561
562 if (secondsViewed > minSecondsToView) {
563 this.clearVideoViewInterval()
564
565 this.addViewToVideo().catch(err => console.error(err))
566 } 478 }
567 } 479 } as PlayerNetworkInfo)
568 }, 1000) 480 }, this.CONSTANTS.INFO_SCHEDULER)
569 }
570
571 private runUserWatchVideo (options: UserWatching) {
572 let lastCurrentTime = 0
573
574 this.userWatchingVideoInterval = setInterval(() => {
575 const currentTime = Math.floor(this.player.currentTime())
576
577 if (currentTime - lastCurrentTime >= 1) {
578 lastCurrentTime = currentTime
579
580 this.notifyUserIsWatching(currentTime, options.url, options.authorizationHeader)
581 .catch(err => console.error('Cannot notify user is watching.', err))
582 }
583 }, this.CONSTANTS.USER_WATCHING_VIDEO_INTERVAL)
584 }
585
586 private clearVideoViewInterval () {
587 if (this.videoViewInterval !== undefined) {
588 clearInterval(this.videoViewInterval)
589 this.videoViewInterval = undefined
590 }
591 }
592
593 private addViewToVideo () {
594 if (!this.videoViewUrl) return Promise.resolve(undefined)
595
596 return fetch(this.videoViewUrl, { method: 'POST' })
597 }
598
599 private notifyUserIsWatching (currentTime: number, url: string, authorizationHeader: string) {
600 const body = new URLSearchParams()
601 body.append('currentTime', currentTime.toString())
602
603 const headers = new Headers({ 'Authorization': authorizationHeader })
604
605 return fetch(url, { method: 'PUT', body, headers })
606 } 481 }
607 482
608 private fallbackToHttp (options: PlayOptions, done?: Function) { 483 private fallbackToHttp (options: PlayOptions, done?: Function) {
@@ -620,8 +495,10 @@ class PeerTubePlugin extends Plugin {
620 this.player.src = this.savePlayerSrcFunction 495 this.player.src = this.savePlayerSrcFunction
621 this.player.src(httpUrl) 496 this.player.src(httpUrl)
622 497
498 this.changeQuality()
499
623 // We changed the source, so reinit captions 500 // We changed the source, so reinit captions
624 this.initCaptions() 501 this.player.trigger('sourcechange')
625 502
626 return this.tryToPlay(err => { 503 return this.tryToPlay(err => {
627 if (err && done) return done(err) 504 if (err && done) return done(err)
@@ -649,25 +526,6 @@ class PeerTubePlugin extends Plugin {
649 return !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform) 526 return !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform)
650 } 527 }
651 528
652 private alterInactivity () {
653 let saveInactivityTimeout: number
654
655 const disableInactivity = () => {
656 saveInactivityTimeout = this.player.options_.inactivityTimeout
657 this.player.options_.inactivityTimeout = 0
658 }
659 const enableInactivity = () => {
660 this.player.options_.inactivityTimeout = saveInactivityTimeout
661 }
662
663 const settingsDialog = this.player.children_.find((c: any) => c.name_ === 'SettingsDialog')
664
665 this.player.controlBar.on('mouseenter', () => disableInactivity())
666 settingsDialog.on('mouseenter', () => disableInactivity())
667 this.player.controlBar.on('mouseleave', () => enableInactivity())
668 settingsDialog.on('mouseleave', () => enableInactivity())
669 }
670
671 private pickAverageVideoFile () { 529 private pickAverageVideoFile () {
672 if (this.videoFiles.length === 1) return this.videoFiles[0] 530 if (this.videoFiles.length === 1) return this.videoFiles[0]
673 531
@@ -712,43 +570,70 @@ class PeerTubePlugin extends Plugin {
712 } 570 }
713 } 571 }
714 572
715 private initCaptions () { 573 private buildQualities () {
716 for (const caption of this.videoCaptions) { 574 const qualityLevelsPayload = []
717 this.player.addRemoteTextTrack({ 575
718 kind: 'captions', 576 for (const file of this.videoFiles) {
719 label: caption.label, 577 const representation = {
720 language: caption.language, 578 id: file.resolution.id,
721 id: caption.language, 579 label: this.buildQualityLabel(file),
722 src: caption.src, 580 height: file.resolution.id,
723 default: this.defaultSubtitle === caption.language 581 _enabled: true
724 }, false) 582 }
583
584 this.player.qualityLevels().addQualityLevel(representation)
585
586 qualityLevelsPayload.push({
587 id: representation.id,
588 label: representation.label,
589 selected: false
590 })
591 }
592
593 const payload: LoadedQualityData = {
594 qualitySwitchCallback: (d: any) => this.qualitySwitchCallback(d),
595 qualityData: {
596 video: qualityLevelsPayload
597 }
598 }
599 this.player.tech_.trigger('loadedqualitydata', payload)
600 }
601
602 private buildQualityLabel (file: VideoFile) {
603 let label = file.resolution.label
604
605 if (file.fps && file.fps >= 50) {
606 label += file.fps
725 } 607 }
726 608
727 this.player.trigger('captionsChanged') 609 return label
728 } 610 }
729 611
730 // Thanks: https://github.com/videojs/video.js/issues/4460#issuecomment-312861657 612 private qualitySwitchCallback (id: number) {
731 private initSmoothProgressBar () { 613 if (id === -1) {
732 const SeekBar = videojsUntyped.getComponent('SeekBar') 614 if (this.autoResolutionPossible === true) this.enableAutoResolution()
733 SeekBar.prototype.getPercent = function getPercent () { 615 return
734 // Allows for smooth scrubbing, when player can't keep up.
735 // const time = (this.player_.scrubbing()) ?
736 // this.player_.getCache().currentTime :
737 // this.player_.currentTime()
738 const time = this.player_.currentTime()
739 const percent = time / this.player_.duration()
740 return percent >= 1 ? 1 : percent
741 } 616 }
742 SeekBar.prototype.handleMouseMove = function handleMouseMove (event: any) { 617
743 let newTime = this.calculateDistance(event) * this.player_.duration() 618 this.disableAutoResolution()
744 if (newTime === this.player_.duration()) { 619 this.updateResolution(id)
745 newTime = newTime - 0.1 620 }
746 } 621
747 this.player_.currentTime(newTime) 622 private changeQuality () {
748 this.update() 623 const resolutionId = this.currentVideoFile.resolution.id
624 const qualityLevels = this.player.qualityLevels()
625
626 if (resolutionId === -1) {
627 qualityLevels.selectedIndex = -1
628 return
629 }
630
631 for (let i = 0; i < qualityLevels; i++) {
632 const q = this.player.qualityLevels[i]
633 if (q.height === resolutionId) qualityLevels.selectedIndex = i
749 } 634 }
750 } 635 }
751} 636}
752 637
753videojs.registerPlugin('peertube', PeerTubePlugin) 638videojs.registerPlugin('webtorrent', WebTorrentPlugin)
754export { PeerTubePlugin } 639export { WebTorrentPlugin }
diff --git a/client/src/index.html b/client/src/index.html
index 2af0020ad..8c257824e 100644
--- a/client/src/index.html
+++ b/client/src/index.html
@@ -5,7 +5,7 @@
5 <meta name="viewport" content="width=device-width, initial-scale=1"> 5 <meta name="viewport" content="width=device-width, initial-scale=1">
6 6
7 <meta name="theme-color" content="#fff" /> 7 <meta name="theme-color" content="#fff" />
8 8 <meta property="og:platform" content="PeerTube" />
9 <!-- Web Manifest file --> 9 <!-- Web Manifest file -->
10 <link rel="manifest" href="/manifest.webmanifest"> 10 <link rel="manifest" href="/manifest.webmanifest">
11 11
diff --git a/client/src/main.ts b/client/src/main.ts
index dee962180..86fdabba5 100644
--- a/client/src/main.ts
+++ b/client/src/main.ts
@@ -34,7 +34,7 @@ const bootstrap = () => platformBrowserDynamic()
34 // .catch(err => console.error('Cannot register service worker.', err)) 34 // .catch(err => console.error('Cannot register service worker.', err))
35 // } 35 // }
36 36
37 if (navigator.serviceWorker) { 37 if (navigator.serviceWorker && typeof navigator.serviceWorker.getRegistrations === 'function') {
38 navigator.serviceWorker.getRegistrations() 38 navigator.serviceWorker.getRegistrations()
39 .then(registrations => { 39 .then(registrations => {
40 for (const registration of registrations) { 40 for (const registration of registrations) {
diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss
index 2356f9837..478737a43 100644
--- a/client/src/sass/application.scss
+++ b/client/src/sass/application.scss
@@ -23,7 +23,7 @@ body {
23 // now beware node-sass requires interpolation 23 // now beware node-sass requires interpolation
24 // for css custom properties #{$var} 24 // for css custom properties #{$var}
25 --mainColor: #{$orange-color}; 25 --mainColor: #{$orange-color};
26 --mainHoverColor: #{$orange-hoover-color}; 26 --mainHoverColor: #{$orange-hover-color};
27 --mainBackgroundColor: #{$bg-color}; 27 --mainBackgroundColor: #{$bg-color};
28 --mainForegroundColor: #{$fg-color}; 28 --mainForegroundColor: #{$fg-color};
29 --menuBackgroundColor: #{$menu-background}; 29 --menuBackgroundColor: #{$menu-background};
@@ -229,13 +229,12 @@ label {
229 font-weight: $font-semibold; 229 font-weight: $font-semibold;
230 } 230 }
231 231
232 .close { 232 my-global-icon {
233 @include icon(24px); 233 @include icon(24px);
234 234
235 position: relative; 235 position: relative;
236 top: 3px; 236 top: 3px;
237 float: right; 237 float: right;
238 background-image: url('../assets/images/global/cross.svg');
239 238
240 margin: 0; 239 margin: 0;
241 padding: 0; 240 padding: 0;
@@ -293,6 +292,10 @@ ngb-tabset.bootstrap {
293 color: var(--mainForegroundColor) !important; 292 color: var(--mainForegroundColor) !important;
294 } 293 }
295 } 294 }
295
296 .nav-pills .nav-link.active {
297 color: #000 !important;
298 }
296} 299}
297 300
298.nav-tabs .nav-link.active { 301.nav-tabs .nav-link.active {
@@ -324,7 +327,7 @@ ngb-tabset.bootstrap {
324table { 327table {
325 .action-button-edit, .action-button-delete { 328 .action-button-edit, .action-button-delete {
326 &:hover, &:active, &:focus, &[disabled], &.disabled { 329 &:hover, &:active, &:focus, &[disabled], &.disabled {
327 background-color: $grey-color !important; 330 background-color: $grey-background-color !important;
328 } 331 }
329 } 332 }
330} 333}
@@ -389,4 +392,4 @@ table {
389 } 392 }
390 } 393 }
391 } 394 }
392} \ No newline at end of file 395}
diff --git a/client/src/sass/include/_mixins.scss b/client/src/sass/include/_mixins.scss
index d6f391a45..e18e9ae9d 100644
--- a/client/src/sass/include/_mixins.scss
+++ b/client/src/sass/include/_mixins.scss
@@ -55,6 +55,18 @@
55 hyphens: auto; 55 hyphens: auto;
56} 56}
57 57
58@mixin apply-svg-color ($color) {
59 /deep/ svg {
60 path[fill="#000000"], g[fill="#000000"], rect[fill="#000000"], circle[fill="#000000"] {
61 fill: $color;
62 }
63
64 path[stroke="#000000"], g[stroke="#000000"], rect[stroke="#000000"], circle[stroke="#000000"] {
65 stroke: $color;
66 }
67 }
68}
69
58@mixin peertube-input-text($width) { 70@mixin peertube-input-text($width) {
59 display: inline-block; 71 display: inline-block;
60 height: $button-height; 72 height: $button-height;
@@ -64,6 +76,7 @@
64 border-radius: 3px; 76 border-radius: 3px;
65 padding-left: 15px; 77 padding-left: 15px;
66 padding-right: 15px; 78 padding-right: 15px;
79 font-size: 15px;
67 80
68 &::placeholder { 81 &::placeholder {
69 color: var(--inputPlaceholderColor); 82 color: var(--inputPlaceholderColor);
@@ -110,22 +123,30 @@
110 color: #fff; 123 color: #fff;
111 background-color: #C6C6C6; 124 background-color: #C6C6C6;
112 } 125 }
126
127 my-global-icon {
128 @include apply-svg-color(#fff)
129 }
113} 130}
114 131
115@mixin grey-button { 132@mixin grey-button {
116 &, &:active, &:focus { 133 &, &:active, &:focus {
117 background-color: $grey-color; 134 background-color: $grey-background-color;
118 color: #585858; 135 color: $grey-foreground-color;
119 } 136 }
120 137
121 &:hover, &:active, &:focus, &[disabled], &.disabled { 138 &:hover, &:active, &:focus, &[disabled], &.disabled {
122 color: #585858; 139 color: $grey-foreground-color;
123 background-color: $grey-hoover-color; 140 background-color: $grey-background-hover-color;
124 } 141 }
125 142
126 &[disabled], &.disabled { 143 &[disabled], &.disabled {
127 cursor: default; 144 cursor: default;
128 } 145 }
146
147 my-global-icon {
148 @include apply-svg-color($grey-foreground-color)
149 }
129} 150}
130 151
131@mixin peertube-button { 152@mixin peertube-button {
@@ -148,6 +169,15 @@
148 @include peertube-button; 169 @include peertube-button;
149} 170}
150 171
172@mixin button-with-icon($width: 20px, $margin-right: 3px, $top: -1px) {
173 my-global-icon {
174 position: relative;
175 width: $width;
176 margin-right: $margin-right;
177 top: $top;
178 }
179}
180
151@mixin peertube-button-file ($width) { 181@mixin peertube-button-file ($width) {
152 position: relative; 182 position: relative;
153 overflow: hidden; 183 overflow: hidden;
@@ -231,6 +261,10 @@
231 color: transparent; 261 color: transparent;
232 text-shadow: 0 0 0 #000; 262 text-shadow: 0 0 0 #000;
233 } 263 }
264
265 option {
266 color: #000;
267 }
234 } 268 }
235} 269}
236 270
@@ -455,18 +489,10 @@
455 } 489 }
456} 490}
457 491
458@mixin create-button ($imageUrl) { 492@mixin create-button {
459 @include peertube-button-link; 493 @include peertube-button-link;
460 @include orange-button; 494 @include orange-button;
461 495 @include button-with-icon(20px, 5px, -1px);
462 .icon.icon-add {
463 @include icon(20px);
464
465 position: relative;
466 top: -1px;
467 margin-right: 5px;
468 background-image: url($imageUrl);
469 }
470} 496}
471 497
472@mixin row-blocks { 498@mixin row-blocks {
diff --git a/client/src/sass/include/_variables.scss b/client/src/sass/include/_variables.scss
index fdf33b12a..3780b7501 100644
--- a/client/src/sass/include/_variables.scss
+++ b/client/src/sass/include/_variables.scss
@@ -6,10 +6,13 @@ $font-regular: 400;
6$font-semibold: 600; 6$font-semibold: 600;
7$font-bold: 700; 7$font-bold: 700;
8 8
9$grey-color: #E5E5E5; 9$grey-background-color: #E5E5E5;
10$grey-hoover-color: #EFEFEF;; 10$grey-background-hover-color: #EFEFEF;
11$grey-foreground-color: #585858;
12$grey-foreground-hover-color: #303030;
13
11$orange-color: #F1680D; 14$orange-color: #F1680D;
12$orange-hoover-color: #F97D46; 15$orange-hover-color: #F97D46;
13 16
14$bg-color: #fff; 17$bg-color: #fff;
15$fg-color: #000; 18$fg-color: #000;
diff --git a/client/src/sass/primeng-custom.scss b/client/src/sass/primeng-custom.scss
index 6057b1db0..6e502b028 100644
--- a/client/src/sass/primeng-custom.scss
+++ b/client/src/sass/primeng-custom.scss
@@ -14,7 +14,7 @@
14p-table { 14p-table {
15 .ui-table-caption { 15 .ui-table-caption {
16 border: none !important; 16 border: none !important;
17 background-color: #fff !important; 17 background-color: var(--mainBackgroundColor) !important;
18 18
19 .caption { 19 .caption {
20 height: 40px; 20 height: 40px;
@@ -24,7 +24,7 @@ p-table {
24 } 24 }
25 25
26 th { 26 th {
27 background-color: #fff !important; 27 background-color: var(--mainBackgroundColor) !important;
28 outline: 0; 28 outline: 0;
29 } 29 }
30 30
@@ -122,10 +122,14 @@ p-table {
122 122
123 &.pi-sort-up { 123 &.pi-sort-up {
124 @extend .glyphicon-triangle-top; 124 @extend .glyphicon-triangle-top;
125
126 color: var(--mainForegroundColor) !important;
125 } 127 }
126 128
127 &.pi-sort-down { 129 &.pi-sort-down {
128 @extend .glyphicon-triangle-bottom; 130 @extend .glyphicon-triangle-bottom;
131
132 color: var(--mainForegroundColor) !important;
129 } 133 }
130 } 134 }
131 } 135 }
@@ -193,7 +197,7 @@ p-table {
193 height: auto !important; 197 height: auto !important;
194 198
195 a { 199 a {
196 color: #000 !important; 200 color: var(--mainForegroundColor) !important;
197 font-weight: $font-semibold !important; 201 font-weight: $font-semibold !important;
198 margin: 0 5px !important; 202 margin: 0 5px !important;
199 outline: 0 !important; 203 outline: 0 !important;
@@ -230,6 +234,7 @@ p-calendar .ui-datepicker {
230 @extend .glyphicon-chevron-right; 234 @extend .glyphicon-chevron-right;
231 @include glyphicon-light; 235 @include glyphicon-light;
232 236
237 color: #000 !important;
233 text-align: right; 238 text-align: right;
234 239
235 .pi.pi-chevron-right { 240 .pi.pi-chevron-right {
@@ -241,6 +246,7 @@ p-calendar .ui-datepicker {
241 @extend .glyphicon-chevron-left; 246 @extend .glyphicon-chevron-left;
242 @include glyphicon-light; 247 @include glyphicon-light;
243 248
249 color: #000 !important;
244 text-align: left; 250 text-align: left;
245 251
246 .pi.pi-chevron-left { 252 .pi.pi-chevron-left {
@@ -254,42 +260,53 @@ p-calendar .ui-datepicker {
254 .pi.pi-chevron-up { 260 .pi.pi-chevron-up {
255 @extend .glyphicon-chevron-up; 261 @extend .glyphicon-chevron-up;
256 @include glyphicon-light; 262 @include glyphicon-light;
263
264 color: #000 !important;
257 } 265 }
258 266
259 .pi.pi-chevron-down { 267 .pi.pi-chevron-down {
260 @extend .glyphicon-chevron-down; 268 @extend .glyphicon-chevron-down;
261 @include glyphicon-light; 269 @include glyphicon-light;
270
271 color: #000 !important;
262 } 272 }
263 } 273 }
264} 274}
265 275
276.ui-chkbox {
266 277
267.ui-chkbox-box { 278 &, .ui-chkbox-box {
268 &.ui-state-active { 279 width: 18px !important;
269 border-color: var(--mainColor) !important; 280 height: 18px !important;
270 background-color: var(--mainColor) !important;
271 } 281 }
272 282
273 .ui-chkbox-icon { 283 .ui-chkbox-box {
274 position: relative; 284 &.ui-state-active {
275 overflow: visible !important; 285 border-color: var(--mainColor) !important;
276 286 background-color: var(--mainColor) !important;
277 &:after {
278 content: '';
279 position: absolute;
280 top: 1px;
281 left: 7px;
282 width: 5px;
283 height: 13px;
284 opacity: 0;
285 transform: rotate(45deg) scale(0);
286 border-right: 2px solid var(--mainBackgroundColor);
287 border-bottom: 2px solid var(--mainBackgroundColor);
288 } 287 }
289 288
290 &.pi-check:after { 289 .ui-chkbox-icon {
291 opacity: 1; 290 position: relative;
292 transform: rotate(45deg) scale(1); 291 overflow: visible !important;
292
293 &:after {
294 content: '';
295 position: absolute;
296 top: 1px;
297 left: 6px;
298 width: 5px;
299 height: 12px;
300 opacity: 0;
301 transform: rotate(45deg) scale(0);
302 border-right: 2px solid var(--mainBackgroundColor);
303 border-bottom: 2px solid var(--mainBackgroundColor);
304 }
305
306 &.pi-check:after {
307 opacity: 1;
308 transform: rotate(45deg) scale(1);
309 }
293 } 310 }
294 } 311 }
295} 312}
@@ -354,3 +371,7 @@ p-toast {
354 } 371 }
355 } 372 }
356} 373}
374
375.ui-widget {
376 font-family: $main-fonts !important;
377}
diff --git a/client/src/standalone/videos/embed.html b/client/src/standalone/videos/embed.html
index f79cf68df..c3b6e08ca 100644
--- a/client/src/standalone/videos/embed.html
+++ b/client/src/standalone/videos/embed.html
@@ -6,6 +6,7 @@
6 <meta charset="UTF-8"> 6 <meta charset="UTF-8">
7 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <meta name="viewport" content="width=device-width, initial-scale=1">
8 <meta name="robots" content="noindex"> 8 <meta name="robots" content="noindex">
9 <meta property="og:platform" content="PeerTube" />
9 10
10 <link rel="icon" type="image/png" href="/client/assets/images/favicon.png" /> 11 <link rel="icon" type="image/png" href="/client/assets/images/favicon.png" />
11 </head> 12 </head>
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts
index 54b8fb543..32bf42e12 100644
--- a/client/src/standalone/videos/embed.ts
+++ b/client/src/standalone/videos/embed.ts
@@ -17,17 +17,19 @@ import 'core-js/es6/set'
17// For google bot that uses Chrome 41 and does not understand fetch 17// For google bot that uses Chrome 41 and does not understand fetch
18import 'whatwg-fetch' 18import 'whatwg-fetch'
19 19
20// FIXME: something weird with our path definition in tsconfig and typings
21// @ts-ignore
22import * as vjs from 'video.js'
23
24import * as Channel from 'jschannel' 20import * as Channel from 'jschannel'
25 21
26import { peertubeTranslate, ResultList, VideoDetails } from '../../../../shared' 22import { peertubeTranslate, ResultList, VideoDetails } from '../../../../shared'
27import { addContextMenu, getServerTranslations, getVideojsOptions, loadLocaleInVideoJS } from '../../assets/player/peertube-player'
28import { PeerTubeResolution } from '../player/definitions' 23import { PeerTubeResolution } from '../player/definitions'
29import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' 24import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
30import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model' 25import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model'
26import {
27 P2PMediaLoaderOptions,
28 PeertubePlayerManager,
29 PeertubePlayerManagerOptions,
30 PlayerMode
31} from '../../assets/player/peertube-player-manager'
32import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type'
31 33
32/** 34/**
33 * Embed API exposes control of the embed player to the outside world via 35 * Embed API exposes control of the embed player to the outside world via
@@ -73,16 +75,16 @@ class PeerTubeEmbedApi {
73 } 75 }
74 76
75 private setResolution (resolutionId: number) { 77 private setResolution (resolutionId: number) {
76 if (resolutionId === -1 && this.embed.player.peertube().isAutoResolutionForbidden()) return 78 if (resolutionId === -1 && this.embed.player.webtorrent().isAutoResolutionForbidden()) return
77 79
78 // Auto resolution 80 // Auto resolution
79 if (resolutionId === -1) { 81 if (resolutionId === -1) {
80 this.embed.player.peertube().enableAutoResolution() 82 this.embed.player.webtorrent().enableAutoResolution()
81 return 83 return
82 } 84 }
83 85
84 this.embed.player.peertube().disableAutoResolution() 86 this.embed.player.webtorrent().disableAutoResolution()
85 this.embed.player.peertube().updateResolution(resolutionId) 87 this.embed.player.webtorrent().updateResolution(resolutionId)
86 } 88 }
87 89
88 /** 90 /**
@@ -122,15 +124,17 @@ class PeerTubeEmbedApi {
122 124
123 // PeerTube specific capabilities 125 // PeerTube specific capabilities
124 126
125 this.embed.player.peertube().on('autoResolutionUpdate', () => this.loadResolutions()) 127 if (this.embed.player.webtorrent) {
126 this.embed.player.peertube().on('videoFileUpdate', () => this.loadResolutions()) 128 this.embed.player.webtorrent().on('autoResolutionUpdate', () => this.loadWebTorrentResolutions())
129 this.embed.player.webtorrent().on('videoFileUpdate', () => this.loadWebTorrentResolutions())
130 }
127 } 131 }
128 132
129 private loadResolutions () { 133 private loadWebTorrentResolutions () {
130 let resolutions = [] 134 let resolutions = []
131 let currentResolutionId = this.embed.player.peertube().getCurrentResolutionId() 135 let currentResolutionId = this.embed.player.webtorrent().getCurrentResolutionId()
132 136
133 for (const videoFile of this.embed.player.peertube().videoFiles) { 137 for (const videoFile of this.embed.player.webtorrent().videoFiles) {
134 let label = videoFile.resolution.label 138 let label = videoFile.resolution.label
135 if (videoFile.fps && videoFile.fps >= 50) { 139 if (videoFile.fps && videoFile.fps >= 50) {
136 label += videoFile.fps 140 label += videoFile.fps
@@ -164,6 +168,7 @@ class PeerTubeEmbed {
164 subtitle: string 168 subtitle: string
165 enableApi = false 169 enableApi = false
166 startTime: number | string = 0 170 startTime: number | string = 0
171 mode: PlayerMode
167 scope = 'peertube' 172 scope = 'peertube'
168 173
169 static async main () { 174 static async main () {
@@ -257,6 +262,8 @@ class PeerTubeEmbed {
257 this.scope = this.getParamString(params, 'scope', this.scope) 262 this.scope = this.getParamString(params, 'scope', this.scope)
258 this.subtitle = this.getParamString(params, 'subtitle') 263 this.subtitle = this.getParamString(params, 'subtitle')
259 this.startTime = this.getParamString(params, 'start') 264 this.startTime = this.getParamString(params, 'start')
265
266 this.mode = this.getParamString(params, 'mode') === 'p2p-media-loader' ? 'p2p-media-loader' : 'webtorrent'
260 } catch (err) { 267 } catch (err) {
261 console.error('Cannot get params from URL.', err) 268 console.error('Cannot get params from URL.', err)
262 } 269 }
@@ -266,9 +273,8 @@ class PeerTubeEmbed {
266 const urlParts = window.location.pathname.split('/') 273 const urlParts = window.location.pathname.split('/')
267 const videoId = urlParts[ urlParts.length - 1 ] 274 const videoId = urlParts[ urlParts.length - 1 ]
268 275
269 const [ , serverTranslations, videoResponse, captionsResponse ] = await Promise.all([ 276 const [ serverTranslations, videoResponse, captionsResponse ] = await Promise.all([
270 loadLocaleInVideoJS(window.location.origin, vjs, navigator.language), 277 PeertubePlayerManager.getServerTranslations(window.location.origin, navigator.language),
271 getServerTranslations(window.location.origin, navigator.language),
272 this.loadVideoInfo(videoId), 278 this.loadVideoInfo(videoId),
273 this.loadVideoCaptions(videoId) 279 this.loadVideoCaptions(videoId)
274 ]) 280 ])
@@ -292,43 +298,67 @@ class PeerTubeEmbed {
292 298
293 this.loadParams() 299 this.loadParams()
294 300
295 const videojsOptions = getVideojsOptions({ 301 const options: PeertubePlayerManagerOptions = {
296 autoplay: this.autoplay, 302 common: {
297 controls: this.controls, 303 autoplay: this.autoplay,
298 muted: this.muted, 304 controls: this.controls,
299 loop: this.loop, 305 muted: this.muted,
300 startTime: this.startTime, 306 loop: this.loop,
301 subtitle: this.subtitle, 307 captions: videoCaptions.length !== 0,
302 308 startTime: this.startTime,
303 videoCaptions, 309 subtitle: this.subtitle,
304 inactivityTimeout: 1500, 310
305 videoViewUrl: this.getVideoUrl(videoId) + '/views', 311 videoCaptions,
306 playerElement: this.videoElement, 312 inactivityTimeout: 1500,
307 videoFiles: videoInfo.files, 313 videoViewUrl: this.getVideoUrl(videoId) + '/views',
308 videoDuration: videoInfo.duration, 314
309 enableHotkeys: true, 315 playerElement: this.videoElement,
310 peertubeLink: true, 316 onPlayerElementChange: (element: HTMLVideoElement) => this.videoElement = element,
311 poster: window.location.origin + videoInfo.previewPath, 317
312 theaterMode: false 318 videoDuration: videoInfo.duration,
313 }) 319 enableHotkeys: true,
320 peertubeLink: true,
321 poster: window.location.origin + videoInfo.previewPath,
322 theaterMode: false,
323
324 serverUrl: window.location.origin,
325 language: navigator.language,
326 embedUrl: window.location.origin + videoInfo.embedPath
327 },
328
329 webtorrent: {
330 videoFiles: videoInfo.files
331 }
332 }
314 333
315 this.playerOptions = videojsOptions 334 if (this.mode === 'p2p-media-loader') {
316 this.player = vjs(this.videoContainerId, videojsOptions, () => { 335 const hlsPlaylist = videoInfo.streamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS)
317 this.player.on('customError', (event: any, data: any) => this.handleError(data.err, serverTranslations)) 336
337 Object.assign(options, {
338 p2pMediaLoader: {
339 playlistUrl: hlsPlaylist.playlistUrl,
340 segmentsSha256Url: hlsPlaylist.segmentsSha256Url,
341 redundancyBaseUrls: hlsPlaylist.redundancies.map(r => r.baseUrl),
342 trackerAnnounce: videoInfo.trackerUrls,
343 videoFiles: videoInfo.files
344 } as P2PMediaLoaderOptions
345 })
346 }
318 347
319 window[ 'videojsPlayer' ] = this.player 348 this.player = await PeertubePlayerManager.initialize(this.mode, options)
320 349
321 if (this.controls) { 350 this.player.on('customError', (event: any, data: any) => this.handleError(data.err, serverTranslations))
322 this.player.dock({
323 title: videoInfo.name,
324 description: this.player.localize('Uses P2P, others may know your IP is downloading this video.')
325 })
326 }
327 351
328 addContextMenu(this.player, window.location.origin + videoInfo.embedPath) 352 window[ 'videojsPlayer' ] = this.player
329 353
330 this.initializeApi() 354 if (this.controls) {
331 }) 355 this.player.dock({
356 title: videoInfo.name,
357 description: this.player.localize('Uses P2P, others may know your IP is downloading this video.')
358 })
359 }
360
361 this.initializeApi()
332 } 362 }
333 363
334 private handleError (err: Error, translations?: { [ id: string ]: string }) { 364 private handleError (err: Error, translations?: { [ id: string ]: string }) {
diff --git a/client/src/tsconfig.app.json b/client/src/tsconfig.app.json
index af7a74e9e..729eee353 100644
--- a/client/src/tsconfig.app.json
+++ b/client/src/tsconfig.app.json
@@ -3,7 +3,7 @@
3 "compilerOptions": { 3 "compilerOptions": {
4 "outDir": "../out-tsc/app", 4 "outDir": "../out-tsc/app",
5 "baseUrl": "./", 5 "baseUrl": "./",
6 "module": "es2015", 6 "module": "esnext",
7 "types": [], 7 "types": [],
8 "lib": [ 8 "lib": [
9 "es2017", 9 "es2017",
diff --git a/client/tsconfig.json b/client/tsconfig.json
index beca79e01..3f9986f8a 100644
--- a/client/tsconfig.json
+++ b/client/tsconfig.json
@@ -5,6 +5,7 @@
5 "sourceMap": true, 5 "sourceMap": true,
6 "declaration": false, 6 "declaration": false,
7 "moduleResolution": "node", 7 "moduleResolution": "node",
8 "module": "esnext",
8 "emitDecoratorMetadata": true, 9 "emitDecoratorMetadata": true,
9 "experimentalDecorators": true, 10 "experimentalDecorators": true,
10 "noImplicitAny": true, 11 "noImplicitAny": true,
diff --git a/client/yarn.lock b/client/yarn.lock
index 5ed43117a..63394e0f7 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -2,139 +2,141 @@
2# yarn lockfile v1 2# yarn lockfile v1
3 3
4 4
5"@angular-devkit/architect@0.11.1": 5"@angular-devkit/architect@0.13.1":
6 version "0.11.1" 6 version "0.13.1"
7 resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.11.1.tgz#fb8429b583d4d7efafe5ff551ffdd30a705b9ab0" 7 resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.13.1.tgz#39597ce94f72d89bdd89ee567cb937cff4c13b98"
8 integrity sha512-MdcZ5KclwL2SBXCQSn8uI2hakBX58EyuAwFWsM/pKrNt9j8RqIk93l4amd2OkaMtZRFP5zWodyf/3qOwacjuQg== 8 integrity sha512-QDmIbqde75ZZSEFbw6Q6kQWq4cY6C7D67yujXw6XTyubDNAs1tyXJyxTIB8vjSlEKwRizTTDd/B0ZXVcke3Mvw==
9 dependencies: 9 dependencies:
10 "@angular-devkit/core" "7.1.1" 10 "@angular-devkit/core" "7.3.1"
11 rxjs "6.3.3" 11 rxjs "6.3.3"
12 12
13"@angular-devkit/build-angular@~0.11.1": 13"@angular-devkit/build-angular@~0.13.1":
14 version "0.11.1" 14 version "0.13.1"
15 resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.11.1.tgz#a828797d9177227aee70a65bb06d4b189b1404cd" 15 resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.13.1.tgz#369febda48dd40e47a4f0077064e792612a8e1c1"
16 integrity sha512-hA/3GVMmRwOPXWhImrBG9gZTdERr937NMuedKhTXuNj6TNMNjk9XQ+q2erd0LZVbgfhL/nC0wHnpy0dUWXu8jA== 16 integrity sha512-vkKwMVQ+NNCcVR3HFMffS+Mq4b2afXeUjI+02N38hBuFTppnC83uivUB6Uu2NUk5NTSQA4BnJlG5CbMs6N4QYg==
17 dependencies: 17 dependencies:
18 "@angular-devkit/architect" "0.11.1" 18 "@angular-devkit/architect" "0.13.1"
19 "@angular-devkit/build-optimizer" "0.11.1" 19 "@angular-devkit/build-optimizer" "0.13.1"
20 "@angular-devkit/build-webpack" "0.11.1" 20 "@angular-devkit/build-webpack" "0.13.1"
21 "@angular-devkit/core" "7.1.1" 21 "@angular-devkit/core" "7.3.1"
22 "@ngtools/webpack" "7.1.1" 22 "@ngtools/webpack" "7.3.1"
23 ajv "6.5.3" 23 ajv "6.7.0"
24 autoprefixer "9.3.1" 24 autoprefixer "9.4.6"
25 circular-dependency-plugin "5.0.2" 25 circular-dependency-plugin "5.0.2"
26 clean-css "4.2.1" 26 clean-css "4.2.1"
27 copy-webpack-plugin "4.5.4" 27 copy-webpack-plugin "4.6.0"
28 file-loader "2.0.0" 28 file-loader "3.0.1"
29 glob "7.1.3" 29 glob "7.1.3"
30 istanbul "0.4.5"
31 istanbul-instrumenter-loader "3.0.1" 30 istanbul-instrumenter-loader "3.0.1"
32 karma-source-map-support "1.3.0" 31 karma-source-map-support "1.3.0"
33 less "3.8.1" 32 less "3.9.0"
34 less-loader "4.1.0" 33 less-loader "4.1.0"
35 license-webpack-plugin "2.0.2" 34 license-webpack-plugin "2.1.0"
36 loader-utils "1.1.0" 35 loader-utils "1.2.3"
37 mini-css-extract-plugin "0.4.4" 36 mini-css-extract-plugin "0.5.0"
38 minimatch "3.0.4" 37 minimatch "3.0.4"
39 opn "5.3.0" 38 opn "5.4.0"
40 parse5 "4.0.0" 39 parse5 "4.0.0"
41 portfinder "1.0.17" 40 postcss "7.0.14"
42 postcss "7.0.5" 41 postcss-import "12.0.1"
43 postcss-import "12.0.0"
44 postcss-loader "3.0.0" 42 postcss-loader "3.0.0"
45 raw-loader "0.5.1" 43 raw-loader "1.0.0"
46 rxjs "6.3.3" 44 rxjs "6.3.3"
47 sass-loader "7.1.0" 45 sass-loader "7.1.0"
48 semver "5.5.1" 46 semver "5.6.0"
49 source-map-loader "0.2.4" 47 source-map-loader "0.2.4"
50 source-map-support "0.5.9" 48 source-map-support "0.5.10"
51 speed-measure-webpack-plugin "1.2.3" 49 speed-measure-webpack-plugin "1.3.0"
52 stats-webpack-plugin "0.7.0" 50 stats-webpack-plugin "0.7.0"
53 style-loader "0.23.1" 51 style-loader "0.23.1"
54 stylus "0.54.5" 52 stylus "0.54.5"
55 stylus-loader "3.0.2" 53 stylus-loader "3.0.2"
56 terser-webpack-plugin "1.1.0" 54 terser-webpack-plugin "1.2.2"
57 tree-kill "1.2.0" 55 tree-kill "1.2.1"
58 webpack "4.23.1" 56 webpack "4.29.0"
59 webpack-dev-middleware "3.4.0" 57 webpack-dev-middleware "3.5.1"
60 webpack-dev-server "3.1.10" 58 webpack-dev-server "3.1.14"
61 webpack-merge "4.1.4" 59 webpack-merge "4.2.1"
62 webpack-sources "1.3.0" 60 webpack-sources "1.3.0"
63 webpack-subresource-integrity "1.1.0-rc.6" 61 webpack-subresource-integrity "1.1.0-rc.6"
64 optionalDependencies: 62 optionalDependencies:
65 node-sass "4.10.0" 63 node-sass "4.11.0"
66 64
67"@angular-devkit/build-optimizer@0.11.1": 65"@angular-devkit/build-optimizer@0.13.1":
68 version "0.11.1" 66 version "0.13.1"
69 resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.11.1.tgz#1079737a44b26b39e35cea7f966a39cd11bbbf48" 67 resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.13.1.tgz#56151179bd6427918ba8e0f5a7e581e5daa00294"
70 integrity sha512-pyFP6ykZf8Iq8nRkgP2XKq8knpIG6ye0qYklnBC9815AC5RAO126Y4fmtd6tnH+5p1mQxnt5HegG0j5xOCgDRw== 68 integrity sha512-LmvHiI3H451aVWY5Ac6Fqz0i1eX/mUfWN+uJvo8NaL6Jc0HKYX2o3l4ODr8UUECWWctUC9AMD522ZMwAvnvsKQ==
71 dependencies: 69 dependencies:
72 loader-utils "1.1.0" 70 loader-utils "1.2.3"
73 source-map "0.5.6" 71 source-map "0.5.6"
74 typescript "3.1.6" 72 typescript "3.2.4"
75 webpack-sources "1.2.0" 73 webpack-sources "1.3.0"
76 74
77"@angular-devkit/build-webpack@0.11.1": 75"@angular-devkit/build-webpack@0.13.1":
78 version "0.11.1" 76 version "0.13.1"
79 resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.11.1.tgz#bd98ff3dea633c5b77671b471e72cf6c91f6c679" 77 resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.13.1.tgz#98d666765705e9379c9b2e0a3b6dfcd0347a2a32"
80 integrity sha512-p7fPHOi2Wfq2VPtnRVowg3n99MujghpOp6zW0gBJQD1TQhGVzPK6AX42S0NA4d05ahNBCDU2n7Y+5TjNJRIGJw== 78 integrity sha512-OGwC7bAl3u+w7Glw+OqIrN7OD1BkDXgrWbeQSpKAmsx6VdNPCnI4NPS+JldWNp70LVlE2nQlJUhtEqMVfBMnlg==
81 dependencies: 79 dependencies:
82 "@angular-devkit/architect" "0.11.1" 80 "@angular-devkit/architect" "0.13.1"
83 "@angular-devkit/core" "7.1.1" 81 "@angular-devkit/core" "7.3.1"
84 rxjs "6.3.3" 82 rxjs "6.3.3"
85 83
86"@angular-devkit/core@7.1.1": 84"@angular-devkit/core@7.3.1":
87 version "7.1.1" 85 version "7.3.1"
88 resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-7.1.1.tgz#ce0a674f16188072988502cc3f073b15efcfe194" 86 resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-7.3.1.tgz#d92f6545796579cabdcfc29579a2c977f7a96c6c"
89 integrity sha512-rODqECpOiV6vX+L1qd63GLiF3SG+V1O+d8WYtnKPOxnsMM9yWpWmqmroHtXfisjucu/zwoqj8HoO/noJZCfynw== 87 integrity sha512-56XDWWfIzOAkEk69lBLgmCYybPUA4yjunhmMlCk7vVdb7gbQUyzNjFD04Uj0GjlejatAQ5F76tRwygD9C+3RXQ==
90 dependencies: 88 dependencies:
91 ajv "6.5.3" 89 ajv "6.7.0"
92 chokidar "2.0.4" 90 chokidar "2.0.4"
93 fast-json-stable-stringify "2.0.0" 91 fast-json-stable-stringify "2.0.0"
94 rxjs "6.3.3" 92 rxjs "6.3.3"
95 source-map "0.7.3" 93 source-map "0.7.3"
96 94
97"@angular-devkit/schematics@7.1.1": 95"@angular-devkit/schematics@7.3.1":
98 version "7.1.1" 96 version "7.3.1"
99 resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-7.1.1.tgz#328ec6071c5ef3b1588a9f4bc97f5edfc3620b09" 97 resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-7.3.1.tgz#7dc704005b966ea6c1ee62f380120183bb76eee6"
100 integrity sha512-yjzTw8ZWMPg0Fc9VQCHNpUCAH7aiNxrUDs0IbhdC0CyKTBoqH+cx2xP4Z6ECf4uNwceLKJlE0l3ot42Ypnlziw== 98 integrity sha512-cd7usiasfSgw75INz72/VssrLr9tiVRYfo1TEdvr9ww0GuQbuQpB33xbV8W135eAV8+wzQ3Ce8ohaDHibvj6Yg==
101 dependencies: 99 dependencies:
102 "@angular-devkit/core" "7.1.1" 100 "@angular-devkit/core" "7.3.1"
103 rxjs "6.3.3" 101 rxjs "6.3.3"
104 102
105"@angular/animations@~7.1.1": 103"@angular/animations@~7.2.4":
106 version "7.1.1" 104 version "7.2.4"
107 resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-7.1.1.tgz#8fecbd19417364946a9ea40c8fdf32462110232f" 105 resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-7.2.4.tgz#4d0a0b9f14d6bfc38ca773613b61729d020435e6"
108 integrity sha512-iTNxhPPraCZsE4rgM23lguT1kDV4mfYAr+Bsi5J0+v9ZJA+VaKvi6eRW8ZGrx4/rDz6hzTnBn1jgPppHFbsOcw== 106 integrity sha512-Wx6cqU6koFOASlyl4aCygtbtROoehU6OKwV2EZTkfzHx6Eu/QyTiSa5kyoApVM5LMmCNeb8SxJMSAnKXztNl0A==
109 dependencies: 107 dependencies:
110 tslib "^1.9.0" 108 tslib "^1.9.0"
111 109
112"@angular/cli@~7.1.1": 110"@angular/cli@~7.3.1":
113 version "7.1.1" 111 version "7.3.1"
114 resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-7.1.1.tgz#c5dd2b92c5c3391f20262b5e530813e4e2d31777" 112 resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-7.3.1.tgz#a18acdec84deb03a1fae79cae415bbc8f9c87ffa"
115 integrity sha512-lPVKsk035T5Ls0Mf83OngrNoLZu/ucZSjRLN/GWZK1O/YYVmb/dTgVl/a7HC+G480tWQ34nlqnCRbrP7sE9v7g== 113 integrity sha512-8EvXYRhTqTaTk5PKv7VZxIWJiyG51R9RC9gtpRFx4bbnurqBHdEUxGMmaRsGT8QDbfvVsWnuakE0eeW1CrfZAQ==
116 dependencies: 114 dependencies:
117 "@angular-devkit/architect" "0.11.1" 115 "@angular-devkit/architect" "0.13.1"
118 "@angular-devkit/core" "7.1.1" 116 "@angular-devkit/core" "7.3.1"
119 "@angular-devkit/schematics" "7.1.1" 117 "@angular-devkit/schematics" "7.3.1"
120 "@schematics/angular" "7.1.1" 118 "@schematics/angular" "7.3.1"
121 "@schematics/update" "0.11.1" 119 "@schematics/update" "0.13.1"
122 inquirer "6.2.0" 120 "@yarnpkg/lockfile" "1.1.0"
123 opn "5.3.0" 121 ini "1.3.5"
124 semver "5.5.1" 122 inquirer "6.2.1"
123 npm-package-arg "6.1.0"
124 opn "5.4.0"
125 pacote "9.4.0"
126 semver "5.6.0"
125 symbol-observable "1.2.0" 127 symbol-observable "1.2.0"
126 128
127"@angular/common@~7.1.1": 129"@angular/common@~7.2.4":
128 version "7.1.1" 130 version "7.2.4"
129 resolved "https://registry.yarnpkg.com/@angular/common/-/common-7.1.1.tgz#f78f884614ef81ab2fd648f1aa3e83aae370a6c8" 131 resolved "https://registry.yarnpkg.com/@angular/common/-/common-7.2.4.tgz#9f1ed530e5dc7613a263e015c203ead390d50336"
130 integrity sha512-SngekFx9v39sjgi9pON0Wehxpu+NdUk7OEebw4Fa8dKqTgydTkuhmnNH+9WQe264asoeCt51oufPRjIqMLNohA== 132 integrity sha512-3/i8RtnLTx/90gJHk5maE8zwsSiHgHvLItaa0qVfNlWiU0eCId/PL6TgDkut5vN9SQYL0oxhxFaVd35HmwsmuQ==
131 dependencies: 133 dependencies:
132 tslib "^1.9.0" 134 tslib "^1.9.0"
133 135
134"@angular/compiler-cli@~7.1.1": 136"@angular/compiler-cli@~7.2.4":
135 version "7.1.1" 137 version "7.2.4"
136 resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-7.1.1.tgz#c5f6225fb72b56f42fa78c332fdee9755c64604e" 138 resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-7.2.4.tgz#3de23fd5f558a859a444c58dab18f2981c9c2937"
137 integrity sha512-4NXlkDhOEQgaP3Agigqw93CvXJvsfnXa0xiglq9e/wjL+6XbtM9WcDb5lfRQz41N9RSkO3pEHGvKMweKZGgogA== 139 integrity sha512-UhLosSeuwFIfaGqGcYOh9WSOuzEpeuhIRAOt81MeqOQEqkoreUjfxrQq8XWNkdqsPZHtiptF5ZwXlMBxlj9jJg==
138 dependencies: 140 dependencies:
139 canonical-path "1.0.0" 141 canonical-path "1.0.0"
140 chokidar "^1.4.2" 142 chokidar "^1.4.2"
@@ -148,64 +150,64 @@
148 tslib "^1.9.0" 150 tslib "^1.9.0"
149 yargs "9.0.1" 151 yargs "9.0.1"
150 152
151"@angular/compiler@~7.1.1": 153"@angular/compiler@~7.2.4":
152 version "7.1.1" 154 version "7.2.4"
153 resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-7.1.1.tgz#4efbcad27ab43d4cd36d936a8df2e073f6d02d0a" 155 resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-7.2.4.tgz#133eb97fc3169ec9ff84f134eb9e3497fa37537e"
154 integrity sha512-oJvBe8XZ+DXF/W/DxWBTbBcixJTuPeZWdkcZIGWhJoQP7K5GnGnj8ffP9Lp6Dh4TKv85awtC6OfIKhbHxa650Q== 156 integrity sha512-+zyMzPCL45ePEV9nrnYJvhAVgp2Y19bDaq0f0YdZAqAjgDqHzXGGR6wX8GueyJWmUYWx5vwK6Apla4HwDrYA1w==
155 dependencies: 157 dependencies:
156 tslib "^1.9.0" 158 tslib "^1.9.0"
157 159
158"@angular/core@~7.1.1": 160"@angular/core@~7.2.4":
159 version "7.1.1" 161 version "7.2.4"
160 resolved "https://registry.yarnpkg.com/@angular/core/-/core-7.1.1.tgz#9748b0103cd86226554e1ccbd0f43dd8c46f1ed1" 162 resolved "https://registry.yarnpkg.com/@angular/core/-/core-7.2.4.tgz#a6c84940c8edcfa37158f666a1f99c6e4a97bf95"
161 integrity sha512-Osig5SRgDRQ+Hec/liN7nq/BCJieB+4/pqRh9rFbOXezb2ptgRZqdXOXN8P17i4AwPVf308Mh55V0niJ5Eu3Rw== 163 integrity sha512-kfAxhIxl89PmB7y81FR/RAv0yWRFcEYxEnTwV+o8jKGfemAXtQ0g/Vh+lJR0SD/TBgFilMxotN1mhwH4A8GShw==
162 dependencies: 164 dependencies:
163 tslib "^1.9.0" 165 tslib "^1.9.0"
164 166
165"@angular/forms@~7.1.1": 167"@angular/forms@~7.2.4":
166 version "7.1.1" 168 version "7.2.4"
167 resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-7.1.1.tgz#d16ef10a901c007062fd19144cd77917ef55ee24" 169 resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-7.2.4.tgz#be89cf83ad16fa3c813c12e4cff85da5409cf7a0"
168 integrity sha512-yCWuPjpu23Wc3XUw7v/ACNn/e249oT0bYlM8aaMQ1F5OwrmmC4NJC12Rpl9Ihza61RIHIKzNcHVEgzc7WhcSag== 170 integrity sha512-DAtOrdlTRsgvmZrsvczCAkY8dhTwZb5DXBmPuSXh0UR9lvEiCgNHGbwEiIiIkAHpw1wSeXZrq0qyy/oJRvf18g==
169 dependencies: 171 dependencies:
170 tslib "^1.9.0" 172 tslib "^1.9.0"
171 173
172"@angular/http@~7.1.1": 174"@angular/http@~7.2.4":
173 version "7.1.1" 175 version "7.2.4"
174 resolved "https://registry.yarnpkg.com/@angular/http/-/http-7.1.1.tgz#f19f17ad42e7f3cdabcf1250ca757640d0f02219" 176 resolved "https://registry.yarnpkg.com/@angular/http/-/http-7.2.4.tgz#fc151ac15c8c7542cb7242a430ad2319655c2ff5"
175 integrity sha512-pRk+c/kz9aJ8te5xzCxlPLpFnwB0d/E9YkOo3/ydaXF9vZw13RTzk00YyzJ41PDzJf8oPDdXtueTQ+vtJ7Srtw== 177 integrity sha512-kazJREm7MtSCYbE+9zU/CcUXI5Csu53PooeQlAp80/TOHqry6fVKIMHCI892Db9ScY2ds0SzbyTmrxEQo7PP1A==
176 dependencies: 178 dependencies:
177 tslib "^1.9.0" 179 tslib "^1.9.0"
178 180
179"@angular/language-service@~7.1.1": 181"@angular/language-service@~7.2.4":
180 version "7.1.1" 182 version "7.2.4"
181 resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-7.1.1.tgz#6bbe35b2430ad54618a1803f881efb5894b296c9" 183 resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-7.2.4.tgz#db72460040b070410cbff678410c142f4d682af8"
182 integrity sha512-X+5g20PMtNRGZIa3svMv4PLJdJehn4wqrS8nwOtzH5XkSn5vA3IxjsJVdSzAy2AN0/sKKJK5jmQorPtKO4saJg== 184 integrity sha512-A9Rud/27hHMSUUjpgn57nVeLsoYgdvFwJhtlZA/oCuSpmlD+LqqBsEpPhivwn++u44+DSrFXsic29jlFnsBotw==
183 185
184"@angular/platform-browser-dynamic@~7.1.1": 186"@angular/platform-browser-dynamic@~7.2.4":
185 version "7.1.1" 187 version "7.2.4"
186 resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.1.1.tgz#6945298446173338782f437a996226110cda0d3e" 188 resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.2.4.tgz#24dce1bb0d9dab541b3b1b3eda3084a732f11b64"
187 integrity sha512-ZIu48Vn4S6gjD7CMbGlKGaPQ8v9rYkWzlNYi4vTYzgiqKKNC3hqLsVESU3mSvr5oeQBxSIBidTdHSyafHFrA2w== 189 integrity sha512-J/xWlmaYOPUoCHZ5TiIRiyYa4uRMtCz3aGdBfY8k/NWtNo8SCYaS3aut7Sk4RS5rK8aAVi+aYFlY5YOrlW+Hbg==
188 dependencies: 190 dependencies:
189 tslib "^1.9.0" 191 tslib "^1.9.0"
190 192
191"@angular/platform-browser@~7.1.1": 193"@angular/platform-browser@~7.2.4":
192 version "7.1.1" 194 version "7.2.4"
193 resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-7.1.1.tgz#a6bd408f656dc43ee5a2d8af3dfaa786c7c1dfca" 195 resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-7.2.4.tgz#2cf5305878d0620d6b8c02eff00ac3ca8dbc5970"
194 integrity sha512-I6OPjecynGJSbPtzu0gvEgSmIR6X6/xEAhg4L9PycW1ryjzptTC9klWRTWIqsIBqMxhVnY44uKLeRNrDwMOwyA== 196 integrity sha512-Klt8aKR5SP9bqfMfpSY5vQOY7AQEs8JGuZOk5Bfc2dUtYT2IEIvK2IqO8v2rcFRVO13HOPUxl328efyHqLgI7g==
195 dependencies: 197 dependencies:
196 tslib "^1.9.0" 198 tslib "^1.9.0"
197 199
198"@angular/router@~7.1.1": 200"@angular/router@~7.2.4":
199 version "7.1.1" 201 version "7.2.4"
200 resolved "https://registry.yarnpkg.com/@angular/router/-/router-7.1.1.tgz#80a4cdffc03a529b73485c2ad63a30ec435364ea" 202 resolved "https://registry.yarnpkg.com/@angular/router/-/router-7.2.4.tgz#83f1997c2a4e6acda93b991b8d7f3dad2b3f91f0"
201 integrity sha512-jbnqEq/1iDBkeH8Vn13hauGPTzhwllWM+MLfmdNGTiMzGRx4pmkWa57seDOeBF/GNYBL9JjkWTCrkKFAc2FJKw== 203 integrity sha512-T8Uqf2H1SV1MQI38WwYJ4aa+4NNnvlp2Tp/rkfg6tKcp/cLkKqE6OOfiy9lmW+i/624v8tMgYoBMOUNBjAG23g==
202 dependencies: 204 dependencies:
203 tslib "^1.9.0" 205 tslib "^1.9.0"
204 206
205"@angular/service-worker@~7.1.1": 207"@angular/service-worker@~7.2.4":
206 version "7.1.1" 208 version "7.2.4"
207 resolved "https://registry.yarnpkg.com/@angular/service-worker/-/service-worker-7.1.1.tgz#c9e6f0265d7e102d8271483519cf09a180f0e08b" 209 resolved "https://registry.yarnpkg.com/@angular/service-worker/-/service-worker-7.2.4.tgz#d16d6d08c0d5c29c93e9f80cddc4013c9b8859fb"
208 integrity sha512-xX00x0XMW47jEfYTZLwdJCqkmPE7+mdtlSeOGpjaKv6Y2hqZodz80RYgH5JltM4RKEzOvQolR6KmdKcw1ANs9Q== 210 integrity sha512-IYsHshkgCYYmWLwtP7wwk8tfwphE4IJrkUitEu+ST6x+er/K9LyLo09WQeEZHIwDaPm9icoqc3TJJdXI46mrmg==
209 dependencies: 211 dependencies:
210 tslib "^1.9.0" 212 tslib "^1.9.0"
211 213
@@ -321,16 +323,16 @@
321 dependencies: 323 dependencies:
322 tslib "^1.9.0" 324 tslib "^1.9.0"
323 325
324"@ngtools/webpack@7.1.1": 326"@ngtools/webpack@7.3.1":
325 version "7.1.1" 327 version "7.3.1"
326 resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-7.1.1.tgz#c418e1cb0d70a77d06e8c32500fe2e92e606ea52" 328 resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-7.3.1.tgz#4ff68007fd68fdc26f22e19115182f96fb6f7335"
327 integrity sha512-XW/YDjiDZlwOYK4YvGAIKIVEkqtdwPLwTWAmDbnfpEHQc8UALsBrzGdjze0jSfXQdQxkbmXo0aolZgNc7uL/wQ== 329 integrity sha512-EGQRjgDf5XP+Fm1MdZNRFiPd9e1vhl11BhjkwqkAsewic4eoz6fqXfj/Osz1hQy8xU+2dPPf/byQ/+nY3E02Zg==
328 dependencies: 330 dependencies:
329 "@angular-devkit/core" "7.1.1" 331 "@angular-devkit/core" "7.3.1"
330 enhanced-resolve "4.1.0" 332 enhanced-resolve "4.1.0"
331 rxjs "6.3.3" 333 rxjs "6.3.3"
332 tree-kill "1.2.0" 334 tree-kill "1.2.1"
333 webpack-sources "1.2.0" 335 webpack-sources "1.3.0"
334 336
335"@ngx-loading-bar/core@3.0.0", "@ngx-loading-bar/core@^3.0.0": 337"@ngx-loading-bar/core@3.0.0", "@ngx-loading-bar/core@^3.0.0":
336 version "3.0.0" 338 version "3.0.0"
@@ -371,29 +373,34 @@
371 tslib "^1.9.0" 373 tslib "^1.9.0"
372 yargs "10.0.3" 374 yargs "10.0.3"
373 375
374"@schematics/angular@7.1.1": 376"@schematics/angular@7.3.1":
375 version "7.1.1" 377 version "7.3.1"
376 resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-7.1.1.tgz#4ee17a17d221eaf48009db0b991766d1074d0b4f" 378 resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-7.3.1.tgz#6fcd7004210fa9305310c3109c084df5c5521776"
377 integrity sha512-jMaj8y3rNTQQXuH38uoWfAOmwYjtzqo1RelNfACnT54mfO/Dat+k7WasBLHWuvzvnN4/Ga3kXL7sJpkeMciiIg== 379 integrity sha512-0Ne8APPlTAjKg5CSZqluwCuW/5yPjr3ALCWzqwPxN0suE745usThtasBmqrjw0RMIt8nRqRgtg54Z7lCPO9ZFg==
378 dependencies: 380 dependencies:
379 "@angular-devkit/core" "7.1.1" 381 "@angular-devkit/core" "7.3.1"
380 "@angular-devkit/schematics" "7.1.1" 382 "@angular-devkit/schematics" "7.3.1"
381 typescript "3.1.6" 383 typescript "3.2.4"
382 384
383"@schematics/update@0.11.1": 385"@schematics/update@0.13.1":
384 version "0.11.1" 386 version "0.13.1"
385 resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.11.1.tgz#5129a800043dc38ee1f1c879865e0df82ddac7ed" 387 resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.13.1.tgz#481475aee18b4a9472a06512b2e4d6429af68231"
386 integrity sha512-IzPXamoMpDb2eY2zSW4fPuuH+7RfJLte9XVzQM2y3ZTBhlJQFLqx7qJtOXdcXUboonC6o61KCayNDERFnDUdPg== 388 integrity sha512-EHOqolT/d/jRGuVTCUESLpk8JNpuaPlsVHfeK7Kdp/t0wSEnmtOelZX4+leS25lGXDaDUF3138ntjrZR4n6bGw==
387 dependencies: 389 dependencies:
388 "@angular-devkit/core" "7.1.1" 390 "@angular-devkit/core" "7.3.1"
389 "@angular-devkit/schematics" "7.1.1" 391 "@angular-devkit/schematics" "7.3.1"
390 "@yarnpkg/lockfile" "1.1.0" 392 "@yarnpkg/lockfile" "1.1.0"
391 ini "1.3.5" 393 ini "1.3.5"
392 pacote "9.1.1" 394 pacote "9.4.0"
393 rxjs "6.3.3" 395 rxjs "6.3.3"
394 semver "5.5.1" 396 semver "5.6.0"
395 semver-intersect "1.4.0" 397 semver-intersect "1.4.0"
396 398
399"@streamroot/videojs-hlsjs-plugin@^1.0.7":
400 version "1.0.7"
401 resolved "https://registry.yarnpkg.com/@streamroot/videojs-hlsjs-plugin/-/videojs-hlsjs-plugin-1.0.7.tgz#581aecdf6a966162b404c60bd3ab8264eb89d334"
402 integrity sha512-7oAIOhEFxkfLOYWDfg7Oh3+OrnoTElRvUE3Jblg2B+SHmnrw4YXQnAwYJ0AHjNIBKoHnQubzZGttLaHAFJVspQ==
403
397"@types/bittorrent-protocol@*": 404"@types/bittorrent-protocol@*":
398 version "2.2.2" 405 version "2.2.2"
399 resolved "https://registry.yarnpkg.com/@types/bittorrent-protocol/-/bittorrent-protocol-2.2.2.tgz#169e9633e1bd18e6b830d11cf42e611b1972cb83" 406 resolved "https://registry.yarnpkg.com/@types/bittorrent-protocol/-/bittorrent-protocol-2.2.2.tgz#169e9633e1bd18e6b830d11cf42e611b1972cb83"
@@ -406,6 +413,11 @@
406 resolved "https://registry.yarnpkg.com/@types/core-js/-/core-js-2.5.0.tgz#35cc282488de6f10af1d92902899a3b8ca3fbc47" 413 resolved "https://registry.yarnpkg.com/@types/core-js/-/core-js-2.5.0.tgz#35cc282488de6f10af1d92902899a3b8ca3fbc47"
407 integrity sha512-qjkHL3wF0JMHMqgm/kmL8Pf8rIiqvueEiZ0g6NVTcBX1WN46GWDr+V5z+gsHUeL0n8TfAmXnYmF7ajsxmBp4PQ== 414 integrity sha512-qjkHL3wF0JMHMqgm/kmL8Pf8rIiqvueEiZ0g6NVTcBX1WN46GWDr+V5z+gsHUeL0n8TfAmXnYmF7ajsxmBp4PQ==
408 415
416"@types/hls.js@^0.12.0":
417 version "0.12.0"
418 resolved "https://registry.yarnpkg.com/@types/hls.js/-/hls.js-0.12.0.tgz#33f73e542201a766fa56792cb81fe9f97d7097ed"
419 integrity sha512-hJ7eJAQVEazAANK4Ay0YbXlZF36SDy9c8kcHTF7//77ylgV6hV/JrlwhVmobsSacr5aZcbw5MbZ2bSHbS36eOQ==
420
409"@types/jasmine@*": 421"@types/jasmine@*":
410 version "3.3.1" 422 version "3.3.1"
411 resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.3.1.tgz#b6c4f356013364e98b583647c7b3b6de6fccd2cc" 423 resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.3.1.tgz#b6c4f356013364e98b583647c7b3b6de6fccd2cc"
@@ -515,11 +527,25 @@
515 resolved "https://registry.yarnpkg.com/@types/socket.io-client/-/socket.io-client-1.4.32.tgz#988a65a0386c274b1c22a55377fab6a30789ac14" 527 resolved "https://registry.yarnpkg.com/@types/socket.io-client/-/socket.io-client-1.4.32.tgz#988a65a0386c274b1c22a55377fab6a30789ac14"
516 integrity sha512-Vs55Kq8F+OWvy1RLA31rT+cAyemzgm0EWNeax6BWF8H7QiiOYMJIdcwSDdm5LVgfEkoepsWkS+40+WNb7BUMbg== 528 integrity sha512-Vs55Kq8F+OWvy1RLA31rT+cAyemzgm0EWNeax6BWF8H7QiiOYMJIdcwSDdm5LVgfEkoepsWkS+40+WNb7BUMbg==
517 529
530"@types/source-list-map@*":
531 version "0.1.2"
532 resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"
533 integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==
534
518"@types/video.js@^7.2.5": 535"@types/video.js@^7.2.5":
519 version "7.2.5" 536 version "7.2.5"
520 resolved "https://registry.yarnpkg.com/@types/video.js/-/video.js-7.2.5.tgz#20896c81141d3517c3a89bb6eb97c6a191aa5d4c" 537 resolved "https://registry.yarnpkg.com/@types/video.js/-/video.js-7.2.5.tgz#20896c81141d3517c3a89bb6eb97c6a191aa5d4c"
521 integrity sha512-5WUDOme0q81d58nEqf7qnz7B2Jc4jlA7/MQGOgoqI5VE6oied0KUfk5x/XqPSZSAHNwDDREAkrcK8JXcB+iruQ== 538 integrity sha512-5WUDOme0q81d58nEqf7qnz7B2Jc4jlA7/MQGOgoqI5VE6oied0KUfk5x/XqPSZSAHNwDDREAkrcK8JXcB+iruQ==
522 539
540"@types/webpack-sources@^0.1.5":
541 version "0.1.5"
542 resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-0.1.5.tgz#be47c10f783d3d6efe1471ff7f042611bd464a92"
543 integrity sha512-zfvjpp7jiafSmrzJ2/i3LqOyTYTuJ7u1KOXlKgDlvsj9Rr0x7ZiYu5lZbXwobL7lmsRNtPXlBfmaUD8eU2Hu8w==
544 dependencies:
545 "@types/node" "*"
546 "@types/source-list-map" "*"
547 source-map "^0.6.1"
548
523"@types/webtorrent@^0.98.4": 549"@types/webtorrent@^0.98.4":
524 version "0.98.4" 550 version "0.98.4"
525 resolved "https://registry.yarnpkg.com/@types/webtorrent/-/webtorrent-0.98.4.tgz#cf8dbe22e3d5cf6915305f7f970b52bca01bf8b4" 551 resolved "https://registry.yarnpkg.com/@types/webtorrent/-/webtorrent-0.98.4.tgz#cf8dbe22e3d5cf6915305f7f970b52bca01bf8b4"
@@ -543,15 +569,6 @@
543 url-toolkit "^2.1.3" 569 url-toolkit "^2.1.3"
544 video.js "^6.8.0 || ^7.0.0" 570 video.js "^6.8.0 || ^7.0.0"
545 571
546"@webassemblyjs/ast@1.7.10":
547 version "1.7.10"
548 resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.10.tgz#0cfc61d61286240b72fc522cb755613699eea40a"
549 integrity sha512-wTUeaByYN2EA6qVqhbgavtGc7fLTOx0glG2IBsFlrFG51uXIGlYBTyIZMf4SPLo3v1bgV/7lBN3l7Z0R6Hswew==
550 dependencies:
551 "@webassemblyjs/helper-module-context" "1.7.10"
552 "@webassemblyjs/helper-wasm-bytecode" "1.7.10"
553 "@webassemblyjs/wast-parser" "1.7.10"
554
555"@webassemblyjs/ast@1.7.11": 572"@webassemblyjs/ast@1.7.11":
556 version "1.7.11" 573 version "1.7.11"
557 resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.11.tgz#b988582cafbb2b095e8b556526f30c90d057cace" 574 resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.11.tgz#b988582cafbb2b095e8b556526f30c90d057cace"
@@ -561,43 +578,21 @@
561 "@webassemblyjs/helper-wasm-bytecode" "1.7.11" 578 "@webassemblyjs/helper-wasm-bytecode" "1.7.11"
562 "@webassemblyjs/wast-parser" "1.7.11" 579 "@webassemblyjs/wast-parser" "1.7.11"
563 580
564"@webassemblyjs/floating-point-hex-parser@1.7.10":
565 version "1.7.10"
566 resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.10.tgz#ee63d729c6311a85863e369a473f9983f984e4d9"
567 integrity sha512-gMsGbI6I3p/P1xL2UxqhNh1ga2HCsx5VBB2i5VvJFAaqAjd2PBTRULc3BpTydabUQEGlaZCzEUQhLoLG7TvEYQ==
568
569"@webassemblyjs/floating-point-hex-parser@1.7.11": 581"@webassemblyjs/floating-point-hex-parser@1.7.11":
570 version "1.7.11" 582 version "1.7.11"
571 resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.11.tgz#a69f0af6502eb9a3c045555b1a6129d3d3f2e313" 583 resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.11.tgz#a69f0af6502eb9a3c045555b1a6129d3d3f2e313"
572 integrity sha512-zY8dSNyYcgzNRNT666/zOoAyImshm3ycKdoLsyDw/Bwo6+/uktb7p4xyApuef1dwEBo/U/SYQzbGBvV+nru2Xg== 584 integrity sha512-zY8dSNyYcgzNRNT666/zOoAyImshm3ycKdoLsyDw/Bwo6+/uktb7p4xyApuef1dwEBo/U/SYQzbGBvV+nru2Xg==
573 585
574"@webassemblyjs/helper-api-error@1.7.10":
575 version "1.7.10"
576 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.10.tgz#bfcb3bbe59775357475790a2ad7b289f09b2f198"
577 integrity sha512-DoYRlPWtuw3yd5BOr9XhtrmB6X1enYF0/54yNvQWGXZEPDF5PJVNI7zQ7gkcKfTESzp8bIBWailaFXEK/jjCsw==
578
579"@webassemblyjs/helper-api-error@1.7.11": 586"@webassemblyjs/helper-api-error@1.7.11":
580 version "1.7.11" 587 version "1.7.11"
581 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.11.tgz#c7b6bb8105f84039511a2b39ce494f193818a32a" 588 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.11.tgz#c7b6bb8105f84039511a2b39ce494f193818a32a"
582 integrity sha512-7r1qXLmiglC+wPNkGuXCvkmalyEstKVwcueZRP2GNC2PAvxbLYwLLPr14rcdJaE4UtHxQKfFkuDFuv91ipqvXg== 589 integrity sha512-7r1qXLmiglC+wPNkGuXCvkmalyEstKVwcueZRP2GNC2PAvxbLYwLLPr14rcdJaE4UtHxQKfFkuDFuv91ipqvXg==
583 590
584"@webassemblyjs/helper-buffer@1.7.10":
585 version "1.7.10"
586 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.10.tgz#0a8c624c67ad0b214d2e003859921a1988cb151b"
587 integrity sha512-+RMU3dt/dPh4EpVX4u5jxsOlw22tp3zjqE0m3ftU2tsYxnPULb4cyHlgaNd2KoWuwasCQqn8Mhr+TTdbtj3LlA==
588
589"@webassemblyjs/helper-buffer@1.7.11": 591"@webassemblyjs/helper-buffer@1.7.11":
590 version "1.7.11" 592 version "1.7.11"
591 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.11.tgz#3122d48dcc6c9456ed982debe16c8f37101df39b" 593 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.11.tgz#3122d48dcc6c9456ed982debe16c8f37101df39b"
592 integrity sha512-MynuervdylPPh3ix+mKZloTcL06P8tenNH3sx6s0qE8SLR6DdwnfgA7Hc9NSYeob2jrW5Vql6GVlsQzKQCa13w== 594 integrity sha512-MynuervdylPPh3ix+mKZloTcL06P8tenNH3sx6s0qE8SLR6DdwnfgA7Hc9NSYeob2jrW5Vql6GVlsQzKQCa13w==
593 595
594"@webassemblyjs/helper-code-frame@1.7.10":
595 version "1.7.10"
596 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.10.tgz#0ab7e22fad0241a173178c73976fc0edf50832ce"
597 integrity sha512-UiytbpKAULOEab2hUZK2ywXen4gWJVrgxtwY3Kn+eZaaSWaRM8z/7dAXRSoamhKFiBh1uaqxzE/XD9BLlug3gw==
598 dependencies:
599 "@webassemblyjs/wast-printer" "1.7.10"
600
601"@webassemblyjs/helper-code-frame@1.7.11": 596"@webassemblyjs/helper-code-frame@1.7.11":
602 version "1.7.11" 597 version "1.7.11"
603 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.11.tgz#cf8f106e746662a0da29bdef635fcd3d1248364b" 598 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.11.tgz#cf8f106e746662a0da29bdef635fcd3d1248364b"
@@ -605,46 +600,21 @@
605 dependencies: 600 dependencies:
606 "@webassemblyjs/wast-printer" "1.7.11" 601 "@webassemblyjs/wast-printer" "1.7.11"
607 602
608"@webassemblyjs/helper-fsm@1.7.10":
609 version "1.7.10"
610 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.10.tgz#0915e7713fbbb735620a9d3e4fa3d7951f97ac64"
611 integrity sha512-w2vDtUK9xeSRtt5+RnnlRCI7wHEvLjF0XdnxJpgx+LJOvklTZPqWkuy/NhwHSLP19sm9H8dWxKeReMR7sCkGZA==
612
613"@webassemblyjs/helper-fsm@1.7.11": 603"@webassemblyjs/helper-fsm@1.7.11":
614 version "1.7.11" 604 version "1.7.11"
615 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.11.tgz#df38882a624080d03f7503f93e3f17ac5ac01181" 605 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.11.tgz#df38882a624080d03f7503f93e3f17ac5ac01181"
616 integrity sha512-nsAQWNP1+8Z6tkzdYlXT0kxfa2Z1tRTARd8wYnc/e3Zv3VydVVnaeePgqUzFrpkGUyhUUxOl5ML7f1NuT+gC0A== 606 integrity sha512-nsAQWNP1+8Z6tkzdYlXT0kxfa2Z1tRTARd8wYnc/e3Zv3VydVVnaeePgqUzFrpkGUyhUUxOl5ML7f1NuT+gC0A==
617 607
618"@webassemblyjs/helper-module-context@1.7.10":
619 version "1.7.10"
620 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.10.tgz#9beb83f72740f5ac8075313b5cac5e796510f755"
621 integrity sha512-yE5x/LzZ3XdPdREmJijxzfrf+BDRewvO0zl8kvORgSWmxpRrkqY39KZSq6TSgIWBxkK4SrzlS3BsMCv2s1FpsQ==
622
623"@webassemblyjs/helper-module-context@1.7.11": 608"@webassemblyjs/helper-module-context@1.7.11":
624 version "1.7.11" 609 version "1.7.11"
625 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.11.tgz#d874d722e51e62ac202476935d649c802fa0e209" 610 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.11.tgz#d874d722e51e62ac202476935d649c802fa0e209"
626 integrity sha512-JxfD5DX8Ygq4PvXDucq0M+sbUFA7BJAv/GGl9ITovqE+idGX+J3QSzJYz+LwQmL7fC3Rs+utvWoJxDb6pmC0qg== 611 integrity sha512-JxfD5DX8Ygq4PvXDucq0M+sbUFA7BJAv/GGl9ITovqE+idGX+J3QSzJYz+LwQmL7fC3Rs+utvWoJxDb6pmC0qg==
627 612
628"@webassemblyjs/helper-wasm-bytecode@1.7.10":
629 version "1.7.10"
630 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.10.tgz#797b1e734bbcfdea8399669cdc58308ef1c7ffc0"
631 integrity sha512-u5qy4SJ/OrxKxZqJ9N3qH4ZQgHaAzsopsYwLvoWJY6Q33r8PhT3VPyNMaJ7ZFoqzBnZlCcS/0f4Sp8WBxylXfg==
632
633"@webassemblyjs/helper-wasm-bytecode@1.7.11": 613"@webassemblyjs/helper-wasm-bytecode@1.7.11":
634 version "1.7.11" 614 version "1.7.11"
635 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.11.tgz#dd9a1e817f1c2eb105b4cf1013093cb9f3c9cb06" 615 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.11.tgz#dd9a1e817f1c2eb105b4cf1013093cb9f3c9cb06"
636 integrity sha512-cMXeVS9rhoXsI9LLL4tJxBgVD/KMOKXuFqYb5oCJ/opScWpkCMEz9EJtkonaNcnLv2R3K5jIeS4TRj/drde1JQ== 616 integrity sha512-cMXeVS9rhoXsI9LLL4tJxBgVD/KMOKXuFqYb5oCJ/opScWpkCMEz9EJtkonaNcnLv2R3K5jIeS4TRj/drde1JQ==
637 617
638"@webassemblyjs/helper-wasm-section@1.7.10":
639 version "1.7.10"
640 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.10.tgz#c0ea3703c615d7bc3e3507c3b7991c8767b2f20e"
641 integrity sha512-Ecvww6sCkcjatcyctUrn22neSJHLN/TTzolMGG/N7S9rpbsTZ8c6Bl98GpSpV77EvzNijiNRHBG0+JO99qKz6g==
642 dependencies:
643 "@webassemblyjs/ast" "1.7.10"
644 "@webassemblyjs/helper-buffer" "1.7.10"
645 "@webassemblyjs/helper-wasm-bytecode" "1.7.10"
646 "@webassemblyjs/wasm-gen" "1.7.10"
647
648"@webassemblyjs/helper-wasm-section@1.7.11": 618"@webassemblyjs/helper-wasm-section@1.7.11":
649 version "1.7.11" 619 version "1.7.11"
650 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.11.tgz#9c9ac41ecf9fbcfffc96f6d2675e2de33811e68a" 620 resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.11.tgz#9c9ac41ecf9fbcfffc96f6d2675e2de33811e68a"
@@ -655,13 +625,6 @@
655 "@webassemblyjs/helper-wasm-bytecode" "1.7.11" 625 "@webassemblyjs/helper-wasm-bytecode" "1.7.11"
656 "@webassemblyjs/wasm-gen" "1.7.11" 626 "@webassemblyjs/wasm-gen" "1.7.11"
657 627
658"@webassemblyjs/ieee754@1.7.10":
659 version "1.7.10"
660 resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.7.10.tgz#62c1728b7ef0f66ef8221e2966a0afd75db430df"
661 integrity sha512-HRcWcY+YWt4+s/CvQn+vnSPfRaD4KkuzQFt5MNaELXXHSjelHlSEA8ZcqT69q0GTIuLWZ6JaoKar4yWHVpZHsQ==
662 dependencies:
663 "@xtuc/ieee754" "^1.2.0"
664
665"@webassemblyjs/ieee754@1.7.11": 628"@webassemblyjs/ieee754@1.7.11":
666 version "1.7.11" 629 version "1.7.11"
667 resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.7.11.tgz#c95839eb63757a31880aaec7b6512d4191ac640b" 630 resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.7.11.tgz#c95839eb63757a31880aaec7b6512d4191ac640b"
@@ -669,13 +632,6 @@
669 dependencies: 632 dependencies:
670 "@xtuc/ieee754" "^1.2.0" 633 "@xtuc/ieee754" "^1.2.0"
671 634
672"@webassemblyjs/leb128@1.7.10":
673 version "1.7.10"
674 resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.7.10.tgz#167e0bb4b06d7701585772a73fba9f4df85439f6"
675 integrity sha512-og8MciYlA8hvzCLR71hCuZKPbVBfLQeHv7ImKZ4nlyxrYbG7uJHYtHiHu6OV9SqrGuD03H/HtXC4Bgdjfm9FHw==
676 dependencies:
677 "@xtuc/long" "4.2.1"
678
679"@webassemblyjs/leb128@1.7.11": 635"@webassemblyjs/leb128@1.7.11":
680 version "1.7.11" 636 version "1.7.11"
681 resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.7.11.tgz#d7267a1ee9c4594fd3f7e37298818ec65687db63" 637 resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.7.11.tgz#d7267a1ee9c4594fd3f7e37298818ec65687db63"
@@ -683,30 +639,11 @@
683 dependencies: 639 dependencies:
684 "@xtuc/long" "4.2.1" 640 "@xtuc/long" "4.2.1"
685 641
686"@webassemblyjs/utf8@1.7.10":
687 version "1.7.10"
688 resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.7.10.tgz#b6728f5b6f50364abc155be029f9670e6685605a"
689 integrity sha512-Ng6Pxv6siyZp635xCSnH3mKmIFgqWPCcGdoo0GBYgyGdxu7cUj4agV7Uu1a8REP66UYUFXJLudeGgd4RvuJAnQ==
690
691"@webassemblyjs/utf8@1.7.11": 642"@webassemblyjs/utf8@1.7.11":
692 version "1.7.11" 643 version "1.7.11"
693 resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.7.11.tgz#06d7218ea9fdc94a6793aa92208160db3d26ee82" 644 resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.7.11.tgz#06d7218ea9fdc94a6793aa92208160db3d26ee82"
694 integrity sha512-C6GFkc7aErQIAH+BMrIdVSmW+6HSe20wg57HEC1uqJP8E/xpMjXqQUxkQw07MhNDSDcGpxI9G5JSNOQCqJk4sA== 645 integrity sha512-C6GFkc7aErQIAH+BMrIdVSmW+6HSe20wg57HEC1uqJP8E/xpMjXqQUxkQw07MhNDSDcGpxI9G5JSNOQCqJk4sA==
695 646
696"@webassemblyjs/wasm-edit@1.7.10":
697 version "1.7.10"
698 resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.10.tgz#83fe3140f5a58f5a30b914702be9f0e59a399092"
699 integrity sha512-e9RZFQlb+ZuYcKRcW9yl+mqX/Ycj9+3/+ppDI8nEE/NCY6FoK8f3dKBcfubYV/HZn44b+ND4hjh+4BYBt+sDnA==
700 dependencies:
701 "@webassemblyjs/ast" "1.7.10"
702 "@webassemblyjs/helper-buffer" "1.7.10"
703 "@webassemblyjs/helper-wasm-bytecode" "1.7.10"
704 "@webassemblyjs/helper-wasm-section" "1.7.10"
705 "@webassemblyjs/wasm-gen" "1.7.10"
706 "@webassemblyjs/wasm-opt" "1.7.10"
707 "@webassemblyjs/wasm-parser" "1.7.10"
708 "@webassemblyjs/wast-printer" "1.7.10"
709
710"@webassemblyjs/wasm-edit@1.7.11": 647"@webassemblyjs/wasm-edit@1.7.11":
711 version "1.7.11" 648 version "1.7.11"
712 resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.11.tgz#8c74ca474d4f951d01dbae9bd70814ee22a82005" 649 resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.11.tgz#8c74ca474d4f951d01dbae9bd70814ee22a82005"
@@ -721,17 +658,6 @@
721 "@webassemblyjs/wasm-parser" "1.7.11" 658 "@webassemblyjs/wasm-parser" "1.7.11"
722 "@webassemblyjs/wast-printer" "1.7.11" 659 "@webassemblyjs/wast-printer" "1.7.11"
723 660
724"@webassemblyjs/wasm-gen@1.7.10":
725 version "1.7.10"
726 resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.10.tgz#4de003806ae29c97ab3707782469b53299570174"
727 integrity sha512-M0lb6cO2Y0PzDye/L39PqwV+jvO+2YxEG5ax+7dgq7EwXdAlpOMx1jxyXJTScQoeTpzOPIb+fLgX/IkLF8h2yw==
728 dependencies:
729 "@webassemblyjs/ast" "1.7.10"
730 "@webassemblyjs/helper-wasm-bytecode" "1.7.10"
731 "@webassemblyjs/ieee754" "1.7.10"
732 "@webassemblyjs/leb128" "1.7.10"
733 "@webassemblyjs/utf8" "1.7.10"
734
735"@webassemblyjs/wasm-gen@1.7.11": 661"@webassemblyjs/wasm-gen@1.7.11":
736 version "1.7.11" 662 version "1.7.11"
737 resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.11.tgz#9bbba942f22375686a6fb759afcd7ac9c45da1a8" 663 resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.11.tgz#9bbba942f22375686a6fb759afcd7ac9c45da1a8"
@@ -743,16 +669,6 @@
743 "@webassemblyjs/leb128" "1.7.11" 669 "@webassemblyjs/leb128" "1.7.11"
744 "@webassemblyjs/utf8" "1.7.11" 670 "@webassemblyjs/utf8" "1.7.11"
745 671
746"@webassemblyjs/wasm-opt@1.7.10":
747 version "1.7.10"
748 resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.10.tgz#d151e31611934a556c82789fdeec41a814993c2a"
749 integrity sha512-R66IHGCdicgF5ZliN10yn5HaC7vwYAqrSVJGjtJJQp5+QNPBye6heWdVH/at40uh0uoaDN/UVUfXK0gvuUqtVg==
750 dependencies:
751 "@webassemblyjs/ast" "1.7.10"
752 "@webassemblyjs/helper-buffer" "1.7.10"
753 "@webassemblyjs/wasm-gen" "1.7.10"
754 "@webassemblyjs/wasm-parser" "1.7.10"
755
756"@webassemblyjs/wasm-opt@1.7.11": 672"@webassemblyjs/wasm-opt@1.7.11":
757 version "1.7.11" 673 version "1.7.11"
758 resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.11.tgz#b331e8e7cef8f8e2f007d42c3a36a0580a7d6ca7" 674 resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.11.tgz#b331e8e7cef8f8e2f007d42c3a36a0580a7d6ca7"
@@ -763,18 +679,6 @@
763 "@webassemblyjs/wasm-gen" "1.7.11" 679 "@webassemblyjs/wasm-gen" "1.7.11"
764 "@webassemblyjs/wasm-parser" "1.7.11" 680 "@webassemblyjs/wasm-parser" "1.7.11"
765 681
766"@webassemblyjs/wasm-parser@1.7.10":
767 version "1.7.10"
768 resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.10.tgz#0367be7bf8f09e3e6abc95f8e483b9206487ec65"
769 integrity sha512-AEv8mkXVK63n/iDR3T693EzoGPnNAwKwT3iHmKJNBrrALAhhEjuPzo/lTE4U7LquEwyvg5nneSNdTdgrBaGJcA==
770 dependencies:
771 "@webassemblyjs/ast" "1.7.10"
772 "@webassemblyjs/helper-api-error" "1.7.10"
773 "@webassemblyjs/helper-wasm-bytecode" "1.7.10"
774 "@webassemblyjs/ieee754" "1.7.10"
775 "@webassemblyjs/leb128" "1.7.10"
776 "@webassemblyjs/utf8" "1.7.10"
777
778"@webassemblyjs/wasm-parser@1.7.11": 682"@webassemblyjs/wasm-parser@1.7.11":
779 version "1.7.11" 683 version "1.7.11"
780 resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.11.tgz#6e3d20fa6a3519f6b084ef9391ad58211efb0a1a" 684 resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.11.tgz#6e3d20fa6a3519f6b084ef9391ad58211efb0a1a"
@@ -787,18 +691,6 @@
787 "@webassemblyjs/leb128" "1.7.11" 691 "@webassemblyjs/leb128" "1.7.11"
788 "@webassemblyjs/utf8" "1.7.11" 692 "@webassemblyjs/utf8" "1.7.11"
789 693
790"@webassemblyjs/wast-parser@1.7.10":
791 version "1.7.10"
792 resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.7.10.tgz#058f598b52f730b23fc874d4775b6286b6247264"
793 integrity sha512-YTPEtOBljkCL0VjDp4sHe22dAYSm3ZwdJ9+2NTGdtC7ayNvuip1wAhaAS8Zt9Q6SW9E5Jf5PX7YE3XWlrzR9cw==
794 dependencies:
795 "@webassemblyjs/ast" "1.7.10"
796 "@webassemblyjs/floating-point-hex-parser" "1.7.10"
797 "@webassemblyjs/helper-api-error" "1.7.10"
798 "@webassemblyjs/helper-code-frame" "1.7.10"
799 "@webassemblyjs/helper-fsm" "1.7.10"
800 "@xtuc/long" "4.2.1"
801
802"@webassemblyjs/wast-parser@1.7.11": 694"@webassemblyjs/wast-parser@1.7.11":
803 version "1.7.11" 695 version "1.7.11"
804 resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.7.11.tgz#25bd117562ca8c002720ff8116ef9072d9ca869c" 696 resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.7.11.tgz#25bd117562ca8c002720ff8116ef9072d9ca869c"
@@ -811,15 +703,6 @@
811 "@webassemblyjs/helper-fsm" "1.7.11" 703 "@webassemblyjs/helper-fsm" "1.7.11"
812 "@xtuc/long" "4.2.1" 704 "@xtuc/long" "4.2.1"
813 705
814"@webassemblyjs/wast-printer@1.7.10":
815 version "1.7.10"
816 resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.7.10.tgz#d817909d2450ae96c66b7607624d98a33b84223b"
817 integrity sha512-mJ3QKWtCchL1vhU/kZlJnLPuQZnlDOdZsyP0bbLWPGdYsQDnSBvyTLhzwBA3QAMlzEL9V4JHygEmK6/OTEyytA==
818 dependencies:
819 "@webassemblyjs/ast" "1.7.10"
820 "@webassemblyjs/wast-parser" "1.7.10"
821 "@xtuc/long" "4.2.1"
822
823"@webassemblyjs/wast-printer@1.7.11": 706"@webassemblyjs/wast-printer@1.7.11":
824 version "1.7.11" 707 version "1.7.11"
825 resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.7.11.tgz#c4245b6de242cb50a2cc950174fdbf65c78d7813" 708 resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.7.11.tgz#c4245b6de242cb50a2cc950174fdbf65c78d7813"
@@ -862,11 +745,6 @@ abbrev@1:
862 resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" 745 resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
863 integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== 746 integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
864 747
865abbrev@1.0.x:
866 version "1.0.9"
867 resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135"
868 integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU=
869
870accepts@~1.3.4, accepts@~1.3.5: 748accepts@~1.3.4, accepts@~1.3.5:
871 version "1.3.5" 749 version "1.3.5"
872 resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" 750 resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2"
@@ -875,12 +753,10 @@ accepts@~1.3.4, accepts@~1.3.5:
875 mime-types "~2.1.18" 753 mime-types "~2.1.18"
876 negotiator "0.6.1" 754 negotiator "0.6.1"
877 755
878acorn-dynamic-import@^3.0.0: 756acorn-dynamic-import@^4.0.0:
879 version "3.0.0" 757 version "4.0.0"
880 resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz#901ceee4c7faaef7e07ad2a47e890675da50a278" 758 resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz#482210140582a36b83c3e342e1cfebcaa9240948"
881 integrity sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg== 759 integrity sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==
882 dependencies:
883 acorn "^5.0.0"
884 760
885acorn-globals@^4.1.0: 761acorn-globals@^4.1.0:
886 version "4.3.0" 762 version "4.3.0"
@@ -895,7 +771,7 @@ acorn-walk@^6.0.1:
895 resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.1.1.tgz#d363b66f5fac5f018ff9c3a1e7b6f8e310cc3913" 771 resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.1.1.tgz#d363b66f5fac5f018ff9c3a1e7b6f8e310cc3913"
896 integrity sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw== 772 integrity sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==
897 773
898acorn@^5.0.0, acorn@^5.5.3, acorn@^5.6.2, acorn@^5.7.3: 774acorn@^5.5.3, acorn@^5.7.3:
899 version "5.7.3" 775 version "5.7.3"
900 resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" 776 resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279"
901 integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== 777 integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==
@@ -905,6 +781,11 @@ acorn@^6.0.1:
905 resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.4.tgz#77377e7353b72ec5104550aa2d2097a2fd40b754" 781 resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.4.tgz#77377e7353b72ec5104550aa2d2097a2fd40b754"
906 integrity sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg== 782 integrity sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg==
907 783
784acorn@^6.0.5:
785 version "6.1.0"
786 resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.0.tgz#b0a3be31752c97a0f7013c5f4903b71a05db6818"
787 integrity sha512-MW/FjM+IvU9CgBzjO3UIPCE2pyEwUsoFl+VGdczOPEdxfGFjuKny/gN54mOuX7Qxmb9Rg9MCn2oKiSUeW+pjrw==
788
908addr-to-ip-port@^1.0.1, addr-to-ip-port@^1.4.2: 789addr-to-ip-port@^1.0.1, addr-to-ip-port@^1.4.2:
909 version "1.5.1" 790 version "1.5.1"
910 resolved "https://registry.yarnpkg.com/addr-to-ip-port/-/addr-to-ip-port-1.5.1.tgz#bfada13fd6aeeeac19f1e9f7d84b4bbab45e5208" 791 resolved "https://registry.yarnpkg.com/addr-to-ip-port/-/addr-to-ip-port-1.5.1.tgz#bfada13fd6aeeeac19f1e9f7d84b4bbab45e5208"
@@ -953,10 +834,10 @@ ajv-keywords@^3.1.0:
953 resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" 834 resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a"
954 integrity sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo= 835 integrity sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=
955 836
956ajv@6.5.3: 837ajv@6.7.0:
957 version "6.5.3" 838 version "6.7.0"
958 resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.3.tgz#71a569d189ecf4f4f321224fecb166f071dd90f9" 839 resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.7.0.tgz#e3ce7bb372d6577bb1839f1dfdfcbf5ad2948d96"
959 integrity sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg== 840 integrity sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg==
960 dependencies: 841 dependencies:
961 fast-deep-equal "^2.0.1" 842 fast-deep-equal "^2.0.1"
962 fast-json-stable-stringify "^2.0.0" 843 fast-json-stable-stringify "^2.0.0"
@@ -1029,6 +910,11 @@ ansi-regex@^3.0.0:
1029 resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" 910 resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
1030 integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= 911 integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
1031 912
913ansi-regex@^4.0.0:
914 version "4.0.0"
915 resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.0.0.tgz#70de791edf021404c3fd615aa89118ae0432e5a9"
916 integrity sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==
917
1032ansi-styles@^2.2.1: 918ansi-styles@^2.2.1:
1033 version "2.2.1" 919 version "2.2.1"
1034 resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" 920 resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
@@ -1238,7 +1124,7 @@ async-limiter@~1.0.0:
1238 resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" 1124 resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
1239 integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg== 1125 integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==
1240 1126
1241async@1.x, async@^1.5.2: 1127async@^1.5.2:
1242 version "1.5.2" 1128 version "1.5.2"
1243 resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" 1129 resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
1244 integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= 1130 integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=
@@ -1260,16 +1146,16 @@ atob@^2.1.1:
1260 resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" 1146 resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
1261 integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== 1147 integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
1262 1148
1263autoprefixer@9.3.1: 1149autoprefixer@9.4.6:
1264 version "9.3.1" 1150 version "9.4.6"
1265 resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.3.1.tgz#71b622174de2b783d5fd99f9ad617b7a3c78443e" 1151 resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.4.6.tgz#0ace275e33b37de16b09a5547dbfe73a98c1d446"
1266 integrity sha512-DY9gOh8z3tnCbJ13JIWaeQsoYncTGdsrgCceBaQSIL4nvdrLxgbRSBPevg2XbX7u4QCSfLheSJEEIUUSlkbx6Q== 1152 integrity sha512-Yp51mevbOEdxDUy5WjiKtpQaecqYq9OqZSL04rSoCiry7Tc5I9FEyo3bfxiTJc1DfHeKwSFCUYbBAiOQ2VGfiw==
1267 dependencies: 1153 dependencies:
1268 browserslist "^4.3.3" 1154 browserslist "^4.4.1"
1269 caniuse-lite "^1.0.30000898" 1155 caniuse-lite "^1.0.30000929"
1270 normalize-range "^0.1.2" 1156 normalize-range "^0.1.2"
1271 num2fraction "^1.2.2" 1157 num2fraction "^1.2.2"
1272 postcss "^7.0.5" 1158 postcss "^7.0.13"
1273 postcss-value-parser "^3.3.1" 1159 postcss-value-parser "^3.3.1"
1274 1160
1275awesome-typescript-loader@5.2.1: 1161awesome-typescript-loader@5.2.1:
@@ -1536,6 +1422,11 @@ big.js@^3.1.3:
1536 resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" 1422 resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e"
1537 integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q== 1423 integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==
1538 1424
1425big.js@^5.2.2:
1426 version "5.2.2"
1427 resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
1428 integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
1429
1539binary-extensions@^1.0.0: 1430binary-extensions@^1.0.0:
1540 version "1.12.0" 1431 version "1.12.0"
1541 resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.12.0.tgz#c2d780f53d45bba8317a8902d4ceeaf3a6385b14" 1432 resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.12.0.tgz#c2d780f53d45bba8317a8902d4ceeaf3a6385b14"
@@ -1588,7 +1479,7 @@ bittorrent-protocol@^3.0.0:
1588 unordered-array-remove "^1.0.2" 1479 unordered-array-remove "^1.0.2"
1589 xtend "^4.0.0" 1480 xtend "^4.0.0"
1590 1481
1591bittorrent-tracker@^9.0.0: 1482bittorrent-tracker@^9.0.0, bittorrent-tracker@^9.10.1:
1592 version "9.10.1" 1483 version "9.10.1"
1593 resolved "https://registry.yarnpkg.com/bittorrent-tracker/-/bittorrent-tracker-9.10.1.tgz#5de14aac012a287af394d3cc9eda1ec6cc956f11" 1484 resolved "https://registry.yarnpkg.com/bittorrent-tracker/-/bittorrent-tracker-9.10.1.tgz#5de14aac012a287af394d3cc9eda1ec6cc956f11"
1594 integrity sha512-n5zTL/g6Wt0rb2EnkiyiaGYhth7I/N0/xMqGUpvGX/7g1scDGBVPhJnXR8lfp3/OMj681fv40o4q/otECMtZSA== 1485 integrity sha512-n5zTL/g6Wt0rb2EnkiyiaGYhth7I/N0/xMqGUpvGX/7g1scDGBVPhJnXR8lfp3/OMj681fv40o4q/otECMtZSA==
@@ -1653,7 +1544,7 @@ blocking-proxy@^1.0.0:
1653 dependencies: 1544 dependencies:
1654 minimist "^1.2.0" 1545 minimist "^1.2.0"
1655 1546
1656bluebird@^3.3.0, bluebird@^3.5.1, bluebird@^3.5.2: 1547bluebird@^3.3.0, bluebird@^3.5.1, bluebird@^3.5.3:
1657 version "3.5.3" 1548 version "3.5.3"
1658 resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7" 1549 resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7"
1659 integrity sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw== 1550 integrity sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==
@@ -1822,14 +1713,14 @@ browserify-zlib@^0.2.0:
1822 dependencies: 1713 dependencies:
1823 pako "~1.0.5" 1714 pako "~1.0.5"
1824 1715
1825browserslist@^4.3.3: 1716browserslist@^4.4.1:
1826 version "4.3.5" 1717 version "4.4.1"
1827 resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.3.5.tgz#1a917678acc07b55606748ea1adf9846ea8920f7" 1718 resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.4.1.tgz#42e828954b6b29a7a53e352277be429478a69062"
1828 integrity sha512-z9ZhGc3d9e/sJ9dIx5NFXkKoaiQTnrvrMsN3R1fGb1tkWWNSz12UewJn9TNxGo1l7J23h0MRaPmk7jfeTZYs1w== 1719 integrity sha512-pEBxEXg7JwaakBXjATYw/D1YZh4QUSCX/Mnd/wnqSRPPSi1U39iDhDoKGoBUcraKdxDlrYqJxSI5nNvD+dWP2A==
1829 dependencies: 1720 dependencies:
1830 caniuse-lite "^1.0.30000912" 1721 caniuse-lite "^1.0.30000929"
1831 electron-to-chromium "^1.3.86" 1722 electron-to-chromium "^1.3.103"
1832 node-releases "^1.0.5" 1723 node-releases "^1.1.3"
1833 1724
1834browserstack@^1.5.1: 1725browserstack@^1.5.1:
1835 version "1.5.1" 1726 version "1.5.1"
@@ -1953,7 +1844,7 @@ cacache@^10.0.4:
1953 unique-filename "^1.1.0" 1844 unique-filename "^1.1.0"
1954 y18n "^4.0.0" 1845 y18n "^4.0.0"
1955 1846
1956cacache@^11.0.1, cacache@^11.0.2, cacache@^11.2.0: 1847cacache@^11.0.1, cacache@^11.0.2:
1957 version "11.3.1" 1848 version "11.3.1"
1958 resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.1.tgz#d09d25f6c4aca7a6d305d141ae332613aa1d515f" 1849 resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.1.tgz#d09d25f6c4aca7a6d305d141ae332613aa1d515f"
1959 integrity sha512-2PEw4cRRDu+iQvBTTuttQifacYjLPhET+SYO/gEFMy8uhi+jlJREDAjSF5FWSdV/Aw5h18caHA7vMTw2c+wDzA== 1850 integrity sha512-2PEw4cRRDu+iQvBTTuttQifacYjLPhET+SYO/gEFMy8uhi+jlJREDAjSF5FWSdV/Aw5h18caHA7vMTw2c+wDzA==
@@ -1973,6 +1864,26 @@ cacache@^11.0.1, cacache@^11.0.2, cacache@^11.2.0:
1973 unique-filename "^1.1.0" 1864 unique-filename "^1.1.0"
1974 y18n "^4.0.0" 1865 y18n "^4.0.0"
1975 1866
1867cacache@^11.3.2:
1868 version "11.3.2"
1869 resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.2.tgz#2d81e308e3d258ca38125b676b98b2ac9ce69bfa"
1870 integrity sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==
1871 dependencies:
1872 bluebird "^3.5.3"
1873 chownr "^1.1.1"
1874 figgy-pudding "^3.5.1"
1875 glob "^7.1.3"
1876 graceful-fs "^4.1.15"
1877 lru-cache "^5.1.1"
1878 mississippi "^3.0.0"
1879 mkdirp "^0.5.1"
1880 move-concurrently "^1.0.1"
1881 promise-inflight "^1.0.1"
1882 rimraf "^2.6.2"
1883 ssri "^6.0.1"
1884 unique-filename "^1.1.1"
1885 y18n "^4.0.0"
1886
1976cache-base@^1.0.1: 1887cache-base@^1.0.1:
1977 version "1.0.1" 1888 version "1.0.1"
1978 resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" 1889 resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
@@ -2041,10 +1952,10 @@ camelcase@^5.0.0:
2041 resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" 1952 resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42"
2042 integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== 1953 integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==
2043 1954
2044caniuse-lite@^1.0.30000898, caniuse-lite@^1.0.30000912: 1955caniuse-lite@^1.0.30000929:
2045 version "1.0.30000914" 1956 version "1.0.30000935"
2046 resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000914.tgz#f802b4667c24d0255f54a95818dcf8e1aa41f624" 1957 resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000935.tgz#d1b59df00b46f4921bb84a8a34c1d172b346df59"
2047 integrity sha512-qqj0CL1xANgg6iDOybiPTIxtsmAnfIky9mBC35qgWrnK4WwmhqfpmkDYMYgwXJ8LRZ3/2jXlCntulO8mBaAgSg== 1958 integrity sha512-1Y2uJ5y56qDt3jsDTdBHL1OqiImzjoQcBG6Yl3Qizq8mcc2SgCFpi+ZwLLqkztYnk9l87IYqRlNBnPSOTbFkXQ==
2048 1959
2049canonical-path@1.0.0: 1960canonical-path@1.0.0:
2050 version "1.0.0" 1961 version "1.0.0"
@@ -2083,6 +1994,15 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1:
2083 escape-string-regexp "^1.0.5" 1994 escape-string-regexp "^1.0.5"
2084 supports-color "^5.3.0" 1995 supports-color "^5.3.0"
2085 1996
1997chalk@^2.4.2:
1998 version "2.4.2"
1999 resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
2000 integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
2001 dependencies:
2002 ansi-styles "^3.2.1"
2003 escape-string-regexp "^1.0.5"
2004 supports-color "^5.3.0"
2005
2086chardet@^0.7.0: 2006chardet@^0.7.0:
2087 version "0.7.0" 2007 version "0.7.0"
2088 resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" 2008 resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
@@ -2320,11 +2240,6 @@ commander@^2.12.1, commander@^2.18.0, commander@^2.9.0:
2320 resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" 2240 resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
2321 integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== 2241 integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
2322 2242
2323commander@~2.13.0:
2324 version "2.13.0"
2325 resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
2326 integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==
2327
2328commondir@^1.0.1: 2243commondir@^1.0.1:
2329 version "1.0.1" 2244 version "1.0.1"
2330 resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" 2245 resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@@ -2473,10 +2388,10 @@ copy-descriptor@^0.1.0:
2473 resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" 2388 resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
2474 integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= 2389 integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
2475 2390
2476copy-webpack-plugin@4.5.4: 2391copy-webpack-plugin@4.6.0:
2477 version "4.5.4" 2392 version "4.6.0"
2478 resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.5.4.tgz#f2b2782b3cd5225535c3dc166a80067e7d940f27" 2393 resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz#e7f40dd8a68477d405dd1b7a854aae324b158bae"
2479 integrity sha512-0lstlEyj74OAtYMrDxlNZsU7cwFijAI3Ofz2fD6Mpo9r4xCv4yegfa3uHIKvZY1NSuOtE9nvG6TAhJ+uz9gDaQ== 2394 integrity sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==
2480 dependencies: 2395 dependencies:
2481 cacache "^10.0.4" 2396 cacache "^10.0.4"
2482 find-cache-dir "^1.0.0" 2397 find-cache-dir "^1.0.0"
@@ -2774,6 +2689,13 @@ debug@^3.1.0, debug@^3.2.5:
2774 dependencies: 2689 dependencies:
2775 ms "^2.1.1" 2690 ms "^2.1.1"
2776 2691
2692debug@^4.1.1:
2693 version "4.1.1"
2694 resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
2695 integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
2696 dependencies:
2697 ms "^2.1.1"
2698
2777decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: 2699decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0:
2778 version "1.2.0" 2700 version "1.2.0"
2779 resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" 2701 resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@@ -2944,7 +2866,7 @@ detect-newline@^2.1.0:
2944 resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" 2866 resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2"
2945 integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= 2867 integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=
2946 2868
2947detect-node@^2.0.3: 2869detect-node@^2.0.4:
2948 version "2.0.4" 2870 version "2.0.4"
2949 resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" 2871 resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
2950 integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== 2872 integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==
@@ -3141,10 +3063,10 @@ ejs@^2.6.1:
3141 resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0" 3063 resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0"
3142 integrity sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ== 3064 integrity sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==
3143 3065
3144electron-to-chromium@^1.3.86: 3066electron-to-chromium@^1.3.103:
3145 version "1.3.88" 3067 version "1.3.113"
3146 resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.88.tgz#f36ab32634f49ef2b0fdc1e82e2d1cc17feb29e7" 3068 resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.113.tgz#b1ccf619df7295aea17bc6951dc689632629e4a9"
3147 integrity sha512-UPV4NuQMKeUh1S0OWRvwg0PI8ASHN9kBC8yDTk1ROXLC85W5GnhTRu/MZu3Teqx3JjlQYuckuHYXSUSgtb3J+A== 3069 integrity sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g==
3148 3070
3149elliptic@^6.0.0: 3071elliptic@^6.0.0:
3150 version "6.4.1" 3072 version "6.4.1"
@@ -3359,18 +3281,6 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
3359 resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 3281 resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
3360 integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 3282 integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
3361 3283
3362escodegen@1.8.x:
3363 version "1.8.1"
3364 resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018"
3365 integrity sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=
3366 dependencies:
3367 esprima "^2.7.1"
3368 estraverse "^1.9.1"
3369 esutils "^2.0.2"
3370 optionator "^0.8.1"
3371 optionalDependencies:
3372 source-map "~0.2.0"
3373
3374escodegen@^1.9.1: 3284escodegen@^1.9.1:
3375 version "1.11.0" 3285 version "1.11.0"
3376 resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.0.tgz#b27a9389481d5bfd5bec76f7bb1eb3f8f4556589" 3286 resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.0.tgz#b27a9389481d5bfd5bec76f7bb1eb3f8f4556589"
@@ -3391,11 +3301,6 @@ eslint-scope@^4.0.0:
3391 esrecurse "^4.1.0" 3301 esrecurse "^4.1.0"
3392 estraverse "^4.1.1" 3302 estraverse "^4.1.1"
3393 3303
3394esprima@2.7.x, esprima@^2.7.1:
3395 version "2.7.3"
3396 resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
3397 integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=
3398
3399esprima@^3.1.3, esprima@~3.1.0: 3304esprima@^3.1.3, esprima@~3.1.0:
3400 version "3.1.3" 3305 version "3.1.3"
3401 resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" 3306 resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
@@ -3413,11 +3318,6 @@ esrecurse@^4.1.0:
3413 dependencies: 3318 dependencies:
3414 estraverse "^4.1.0" 3319 estraverse "^4.1.0"
3415 3320
3416estraverse@^1.9.1:
3417 version "1.9.3"
3418 resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44"
3419 integrity sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=
3420
3421estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: 3321estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
3422 version "4.2.0" 3322 version "4.2.0"
3423 resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" 3323 resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
@@ -3438,7 +3338,7 @@ etag@~1.8.1:
3438 resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" 3338 resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
3439 integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= 3339 integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
3440 3340
3441eventemitter3@^3.0.0: 3341eventemitter3@3.1.0, eventemitter3@^3.0.0:
3442 version "3.1.0" 3342 version "3.1.0"
3443 resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163" 3343 resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163"
3444 integrity sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA== 3344 integrity sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==
@@ -3448,6 +3348,11 @@ events@^1.0.0:
3448 resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" 3348 resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
3449 integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= 3349 integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=
3450 3350
3351events@^3.0.0:
3352 version "3.0.0"
3353 resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88"
3354 integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==
3355
3451eventsource@^1.0.7: 3356eventsource@^1.0.7:
3452 version "1.0.7" 3357 version "1.0.7"
3453 resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0" 3358 resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0"
@@ -3721,7 +3626,15 @@ figures@^2.0.0:
3721 dependencies: 3626 dependencies:
3722 escape-string-regexp "^1.0.5" 3627 escape-string-regexp "^1.0.5"
3723 3628
3724file-loader@2.0.0, file-loader@^2.0.0: 3629file-loader@3.0.1:
3630 version "3.0.1"
3631 resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-3.0.1.tgz#f8e0ba0b599918b51adfe45d66d1e771ad560faa"
3632 integrity sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==
3633 dependencies:
3634 loader-utils "^1.0.2"
3635 schema-utils "^1.0.0"
3636
3637file-loader@^2.0.0:
3725 version "2.0.0" 3638 version "2.0.0"
3726 resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-2.0.0.tgz#39749c82f020b9e85901dcff98e8004e6401cfde" 3639 resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-2.0.0.tgz#39749c82f020b9e85901dcff98e8004e6401cfde"
3727 integrity sha512-YCsBfd1ZGCyonOKLxPiKPdu+8ld9HAaMEvJewzz+b2eTF7uL5Zm/HdBF6FjCrpCMRq25Mi0U1gl4pwn2TlH7hQ== 3640 integrity sha512-YCsBfd1ZGCyonOKLxPiKPdu+8ld9HAaMEvJewzz+b2eTF7uL5Zm/HdBF6FjCrpCMRq25Mi0U1gl4pwn2TlH7hQ==
@@ -4043,7 +3956,7 @@ genfun@^5.0.0:
4043 resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537" 3956 resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537"
4044 integrity sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA== 3957 integrity sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==
4045 3958
4046get-browser-rtc@^1.0.0: 3959get-browser-rtc@^1.0.0, get-browser-rtc@^1.0.2:
4047 version "1.0.2" 3960 version "1.0.2"
4048 resolved "https://registry.yarnpkg.com/get-browser-rtc/-/get-browser-rtc-1.0.2.tgz#bbcd40c8451a7ed4ef5c373b8169a409dd1d11d9" 3961 resolved "https://registry.yarnpkg.com/get-browser-rtc/-/get-browser-rtc-1.0.2.tgz#bbcd40c8451a7ed4ef5c373b8169a409dd1d11d9"
4049 integrity sha1-u81AyEUaftTvXDc7gWmkCd0dEdk= 3962 integrity sha1-u81AyEUaftTvXDc7gWmkCd0dEdk=
@@ -4146,17 +4059,6 @@ glob@7.1.3, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glo
4146 once "^1.3.0" 4059 once "^1.3.0"
4147 path-is-absolute "^1.0.0" 4060 path-is-absolute "^1.0.0"
4148 4061
4149glob@^5.0.15:
4150 version "5.0.15"
4151 resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
4152 integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=
4153 dependencies:
4154 inflight "^1.0.4"
4155 inherits "2"
4156 minimatch "2 || 3"
4157 once "^1.3.0"
4158 path-is-absolute "^1.0.0"
4159
4160global-modules-path@^2.3.0: 4062global-modules-path@^2.3.0:
4161 version "2.3.1" 4063 version "2.3.1"
4162 resolved "https://registry.yarnpkg.com/global-modules-path/-/global-modules-path-2.3.1.tgz#e541f4c800a1a8514a990477b267ac67525b9931" 4064 resolved "https://registry.yarnpkg.com/global-modules-path/-/global-modules-path-2.3.1.tgz#e541f4c800a1a8514a990477b267ac67525b9931"
@@ -4224,7 +4126,7 @@ globule@^1.0.0:
4224 lodash "~4.17.10" 4126 lodash "~4.17.10"
4225 minimatch "~3.0.2" 4127 minimatch "~3.0.2"
4226 4128
4227graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6: 4129graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6:
4228 version "4.1.15" 4130 version "4.1.15"
4229 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" 4131 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
4230 integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== 4132 integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
@@ -4242,12 +4144,12 @@ gzip-size@^5.0.0:
4242 duplexer "^0.1.1" 4144 duplexer "^0.1.1"
4243 pify "^3.0.0" 4145 pify "^3.0.0"
4244 4146
4245handle-thing@^1.2.5: 4147handle-thing@^2.0.0:
4246 version "1.2.5" 4148 version "2.0.0"
4247 resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4" 4149 resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754"
4248 integrity sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ= 4150 integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==
4249 4151
4250handlebars@^4.0.1, handlebars@^4.0.11, handlebars@^4.0.3: 4152handlebars@^4.0.11, handlebars@^4.0.3:
4251 version "4.0.12" 4153 version "4.0.12"
4252 resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.12.tgz#2c15c8a96d46da5e266700518ba8cb8d919d5bc5" 4154 resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.12.tgz#2c15c8a96d46da5e266700518ba8cb8d919d5bc5"
4253 integrity sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA== 4155 integrity sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA==
@@ -4369,6 +4271,14 @@ he@1.2.x:
4369 resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" 4271 resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
4370 integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== 4272 integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
4371 4273
4274hls.js@^0.12.2:
4275 version "0.12.2"
4276 resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-0.12.2.tgz#64a969a78cc25991ed5de19357b1dc3f178ac23b"
4277 integrity sha512-lQBSXggw9OzEuaUllUBoSxPcf7neFgnEiDRfCdCNdIPtUeV7vXZ0OeASx6EWtZTBiqSSPigoOX1Y+AR5dA1Feg==
4278 dependencies:
4279 eventemitter3 "3.1.0"
4280 url-toolkit "^2.1.6"
4281
4372hmac-drbg@^1.0.0: 4282hmac-drbg@^1.0.0:
4373 version "1.0.1" 4283 version "1.0.1"
4374 resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" 4284 resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
@@ -4701,10 +4611,10 @@ ini@1.3.5, ini@^1.3.4, ini@~1.3.0:
4701 resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" 4611 resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
4702 integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== 4612 integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
4703 4613
4704inquirer@6.2.0: 4614inquirer@6.2.1:
4705 version "6.2.0" 4615 version "6.2.1"
4706 resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.0.tgz#51adcd776f661369dc1e894859c2560a224abdd8" 4616 resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.1.tgz#9943fc4882161bdb0b0c9276769c75b32dbfcd52"
4707 integrity sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg== 4617 integrity sha512-088kl3DRT2dLU5riVMKKr1DlImd6X7smDhpXUCkJDCKvTEJeRiXh0G132HG9u5a+6Ylw9plFRY7RuTnwohYSpg==
4708 dependencies: 4618 dependencies:
4709 ansi-escapes "^3.0.0" 4619 ansi-escapes "^3.0.0"
4710 chalk "^2.0.0" 4620 chalk "^2.0.0"
@@ -4717,7 +4627,7 @@ inquirer@6.2.0:
4717 run-async "^2.2.0" 4627 run-async "^2.2.0"
4718 rxjs "^6.1.0" 4628 rxjs "^6.1.0"
4719 string-width "^2.1.0" 4629 string-width "^2.1.0"
4720 strip-ansi "^4.0.0" 4630 strip-ansi "^5.0.0"
4721 through "^2.3.6" 4631 through "^2.3.6"
4722 4632
4723internal-ip@^3.0.1: 4633internal-ip@^3.0.1:
@@ -5266,26 +5176,6 @@ istanbul-reports@^2.0.1:
5266 dependencies: 5176 dependencies:
5267 handlebars "^4.0.11" 5177 handlebars "^4.0.11"
5268 5178
5269istanbul@0.4.5:
5270 version "0.4.5"
5271 resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b"
5272 integrity sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=
5273 dependencies:
5274 abbrev "1.0.x"
5275 async "1.x"
5276 escodegen "1.8.x"
5277 esprima "2.7.x"
5278 glob "^5.0.15"
5279 handlebars "^4.0.1"
5280 js-yaml "3.x"
5281 mkdirp "0.5.x"
5282 nopt "3.x"
5283 once "1.x"
5284 resolve "1.1.x"
5285 supports-color "^3.1.0"
5286 which "^1.1.1"
5287 wordwrap "^1.0.0"
5288
5289jasmine-core@^3.1.0: 5179jasmine-core@^3.1.0:
5290 version "3.3.0" 5180 version "3.3.0"
5291 resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.3.0.tgz#dea1cdc634bc93c7e0d4ad27185df30fa971b10e" 5181 resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.3.0.tgz#dea1cdc634bc93c7e0d4ad27185df30fa971b10e"
@@ -5668,7 +5558,7 @@ js-tokens@^3.0.2:
5668 resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" 5558 resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
5669 integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= 5559 integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls=
5670 5560
5671js-yaml@3.x, js-yaml@^3.12.0, js-yaml@^3.7.0, js-yaml@^3.9.0: 5561js-yaml@^3.12.0, js-yaml@^3.7.0, js-yaml@^3.9.0:
5672 version "3.12.0" 5562 version "3.12.0"
5673 resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" 5563 resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1"
5674 integrity sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A== 5564 integrity sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==
@@ -5782,6 +5672,13 @@ json5@^0.5.0, json5@^0.5.1:
5782 resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" 5672 resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
5783 integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= 5673 integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=
5784 5674
5675json5@^1.0.1:
5676 version "1.0.1"
5677 resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
5678 integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
5679 dependencies:
5680 minimist "^1.2.0"
5681
5785jsonfile@^4.0.0: 5682jsonfile@^4.0.0:
5786 version "4.0.0" 5683 version "4.0.0"
5787 resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" 5684 resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
@@ -5994,10 +5891,10 @@ less-loader@4.1.0:
5994 loader-utils "^1.1.0" 5891 loader-utils "^1.1.0"
5995 pify "^3.0.0" 5892 pify "^3.0.0"
5996 5893
5997less@3.8.1: 5894less@3.9.0:
5998 version "3.8.1" 5895 version "3.9.0"
5999 resolved "https://registry.yarnpkg.com/less/-/less-3.8.1.tgz#f31758598ef5a1930dd4caefa9e4340641e71e1d" 5896 resolved "https://registry.yarnpkg.com/less/-/less-3.9.0.tgz#b7511c43f37cf57dc87dffd9883ec121289b1474"
6000 integrity sha512-8HFGuWmL3FhQR0aH89escFNBQH/nEiYPP2ltDFdQw2chE28Yx2E3lhAIq9Y2saYwLSwa699s4dBVEfCY8Drf7Q== 5897 integrity sha512-31CmtPEZraNUtuUREYjSqRkeETFdyEHSEPAGq4erDlUXtda7pzNmctdljdIagSb589d/qXGWiiP31R5JVf+v0w==
6001 dependencies: 5898 dependencies:
6002 clone "^2.1.2" 5899 clone "^2.1.2"
6003 optionalDependencies: 5900 optionalDependencies:
@@ -6023,11 +5920,12 @@ levn@~0.3.0:
6023 prelude-ls "~1.1.2" 5920 prelude-ls "~1.1.2"
6024 type-check "~0.3.2" 5921 type-check "~0.3.2"
6025 5922
6026license-webpack-plugin@2.0.2: 5923license-webpack-plugin@2.1.0:
6027 version "2.0.2" 5924 version "2.1.0"
6028 resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.0.2.tgz#9d34b521cb7fca8527945310b05be6ef0248b687" 5925 resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.1.0.tgz#83acaa6e89c3c5316effdd80cb4ec9c5cd8efc2f"
6029 integrity sha512-GsomZw5VoT20ST8qH2tOjBgbyhn6Pgs9M94g0mbvfBIV1VXufm1iKY+4dbgfTObj1Mp6nSRE3Zf74deOZr0KwA== 5926 integrity sha512-vDiBeMWxjE9n6TabQ9J4FH8urFdsRK0Nvxn1cit9biCiR9aq1zBR0X2BlAkEiIG6qPamLeU0GzvIgLkrFc398A==
6030 dependencies: 5927 dependencies:
5928 "@types/webpack-sources" "^0.1.5"
6031 webpack-sources "^1.2.0" 5929 webpack-sources "^1.2.0"
6032 5930
6033lie@~3.1.0: 5931lie@~3.1.0:
@@ -6090,14 +5988,14 @@ loader-runner@^2.3.0:
6090 resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.1.tgz#026f12fe7c3115992896ac02ba022ba92971b979" 5988 resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.1.tgz#026f12fe7c3115992896ac02ba022ba92971b979"
6091 integrity sha512-By6ZFY7ETWOc9RFaAIb23IjJVcM4dvJC/N57nmdz9RSkMXvAXGI7SyVlAw3v8vjtDRlqThgVDVmTnr9fqMlxkw== 5989 integrity sha512-By6ZFY7ETWOc9RFaAIb23IjJVcM4dvJC/N57nmdz9RSkMXvAXGI7SyVlAw3v8vjtDRlqThgVDVmTnr9fqMlxkw==
6092 5990
6093loader-utils@1.1.0, loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.0.4, loader-utils@^1.1.0: 5991loader-utils@1.2.3:
6094 version "1.1.0" 5992 version "1.2.3"
6095 resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" 5993 resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7"
6096 integrity sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0= 5994 integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==
6097 dependencies: 5995 dependencies:
6098 big.js "^3.1.3" 5996 big.js "^5.2.2"
6099 emojis-list "^2.0.0" 5997 emojis-list "^2.0.0"
6100 json5 "^0.5.0" 5998 json5 "^1.0.1"
6101 5999
6102loader-utils@^0.2.16: 6000loader-utils@^0.2.16:
6103 version "0.2.17" 6001 version "0.2.17"
@@ -6109,6 +6007,15 @@ loader-utils@^0.2.16:
6109 json5 "^0.5.0" 6007 json5 "^0.5.0"
6110 object-assign "^4.0.1" 6008 object-assign "^4.0.1"
6111 6009
6010loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.0.4, loader-utils@^1.1.0:
6011 version "1.1.0"
6012 resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd"
6013 integrity sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=
6014 dependencies:
6015 big.js "^3.1.3"
6016 emojis-list "^2.0.0"
6017 json5 "^0.5.0"
6018
6112locate-path@^2.0.0: 6019locate-path@^2.0.0:
6113 version "2.0.0" 6020 version "2.0.0"
6114 resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" 6021 resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
@@ -6239,6 +6146,13 @@ lru-cache@4.1.x, lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.2, lru-cache
6239 pseudomap "^1.0.2" 6146 pseudomap "^1.0.2"
6240 yallist "^2.1.2" 6147 yallist "^2.1.2"
6241 6148
6149lru-cache@^5.1.1:
6150 version "5.1.1"
6151 resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
6152 integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
6153 dependencies:
6154 yallist "^3.0.2"
6155
6242lru@^3.0.0, lru@^3.1.0: 6156lru@^3.0.0, lru@^3.1.0:
6243 version "3.1.0" 6157 version "3.1.0"
6244 resolved "https://registry.yarnpkg.com/lru/-/lru-3.1.0.tgz#ea7fb8546d83733396a13091d76cfeb4c06837d5" 6158 resolved "https://registry.yarnpkg.com/lru/-/lru-3.1.0.tgz#ea7fb8546d83733396a13091d76cfeb4c06837d5"
@@ -6251,6 +6165,13 @@ m3u8-parser@4.2.0:
6251 resolved "https://registry.yarnpkg.com/m3u8-parser/-/m3u8-parser-4.2.0.tgz#c8e0785fd17f741f4408b49466889274a9e36447" 6165 resolved "https://registry.yarnpkg.com/m3u8-parser/-/m3u8-parser-4.2.0.tgz#c8e0785fd17f741f4408b49466889274a9e36447"
6252 integrity sha512-LVHw0U6IPJjwk9i9f7Xe26NqaUHTNlIt4SSWoEfYFROeVKHN6MIjOhbRheI3dg8Jbq5WCuMFQ0QU3EgZpmzFPg== 6166 integrity sha512-LVHw0U6IPJjwk9i9f7Xe26NqaUHTNlIt4SSWoEfYFROeVKHN6MIjOhbRheI3dg8Jbq5WCuMFQ0QU3EgZpmzFPg==
6253 6167
6168m3u8-parser@^4.3.0:
6169 version "4.3.0"
6170 resolved "https://registry.yarnpkg.com/m3u8-parser/-/m3u8-parser-4.3.0.tgz#4b4e988f87b6d8b2401d209a1d17798285a9da04"
6171 integrity sha512-bVbjuBMoVIgFL1vpXVIxjeaoB5TPDJRb0m5qiTdM738SGqv/LAmsnVVPlKjM4fulm/rr1XZsKM+owHm+zvqxYA==
6172 dependencies:
6173 global "^4.3.2"
6174
6254magic-string@^0.25.0: 6175magic-string@^0.25.0:
6255 version "0.25.1" 6176 version "0.25.1"
6256 resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.1.tgz#b1c248b399cd7485da0fe7385c2fc7011843266e" 6177 resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.1.tgz#b1c248b399cd7485da0fe7385c2fc7011843266e"
@@ -6532,10 +6453,10 @@ min-document@^2.19.0:
6532 dependencies: 6453 dependencies:
6533 dom-walk "^0.1.0" 6454 dom-walk "^0.1.0"
6534 6455
6535mini-css-extract-plugin@0.4.4: 6456mini-css-extract-plugin@0.5.0:
6536 version "0.4.4" 6457 version "0.5.0"
6537 resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.4.tgz#c10410a004951bd3cedac1da69053940fccb625d" 6458 resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz#ac0059b02b9692515a637115b0cc9fed3a35c7b0"
6538 integrity sha512-o+Jm+ocb0asEngdM6FsZWtZsRzA8koFUudIDwYUfl94M3PejPHG7Vopw5hN9V8WsMkSFpm3tZP3Fesz89EyrfQ== 6459 integrity sha512-IuaLjruM0vMKhUUT51fQdQzBYTX49dLj8w68ALEAe2A4iYNpIC4eMac67mt3NzycvjOlf07/kYxJDc0RTl1Wqw==
6539 dependencies: 6460 dependencies:
6540 loader-utils "^1.1.0" 6461 loader-utils "^1.1.0"
6541 schema-utils "^1.0.0" 6462 schema-utils "^1.0.0"
@@ -6551,7 +6472,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
6551 resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" 6472 resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
6552 integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= 6473 integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
6553 6474
6554"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2: 6475minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2:
6555 version "3.0.4" 6476 version "3.0.4"
6556 resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 6477 resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
6557 integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 6478 integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
@@ -6943,14 +6864,39 @@ node-pre-gyp@^0.10.0:
6943 semver "^5.3.0" 6864 semver "^5.3.0"
6944 tar "^4" 6865 tar "^4"
6945 6866
6946node-releases@^1.0.5: 6867node-releases@^1.1.3:
6947 version "1.0.5" 6868 version "1.1.7"
6948 resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.0.5.tgz#a641adcc968b039a27345d92ef10b093e5cbd41d" 6869 resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.7.tgz#b09a10394d0ed8f7778f72bb861dde68b146303b"
6949 integrity sha512-Ky7q0BO1BBkG/rQz6PkEZ59rwo+aSfhczHP1wwq8IowoVdN/FpiP7qp0XW0P2+BVCWe5fQUBozdbVd54q1RbCQ== 6870 integrity sha512-bKdrwaqJUPHqlCzDD7so/R+Nk0jGv9a11ZhLrD9f6i947qGLrGAhU3OxRENa19QQmwzGy/g6zCDEuLGDO8HPvA==
6950 dependencies: 6871 dependencies:
6951 semver "^5.3.0" 6872 semver "^5.3.0"
6952 6873
6953node-sass@4.10.0, node-sass@^4.9.3: 6874node-sass@4.11.0:
6875 version "4.11.0"
6876 resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.11.0.tgz#183faec398e9cbe93ba43362e2768ca988a6369a"
6877 integrity sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==
6878 dependencies:
6879 async-foreach "^0.1.3"
6880 chalk "^1.1.1"
6881 cross-spawn "^3.0.0"
6882 gaze "^1.0.0"
6883 get-stdin "^4.0.1"
6884 glob "^7.0.3"
6885 in-publish "^2.0.0"
6886 lodash.assign "^4.2.0"
6887 lodash.clonedeep "^4.3.2"
6888 lodash.mergewith "^4.6.0"
6889 meow "^3.7.0"
6890 mkdirp "^0.5.1"
6891 nan "^2.10.0"
6892 node-gyp "^3.8.0"
6893 npmlog "^4.0.0"
6894 request "^2.88.0"
6895 sass-graph "^2.2.4"
6896 stdout-stream "^1.4.0"
6897 "true-case-path" "^1.0.2"
6898
6899node-sass@^4.9.3:
6954 version "4.10.0" 6900 version "4.10.0"
6955 resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.10.0.tgz#dcc2b364c0913630945ccbf7a2bbf1f926effca4" 6901 resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.10.0.tgz#dcc2b364c0913630945ccbf7a2bbf1f926effca4"
6956 integrity sha512-fDQJfXszw6vek63Fe/ldkYXmRYK/QS6NbvM3i5oEo9ntPDy4XX7BcKZyTKv+/kSSxRtXXc7l+MSwEmYc0CSy6Q== 6902 integrity sha512-fDQJfXszw6vek63Fe/ldkYXmRYK/QS6NbvM3i5oEo9ntPDy4XX7BcKZyTKv+/kSSxRtXXc7l+MSwEmYc0CSy6Q==
@@ -6975,7 +6921,7 @@ node-sass@4.10.0, node-sass@^4.9.3:
6975 stdout-stream "^1.4.0" 6921 stdout-stream "^1.4.0"
6976 "true-case-path" "^1.0.2" 6922 "true-case-path" "^1.0.2"
6977 6923
6978"nopt@2 || 3", nopt@3.x: 6924"nopt@2 || 3":
6979 version "3.0.6" 6925 version "3.0.6"
6980 resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" 6926 resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
6981 integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= 6927 integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k=
@@ -7022,7 +6968,7 @@ npm-font-source-sans-pro@^1.0.2:
7022 resolved "https://registry.yarnpkg.com/npm-font-source-sans-pro/-/npm-font-source-sans-pro-1.0.2.tgz#c55c8ae368eebdbcaca65425a0d7e1f9a192a03e" 6968 resolved "https://registry.yarnpkg.com/npm-font-source-sans-pro/-/npm-font-source-sans-pro-1.0.2.tgz#c55c8ae368eebdbcaca65425a0d7e1f9a192a03e"
7023 integrity sha1-xVyK42juvbysplQloNfh+aGSoD4= 6969 integrity sha1-xVyK42juvbysplQloNfh+aGSoD4=
7024 6970
7025npm-package-arg@^6.0.0, npm-package-arg@^6.1.0: 6971npm-package-arg@6.1.0, npm-package-arg@^6.0.0, npm-package-arg@^6.1.0:
7026 version "6.1.0" 6972 version "6.1.0"
7027 resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-6.1.0.tgz#15ae1e2758a5027efb4c250554b85a737db7fcc1" 6973 resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-6.1.0.tgz#15ae1e2758a5027efb4c250554b85a737db7fcc1"
7028 integrity sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA== 6974 integrity sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==
@@ -7040,7 +6986,7 @@ npm-packlist@^1.1.12, npm-packlist@^1.1.6:
7040 ignore-walk "^3.0.1" 6986 ignore-walk "^3.0.1"
7041 npm-bundled "^1.0.1" 6987 npm-bundled "^1.0.1"
7042 6988
7043npm-pick-manifest@^2.1.0: 6989npm-pick-manifest@^2.2.3:
7044 version "2.2.3" 6990 version "2.2.3"
7045 resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-2.2.3.tgz#32111d2a9562638bb2c8f2bf27f7f3092c8fae40" 6991 resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-2.2.3.tgz#32111d2a9562638bb2c8f2bf27f7f3092c8fae40"
7046 integrity sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA== 6992 integrity sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA==
@@ -7174,7 +7120,7 @@ object.pick@^1.3.0:
7174 dependencies: 7120 dependencies:
7175 isobject "^3.0.1" 7121 isobject "^3.0.1"
7176 7122
7177obuf@^1.0.0, obuf@^1.1.1: 7123obuf@^1.0.0, obuf@^1.1.2:
7178 version "1.1.2" 7124 version "1.1.2"
7179 resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" 7125 resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
7180 integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== 7126 integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==
@@ -7191,7 +7137,7 @@ on-headers@~1.0.1:
7191 resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" 7137 resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7"
7192 integrity sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c= 7138 integrity sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=
7193 7139
7194once@1.x, once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0: 7140once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0:
7195 version "1.4.0" 7141 version "1.4.0"
7196 resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 7142 resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
7197 integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 7143 integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
@@ -7210,14 +7156,7 @@ opener@^1.5.1:
7210 resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed" 7156 resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed"
7211 integrity sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA== 7157 integrity sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==
7212 7158
7213opn@5.3.0: 7159opn@5.4.0, opn@^5.1.0:
7214 version "5.3.0"
7215 resolved "https://registry.yarnpkg.com/opn/-/opn-5.3.0.tgz#64871565c863875f052cfdf53d3e3cb5adb53b1c"
7216 integrity sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==
7217 dependencies:
7218 is-wsl "^1.1.0"
7219
7220opn@^5.1.0:
7221 version "5.4.0" 7160 version "5.4.0"
7222 resolved "https://registry.yarnpkg.com/opn/-/opn-5.4.0.tgz#cb545e7aab78562beb11aa3bfabc7042e1761035" 7161 resolved "https://registry.yarnpkg.com/opn/-/opn-5.4.0.tgz#cb545e7aab78562beb11aa3bfabc7042e1761035"
7223 integrity sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw== 7162 integrity sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw==
@@ -7357,6 +7296,26 @@ p-try@^2.0.0:
7357 resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1" 7296 resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1"
7358 integrity sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ== 7297 integrity sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==
7359 7298
7299p2p-media-loader-core@^0.4.0:
7300 version "0.4.0"
7301 resolved "https://registry.yarnpkg.com/p2p-media-loader-core/-/p2p-media-loader-core-0.4.0.tgz#767d56785545bc9c0d8c1a04eb7b67a33e40d0c8"
7302 integrity sha512-llcFqEDs19o916g2OSIPHPjZweO5caHUm/7P18Qu+qb3swYQYSPNwMLoHnpXROHiH5I+00K8w5enz31oUwiCgA==
7303 dependencies:
7304 bittorrent-tracker "^9.10.1"
7305 debug "^4.1.1"
7306 events "^3.0.0"
7307 get-browser-rtc "^1.0.2"
7308 sha.js "^2.4.11"
7309
7310p2p-media-loader-hlsjs@^0.4.0:
7311 version "0.4.0"
7312 resolved "https://registry.yarnpkg.com/p2p-media-loader-hlsjs/-/p2p-media-loader-hlsjs-0.4.0.tgz#1b90c88580503d4c3d8017c813abe41803b613ed"
7313 integrity sha512-IWRs/aGasKD//+dtQkYWAjD/cQx3LMaLkMn0EzLhLpeBj4SLNjlbwOPlbx36M4i39X04Y3WZe9YUeIciId3G5Q==
7314 dependencies:
7315 events "^3.0.0"
7316 m3u8-parser "^4.3.0"
7317 p2p-media-loader-core "^0.4.0"
7318
7360package-json-versionify@^1.0.2: 7319package-json-versionify@^1.0.2:
7361 version "1.0.4" 7320 version "1.0.4"
7362 resolved "https://registry.yarnpkg.com/package-json-versionify/-/package-json-versionify-1.0.4.tgz#5860587a944873a6b7e6d26e8e51ffb22315bf17" 7321 resolved "https://registry.yarnpkg.com/package-json-versionify/-/package-json-versionify-1.0.4.tgz#5860587a944873a6b7e6d26e8e51ffb22315bf17"
@@ -7364,17 +7323,17 @@ package-json-versionify@^1.0.2:
7364 dependencies: 7323 dependencies:
7365 browserify-package-json "^1.0.0" 7324 browserify-package-json "^1.0.0"
7366 7325
7367pacote@9.1.1: 7326pacote@9.4.0:
7368 version "9.1.1" 7327 version "9.4.0"
7369 resolved "https://registry.yarnpkg.com/pacote/-/pacote-9.1.1.tgz#25091f75a25021de8be8d34cc6408728fca3579b" 7328 resolved "https://registry.yarnpkg.com/pacote/-/pacote-9.4.0.tgz#af979abdeb175cd347c3e33be3241af1ed254807"
7370 integrity sha512-f28Rq5ozzKAA9YwIKw61/ipwAatUZseYmVssDbHHaexF0wRIVotapVEZPAjOT7Eu3LYVqEp0NVpNizoAnYBUaA== 7329 integrity sha512-WQ1KL/phGMkedYEQx9ODsjj7xvwLSpdFJJdEXrLyw5SILMxcTNt5DTxT2Z93fXuLFYJBlZJdnwdalrQdB/rX5w==
7371 dependencies: 7330 dependencies:
7372 bluebird "^3.5.2" 7331 bluebird "^3.5.3"
7373 cacache "^11.2.0" 7332 cacache "^11.3.2"
7374 figgy-pudding "^3.5.1" 7333 figgy-pudding "^3.5.1"
7375 get-stream "^4.1.0" 7334 get-stream "^4.1.0"
7376 glob "^7.1.3" 7335 glob "^7.1.3"
7377 lru-cache "^4.1.3" 7336 lru-cache "^5.1.1"
7378 make-fetch-happen "^4.0.1" 7337 make-fetch-happen "^4.0.1"
7379 minimatch "^3.0.4" 7338 minimatch "^3.0.4"
7380 minipass "^2.3.5" 7339 minipass "^2.3.5"
@@ -7383,7 +7342,7 @@ pacote@9.1.1:
7383 normalize-package-data "^2.4.0" 7342 normalize-package-data "^2.4.0"
7384 npm-package-arg "^6.1.0" 7343 npm-package-arg "^6.1.0"
7385 npm-packlist "^1.1.12" 7344 npm-packlist "^1.1.12"
7386 npm-pick-manifest "^2.1.0" 7345 npm-pick-manifest "^2.2.3"
7387 npm-registry-fetch "^3.8.0" 7346 npm-registry-fetch "^3.8.0"
7388 osenv "^0.1.5" 7347 osenv "^0.1.5"
7389 promise-inflight "^1.0.1" 7348 promise-inflight "^1.0.1"
@@ -7393,7 +7352,7 @@ pacote@9.1.1:
7393 safe-buffer "^5.1.2" 7352 safe-buffer "^5.1.2"
7394 semver "^5.6.0" 7353 semver "^5.6.0"
7395 ssri "^6.0.1" 7354 ssri "^6.0.1"
7396 tar "^4.4.6" 7355 tar "^4.4.8"
7397 unique-filename "^1.1.1" 7356 unique-filename "^1.1.1"
7398 which "^1.3.1" 7357 which "^1.3.1"
7399 7358
@@ -7658,15 +7617,6 @@ pngjs@^2.3.1:
7658 resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-2.3.1.tgz#11d1e12b9cb64d63e30c143a330f4c1f567da85f" 7617 resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-2.3.1.tgz#11d1e12b9cb64d63e30c143a330f4c1f567da85f"
7659 integrity sha1-EdHhK5y2TWPjDBQ6Mw9MH1Z9qF8= 7618 integrity sha1-EdHhK5y2TWPjDBQ6Mw9MH1Z9qF8=
7660 7619
7661portfinder@1.0.17:
7662 version "1.0.17"
7663 resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.17.tgz#a8a1691143e46c4735edefcf4fbcccedad26456a"
7664 integrity sha512-syFcRIRzVI1BoEFOCaAiizwDolh1S1YXSodsVhncbhjzjZQulhczNRbqnUl9N31Q4dKGOXsNDqxC2BWBgSMqeQ==
7665 dependencies:
7666 async "^1.5.2"
7667 debug "^2.2.0"
7668 mkdirp "0.5.x"
7669
7670portfinder@^1.0.9: 7620portfinder@^1.0.9:
7671 version "1.0.20" 7621 version "1.0.20"
7672 resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.20.tgz#bea68632e54b2e13ab7b0c4775e9b41bf270e44a" 7622 resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.20.tgz#bea68632e54b2e13ab7b0c4775e9b41bf270e44a"
@@ -7681,10 +7631,10 @@ posix-character-classes@^0.1.0:
7681 resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" 7631 resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
7682 integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= 7632 integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
7683 7633
7684postcss-import@12.0.0: 7634postcss-import@12.0.1:
7685 version "12.0.0" 7635 version "12.0.1"
7686 resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-12.0.0.tgz#149f96a4ef0b27525c419784be8517ebd17e92c5" 7636 resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-12.0.1.tgz#cf8c7ab0b5ccab5649024536e565f841928b7153"
7687 integrity sha512-3KqKRZcaZAvxbY8DVLdd81tG5uKzbUQuiWIvy0o0fzEC42bKacqPYFWbfCQyw6L4LWUaqPz/idvIdbhpgQ32eQ== 7637 integrity sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw==
7688 dependencies: 7638 dependencies:
7689 postcss "^7.0.1" 7639 postcss "^7.0.1"
7690 postcss-value-parser "^3.2.3" 7640 postcss-value-parser "^3.2.3"
@@ -7745,14 +7695,14 @@ postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0, postcss-value-parser@^
7745 resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" 7695 resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281"
7746 integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== 7696 integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==
7747 7697
7748postcss@7.0.5: 7698postcss@7.0.14, postcss@^7.0.13:
7749 version "7.0.5" 7699 version "7.0.14"
7750 resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.5.tgz#70e6443e36a6d520b0fd4e7593fcca3635ee9f55" 7700 resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.14.tgz#4527ed6b1ca0d82c53ce5ec1a2041c2346bbd6e5"
7751 integrity sha512-HBNpviAUFCKvEh7NZhw1e8MBPivRszIiUnhrJ+sBFVSYSqubrzwX3KG51mYgcRHX8j/cAgZJedONZcm5jTBdgQ== 7701 integrity sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==
7752 dependencies: 7702 dependencies:
7753 chalk "^2.4.1" 7703 chalk "^2.4.2"
7754 source-map "^0.6.1" 7704 source-map "^0.6.1"
7755 supports-color "^5.5.0" 7705 supports-color "^6.1.0"
7756 7706
7757postcss@^6.0.1, postcss@^6.0.23: 7707postcss@^6.0.1, postcss@^6.0.23:
7758 version "6.0.23" 7708 version "6.0.23"
@@ -7763,7 +7713,7 @@ postcss@^6.0.1, postcss@^6.0.23:
7763 source-map "^0.6.1" 7713 source-map "^0.6.1"
7764 supports-color "^5.4.0" 7714 supports-color "^5.4.0"
7765 7715
7766postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.5: 7716postcss@^7.0.0, postcss@^7.0.1:
7767 version "7.0.6" 7717 version "7.0.6"
7768 resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.6.tgz#6dcaa1e999cdd4a255dcd7d4d9547f4ca010cdc2" 7718 resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.6.tgz#6dcaa1e999cdd4a255dcd7d4d9547f4ca010cdc2"
7769 integrity sha512-Nq/rNjnHFcKgCDDZYO0lNsl6YWe6U7tTy+ESN+PnLxebL8uBtYX59HZqvrj7YLK5UCyll2hqDsJOo3ndzEW8Ug== 7719 integrity sha512-Nq/rNjnHFcKgCDDZYO0lNsl6YWe6U7tTy+ESN+PnLxebL8uBtYX59HZqvrj7YLK5UCyll2hqDsJOo3ndzEW8Ug==
@@ -7953,21 +7903,11 @@ pumpify@^1.3.3:
7953 inherits "^2.0.3" 7903 inherits "^2.0.3"
7954 pump "^2.0.0" 7904 pump "^2.0.0"
7955 7905
7956punycode@1.3.2: 7906punycode@1.3.2, punycode@^1.2.4, punycode@^1.4.1, punycode@^2.1.0, punycode@^2.1.1:
7957 version "1.3.2"
7958 resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
7959 integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
7960
7961punycode@^1.2.4, punycode@^1.4.1:
7962 version "1.4.1" 7907 version "1.4.1"
7963 resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" 7908 resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
7964 integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= 7909 integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
7965 7910
7966punycode@^2.1.0, punycode@^2.1.1:
7967 version "2.1.1"
7968 resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
7969 integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
7970
7971purify-css@^1.2.5: 7911purify-css@^1.2.5:
7972 version "1.2.5" 7912 version "1.2.5"
7973 resolved "https://registry.yarnpkg.com/purify-css/-/purify-css-1.2.5.tgz#c4b9ec90735765f3e247ba6a3b49f132f3482500" 7913 resolved "https://registry.yarnpkg.com/purify-css/-/purify-css-1.2.5.tgz#c4b9ec90735765f3e247ba6a3b49f132f3482500"
@@ -8098,7 +8038,15 @@ raw-body@2.3.3:
8098 iconv-lite "0.4.23" 8038 iconv-lite "0.4.23"
8099 unpipe "1.0.0" 8039 unpipe "1.0.0"
8100 8040
8101raw-loader@0.5.1, raw-loader@^0.5.1: 8041raw-loader@1.0.0:
8042 version "1.0.0"
8043 resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-1.0.0.tgz#3f9889e73dadbda9a424bce79809b4133ad46405"
8044 integrity sha512-Uqy5AqELpytJTRxYT4fhltcKPj0TyaEpzJDcGz7DFJi+pQOOi3GjR/DOdxTkTsF+NzhnldIoG6TORaBlInUuqA==
8045 dependencies:
8046 loader-utils "^1.1.0"
8047 schema-utils "^1.0.0"
8048
8049raw-loader@^0.5.1:
8102 version "0.5.1" 8050 version "0.5.1"
8103 resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa" 8051 resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa"
8104 integrity sha1-DD0L6u2KAclm2Xh793goElKpeao= 8052 integrity sha1-DD0L6u2KAclm2Xh793goElKpeao=
@@ -8174,7 +8122,7 @@ read-pkg@^2.0.0:
8174 normalize-package-data "^2.3.2" 8122 normalize-package-data "^2.3.2"
8175 path-type "^2.0.0" 8123 path-type "^2.0.0"
8176 8124
8177"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, 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.2.9, readable-stream@^2.3.0, readable-stream@^2.3.2, readable-stream@^2.3.3, readable-stream@^2.3.4, readable-stream@^2.3.6, readable-stream@~2.3.6: 8125"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, 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.3, readable-stream@^2.3.4, readable-stream@^2.3.6, readable-stream@~2.3.6:
8178 version "2.3.6" 8126 version "2.3.6"
8179 resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" 8127 resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
8180 integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== 8128 integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
@@ -8453,7 +8401,7 @@ resolve-url@^0.2.1:
8453 resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" 8401 resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
8454 integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= 8402 integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
8455 8403
8456resolve@1.1.7, resolve@1.1.x: 8404resolve@1.1.7:
8457 version "1.1.7" 8405 version "1.1.7"
8458 resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" 8406 resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
8459 integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= 8407 integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
@@ -8745,16 +8693,11 @@ semver-intersect@1.4.0:
8745 dependencies: 8693 dependencies:
8746 semver "^5.0.0" 8694 semver "^5.0.0"
8747 8695
8748"semver@2 || 3 || 4 || 5", semver@^5.0.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5, semver@^5.5.0, semver@^5.6.0: 8696"semver@2 || 3 || 4 || 5", semver@5.6.0, semver@^5.0.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5, semver@^5.5.0, semver@^5.6.0:
8749 version "5.6.0" 8697 version "5.6.0"
8750 resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" 8698 resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
8751 integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== 8699 integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
8752 8700
8753semver@5.5.1:
8754 version "5.5.1"
8755 resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477"
8756 integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==
8757
8758semver@~5.3.0: 8701semver@~5.3.0:
8759 version "5.3.0" 8702 version "5.3.0"
8760 resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" 8703 resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
@@ -8842,7 +8785,7 @@ setprototypeof@1.1.0:
8842 resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" 8785 resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
8843 integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== 8786 integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==
8844 8787
8845sha.js@^2.4.0, sha.js@^2.4.8: 8788sha.js@^2.4.0, sha.js@^2.4.11, sha.js@^2.4.8:
8846 version "2.4.11" 8789 version "2.4.11"
8847 resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" 8790 resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7"
8848 integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== 8791 integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==
@@ -9118,10 +9061,10 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2:
9118 source-map-url "^0.4.0" 9061 source-map-url "^0.4.0"
9119 urix "^0.1.0" 9062 urix "^0.1.0"
9120 9063
9121source-map-support@0.5.9, source-map-support@^0.5.3, source-map-support@^0.5.5, source-map-support@^0.5.6, source-map-support@~0.5.6: 9064source-map-support@0.5.10, source-map-support@~0.5.9:
9122 version "0.5.9" 9065 version "0.5.10"
9123 resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.9.tgz#41bc953b2534267ea2d605bccfa7bfa3111ced5f" 9066 resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.10.tgz#2214080bc9d51832511ee2bab96e3c2f9353120c"
9124 integrity sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA== 9067 integrity sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==
9125 dependencies: 9068 dependencies:
9126 buffer-from "^1.0.0" 9069 buffer-from "^1.0.0"
9127 source-map "^0.6.0" 9070 source-map "^0.6.0"
@@ -9133,6 +9076,14 @@ source-map-support@^0.4.15, source-map-support@~0.4.0:
9133 dependencies: 9076 dependencies:
9134 source-map "^0.5.6" 9077 source-map "^0.5.6"
9135 9078
9079source-map-support@^0.5.3, source-map-support@^0.5.5, source-map-support@^0.5.6, source-map-support@~0.5.6:
9080 version "0.5.9"
9081 resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.9.tgz#41bc953b2534267ea2d605bccfa7bfa3111ced5f"
9082 integrity sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==
9083 dependencies:
9084 buffer-from "^1.0.0"
9085 source-map "^0.6.0"
9086
9136source-map-url@^0.4.0: 9087source-map-url@^0.4.0:
9137 version "0.4.0" 9088 version "0.4.0"
9138 resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" 9089 resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
@@ -9172,13 +9123,6 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
9172 resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 9123 resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
9173 integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 9124 integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
9174 9125
9175source-map@~0.2.0:
9176 version "0.2.0"
9177 resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d"
9178 integrity sha1-2rc/vPwrqBm03gO9b26qSBZLP50=
9179 dependencies:
9180 amdefine ">=0.0.4"
9181
9182sourcemap-codec@^1.4.1: 9126sourcemap-codec@^1.4.1:
9183 version "1.4.4" 9127 version "1.4.4"
9184 resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.4.tgz#c63ea927c029dd6bd9a2b7fa03b3fec02ad56e9f" 9128 resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.4.tgz#c63ea927c029dd6bd9a2b7fa03b3fec02ad56e9f"
@@ -9210,35 +9154,33 @@ spdx-license-ids@^3.0.0:
9210 resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.2.tgz#a59efc09784c2a5bada13cfeaf5c75dd214044d2" 9154 resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.2.tgz#a59efc09784c2a5bada13cfeaf5c75dd214044d2"
9211 integrity sha512-qky9CVt0lVIECkEsYbNILVnPvycuEBkXoMFLRWsREkomQLevYhtRKC+R91a5TOAQ3bCMjikRwhyaRqj1VYatYg== 9155 integrity sha512-qky9CVt0lVIECkEsYbNILVnPvycuEBkXoMFLRWsREkomQLevYhtRKC+R91a5TOAQ3bCMjikRwhyaRqj1VYatYg==
9212 9156
9213spdy-transport@^2.0.18: 9157spdy-transport@^3.0.0:
9214 version "2.1.1" 9158 version "3.0.0"
9215 resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-2.1.1.tgz#c54815d73858aadd06ce63001e7d25fa6441623b" 9159 resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31"
9216 integrity sha512-q7D8c148escoB3Z7ySCASadkegMmUZW8Wb/Q1u0/XBgDKMO880rLQDj8Twiew/tYi7ghemKUi/whSYOwE17f5Q== 9160 integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==
9217 dependencies: 9161 dependencies:
9218 debug "^2.6.8" 9162 debug "^4.1.0"
9219 detect-node "^2.0.3" 9163 detect-node "^2.0.4"
9220 hpack.js "^2.1.6" 9164 hpack.js "^2.1.6"
9221 obuf "^1.1.1" 9165 obuf "^1.1.2"
9222 readable-stream "^2.2.9" 9166 readable-stream "^3.0.6"
9223 safe-buffer "^5.0.1" 9167 wbuf "^1.7.3"
9224 wbuf "^1.7.2"
9225 9168
9226spdy@^3.4.1: 9169spdy@^4.0.0:
9227 version "3.4.7" 9170 version "4.0.0"
9228 resolved "https://registry.yarnpkg.com/spdy/-/spdy-3.4.7.tgz#42ff41ece5cc0f99a3a6c28aabb73f5c3b03acbc" 9171 resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.0.tgz#81f222b5a743a329aa12cea6a390e60e9b613c52"
9229 integrity sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw= 9172 integrity sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==
9230 dependencies: 9173 dependencies:
9231 debug "^2.6.8" 9174 debug "^4.1.0"
9232 handle-thing "^1.2.5" 9175 handle-thing "^2.0.0"
9233 http-deceiver "^1.2.7" 9176 http-deceiver "^1.2.7"
9234 safe-buffer "^5.0.1"
9235 select-hose "^2.0.0" 9177 select-hose "^2.0.0"
9236 spdy-transport "^2.0.18" 9178 spdy-transport "^3.0.0"
9237 9179
9238speed-measure-webpack-plugin@1.2.3: 9180speed-measure-webpack-plugin@1.3.0:
9239 version "1.2.3" 9181 version "1.3.0"
9240 resolved "https://registry.yarnpkg.com/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.2.3.tgz#de170b5cefbfa1c039d95e639edd3ad50cfc7c48" 9182 resolved "https://registry.yarnpkg.com/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.0.tgz#c7ffafef513df3d63d5d546c8fc1986dfc4969aa"
9241 integrity sha512-p+taQ69VkRUXYMoZOx2nxV/Tz8tt79ahctoZJyJDHWP7fEYvwFNf5Pd73k5kZ6auu0pTsPNLEUwWpM8mCk85Zw== 9183 integrity sha512-b9Yd0TrzceMVYSbuamM1sFsGM1oVfyFTM22gOoyLhymNvBVApuYpkdFOgYkKJpN/KhTpcCYcTGHg7X+FJ33Vvw==
9242 dependencies: 9184 dependencies:
9243 chalk "^2.0.1" 9185 chalk "^2.0.1"
9244 9186
@@ -9494,6 +9436,13 @@ strip-ansi@^4.0.0:
9494 dependencies: 9436 dependencies:
9495 ansi-regex "^3.0.0" 9437 ansi-regex "^3.0.0"
9496 9438
9439strip-ansi@^5.0.0:
9440 version "5.0.0"
9441 resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.0.0.tgz#f78f68b5d0866c20b2c9b8c61b5298508dc8756f"
9442 integrity sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==
9443 dependencies:
9444 ansi-regex "^4.0.0"
9445
9497strip-bom@3.0.0, strip-bom@^3.0.0: 9446strip-bom@3.0.0, strip-bom@^3.0.0:
9498 version "3.0.0" 9447 version "3.0.0"
9499 resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" 9448 resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
@@ -9557,7 +9506,7 @@ supports-color@^2.0.0:
9557 resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" 9506 resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
9558 integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= 9507 integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
9559 9508
9560supports-color@^3.1.0, supports-color@^3.1.2: 9509supports-color@^3.1.2:
9561 version "3.2.3" 9510 version "3.2.3"
9562 resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" 9511 resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
9563 integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY= 9512 integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=
@@ -9571,6 +9520,13 @@ supports-color@^5.1.0, supports-color@^5.3.0, supports-color@^5.4.0, supports-co
9571 dependencies: 9520 dependencies:
9572 has-flag "^3.0.0" 9521 has-flag "^3.0.0"
9573 9522
9523supports-color@^6.1.0:
9524 version "6.1.0"
9525 resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
9526 integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==
9527 dependencies:
9528 has-flag "^3.0.0"
9529
9574symbol-observable@1.2.0: 9530symbol-observable@1.2.0:
9575 version "1.2.0" 9531 version "1.2.0"
9576 resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" 9532 resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
@@ -9595,7 +9551,7 @@ tar@^2.0.0:
9595 fstream "^1.0.2" 9551 fstream "^1.0.2"
9596 inherits "2" 9552 inherits "2"
9597 9553
9598tar@^4, tar@^4.4.6: 9554tar@^4, tar@^4.4.8:
9599 version "4.4.8" 9555 version "4.4.8"
9600 resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d" 9556 resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d"
9601 integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ== 9557 integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==
@@ -9608,7 +9564,21 @@ tar@^4, tar@^4.4.6:
9608 safe-buffer "^5.1.2" 9564 safe-buffer "^5.1.2"
9609 yallist "^3.0.2" 9565 yallist "^3.0.2"
9610 9566
9611terser-webpack-plugin@1.1.0, terser-webpack-plugin@^1.1.0: 9567terser-webpack-plugin@1.2.2:
9568 version "1.2.2"
9569 resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.2.2.tgz#9bff3a891ad614855a7dde0d707f7db5a927e3d9"
9570 integrity sha512-1DMkTk286BzmfylAvLXwpJrI7dWa5BnFmscV/2dCr8+c56egFcbaeFAl7+sujAjdmpLam21XRdhA4oifLyiWWg==
9571 dependencies:
9572 cacache "^11.0.2"
9573 find-cache-dir "^2.0.0"
9574 schema-utils "^1.0.0"
9575 serialize-javascript "^1.4.0"
9576 source-map "^0.6.1"
9577 terser "^3.16.1"
9578 webpack-sources "^1.1.0"
9579 worker-farm "^1.5.2"
9580
9581terser-webpack-plugin@^1.1.0:
9612 version "1.1.0" 9582 version "1.1.0"
9613 resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.1.0.tgz#cf7c25a1eee25bf121f4a587bb9e004e3f80e528" 9583 resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.1.0.tgz#cf7c25a1eee25bf121f4a587bb9e004e3f80e528"
9614 integrity sha512-61lV0DSxMAZ8AyZG7/A4a3UPlrbOBo8NIQ4tJzLPAdGOQ+yoNC7l5ijEow27lBAL2humer01KLS6bGIMYQxKoA== 9584 integrity sha512-61lV0DSxMAZ8AyZG7/A4a3UPlrbOBo8NIQ4tJzLPAdGOQ+yoNC7l5ijEow27lBAL2humer01KLS6bGIMYQxKoA==
@@ -9622,6 +9592,15 @@ terser-webpack-plugin@1.1.0, terser-webpack-plugin@^1.1.0:
9622 webpack-sources "^1.1.0" 9592 webpack-sources "^1.1.0"
9623 worker-farm "^1.5.2" 9593 worker-farm "^1.5.2"
9624 9594
9595terser@^3.16.1:
9596 version "3.16.1"
9597 resolved "https://registry.yarnpkg.com/terser/-/terser-3.16.1.tgz#5b0dd4fa1ffd0b0b43c2493b2c364fd179160493"
9598 integrity sha512-JDJjgleBROeek2iBcSNzOHLKsB/MdDf+E/BOAJ0Tk9r7p9/fVobfv7LMJ/g/k3v9SXdmjZnIlFd5nfn/Rt0Xow==
9599 dependencies:
9600 commander "~2.17.1"
9601 source-map "~0.6.1"
9602 source-map-support "~0.5.9"
9603
9625terser@^3.8.1: 9604terser@^3.8.1:
9626 version "3.11.0" 9605 version "3.11.0"
9627 resolved "https://registry.yarnpkg.com/terser/-/terser-3.11.0.tgz#60782893e1f4d6788acc696351f40636d0e37af0" 9606 resolved "https://registry.yarnpkg.com/terser/-/terser-3.11.0.tgz#60782893e1f4d6788acc696351f40636d0e37af0"
@@ -9784,10 +9763,10 @@ tr46@^1.0.1:
9784 dependencies: 9763 dependencies:
9785 punycode "^2.1.0" 9764 punycode "^2.1.0"
9786 9765
9787tree-kill@1.2.0: 9766tree-kill@1.2.1:
9788 version "1.2.0" 9767 version "1.2.1"
9789 resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.0.tgz#5846786237b4239014f05db156b643212d4c6f36" 9768 resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.1.tgz#5398f374e2f292b9dcc7b2e71e30a5c3bb6c743a"
9790 integrity sha512-DlX6dR0lOIRDFxI0mjL9IYg6OTncLm/Zt+JiBhE5OlFcAR8yc9S7FFXU9so0oda47frdM/JFsk7UjNt9vscKcg== 9769 integrity sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==
9791 9770
9792trim-newlines@^1.0.0: 9771trim-newlines@^1.0.0:
9793 version "1.0.0" 9772 version "1.0.0"
@@ -9953,19 +9932,16 @@ typescript@3.1.6:
9953 resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.6.tgz#b6543a83cfc8c2befb3f4c8fba6896f5b0c9be68" 9932 resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.6.tgz#b6543a83cfc8c2befb3f4c8fba6896f5b0c9be68"
9954 integrity sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA== 9933 integrity sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==
9955 9934
9935typescript@3.2.4:
9936 version "3.2.4"
9937 resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.4.tgz#c585cb952912263d915b462726ce244ba510ef3d"
9938 integrity sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==
9939
9956uc.micro@^1.0.1, uc.micro@^1.0.5: 9940uc.micro@^1.0.1, uc.micro@^1.0.5:
9957 version "1.0.5" 9941 version "1.0.5"
9958 resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.5.tgz#0c65f15f815aa08b560a61ce8b4db7ffc3f45376" 9942 resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.5.tgz#0c65f15f815aa08b560a61ce8b4db7ffc3f45376"
9959 integrity sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg== 9943 integrity sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg==
9960 9944
9961uglify-es@^3.3.4:
9962 version "3.3.9"
9963 resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677"
9964 integrity sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==
9965 dependencies:
9966 commander "~2.13.0"
9967 source-map "~0.6.1"
9968
9969uglify-js@3.4.x, uglify-js@^3.0.6, uglify-js@^3.1.4: 9945uglify-js@3.4.x, uglify-js@^3.0.6, uglify-js@^3.1.4:
9970 version "3.4.9" 9946 version "3.4.9"
9971 resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3" 9947 resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3"
@@ -9974,20 +9950,6 @@ uglify-js@3.4.x, uglify-js@^3.0.6, uglify-js@^3.1.4:
9974 commander "~2.17.1" 9950 commander "~2.17.1"
9975 source-map "~0.6.1" 9951 source-map "~0.6.1"
9976 9952
9977uglifyjs-webpack-plugin@^1.2.4:
9978 version "1.3.0"
9979 resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz#75f548160858163a08643e086d5fefe18a5d67de"
9980 integrity sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==
9981 dependencies:
9982 cacache "^10.0.4"
9983 find-cache-dir "^1.0.0"
9984 schema-utils "^0.4.5"
9985 serialize-javascript "^1.4.0"
9986 source-map "^0.6.1"
9987 uglify-es "^3.3.4"
9988 webpack-sources "^1.1.0"
9989 worker-farm "^1.5.2"
9990
9991uint64be@^2.0.2: 9953uint64be@^2.0.2:
9992 version "2.0.2" 9954 version "2.0.2"
9993 resolved "https://registry.yarnpkg.com/uint64be/-/uint64be-2.0.2.tgz#ef4a179752fe8f9ddaa29544ecfc13490031e8e5" 9955 resolved "https://registry.yarnpkg.com/uint64be/-/uint64be-2.0.2.tgz#ef4a179752fe8f9ddaa29544ecfc13490031e8e5"
@@ -10082,7 +10044,7 @@ url-parse@^1.4.3:
10082 querystringify "^2.0.0" 10044 querystringify "^2.0.0"
10083 requires-port "^1.0.0" 10045 requires-port "^1.0.0"
10084 10046
10085url-toolkit@^2.1.1, url-toolkit@^2.1.3: 10047url-toolkit@^2.1.1, url-toolkit@^2.1.3, url-toolkit@^2.1.6:
10086 version "2.1.6" 10048 version "2.1.6"
10087 resolved "https://registry.yarnpkg.com/url-toolkit/-/url-toolkit-2.1.6.tgz#6d03246499e519aad224c44044a4ae20544154f2" 10049 resolved "https://registry.yarnpkg.com/url-toolkit/-/url-toolkit-2.1.6.tgz#6d03246499e519aad224c44044a4ae20544154f2"
10088 integrity sha512-UaZ2+50am4HwrV2crR/JAf63Q4VvPYphe63WGeoJxeu8gmOm0qxPt+KsukfakPNrX9aymGNEkkaoICwn+OuvBw== 10050 integrity sha512-UaZ2+50am4HwrV2crR/JAf63Q4VvPYphe63WGeoJxeu8gmOm0qxPt+KsukfakPNrX9aymGNEkkaoICwn+OuvBw==
@@ -10233,6 +10195,14 @@ videojs-contextmenu-ui@^5.0.0:
10233 global "^4.3.2" 10195 global "^4.3.2"
10234 video.js "^6 || ^7" 10196 video.js "^6 || ^7"
10235 10197
10198videojs-contrib-quality-levels@^2.0.9:
10199 version "2.0.9"
10200 resolved "https://registry.yarnpkg.com/videojs-contrib-quality-levels/-/videojs-contrib-quality-levels-2.0.9.tgz#b5d533d5092a6fc7d29eae1b43e4597d89bd527b"
10201 integrity sha512-HJeaJJQdSufi9Y5T7jlyyhkeq+mWPCog86q6ypoTi66boBMMJTo2abiOSHS9KaOGAJjH72gfvrjVY5FRdjlxYA==
10202 dependencies:
10203 global "^4.3.2"
10204 video.js "^6 || ^7"
10205
10236videojs-dock@^2.0.2: 10206videojs-dock@^2.0.2:
10237 version "2.1.4" 10207 version "2.1.4"
10238 resolved "https://registry.yarnpkg.com/videojs-dock/-/videojs-dock-2.1.4.tgz#0ebd198b5d48990e3523fdc87dbfdb9fe96f804c" 10208 resolved "https://registry.yarnpkg.com/videojs-dock/-/videojs-dock-2.1.4.tgz#0ebd198b5d48990e3523fdc87dbfdb9fe96f804c"
@@ -10315,7 +10285,7 @@ watchpack@^1.5.0:
10315 graceful-fs "^4.1.2" 10285 graceful-fs "^4.1.2"
10316 neo-async "^2.5.0" 10286 neo-async "^2.5.0"
10317 10287
10318wbuf@^1.1.0, wbuf@^1.7.2: 10288wbuf@^1.1.0, wbuf@^1.7.3:
10319 version "1.7.3" 10289 version "1.7.3"
10320 resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" 10290 resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df"
10321 integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== 10291 integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==
@@ -10404,10 +10374,20 @@ webpack-dev-middleware@3.4.0:
10404 range-parser "^1.0.3" 10374 range-parser "^1.0.3"
10405 webpack-log "^2.0.0" 10375 webpack-log "^2.0.0"
10406 10376
10407webpack-dev-server@3.1.10: 10377webpack-dev-middleware@3.5.1:
10408 version "3.1.10" 10378 version "3.5.1"
10409 resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.1.10.tgz#507411bee727ee8d2fdffdc621b66a64ab3dea2b" 10379 resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.5.1.tgz#9265b7742ef50f54f54c1d9af022fc17c1be9b88"
10410 integrity sha512-RqOAVjfqZJtQcB0LmrzJ5y4Jp78lv9CK0MZ1YJDTaTmedMZ9PU9FLMQNrMCfVu8hHzaVLVOJKBlGEHMN10z+ww== 10380 integrity sha512-4dwCh/AyMOYAybggUr8fiCkRnjVDp+Cqlr9c+aaNB3GJYgRGYQWJ1YX/WAKUNA9dPNHZ6QSN2lYDKqjKSI8Vqw==
10381 dependencies:
10382 memory-fs "~0.4.1"
10383 mime "^2.3.1"
10384 range-parser "^1.0.3"
10385 webpack-log "^2.0.0"
10386
10387webpack-dev-server@3.1.14:
10388 version "3.1.14"
10389 resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.1.14.tgz#60fb229b997fc5a0a1fc6237421030180959d469"
10390 integrity sha512-mGXDgz5SlTxcF3hUpfC8hrQ11yhAttuUQWf1Wmb+6zo3x6rb7b9mIfuQvAPLdfDRCGRGvakBWHdHOa0I9p/EVQ==
10411 dependencies: 10391 dependencies:
10412 ansi-html "0.0.7" 10392 ansi-html "0.0.7"
10413 bonjour "^3.5.0" 10393 bonjour "^3.5.0"
@@ -10428,12 +10408,14 @@ webpack-dev-server@3.1.10:
10428 portfinder "^1.0.9" 10408 portfinder "^1.0.9"
10429 schema-utils "^1.0.0" 10409 schema-utils "^1.0.0"
10430 selfsigned "^1.9.1" 10410 selfsigned "^1.9.1"
10411 semver "^5.6.0"
10431 serve-index "^1.7.2" 10412 serve-index "^1.7.2"
10432 sockjs "0.3.19" 10413 sockjs "0.3.19"
10433 sockjs-client "1.3.0" 10414 sockjs-client "1.3.0"
10434 spdy "^3.4.1" 10415 spdy "^4.0.0"
10435 strip-ansi "^3.0.0" 10416 strip-ansi "^3.0.0"
10436 supports-color "^5.1.0" 10417 supports-color "^5.1.0"
10418 url "^0.11.0"
10437 webpack-dev-middleware "3.4.0" 10419 webpack-dev-middleware "3.4.0"
10438 webpack-log "^2.0.0" 10420 webpack-log "^2.0.0"
10439 yargs "12.0.2" 10421 yargs "12.0.2"
@@ -10456,21 +10438,13 @@ webpack-log@^2.0.0:
10456 ansi-colors "^3.0.0" 10438 ansi-colors "^3.0.0"
10457 uuid "^3.3.2" 10439 uuid "^3.3.2"
10458 10440
10459webpack-merge@4.1.4: 10441webpack-merge@4.2.1:
10460 version "4.1.4" 10442 version "4.2.1"
10461 resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.1.4.tgz#0fde38eabf2d5fd85251c24a5a8c48f8a3f4eb7b" 10443 resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.1.tgz#5e923cf802ea2ace4fd5af1d3247368a633489b4"
10462 integrity sha512-TmSe1HZKeOPey3oy1Ov2iS3guIZjWvMT2BBJDzzT5jScHTjVC3mpjJofgueEzaEd6ibhxRDD6MIblDr8tzh8iQ== 10444 integrity sha512-4p8WQyS98bUJcCvFMbdGZyZmsKuWjWVnVHnAS3FFg0HDaRVrPbkivx2RYCre8UiemD67RsiFFLfn4JhLAin8Vw==
10463 dependencies: 10445 dependencies:
10464 lodash "^4.17.5" 10446 lodash "^4.17.5"
10465 10447
10466webpack-sources@1.2.0:
10467 version "1.2.0"
10468 resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.2.0.tgz#18181e0d013fce096faf6f8e6d41eeffffdceac2"
10469 integrity sha512-9BZwxR85dNsjWz3blyxdOhTgtnQvv3OEs5xofI0wPYTwu5kaWxS08UuD1oI7WLBLpRO+ylf0ofnXLXWmGb2WMw==
10470 dependencies:
10471 source-list-map "^2.0.0"
10472 source-map "~0.6.1"
10473
10474webpack-sources@1.3.0, webpack-sources@^1.1.0, webpack-sources@^1.2.0, webpack-sources@^1.3.0: 10448webpack-sources@1.3.0, webpack-sources@^1.1.0, webpack-sources@^1.2.0, webpack-sources@^1.3.0:
10475 version "1.3.0" 10449 version "1.3.0"
10476 resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85" 10450 resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85"
@@ -10494,47 +10468,17 @@ webpack-subresource-integrity@1.1.0-rc.6:
10494 dependencies: 10468 dependencies:
10495 webpack-core "^0.6.8" 10469 webpack-core "^0.6.8"
10496 10470
10497webpack@4.23.1: 10471webpack@4.29.0:
10498 version "4.23.1" 10472 version "4.29.0"
10499 resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.23.1.tgz#db7467b116771ae020c58bdfe2a0822785bb8239" 10473 resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.29.0.tgz#f2cfef83f7ae404ba889ff5d43efd285ca26e750"
10500 integrity sha512-iE5Cu4rGEDk7ONRjisTOjVHv3dDtcFfwitSxT7evtYj/rANJpt1OuC/Kozh1pBa99AUBr1L/LsaNB+D9Xz3CEg== 10474 integrity sha512-pxdGG0keDBtamE1mNvT5zyBdx+7wkh6mh7uzMOo/uRQ/fhsdj5FXkh/j5mapzs060forql1oXqXN9HJGju+y7w==
10501 dependencies:
10502 "@webassemblyjs/ast" "1.7.10"
10503 "@webassemblyjs/helper-module-context" "1.7.10"
10504 "@webassemblyjs/wasm-edit" "1.7.10"
10505 "@webassemblyjs/wasm-parser" "1.7.10"
10506 acorn "^5.6.2"
10507 acorn-dynamic-import "^3.0.0"
10508 ajv "^6.1.0"
10509 ajv-keywords "^3.1.0"
10510 chrome-trace-event "^1.0.0"
10511 enhanced-resolve "^4.1.0"
10512 eslint-scope "^4.0.0"
10513 json-parse-better-errors "^1.0.2"
10514 loader-runner "^2.3.0"
10515 loader-utils "^1.1.0"
10516 memory-fs "~0.4.1"
10517 micromatch "^3.1.8"
10518 mkdirp "~0.5.0"
10519 neo-async "^2.5.0"
10520 node-libs-browser "^2.0.0"
10521 schema-utils "^0.4.4"
10522 tapable "^1.1.0"
10523 uglifyjs-webpack-plugin "^1.2.4"
10524 watchpack "^1.5.0"
10525 webpack-sources "^1.3.0"
10526
10527webpack@^4.17.1:
10528 version "4.26.1"
10529 resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.26.1.tgz#ff3a9283d363c07b3494dfa702d08f4f2ef6cb39"
10530 integrity sha512-i2oOvEvuvLLSuSCkdVrknaxAhtUZ9g+nLSoHCWV0gDzqGX2DXaCrMmMUpbRsTSSLrUqAI56PoEiyMUZIZ1msug==
10531 dependencies: 10475 dependencies:
10532 "@webassemblyjs/ast" "1.7.11" 10476 "@webassemblyjs/ast" "1.7.11"
10533 "@webassemblyjs/helper-module-context" "1.7.11" 10477 "@webassemblyjs/helper-module-context" "1.7.11"
10534 "@webassemblyjs/wasm-edit" "1.7.11" 10478 "@webassemblyjs/wasm-edit" "1.7.11"
10535 "@webassemblyjs/wasm-parser" "1.7.11" 10479 "@webassemblyjs/wasm-parser" "1.7.11"
10536 acorn "^5.6.2" 10480 acorn "^6.0.5"
10537 acorn-dynamic-import "^3.0.0" 10481 acorn-dynamic-import "^4.0.0"
10538 ajv "^6.1.0" 10482 ajv "^6.1.0"
10539 ajv-keywords "^3.1.0" 10483 ajv-keywords "^3.1.0"
10540 chrome-trace-event "^1.0.0" 10484 chrome-trace-event "^1.0.0"
@@ -10662,7 +10606,7 @@ which-module@^2.0.0:
10662 resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" 10606 resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
10663 integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= 10607 integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
10664 10608
10665which@1, which@^1.1.1, which@^1.2.1, which@^1.2.12, which@^1.2.9, which@^1.3.0, which@^1.3.1: 10609which@1, which@^1.2.1, which@^1.2.12, which@^1.2.9, which@^1.3.0, which@^1.3.1:
10666 version "1.3.1" 10610 version "1.3.1"
10667 resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" 10611 resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
10668 integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== 10612 integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
@@ -10676,16 +10620,16 @@ wide-align@^1.1.0:
10676 dependencies: 10620 dependencies:
10677 string-width "^1.0.2 || 2" 10621 string-width "^1.0.2 || 2"
10678 10622
10679wordwrap@^1.0.0, wordwrap@~1.0.0:
10680 version "1.0.0"
10681 resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
10682 integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=
10683
10684wordwrap@~0.0.2: 10623wordwrap@~0.0.2:
10685 version "0.0.3" 10624 version "0.0.3"
10686 resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" 10625 resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
10687 integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= 10626 integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc=
10688 10627
10628wordwrap@~1.0.0:
10629 version "1.0.0"
10630 resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
10631 integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=
10632
10689worker-farm@^1.5.2: 10633worker-farm@^1.5.2:
10690 version "1.6.0" 10634 version "1.6.0"
10691 resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.6.0.tgz#aecc405976fab5a95526180846f0dba288f3a4a0" 10635 resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.6.0.tgz#aecc405976fab5a95526180846f0dba288f3a4a0"