aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+videos
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/+videos')
-rw-r--r--client/src/app/+videos/+video-watch/modal/video-share.component.html232
-rw-r--r--client/src/app/+videos/+video-watch/modal/video-share.component.scss79
-rw-r--r--client/src/app/+videos/+video-watch/modal/video-share.component.ts152
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.html3
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.ts6
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.module.ts10
6 files changed, 8 insertions, 474 deletions
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
deleted file mode 100644
index 946e8d8ca..000000000
--- a/client/src/app/+videos/+video-watch/modal/video-share.component.html
+++ /dev/null
@@ -1,232 +0,0 @@
1<ng-template #modal let-hide="close">
2 <div class="modal-header">
3 <h4 i18n class="modal-title">Share</h4>
4 <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
5 </div>
6
7
8 <div class="modal-body">
9
10 <div class="playlist" *ngIf="hasPlaylist()">
11 <div class="title-page title-page-single" i18n>Share the playlist</div>
12
13 <div ngbNav #nav="ngbNav" class="nav-tabs" [(activeId)]="activePlaylistId">
14
15 <ng-container ngbNavItem="url">
16 <a ngbNavLink i18n>URL</a>
17
18 <ng-template ngbNavContent>
19 <div class="nav-content">
20
21 <my-input-readonly-copy [value]="getPlaylistUrl()"></my-input-readonly-copy>
22 </div>
23 </ng-template>
24 </ng-container>
25
26 <ng-container ngbNavItem="qrcode">
27 <a ngbNavLink i18n>QR-Code</a>
28
29 <ng-template ngbNavContent>
30 <div class="nav-content">
31 <qrcode [qrdata]="getPlaylistUrl()" [size]="256" level="Q"></qrcode>
32 </div>
33 </ng-template>
34 </ng-container>
35
36 <ng-container ngbNavItem="embed">
37 <a ngbNavLink i18n>Embed</a>
38
39 <ng-template ngbNavContent>
40 <div class="nav-content">
41 <my-input-readonly-copy [value]="getPlaylistIframeCode()"></my-input-readonly-copy>
42
43 <div i18n *ngIf="notSecure()" class="alert alert-warning">
44 The url is not secured (no HTTPS), so the embed video won't work on HTTPS websites (web browsers block non secured HTTP requests on HTTPS websites).
45 </div>
46 </div>
47 </ng-template>
48 </ng-container>
49
50 </div>
51
52 <div [ngbNavOutlet]="nav"></div>
53
54 <div class="filters">
55
56 <div class="form-group">
57 <my-peertube-checkbox inputName="includeVideoInPlaylist" [(ngModel)]="includeVideoInPlaylist" i18n-labelText
58 labelText="Share the playlist at this video position"></my-peertube-checkbox>
59 </div>
60
61 </div>
62 </div>
63
64
65 <div class="video">
66 <div class="title-page title-page-single" *ngIf="hasPlaylist()" i18n>Share the video</div>
67
68 <div ngbNav #nav="ngbNav" class="nav-tabs" [(activeId)]="activeVideoId">
69
70 <ng-container ngbNavItem="url">
71 <a ngbNavLink i18n>URL</a>
72
73 <ng-template ngbNavContent>
74 <div class="nav-content">
75 <my-input-readonly-copy [value]="getVideoUrl()"></my-input-readonly-copy>
76 </div>
77 </ng-template>
78 </ng-container>
79
80 <ng-container ngbNavItem="qrcode">
81 <a ngbNavLink i18n>QR-Code</a>
82
83 <ng-template ngbNavContent>
84 <div class="nav-content">
85 <qrcode [qrdata]="getVideoUrl()" [size]="256" level="Q"></qrcode>
86 </div>
87 </ng-template>
88 </ng-container>
89
90 <ng-container ngbNavItem="embed">
91 <a ngbNavLink i18n>Embed</a>
92
93 <ng-template ngbNavContent>
94 <div class="nav-content">
95 <my-input-readonly-copy [value]="getVideoIframeCode()"></my-input-readonly-copy>
96
97 <div i18n *ngIf="notSecure()" class="alert alert-warning">
98 The url is not secured (no HTTPS), so the embed video won't work on HTTPS websites (web browsers block non secured HTTP requests on HTTPS websites).
99 </div>
100 </div>
101 </ng-template>
102 </ng-container>
103
104 </div>
105
106 <div [ngbNavOutlet]="nav"></div>
107
108 <div class="filters">
109 <div>
110 <div class="form-group start-at">
111 <my-peertube-checkbox
112 inputName="startAt" [(ngModel)]="customizations.startAtCheckbox"
113 i18n-labelText labelText="Start at"
114 ></my-peertube-checkbox>
115
116 <my-timestamp-input
117 [timestamp]="customizations.startAt"
118 [maxTimestamp]="video.duration"
119 [disabled]="!customizations.startAtCheckbox"
120 [(ngModel)]="customizations.startAt"
121 >
122 </my-timestamp-input>
123 </div>
124
125 <div *ngIf="videoCaptions.length !== 0" class="form-group video-caption-block">
126 <my-peertube-checkbox
127 inputName="subtitleCheckbox" [(ngModel)]="customizations.subtitleCheckbox"
128 i18n-labelText labelText="Auto select subtitle"
129 ></my-peertube-checkbox>
130
131 <div class="peertube-select-container" [ngClass]="{ disabled: !customizations.subtitleCheckbox }">
132 <select [(ngModel)]="customizations.subtitle" [disabled]="!customizations.subtitleCheckbox">
133 <option *ngFor="let caption of videoCaptions" [value]="caption.language.id">{{ caption.language.label }}</option>
134 </select>
135 </div>
136 </div>
137 </div>
138
139 <div class="advanced-filters collapse-transition" [ngbCollapse]="isAdvancedCustomizationCollapsed">
140 <div>
141 <div class="form-group stop-at">
142 <my-peertube-checkbox
143 inputName="stopAt" [(ngModel)]="customizations.stopAtCheckbox"
144 i18n-labelText labelText="Stop at"
145 ></my-peertube-checkbox>
146
147 <my-timestamp-input
148 [timestamp]="customizations.stopAt"
149 [maxTimestamp]="video.duration"
150 [disabled]="!customizations.stopAtCheckbox"
151 [(ngModel)]="customizations.stopAt"
152 >
153 </my-timestamp-input>
154 </div>
155
156 <div class="form-group">
157 <my-peertube-checkbox
158 inputName="autoplay" [(ngModel)]="customizations.autoplay"
159 i18n-labelText labelText="Autoplay"
160 ></my-peertube-checkbox>
161 </div>
162
163 <div class="form-group">
164 <my-peertube-checkbox
165 inputName="muted" [(ngModel)]="customizations.muted"
166 i18n-labelText labelText="Muted"
167 ></my-peertube-checkbox>
168 </div>
169
170 <div class="form-group">
171 <my-peertube-checkbox
172 inputName="loop" [(ngModel)]="customizations.loop"
173 i18n-labelText labelText="Loop"
174 ></my-peertube-checkbox>
175 </div>
176 </div>
177
178 <ng-container *ngIf="isVideoInEmbedTab()">
179 <div class="form-group">
180 <my-peertube-checkbox
181 inputName="title" [(ngModel)]="customizations.title"
182 i18n-labelText labelText="Display video title"
183 ></my-peertube-checkbox>
184 </div>
185
186 <div class="form-group">
187 <my-peertube-checkbox
188 inputName="warningTitle" [(ngModel)]="customizations.warningTitle"
189 i18n-labelText labelText="Display privacy warning"
190 ></my-peertube-checkbox>
191 </div>
192
193 <div class="form-group">
194 <my-peertube-checkbox
195 inputName="controls" [(ngModel)]="customizations.controls"
196 i18n-labelText labelText="Display player controls"
197 ></my-peertube-checkbox>
198 </div>
199
200 <div class="form-group">
201 <my-peertube-checkbox
202 inputName="controls" [(ngModel)]="customizations.peertubeLink"
203 i18n-labelText labelText="Display PeerTube button link"
204 ></my-peertube-checkbox>
205 </div>
206 </ng-container>
207 </div>
208
209 <div (click)="isAdvancedCustomizationCollapsed = !isAdvancedCustomizationCollapsed" role="button" class="advanced-filters-button"
210 [attr.aria-expanded]="!isAdvancedCustomizationCollapsed" aria-controls="collapseBasic">
211
212 <ng-container *ngIf="isAdvancedCustomizationCollapsed">
213 <span class="glyphicon glyphicon-menu-down"></span>
214
215 <ng-container i18n>
216 More customization
217 </ng-container>
218 </ng-container>
219
220 <ng-container *ngIf="!isAdvancedCustomizationCollapsed">
221 <span class="glyphicon glyphicon-menu-up"></span>
222
223 <ng-container i18n>
224 Less customization
225 </ng-container>
226 </ng-container>
227 </div>
228 </div>
229 </div>
230 </div>
231
232</ng-template>
diff --git a/client/src/app/+videos/+video-watch/modal/video-share.component.scss b/client/src/app/+videos/+video-watch/modal/video-share.component.scss
deleted file mode 100644
index 091d4dc3b..000000000
--- a/client/src/app/+videos/+video-watch/modal/video-share.component.scss
+++ /dev/null
@@ -1,79 +0,0 @@
1@import '_mixins';
2@import '_variables';
3
4my-input-readonly-copy {
5 width: 100%;
6}
7
8.title-page.title-page-single {
9 margin-top: 0;
10}
11
12.playlist {
13 margin-bottom: 50px;
14}
15
16.peertube-select-container {
17 @include peertube-select-container(200px);
18}
19
20.qr-code-group {
21 text-align: center;
22}
23
24.nav-content {
25 margin-top: 30px;
26 display: flex;
27 justify-content: center;
28 align-items: center;
29 flex-direction: column;
30}
31
32.alert {
33 margin-top: 20px;
34}
35
36.filters {
37 margin-top: 30px;
38
39 .advanced-filters-button {
40 display: flex;
41 justify-content: center;
42 align-items: center;
43 margin-top: 20px;
44 font-size: 16px;
45 font-weight: $font-semibold;
46 cursor: pointer;
47
48 .glyphicon {
49 margin-right: 5px;
50 }
51 }
52
53 .form-group {
54 margin-bottom: 0;
55 height: 34px;
56 display: flex;
57 align-items: center;
58 }
59
60 .video-caption-block {
61 display: flex;
62 align-items: center;
63
64 .peertube-select-container {
65 margin-left: 10px;
66 }
67 }
68
69 .start-at,
70 .stop-at {
71 width: 300px;
72 display: flex;
73 align-items: center;
74
75 my-timestamp-input {
76 margin-left: 10px;
77 }
78 }
79}
diff --git a/client/src/app/+videos/+video-watch/modal/video-share.component.ts b/client/src/app/+videos/+video-watch/modal/video-share.component.ts
deleted file mode 100644
index d9171fe0e..000000000
--- a/client/src/app/+videos/+video-watch/modal/video-share.component.ts
+++ /dev/null
@@ -1,152 +0,0 @@
1import { Component, ElementRef, Input, ViewChild } from '@angular/core'
2import { buildVideoOrPlaylistEmbed, buildVideoLink, buildPlaylistLink } from '../../../../assets/player/utils'
3import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
4import { VideoCaption } from '@shared/models'
5import { VideoDetails } from '@app/shared/shared-main'
6import { VideoPlaylist } from '@app/shared/shared-video-playlist'
7
8type Customizations = {
9 startAtCheckbox: boolean
10 startAt: number
11
12 stopAtCheckbox: boolean
13 stopAt: number
14
15 subtitleCheckbox: boolean
16 subtitle: string
17
18 loop: boolean
19 autoplay: boolean
20 muted: boolean
21 title: boolean
22 warningTitle: boolean
23 controls: boolean
24 peertubeLink: boolean
25}
26
27type TabId = 'url' | 'qrcode' | 'embed'
28
29@Component({
30 selector: 'my-video-share',
31 templateUrl: './video-share.component.html',
32 styleUrls: [ './video-share.component.scss' ]
33})
34export class VideoShareComponent {
35 @ViewChild('modal', { static: true }) modal: ElementRef
36
37 @Input() video: VideoDetails = null
38 @Input() videoCaptions: VideoCaption[] = []
39 @Input() playlist: VideoPlaylist = null
40
41 activeVideoId: TabId = 'url'
42 activePlaylistId: TabId = 'url'
43
44 customizations: Customizations
45 isAdvancedCustomizationCollapsed = true
46 includeVideoInPlaylist = false
47
48 private playlistPosition: number = null
49
50 constructor (private modalService: NgbModal) { }
51
52 show (currentVideoTimestamp?: number, currentPlaylistPosition?: number) {
53 let subtitle: string
54 if (this.videoCaptions.length !== 0) {
55 subtitle = this.videoCaptions[0].language.id
56 }
57
58 this.customizations = {
59 startAtCheckbox: false,
60 startAt: currentVideoTimestamp ? Math.floor(currentVideoTimestamp) : 0,
61
62 stopAtCheckbox: false,
63 stopAt: this.video.duration,
64
65 subtitleCheckbox: false,
66 subtitle,
67
68 loop: false,
69 autoplay: false,
70 muted: false,
71
72 // Embed options
73 title: true,
74 warningTitle: true,
75 controls: true,
76 peertubeLink: true
77 }
78
79 this.playlistPosition = currentPlaylistPosition
80
81 this.modalService.open(this.modal, { centered: true })
82 }
83
84 getVideoIframeCode () {
85 const options = this.getVideoOptions(this.video.embedUrl)
86
87 const embedUrl = buildVideoLink(options)
88 return buildVideoOrPlaylistEmbed(embedUrl)
89 }
90
91 getPlaylistIframeCode () {
92 const options = this.getPlaylistOptions(this.playlist.embedUrl)
93
94 const embedUrl = buildPlaylistLink(options)
95 return buildVideoOrPlaylistEmbed(embedUrl)
96 }
97
98 getVideoUrl () {
99 const baseUrl = window.location.origin + '/videos/watch/' + this.video.uuid
100 const options = this.getVideoOptions(baseUrl)
101
102 return buildVideoLink(options)
103 }
104
105 getPlaylistUrl () {
106 const base = window.location.origin + '/videos/watch/playlist/' + this.playlist.uuid
107
108 if (!this.includeVideoInPlaylist) return base
109
110 return base + '?videoId=' + this.video.uuid
111 }
112
113 notSecure () {
114 return window.location.protocol === 'http:'
115 }
116
117 isVideoInEmbedTab () {
118 return this.activeVideoId === 'embed'
119 }
120
121 hasPlaylist () {
122 return !!this.playlist
123 }
124
125 private getPlaylistOptions (baseUrl?: string) {
126 return {
127 baseUrl,
128
129 playlistPosition: this.playlistPosition || undefined
130 }
131 }
132
133 private getVideoOptions (baseUrl?: string) {
134 return {
135 baseUrl,
136
137 startTime: this.customizations.startAtCheckbox ? this.customizations.startAt : undefined,
138 stopTime: this.customizations.stopAtCheckbox ? this.customizations.stopAt : undefined,
139
140 subtitle: this.customizations.subtitleCheckbox ? this.customizations.subtitle : undefined,
141
142 loop: this.customizations.loop,
143 autoplay: this.customizations.autoplay,
144 muted: this.customizations.muted,
145
146 title: this.customizations.title,
147 warningTitle: this.customizations.warningTitle,
148 controls: this.customizations.controls,
149 peertubeLink: this.customizations.peertubeLink
150 }
151 }
152}
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 2588b9af5..4279437d2 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.html
+++ b/client/src/app/+videos/+video-watch/video-watch.component.html
@@ -116,9 +116,10 @@
116 <my-global-icon iconName="download" aria-hidden="true"></my-global-icon> 116 <my-global-icon iconName="download" aria-hidden="true"></my-global-icon>
117 <span class="icon-text d-none d-sm-inline" i18n>DOWNLOAD</span> 117 <span class="icon-text d-none d-sm-inline" i18n>DOWNLOAD</span>
118 </button> 118 </button>
119 119
120 <my-video-download #videoDownloadModal></my-video-download> 120 <my-video-download #videoDownloadModal></my-video-download>
121 </ng-container> 121 </ng-container>
122
122 <ng-container *ngIf="isUserLoggedIn()"> 123 <ng-container *ngIf="isUserLoggedIn()">
123 <my-video-actions-dropdown 124 <my-video-actions-dropdown
124 placement="bottom auto" buttonDirection="horizontal" [buttonStyled]="true" [video]="video" [videoCaptions]="videoCaptions" 125 placement="bottom auto" buttonDirection="horizontal" [buttonStyled]="true" [video]="video" [videoCaptions]="videoCaptions"
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 33f998282..a53af210a 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/+videos/+video-watch/video-watch.component.ts
@@ -8,12 +8,14 @@ import { AuthService, AuthUser, ConfirmService, MarkdownService, Notifier, RestE
8import { HooksService } from '@app/core/plugins/hooks.service' 8import { HooksService } from '@app/core/plugins/hooks.service'
9import { RedirectService } from '@app/core/routing/redirect.service' 9import { RedirectService } from '@app/core/routing/redirect.service'
10import { isXPercentInViewport, scrollToTop } from '@app/helpers' 10import { isXPercentInViewport, scrollToTop } from '@app/helpers'
11import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
12import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main' 11import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main'
12import { VideoShareComponent } from '@app/shared/shared-share-modal'
13import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' 13import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
14import { VideoDownloadComponent } from '@app/shared/shared-video-miniature'
14import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' 15import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
15import { MetaService } from '@ngx-meta/core' 16import { MetaService } from '@ngx-meta/core'
16import { I18n } from '@ngx-translate/i18n-polyfill' 17import { I18n } from '@ngx-translate/i18n-polyfill'
18import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
17import { ServerConfig, UserVideoRateType, VideoCaption, VideoPrivacy, VideoState } from '@shared/models' 19import { ServerConfig, UserVideoRateType, VideoCaption, VideoPrivacy, VideoState } from '@shared/models'
18import { getStoredP2PEnabled, getStoredTheater } from '../../../assets/player/peertube-player-local-storage' 20import { getStoredP2PEnabled, getStoredTheater } from '../../../assets/player/peertube-player-local-storage'
19import { 21import {
@@ -26,10 +28,8 @@ import {
26} from '../../../assets/player/peertube-player-manager' 28} from '../../../assets/player/peertube-player-manager'
27import { isWebRTCDisabled, timeToInt } from '../../../assets/player/utils' 29import { isWebRTCDisabled, timeToInt } from '../../../assets/player/utils'
28import { environment } from '../../../environments/environment' 30import { environment } from '../../../environments/environment'
29import { VideoShareComponent } from './modal/video-share.component'
30import { VideoSupportComponent } from './modal/video-support.component' 31import { VideoSupportComponent } from './modal/video-support.component'
31import { VideoWatchPlaylistComponent } from './video-watch-playlist.component' 32import { VideoWatchPlaylistComponent } from './video-watch-playlist.component'
32import { VideoDownloadComponent } from '@app/shared/shared-video-miniature'
33 33
34@Component({ 34@Component({
35 selector: 'my-video-watch', 35 selector: 'my-video-watch',
diff --git a/client/src/app/+videos/+video-watch/video-watch.module.ts b/client/src/app/+videos/+video-watch/video-watch.module.ts
index 5821dc2b7..612bbccc4 100644
--- a/client/src/app/+videos/+video-watch/video-watch.module.ts
+++ b/client/src/app/+videos/+video-watch/video-watch.module.ts
@@ -1,19 +1,17 @@
1import { QRCodeModule } from 'angularx-qrcode'
2import { NgModule } from '@angular/core' 1import { NgModule } from '@angular/core'
3import { SharedFormModule } from '@app/shared/shared-forms' 2import { SharedFormModule } from '@app/shared/shared-forms'
4import { SharedGlobalIconModule } from '@app/shared/shared-icons' 3import { SharedGlobalIconModule } from '@app/shared/shared-icons'
5import { SharedMainModule } from '@app/shared/shared-main' 4import { SharedMainModule } from '@app/shared/shared-main'
6import { SharedModerationModule } from '@app/shared/shared-moderation' 5import { SharedModerationModule } from '@app/shared/shared-moderation'
6import { SharedShareModal } from '@app/shared/shared-share-modal'
7import { SharedUserSubscriptionModule } from '@app/shared/shared-user-subscription' 7import { SharedUserSubscriptionModule } from '@app/shared/shared-user-subscription'
8import { SharedVideoCommentModule } from '@app/shared/shared-video-comment' 8import { SharedVideoCommentModule } from '@app/shared/shared-video-comment'
9import { SharedVideoMiniatureModule } from '@app/shared/shared-video-miniature' 9import { SharedVideoMiniatureModule } from '@app/shared/shared-video-miniature'
10import { SharedVideoPlaylistModule } from '@app/shared/shared-video-playlist' 10import { SharedVideoPlaylistModule } from '@app/shared/shared-video-playlist'
11import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
12import { VideoCommentService } from '../../shared/shared-video-comment/video-comment.service' 11import { VideoCommentService } from '../../shared/shared-video-comment/video-comment.service'
13import { VideoCommentAddComponent } from './comment/video-comment-add.component' 12import { VideoCommentAddComponent } from './comment/video-comment-add.component'
14import { VideoCommentComponent } from './comment/video-comment.component' 13import { VideoCommentComponent } from './comment/video-comment.component'
15import { VideoCommentsComponent } from './comment/video-comments.component' 14import { VideoCommentsComponent } from './comment/video-comments.component'
16import { VideoShareComponent } from './modal/video-share.component'
17import { VideoSupportComponent } from './modal/video-support.component' 15import { VideoSupportComponent } from './modal/video-support.component'
18import { RecommendationsModule } from './recommendations/recommendations.module' 16import { RecommendationsModule } from './recommendations/recommendations.module'
19import { TimestampRouteTransformerDirective } from './timestamp-route-transformer.directive' 17import { TimestampRouteTransformerDirective } from './timestamp-route-transformer.directive'
@@ -25,8 +23,6 @@ import { VideoWatchComponent } from './video-watch.component'
25@NgModule({ 23@NgModule({
26 imports: [ 24 imports: [
27 VideoWatchRoutingModule, 25 VideoWatchRoutingModule,
28 NgbTooltipModule,
29 QRCodeModule,
30 RecommendationsModule, 26 RecommendationsModule,
31 27
32 SharedMainModule, 28 SharedMainModule,
@@ -36,14 +32,14 @@ import { VideoWatchComponent } from './video-watch.component'
36 SharedUserSubscriptionModule, 32 SharedUserSubscriptionModule,
37 SharedModerationModule, 33 SharedModerationModule,
38 SharedGlobalIconModule, 34 SharedGlobalIconModule,
39 SharedVideoCommentModule 35 SharedVideoCommentModule,
36 SharedShareModal
40 ], 37 ],
41 38
42 declarations: [ 39 declarations: [
43 VideoWatchComponent, 40 VideoWatchComponent,
44 VideoWatchPlaylistComponent, 41 VideoWatchPlaylistComponent,
45 42
46 VideoShareComponent,
47 VideoSupportComponent, 43 VideoSupportComponent,
48 VideoCommentsComponent, 44 VideoCommentsComponent,
49 VideoCommentAddComponent, 45 VideoCommentAddComponent,