aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/videos
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-07-12 19:02:00 +0200
committerChocobozzz <me@florianbigard.com>2018-07-16 11:50:08 +0200
commit40e87e9ecc54e3513fb586928330a7855eb192c6 (patch)
treeaf1111ecba85f9cd8286811ff332a67cf21be2f6 /client/src/app/videos
parentd4557fd3ecc8d4ed4fb0e5c868929bc36c959ed2 (diff)
downloadPeerTube-40e87e9ecc54e3513fb586928330a7855eb192c6.tar.gz
PeerTube-40e87e9ecc54e3513fb586928330a7855eb192c6.tar.zst
PeerTube-40e87e9ecc54e3513fb586928330a7855eb192c6.zip
Implement captions/subtitles
Diffstat (limited to 'client/src/app/videos')
-rw-r--r--client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.html47
-rw-r--r--client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.scss15
-rw-r--r--client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.ts80
-rw-r--r--client/src/app/videos/+video-edit/shared/video-edit.component.html32
-rw-r--r--client/src/app/videos/+video-edit/shared/video-edit.component.scss35
-rw-r--r--client/src/app/videos/+video-edit/shared/video-edit.component.ts53
-rw-r--r--client/src/app/videos/+video-edit/shared/video-edit.module.ts4
-rw-r--r--client/src/app/videos/+video-edit/shared/video-image.component.html15
-rw-r--r--client/src/app/videos/+video-edit/shared/video-image.component.scss10
-rw-r--r--client/src/app/videos/+video-edit/shared/video-image.component.ts26
-rw-r--r--client/src/app/videos/+video-edit/video-add.component.html2
-rw-r--r--client/src/app/videos/+video-edit/video-add.component.ts50
-rw-r--r--client/src/app/videos/+video-edit/video-update.component.html1
-rw-r--r--client/src/app/videos/+video-edit/video-update.component.ts48
14 files changed, 333 insertions, 85 deletions
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
new file mode 100644
index 000000000..9cd303b29
--- /dev/null
+++ b/client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.html
@@ -0,0 +1,47 @@
1<div bsModal #modal="bs-modal" class="modal" tabindex="-1">
2 <div class="modal-dialog">
3 <div class="modal-content" [formGroup]="form">
4
5 <div class="modal-header">
6 <span class="close" aria-hidden="true" (click)="hide()"></span>
7 <h4 i18n class="modal-title">Add caption</h4>
8 </div>
9
10 <div class="modal-body">
11 <label i18n for="language">Language</label>
12 <div class="peertube-select-container">
13 <select id="language" formControlName="language">
14 <option></option>
15 <option *ngFor="let language of videoCaptionLanguages" [value]="language.id">{{ language.label }}</option>
16 </select>
17 </div>
18
19 <div *ngIf="formErrors.language" class="form-error">
20 {{ formErrors.language }}
21 </div>
22
23 <div class="caption-file">
24 <my-reactive-file
25 formControlName="captionfile" inputName="captionfile" i18n-inputLabel inputLabel="Select the caption file"
26 [extensions]="videoCaptionExtensions" [maxFileSize]="videoCaptionMaxSize" [displayFilename]="true"
27 ></my-reactive-file>
28 </div>
29
30 <div *ngIf="isReplacingExistingCaption()" class="warning-replace-caption" i18n>
31 This will replace an existing caption!
32 </div>
33
34 <div class="form-group inputs">
35 <span i18n class="action-button action-button-cancel" (click)="hide()">
36 Cancel
37 </span>
38
39 <input
40 type="submit" i18n-value value="Add this caption" class="action-button-submit"
41 [disabled]="!form.valid" (click)="addCaption()"
42 >
43 </div>
44 </div>
45 </div>
46 </div>
47</div>
diff --git a/client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.scss b/client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.scss
new file mode 100644
index 000000000..c6da1877e
--- /dev/null
+++ b/client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.scss
@@ -0,0 +1,15 @@
1@import '_variables';
2@import '_mixins';
3
4.peertube-select-container {
5 @include peertube-select-container(auto);
6}
7
8.caption-file {
9 margin-top: 20px;
10}
11
12.warning-replace-caption {
13 color: red;
14 margin-top: 10px;
15} \ No newline at end of file
diff --git a/client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.ts b/client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.ts
new file mode 100644
index 000000000..45b8c71f8
--- /dev/null
+++ b/client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.ts
@@ -0,0 +1,80 @@
1import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'
2import { ModalDirective } from 'ngx-bootstrap/modal'
3import { FormReactive } from '@app/shared'
4import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
5import { VideoCaptionsValidatorsService } from '@app/shared/forms/form-validators/video-captions-validators.service'
6import { ServerService } from '@app/core'
7import { VideoCaptionEdit } from '@app/shared/video-caption/video-caption-edit.model'
8
9@Component({
10 selector: 'my-video-caption-add-modal',
11 styleUrls: [ './video-caption-add-modal.component.scss' ],
12 templateUrl: './video-caption-add-modal.component.html'
13})
14
15export class VideoCaptionAddModalComponent extends FormReactive implements OnInit {
16 @Input() existingCaptions: string[]
17
18 @Output() captionAdded = new EventEmitter<VideoCaptionEdit>()
19
20 @ViewChild('modal') modal: ModalDirective
21
22 videoCaptionLanguages = []
23
24 private closingModal = false
25
26 constructor (
27 protected formValidatorService: FormValidatorService,
28 private serverService: ServerService,
29 private videoCaptionsValidatorsService: VideoCaptionsValidatorsService
30 ) {
31 super()
32 }
33
34 get videoCaptionExtensions () {
35 return this.serverService.getConfig().videoCaption.file.extensions
36 }
37
38 get videoCaptionMaxSize () {
39 return this.serverService.getConfig().videoCaption.file.size.max
40 }
41
42 ngOnInit () {
43 this.videoCaptionLanguages = this.serverService.getVideoLanguages()
44
45 this.buildForm({
46 language: this.videoCaptionsValidatorsService.VIDEO_CAPTION_LANGUAGE,
47 captionfile: this.videoCaptionsValidatorsService.VIDEO_CAPTION_FILE
48 })
49 }
50
51 show () {
52 this.modal.show()
53 }
54
55 hide () {
56 this.modal.hide()
57 }
58
59 isReplacingExistingCaption () {
60 if (this.closingModal === true) return false
61
62 const languageId = this.form.value[ 'language' ]
63
64 return languageId && this.existingCaptions.indexOf(languageId) !== -1
65 }
66
67 async addCaption () {
68 this.closingModal = true
69
70 const languageId = this.form.value[ 'language' ]
71 const languageObject = this.videoCaptionLanguages.find(l => l.id === languageId)
72
73 this.captionAdded.emit({
74 language: languageObject,
75 captionfile: this.form.value['captionfile']
76 })
77
78 this.hide()
79 }
80}
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 447c5ab9b..14d5f3614 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
@@ -132,13 +132,39 @@
132 <label i18n for="waitTranscoding">Wait transcoding before publishing the video</label> 132 <label i18n for="waitTranscoding">Wait transcoding before publishing the video</label>
133 <my-help 133 <my-help
134 tooltipPlacement="top" helpType="custom" i18n-customHtml 134 tooltipPlacement="top" helpType="custom" i18n-customHtml
135 customHtml="If you decide to not wait transcoding before publishing the video, it can be unplayable until it transcoding ends." 135 customHtml="If you decide not to wait for transcoding before publishing the video, it could be unplayable until transcoding ends."
136 ></my-help> 136 ></my-help>
137 </div> 137 </div>
138 138
139 </div> 139 </div>
140 </tab> 140 </tab>
141 141
142 <tab i18n-heading heading="Captions">
143 <div class="col-md-12 captions">
144
145 <div class="captions-header">
146 <a (click)="openAddCaptionModal()" class="create-caption">
147 <span class="icon icon-add"></span>
148 <ng-container i18n>Add another caption</ng-container>
149 </a>
150 </div>
151
152 <div class="form-group" *ngFor="let videoCaption of videoCaptions">
153
154 <div class="caption-entry">
155 <div class="caption-entry-label">{{ videoCaption.language.label }}</div>
156
157 <span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Delete</span>
158 </div>
159 </div>
160
161 <div class="no-caption" *ngIf="videoCaptions?.length === 0">
162 No captions for now.
163 </div>
164
165 </div>
166 </tab>
167
142 <tab i18n-heading heading="Advanced settings"> 168 <tab i18n-heading heading="Advanced settings">
143 <div class="col-md-12 advanced-settings"> 169 <div class="col-md-12 advanced-settings">
144 <div class="form-group"> 170 <div class="form-group">
@@ -172,3 +198,7 @@
172 </tabset> 198 </tabset>
173 199
174</div> 200</div>
201
202<my-video-caption-add-modal
203 #videoCaptionAddModal [existingCaptions]="getExistingCaptions()" (captionAdded)="onCaptionAdded($event)"
204></my-video-caption-add-modal> \ No newline at end of file
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 061eca4a7..03b8359de 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
@@ -7,6 +7,7 @@
7 7
8.video-edit { 8.video-edit {
9 height: 100%; 9 height: 100%;
10 min-height: 300px;
10 11
11 .form-group { 12 .form-group {
12 margin-bottom: 25px; 13 margin-bottom: 25px;
@@ -49,6 +50,40 @@
49 } 50 }
50} 51}
51 52
53.captions {
54
55 .captions-header {
56 text-align: right;
57
58 .create-caption {
59 @include create-button('../../../../assets/images/global/add.svg');
60 }
61 }
62
63 .caption-entry {
64 display: flex;
65 height: 40px;
66 align-items: center;
67
68 .caption-entry-label {
69 font-size: 15px;
70 font-weight: bold;
71
72 margin-right: 20px;
73 }
74
75 .caption-entry-delete {
76 @include peertube-button;
77 @include grey-button;
78 }
79 }
80
81 .no-caption {
82 text-align: center;
83 font-size: 15px;
84 }
85}
86
52.submit-container { 87.submit-container {
53 text-align: right; 88 text-align: right;
54 89
diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.ts b/client/src/app/videos/+video-edit/shared/video-edit.component.ts
index 66eb6611a..9394d7dab 100644
--- a/client/src/app/videos/+video-edit/shared/video-edit.component.ts
+++ b/client/src/app/videos/+video-edit/shared/video-edit.component.ts
@@ -1,5 +1,5 @@
1import { Component, Input, OnInit } from '@angular/core' 1import { Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'
2import { FormGroup, ValidatorFn, Validators } from '@angular/forms' 2import { FormArray, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms'
3import { ActivatedRoute, Router } from '@angular/router' 3import { ActivatedRoute, Router } from '@angular/router'
4import { FormReactiveValidationMessages, VideoValidatorsService } from '@app/shared' 4import { FormReactiveValidationMessages, VideoValidatorsService } from '@app/shared'
5import { NotificationsService } from 'angular2-notifications' 5import { NotificationsService } from 'angular2-notifications'
@@ -8,6 +8,10 @@ import { VideoEdit } from '../../../shared/video/video-edit.model'
8import { map } from 'rxjs/operators' 8import { map } from 'rxjs/operators'
9import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' 9import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
10import { I18nPrimengCalendarService } from '@app/shared/i18n/i18n-primeng-calendar' 10import { I18nPrimengCalendarService } from '@app/shared/i18n/i18n-primeng-calendar'
11import { VideoCaptionService } from '@app/shared/video-caption'
12import { VideoCaptionAddModalComponent } from '@app/videos/+video-edit/shared/video-caption-add-modal.component'
13import { VideoCaptionEdit } from '@app/shared/video-caption/video-caption-edit.model'
14import { removeElementFromArray } from '@app/shared/misc/utils'
11 15
12@Component({ 16@Component({
13 selector: 'my-video-edit', 17 selector: 'my-video-edit',
@@ -15,13 +19,16 @@ import { I18nPrimengCalendarService } from '@app/shared/i18n/i18n-primeng-calend
15 templateUrl: './video-edit.component.html' 19 templateUrl: './video-edit.component.html'
16}) 20})
17 21
18export class VideoEditComponent implements OnInit { 22export class VideoEditComponent implements OnInit, OnDestroy {
19 @Input() form: FormGroup 23 @Input() form: FormGroup
20 @Input() formErrors: { [ id: string ]: string } = {} 24 @Input() formErrors: { [ id: string ]: string } = {}
21 @Input() validationMessages: FormReactiveValidationMessages = {} 25 @Input() validationMessages: FormReactiveValidationMessages = {}
22 @Input() videoPrivacies = [] 26 @Input() videoPrivacies = []
23 @Input() userVideoChannels: { id: number, label: string, support: string }[] = [] 27 @Input() userVideoChannels: { id: number, label: string, support: string }[] = []
24 @Input() schedulePublicationPossible = true 28 @Input() schedulePublicationPossible = true
29 @Input() videoCaptions: VideoCaptionEdit[] = []
30
31 @ViewChild('videoCaptionAddModal') videoCaptionAddModal: VideoCaptionAddModalComponent
25 32
26 // So that it can be accessed in the template 33 // So that it can be accessed in the template
27 readonly SPECIAL_SCHEDULED_PRIVACY = VideoEdit.SPECIAL_SCHEDULED_PRIVACY 34 readonly SPECIAL_SCHEDULED_PRIVACY = VideoEdit.SPECIAL_SCHEDULED_PRIVACY
@@ -41,9 +48,12 @@ export class VideoEditComponent implements OnInit {
41 calendarTimezone: string 48 calendarTimezone: string
42 calendarDateFormat: string 49 calendarDateFormat: string
43 50
51 private schedulerInterval
52
44 constructor ( 53 constructor (
45 private formValidatorService: FormValidatorService, 54 private formValidatorService: FormValidatorService,
46 private videoValidatorsService: VideoValidatorsService, 55 private videoValidatorsService: VideoValidatorsService,
56 private videoCaptionService: VideoCaptionService,
47 private route: ActivatedRoute, 57 private route: ActivatedRoute,
48 private router: Router, 58 private router: Router,
49 private notificationsService: NotificationsService, 59 private notificationsService: NotificationsService,
@@ -91,6 +101,13 @@ export class VideoEditComponent implements OnInit {
91 defaultValues 101 defaultValues
92 ) 102 )
93 103
104 this.form.addControl('captions', new FormArray([
105 new FormGroup({
106 language: new FormControl(),
107 captionfile: new FormControl()
108 })
109 ]))
110
94 this.trackChannelChange() 111 this.trackChannelChange()
95 this.trackPrivacyChange() 112 this.trackPrivacyChange()
96 } 113 }
@@ -102,7 +119,35 @@ export class VideoEditComponent implements OnInit {
102 this.videoLicences = this.serverService.getVideoLicences() 119 this.videoLicences = this.serverService.getVideoLicences()
103 this.videoLanguages = this.serverService.getVideoLanguages() 120 this.videoLanguages = this.serverService.getVideoLanguages()
104 121
105 setTimeout(() => this.minScheduledDate = new Date(), 1000 * 60) // Update every minute 122 this.schedulerInterval = setInterval(() => this.minScheduledDate = new Date(), 1000 * 60) // Update every minute
123 }
124
125 ngOnDestroy () {
126 if (this.schedulerInterval) clearInterval(this.schedulerInterval)
127 }
128
129 getExistingCaptions () {
130 return this.videoCaptions.map(c => c.language.id)
131 }
132
133 onCaptionAdded (caption: VideoCaptionEdit) {
134 this.videoCaptions.push(
135 Object.assign(caption, { action: 'CREATE' as 'CREATE' })
136 )
137 }
138
139 deleteCaption (caption: VideoCaptionEdit) {
140 // This caption is not on the server, just remove it from our array
141 if (caption.action === 'CREATE') {
142 removeElementFromArray(this.videoCaptions, caption)
143 return
144 }
145
146 caption.action = 'REMOVE' as 'REMOVE'
147 }
148
149 openAddCaptionModal () {
150 this.videoCaptionAddModal.show()
106 } 151 }
107 152
108 private trackPrivacyChange () { 153 private trackPrivacyChange () {
diff --git a/client/src/app/videos/+video-edit/shared/video-edit.module.ts b/client/src/app/videos/+video-edit/shared/video-edit.module.ts
index 6bf3e34b1..f6bd65fdc 100644
--- a/client/src/app/videos/+video-edit/shared/video-edit.module.ts
+++ b/client/src/app/videos/+video-edit/shared/video-edit.module.ts
@@ -5,6 +5,7 @@ import { SharedModule } from '../../../shared/'
5import { VideoEditComponent } from './video-edit.component' 5import { VideoEditComponent } from './video-edit.component'
6import { VideoImageComponent } from './video-image.component' 6import { VideoImageComponent } from './video-image.component'
7import { CalendarModule } from 'primeng/components/calendar/calendar' 7import { CalendarModule } from 'primeng/components/calendar/calendar'
8import { VideoCaptionAddModalComponent } from './video-caption-add-modal.component'
8 9
9@NgModule({ 10@NgModule({
10 imports: [ 11 imports: [
@@ -16,7 +17,8 @@ import { CalendarModule } from 'primeng/components/calendar/calendar'
16 17
17 declarations: [ 18 declarations: [
18 VideoEditComponent, 19 VideoEditComponent,
19 VideoImageComponent 20 VideoImageComponent,
21 VideoCaptionAddModalComponent
20 ], 22 ],
21 23
22 exports: [ 24 exports: [
diff --git a/client/src/app/videos/+video-edit/shared/video-image.component.html b/client/src/app/videos/+video-edit/shared/video-image.component.html
index e319d7ee7..c09c862c4 100644
--- a/client/src/app/videos/+video-edit/shared/video-image.component.html
+++ b/client/src/app/videos/+video-edit/shared/video-image.component.html
@@ -1,15 +1,8 @@
1<div class="root"> 1<div class="root">
2 <div> 2 <my-reactive-file
3 <div class="button-file"> 3 [inputName]="inputName" [inputLabel]="inputLabel" [extensions]="videoImageExtensions" [maxFileSize]="maxVideoImageSize"
4 <span>{{ inputLabel }}</span> 4 (fileChanged)="onFileChanged($event)"
5 <input 5 ></my-reactive-file>
6 type="file"
7 [name]="inputName" [id]="inputName" [accept]="videoImageExtensions"
8 (change)="fileChange($event)"
9 />
10 </div>
11 <div i18n class="image-constraints">(extensions: {{ videoImageExtensions }}, max size: {{ maxVideoImageSize | bytes }})</div>
12 </div>
13 6
14 <img *ngIf="imageSrc" [ngStyle]="{ width: previewWidth, height: previewHeight }" [src]="imageSrc" class="preview" /> 7 <img *ngIf="imageSrc" [ngStyle]="{ width: previewWidth, height: previewHeight }" [src]="imageSrc" class="preview" />
15 <div *ngIf="!imageSrc" [ngStyle]="{ width: previewWidth, height: previewHeight }" class="preview no-image"></div> 8 <div *ngIf="!imageSrc" [ngStyle]="{ width: previewWidth, height: previewHeight }" class="preview no-image"></div>
diff --git a/client/src/app/videos/+video-edit/shared/video-image.component.scss b/client/src/app/videos/+video-edit/shared/video-image.component.scss
index d4901e7ab..b63963bca 100644
--- a/client/src/app/videos/+video-edit/shared/video-image.component.scss
+++ b/client/src/app/videos/+video-edit/shared/video-image.component.scss
@@ -6,16 +6,6 @@
6 display: flex; 6 display: flex;
7 align-items: center; 7 align-items: center;
8 8
9 .button-file {
10 @include peertube-button-file(auto);
11
12 min-width: 190px;
13 }
14
15 .image-constraints {
16 font-size: 13px;
17 }
18
19 .preview { 9 .preview {
20 border: 2px solid grey; 10 border: 2px solid grey;
21 border-radius: 4px; 11 border-radius: 4px;
diff --git a/client/src/app/videos/+video-edit/shared/video-image.component.ts b/client/src/app/videos/+video-edit/shared/video-image.component.ts
index 25955baaa..a604cde90 100644
--- a/client/src/app/videos/+video-edit/shared/video-image.component.ts
+++ b/client/src/app/videos/+video-edit/shared/video-image.component.ts
@@ -2,8 +2,6 @@ import { Component, forwardRef, Input } from '@angular/core'
2import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' 2import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
3import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser' 3import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'
4import { ServerService } from '@app/core' 4import { ServerService } from '@app/core'
5import { NotificationsService } from 'angular2-notifications'
6import { I18n } from '@ngx-translate/i18n-polyfill'
7 5
8@Component({ 6@Component({
9 selector: 'my-video-image', 7 selector: 'my-video-image',
@@ -25,36 +23,26 @@ export class VideoImageComponent implements ControlValueAccessor {
25 23
26 imageSrc: SafeResourceUrl 24 imageSrc: SafeResourceUrl
27 25
28 private file: Blob 26 private file: File
29 27
30 constructor ( 28 constructor (
31 private sanitizer: DomSanitizer, 29 private sanitizer: DomSanitizer,
32 private serverService: ServerService, 30 private serverService: ServerService
33 private notificationsService: NotificationsService,
34 private i18n: I18n
35 ) {} 31 ) {}
36 32
37 get videoImageExtensions () { 33 get videoImageExtensions () {
38 return this.serverService.getConfig().video.image.extensions.join(',') 34 return this.serverService.getConfig().video.image.extensions
39 } 35 }
40 36
41 get maxVideoImageSize () { 37 get maxVideoImageSize () {
42 return this.serverService.getConfig().video.image.size.max 38 return this.serverService.getConfig().video.image.size.max
43 } 39 }
44 40
45 fileChange (event: any) { 41 onFileChanged (file: File) {
46 if (event.target.files && event.target.files.length) { 42 this.file = file
47 const [ file ] = event.target.files
48
49 if (file.size > this.maxVideoImageSize) {
50 this.notificationsService.error(this.i18n('Error'), this.i18n('This image is too large.'))
51 return
52 }
53 43
54 this.file = file 44 this.propagateChange(this.file)
55 this.propagateChange(this.file) 45 this.updatePreview()
56 this.updatePreview()
57 }
58 } 46 }
59 47
60 propagateChange = (_: any) => { /* empty */ } 48 propagateChange = (_: any) => { /* empty */ }
diff --git a/client/src/app/videos/+video-edit/video-add.component.html b/client/src/app/videos/+video-edit/video-add.component.html
index 7d9443209..9c2c01c65 100644
--- a/client/src/app/videos/+video-edit/video-add.component.html
+++ b/client/src/app/videos/+video-edit/video-add.component.html
@@ -46,7 +46,7 @@
46 <!-- Hidden because we want to load the component --> 46 <!-- Hidden because we want to load the component -->
47 <form [hidden]="!isUploadingVideo" novalidate [formGroup]="form"> 47 <form [hidden]="!isUploadingVideo" novalidate [formGroup]="form">
48 <my-video-edit 48 <my-video-edit
49 [form]="form" [formErrors]="formErrors" 49 [form]="form" [formErrors]="formErrors" [videoCaptions]="videoCaptions"
50 [validationMessages]="validationMessages" [videoPrivacies]="videoPrivacies" [userVideoChannels]="userVideoChannels" 50 [validationMessages]="validationMessages" [videoPrivacies]="videoPrivacies" [userVideoChannels]="userVideoChannels"
51 ></my-video-edit> 51 ></my-video-edit>
52 52
diff --git a/client/src/app/videos/+video-edit/video-add.component.ts b/client/src/app/videos/+video-edit/video-add.component.ts
index 7c4b6260b..8c30cedfb 100644
--- a/client/src/app/videos/+video-edit/video-add.component.ts
+++ b/client/src/app/videos/+video-edit/video-add.component.ts
@@ -15,6 +15,8 @@ import { VideoEdit } from '../../shared/video/video-edit.model'
15import { VideoService } from '../../shared/video/video.service' 15import { VideoService } from '../../shared/video/video.service'
16import { I18n } from '@ngx-translate/i18n-polyfill' 16import { I18n } from '@ngx-translate/i18n-polyfill'
17import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' 17import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
18import { switchMap } from 'rxjs/operators'
19import { VideoCaptionService } from '@app/shared/video-caption'
18 20
19@Component({ 21@Component({
20 selector: 'my-videos-add', 22 selector: 'my-videos-add',
@@ -46,6 +48,7 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy
46 videoPrivacies = [] 48 videoPrivacies = []
47 firstStepPrivacyId = 0 49 firstStepPrivacyId = 0
48 firstStepChannelId = 0 50 firstStepChannelId = 0
51 videoCaptions = []
49 52
50 constructor ( 53 constructor (
51 protected formValidatorService: FormValidatorService, 54 protected formValidatorService: FormValidatorService,
@@ -56,7 +59,8 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy
56 private serverService: ServerService, 59 private serverService: ServerService,
57 private videoService: VideoService, 60 private videoService: VideoService,
58 private loadingBar: LoadingBarService, 61 private loadingBar: LoadingBarService,
59 private i18n: I18n 62 private i18n: I18n,
63 private videoCaptionService: VideoCaptionService
60 ) { 64 ) {
61 super() 65 super()
62 } 66 }
@@ -159,11 +163,8 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy
159 let name: string 163 let name: string
160 164
161 // If the name of the file is very small, keep the extension 165 // If the name of the file is very small, keep the extension
162 if (nameWithoutExtension.length < 3) { 166 if (nameWithoutExtension.length < 3) name = videofile.name
163 name = videofile.name 167 else name = nameWithoutExtension
164 } else {
165 name = nameWithoutExtension
166 }
167 168
168 const privacy = this.firstStepPrivacyId.toString() 169 const privacy = this.firstStepPrivacyId.toString()
169 const nsfw = false 170 const nsfw = false
@@ -225,22 +226,25 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy
225 this.isUpdatingVideo = true 226 this.isUpdatingVideo = true
226 this.loadingBar.start() 227 this.loadingBar.start()
227 this.videoService.updateVideo(video) 228 this.videoService.updateVideo(video)
228 .subscribe( 229 .pipe(
229 () => { 230 // Then update captions
230 this.isUpdatingVideo = false 231 switchMap(() => this.videoCaptionService.updateCaptions(video.id, this.videoCaptions))
231 this.isUploadingVideo = false 232 )
232 this.loadingBar.complete() 233 .subscribe(
233 234 () => {
234 this.notificationsService.success(this.i18n('Success'), this.i18n('Video published.')) 235 this.isUpdatingVideo = false
235 this.router.navigate([ '/videos/watch', video.uuid ]) 236 this.isUploadingVideo = false
236 }, 237 this.loadingBar.complete()
237 238
238 err => { 239 this.notificationsService.success(this.i18n('Success'), this.i18n('Video published.'))
239 this.isUpdatingVideo = false 240 this.router.navigate([ '/videos/watch', video.uuid ])
240 this.notificationsService.error(this.i18n('Error'), err.message) 241 },
241 console.error(err) 242
242 } 243 err => {
243 ) 244 this.isUpdatingVideo = false
244 245 this.notificationsService.error(this.i18n('Error'), err.message)
246 console.error(err)
247 }
248 )
245 } 249 }
246} 250}
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 5cb16c8ab..9242c30a0 100644
--- a/client/src/app/videos/+video-edit/video-update.component.html
+++ b/client/src/app/videos/+video-edit/video-update.component.html
@@ -8,6 +8,7 @@
8 <my-video-edit 8 <my-video-edit
9 [form]="form" [formErrors]="formErrors" [schedulePublicationPossible]="schedulePublicationPossible" 9 [form]="form" [formErrors]="formErrors" [schedulePublicationPossible]="schedulePublicationPossible"
10 [validationMessages]="validationMessages" [videoPrivacies]="videoPrivacies" [userVideoChannels]="userVideoChannels" 10 [validationMessages]="validationMessages" [videoPrivacies]="videoPrivacies" [userVideoChannels]="userVideoChannels"
11 [videoCaptions]="videoCaptions"
11 ></my-video-edit> 12 ></my-video-edit>
12 13
13 <div class="submit-container"> 14 <div class="submit-container">
diff --git a/client/src/app/videos/+video-edit/video-update.component.ts b/client/src/app/videos/+video-edit/video-update.component.ts
index c4e6f44de..b67874401 100644
--- a/client/src/app/videos/+video-edit/video-update.component.ts
+++ b/client/src/app/videos/+video-edit/video-update.component.ts
@@ -12,6 +12,7 @@ import { VideoService } from '../../shared/video/video.service'
12import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' 12import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
13import { I18n } from '@ngx-translate/i18n-polyfill' 13import { I18n } from '@ngx-translate/i18n-polyfill'
14import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' 14import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
15import { VideoCaptionService } from '@app/shared/video-caption'
15 16
16@Component({ 17@Component({
17 selector: 'my-videos-update', 18 selector: 'my-videos-update',
@@ -25,6 +26,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
25 videoPrivacies = [] 26 videoPrivacies = []
26 userVideoChannels = [] 27 userVideoChannels = []
27 schedulePublicationPossible = false 28 schedulePublicationPossible = false
29 videoCaptions = []
28 30
29 constructor ( 31 constructor (
30 protected formValidatorService: FormValidatorService, 32 protected formValidatorService: FormValidatorService,
@@ -36,6 +38,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
36 private authService: AuthService, 38 private authService: AuthService,
37 private loadingBar: LoadingBarService, 39 private loadingBar: LoadingBarService,
38 private videoChannelService: VideoChannelService, 40 private videoChannelService: VideoChannelService,
41 private videoCaptionService: VideoCaptionService,
39 private i18n: I18n 42 private i18n: I18n
40 ) { 43 ) {
41 super() 44 super()
@@ -63,12 +66,21 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
63 map(videoChannels => videoChannels.map(c => ({ id: c.id, label: c.displayName, support: c.support }))), 66 map(videoChannels => videoChannels.map(c => ({ id: c.id, label: c.displayName, support: c.support }))),
64 map(videoChannels => ({ video, videoChannels })) 67 map(videoChannels => ({ video, videoChannels }))
65 ) 68 )
69 }),
70 switchMap(({ video, videoChannels }) => {
71 return this.videoCaptionService
72 .listCaptions(video.id)
73 .pipe(
74 map(result => result.data),
75 map(videoCaptions => ({ video, videoChannels, videoCaptions }))
76 )
66 }) 77 })
67 ) 78 )
68 .subscribe( 79 .subscribe(
69 ({ video, videoChannels }) => { 80 ({ video, videoChannels, videoCaptions }) => {
70 this.video = new VideoEdit(video) 81 this.video = new VideoEdit(video)
71 this.userVideoChannels = videoChannels 82 this.userVideoChannels = videoChannels
83 this.videoCaptions = videoCaptions
72 84
73 // We cannot set private a video that was not private 85 // We cannot set private a video that was not private
74 if (this.video.privacy !== VideoPrivacy.PRIVATE) { 86 if (this.video.privacy !== VideoPrivacy.PRIVATE) {
@@ -102,21 +114,27 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
102 114
103 this.loadingBar.start() 115 this.loadingBar.start()
104 this.isUpdatingVideo = true 116 this.isUpdatingVideo = true
117
118 // Update the video
105 this.videoService.updateVideo(this.video) 119 this.videoService.updateVideo(this.video)
106 .subscribe( 120 .pipe(
107 () => { 121 // Then update captions
108 this.isUpdatingVideo = false 122 switchMap(() => this.videoCaptionService.updateCaptions(this.video.id, this.videoCaptions))
109 this.loadingBar.complete() 123 )
110 this.notificationsService.success(this.i18n('Success'), this.i18n('Video updated.')) 124 .subscribe(
111 this.router.navigate([ '/videos/watch', this.video.uuid ]) 125 () => {
112 }, 126 this.isUpdatingVideo = false
113 127 this.loadingBar.complete()
114 err => { 128 this.notificationsService.success(this.i18n('Success'), this.i18n('Video updated.'))
115 this.isUpdatingVideo = false 129 this.router.navigate([ '/videos/watch', this.video.uuid ])
116 this.notificationsService.error(this.i18n('Error'), err.message) 130 },
117 console.error(err) 131
118 } 132 err => {
119 ) 133 this.isUpdatingVideo = false
134 this.notificationsService.error(this.i18n('Error'), err.message)
135 console.error(err)
136 }
137 )
120 138
121 } 139 }
122 140