diff options
author | Chocobozzz <me@florianbigard.com> | 2018-06-12 20:04:58 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-06-12 20:37:51 +0200 |
commit | 2186386cca113506791583cb07d6ccacba7af4e0 (patch) | |
tree | 3c214c0b5fbd64332624267fa6e51fd4a9cf6474 /client | |
parent | 6ccdf3a23ecec5ba2eeaf487fd1fafdc7606b4bf (diff) | |
download | PeerTube-2186386cca113506791583cb07d6ccacba7af4e0.tar.gz PeerTube-2186386cca113506791583cb07d6ccacba7af4e0.tar.zst PeerTube-2186386cca113506791583cb07d6ccacba7af4e0.zip |
Add concept of video state, and add ability to wait transcoding before
publishing a video
Diffstat (limited to 'client')
12 files changed, 189 insertions, 121 deletions
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 35a99d0b3..eb24de7a7 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 | |||
@@ -18,7 +18,7 @@ | |||
18 | <div class="video-info"> | 18 | <div class="video-info"> |
19 | <a class="video-info-name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name">{{ video.name }}</a> | 19 | <a class="video-info-name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name">{{ video.name }}</a> |
20 | <span i18n class="video-info-date-views">{{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span> | 20 | <span i18n class="video-info-date-views">{{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span> |
21 | <div class="video-info-private">{{ video.privacy.label }}</div> | 21 | <div class="video-info-private">{{ video.privacy.label }} - {{ getStateLabel(video) }}</div> |
22 | </div> | 22 | </div> |
23 | 23 | ||
24 | <!-- Display only once --> | 24 | <!-- Display only once --> |
diff --git a/client/src/app/+my-account/my-account-videos/my-account-videos.component.ts b/client/src/app/+my-account/my-account-videos/my-account-videos.component.ts index eed4be01f..afc01073c 100644 --- a/client/src/app/+my-account/my-account-videos/my-account-videos.component.ts +++ b/client/src/app/+my-account/my-account-videos/my-account-videos.component.ts | |||
@@ -12,6 +12,7 @@ import { AbstractVideoList } from '../../shared/video/abstract-video-list' | |||
12 | import { Video } from '../../shared/video/video.model' | 12 | import { Video } from '../../shared/video/video.model' |
13 | import { VideoService } from '../../shared/video/video.service' | 13 | import { VideoService } from '../../shared/video/video.service' |
14 | import { I18n } from '@ngx-translate/i18n-polyfill' | 14 | import { I18n } from '@ngx-translate/i18n-polyfill' |
15 | import { VideoState } from '../../../../../shared/models/videos' | ||
15 | 16 | ||
16 | @Component({ | 17 | @Component({ |
17 | selector: 'my-account-videos', | 18 | selector: 'my-account-videos', |
@@ -59,7 +60,7 @@ export class MyAccountVideosComponent extends AbstractVideoList implements OnIni | |||
59 | } | 60 | } |
60 | 61 | ||
61 | isInSelectionMode () { | 62 | isInSelectionMode () { |
62 | return Object.keys(this.checkedVideos).some(k => this.checkedVideos[k] === true) | 63 | return Object.keys(this.checkedVideos).some(k => this.checkedVideos[ k ] === true) |
63 | } | 64 | } |
64 | 65 | ||
65 | getVideosObservable (page: number) { | 66 | getVideosObservable (page: number) { |
@@ -74,47 +75,68 @@ export class MyAccountVideosComponent extends AbstractVideoList implements OnIni | |||
74 | 75 | ||
75 | async deleteSelectedVideos () { | 76 | async deleteSelectedVideos () { |
76 | const toDeleteVideosIds = Object.keys(this.checkedVideos) | 77 | const toDeleteVideosIds = Object.keys(this.checkedVideos) |
77 | .filter(k => this.checkedVideos[k] === true) | 78 | .filter(k => this.checkedVideos[ k ] === true) |
78 | .map(k => parseInt(k, 10)) | 79 | .map(k => parseInt(k, 10)) |
79 | 80 | ||
80 | const res = await this.confirmService.confirm(`Do you really want to delete ${toDeleteVideosIds.length} videos?`, 'Delete') | 81 | const res = await this.confirmService.confirm( |
82 | this.i18n('Do you really want to delete {{deleteLength}} videos?', { deleteLength: toDeleteVideosIds.length }), | ||
83 | this.i18n('Delete') | ||
84 | ) | ||
81 | if (res === false) return | 85 | if (res === false) return |
82 | 86 | ||
83 | const observables: Observable<any>[] = [] | 87 | const observables: Observable<any>[] = [] |
84 | for (const videoId of toDeleteVideosIds) { | 88 | for (const videoId of toDeleteVideosIds) { |
85 | const o = this.videoService | 89 | const o = this.videoService.removeVideo(videoId) |
86 | .removeVideo(videoId) | ||
87 | .pipe(tap(() => this.spliceVideosById(videoId))) | 90 | .pipe(tap(() => this.spliceVideosById(videoId))) |
88 | 91 | ||
89 | observables.push(o) | 92 | observables.push(o) |
90 | } | 93 | } |
91 | 94 | ||
92 | observableFrom(observables).pipe( | 95 | observableFrom(observables) |
93 | concatAll()) | 96 | .pipe(concatAll()) |
94 | .subscribe( | 97 | .subscribe( |
95 | res => { | 98 | res => { |
96 | this.notificationsService.success('Success', `${toDeleteVideosIds.length} videos deleted.`) | 99 | this.notificationsService.success( |
100 | this.i18n('Success'), | ||
101 | this.i18n('{{deleteLength}} videos deleted.', { deleteLength: toDeleteVideosIds.length }) | ||
102 | ) | ||
103 | |||
97 | this.abortSelectionMode() | 104 | this.abortSelectionMode() |
98 | this.reloadVideos() | 105 | this.reloadVideos() |
99 | }, | 106 | }, |
100 | 107 | ||
101 | err => this.notificationsService.error('Error', err.message) | 108 | err => this.notificationsService.error(this.i18n('Error'), err.message) |
102 | ) | 109 | ) |
103 | } | 110 | } |
104 | 111 | ||
105 | async deleteVideo (video: Video) { | 112 | async deleteVideo (video: Video) { |
106 | const res = await this.confirmService.confirm(`Do you really want to delete ${video.name}?`, 'Delete') | 113 | const res = await this.confirmService.confirm( |
114 | this.i18n('Do you really want to delete {{videoName}}?', { videoName: video.name }), | ||
115 | this.i18n('Delete') | ||
116 | ) | ||
107 | if (res === false) return | 117 | if (res === false) return |
108 | 118 | ||
109 | this.videoService.removeVideo(video.id) | 119 | this.videoService.removeVideo(video.id) |
110 | .subscribe( | 120 | .subscribe( |
111 | status => { | 121 | status => { |
112 | this.notificationsService.success('Success', `Video ${video.name} deleted.`) | 122 | this.notificationsService.success( |
113 | this.reloadVideos() | 123 | this.i18n('Success'), |
114 | }, | 124 | this.i18n('Video {{videoName}} deleted.', { videoName: video.name }) |
125 | ) | ||
126 | this.reloadVideos() | ||
127 | }, | ||
128 | |||
129 | error => this.notificationsService.error(this.i18n('Error'), error.message) | ||
130 | ) | ||
131 | } | ||
115 | 132 | ||
116 | error => this.notificationsService.error('Error', error.message) | 133 | getStateLabel (video: Video) { |
117 | ) | 134 | if (video.state.id === VideoState.PUBLISHED) return this.i18n('Published') |
135 | |||
136 | if (video.state.id === VideoState.TO_TRANSCODE && video.waitTranscoding === true) return this.i18n('Waiting transcoding') | ||
137 | if (video.state.id === VideoState.TO_TRANSCODE) return this.i18n('To transcode') | ||
138 | |||
139 | return this.i18n('Unknown state') | ||
118 | } | 140 | } |
119 | 141 | ||
120 | protected buildVideoHeight () { | 142 | protected buildVideoHeight () { |
@@ -124,7 +146,7 @@ export class MyAccountVideosComponent extends AbstractVideoList implements OnIni | |||
124 | 146 | ||
125 | private spliceVideosById (id: number) { | 147 | private spliceVideosById (id: number) { |
126 | for (const key of Object.keys(this.loadedPages)) { | 148 | for (const key of Object.keys(this.loadedPages)) { |
127 | const videos = this.loadedPages[key] | 149 | const videos = this.loadedPages[ key ] |
128 | const index = videos.findIndex(v => v.id === id) | 150 | const index = videos.findIndex(v => v.id === id) |
129 | 151 | ||
130 | if (index !== -1) { | 152 | if (index !== -1) { |
diff --git a/client/src/app/shared/video/video-details.model.ts b/client/src/app/shared/video/video-details.model.ts index 19c350ab3..e500ad6fc 100644 --- a/client/src/app/shared/video/video-details.model.ts +++ b/client/src/app/shared/video/video-details.model.ts | |||
@@ -1,4 +1,11 @@ | |||
1 | import { UserRight, VideoChannel, VideoDetails as VideoDetailsServerModel, VideoFile } from '../../../../../shared' | 1 | import { |
2 | UserRight, | ||
3 | VideoChannel, | ||
4 | VideoConstant, | ||
5 | VideoDetails as VideoDetailsServerModel, | ||
6 | VideoFile, | ||
7 | VideoState | ||
8 | } from '../../../../../shared' | ||
2 | import { AuthUser } from '../../core' | 9 | import { AuthUser } from '../../core' |
3 | import { Video } from '../../shared/video/video.model' | 10 | import { Video } from '../../shared/video/video.model' |
4 | import { Account } from '@app/shared/account/account.model' | 11 | import { Account } from '@app/shared/account/account.model' |
@@ -12,6 +19,9 @@ export class VideoDetails extends Video implements VideoDetailsServerModel { | |||
12 | account: Account | 19 | account: Account |
13 | commentsEnabled: boolean | 20 | commentsEnabled: boolean |
14 | 21 | ||
22 | waitTranscoding: boolean | ||
23 | state: VideoConstant<VideoState> | ||
24 | |||
15 | likesPercent: number | 25 | likesPercent: number |
16 | dislikesPercent: number | 26 | dislikesPercent: number |
17 | 27 | ||
diff --git a/client/src/app/shared/video/video-edit.model.ts b/client/src/app/shared/video/video-edit.model.ts index ad2929db5..f045a3acd 100644 --- a/client/src/app/shared/video/video-edit.model.ts +++ b/client/src/app/shared/video/video-edit.model.ts | |||
@@ -1,7 +1,8 @@ | |||
1 | import { VideoDetails } from './video-details.model' | 1 | import { VideoDetails } from './video-details.model' |
2 | import { VideoPrivacy } from '../../../../../shared/models/videos/video-privacy.enum' | 2 | import { VideoPrivacy } from '../../../../../shared/models/videos/video-privacy.enum' |
3 | import { VideoUpdate } from '../../../../../shared/models/videos' | ||
3 | 4 | ||
4 | export class VideoEdit { | 5 | export class VideoEdit implements VideoUpdate { |
5 | category: number | 6 | category: number |
6 | licence: number | 7 | licence: number |
7 | language: string | 8 | language: string |
@@ -10,6 +11,7 @@ export class VideoEdit { | |||
10 | tags: string[] | 11 | tags: string[] |
11 | nsfw: boolean | 12 | nsfw: boolean |
12 | commentsEnabled: boolean | 13 | commentsEnabled: boolean |
14 | waitTranscoding: boolean | ||
13 | channelId: number | 15 | channelId: number |
14 | privacy: VideoPrivacy | 16 | privacy: VideoPrivacy |
15 | support: string | 17 | support: string |
@@ -32,6 +34,7 @@ export class VideoEdit { | |||
32 | this.tags = videoDetails.tags | 34 | this.tags = videoDetails.tags |
33 | this.nsfw = videoDetails.nsfw | 35 | this.nsfw = videoDetails.nsfw |
34 | this.commentsEnabled = videoDetails.commentsEnabled | 36 | this.commentsEnabled = videoDetails.commentsEnabled |
37 | this.waitTranscoding = videoDetails.waitTranscoding | ||
35 | this.channelId = videoDetails.channel.id | 38 | this.channelId = videoDetails.channel.id |
36 | this.privacy = videoDetails.privacy.id | 39 | this.privacy = videoDetails.privacy.id |
37 | this.support = videoDetails.support | 40 | this.support = videoDetails.support |
@@ -42,7 +45,7 @@ export class VideoEdit { | |||
42 | 45 | ||
43 | patch (values: Object) { | 46 | patch (values: Object) { |
44 | Object.keys(values).forEach((key) => { | 47 | Object.keys(values).forEach((key) => { |
45 | this[key] = values[key] | 48 | this[ key ] = values[ key ] |
46 | }) | 49 | }) |
47 | } | 50 | } |
48 | 51 | ||
@@ -57,6 +60,7 @@ export class VideoEdit { | |||
57 | tags: this.tags, | 60 | tags: this.tags, |
58 | nsfw: this.nsfw, | 61 | nsfw: this.nsfw, |
59 | commentsEnabled: this.commentsEnabled, | 62 | commentsEnabled: this.commentsEnabled, |
63 | waitTranscoding: this.waitTranscoding, | ||
60 | channelId: this.channelId, | 64 | channelId: this.channelId, |
61 | privacy: this.privacy | 65 | privacy: this.privacy |
62 | } | 66 | } |
diff --git a/client/src/app/shared/video/video.model.ts b/client/src/app/shared/video/video.model.ts index d37dc2c3e..48a4b4260 100644 --- a/client/src/app/shared/video/video.model.ts +++ b/client/src/app/shared/video/video.model.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { User } from '../' | 1 | import { User } from '../' |
2 | import { Video as VideoServerModel, VideoPrivacy } from '../../../../../shared' | 2 | import { Video as VideoServerModel, VideoPrivacy, VideoState } from '../../../../../shared' |
3 | import { Avatar } from '../../../../../shared/models/avatars/avatar.model' | 3 | import { Avatar } from '../../../../../shared/models/avatars/avatar.model' |
4 | import { VideoConstant } from '../../../../../shared/models/videos/video.model' | 4 | import { VideoConstant } from '../../../../../shared/models/videos/video.model' |
5 | import { getAbsoluteAPIUrl } from '../misc/utils' | 5 | import { getAbsoluteAPIUrl } from '../misc/utils' |
@@ -36,6 +36,9 @@ export class Video implements VideoServerModel { | |||
36 | dislikes: number | 36 | dislikes: number |
37 | nsfw: boolean | 37 | nsfw: boolean |
38 | 38 | ||
39 | waitTranscoding?: boolean | ||
40 | state?: VideoConstant<VideoState> | ||
41 | |||
39 | account: { | 42 | account: { |
40 | id: number | 43 | id: number |
41 | uuid: string | 44 | uuid: string |
@@ -58,15 +61,14 @@ export class Video implements VideoServerModel { | |||
58 | 61 | ||
59 | private static createDurationString (duration: number) { | 62 | private static createDurationString (duration: number) { |
60 | const hours = Math.floor(duration / 3600) | 63 | const hours = Math.floor(duration / 3600) |
61 | const minutes = Math.floor(duration % 3600 / 60) | 64 | const minutes = Math.floor((duration % 3600) / 60) |
62 | const seconds = duration % 60 | 65 | const seconds = duration % 60 |
63 | 66 | ||
64 | const minutesPadding = minutes >= 10 ? '' : '0' | 67 | const minutesPadding = minutes >= 10 ? '' : '0' |
65 | const secondsPadding = seconds >= 10 ? '' : '0' | 68 | const secondsPadding = seconds >= 10 ? '' : '0' |
66 | const displayedHours = hours > 0 ? hours.toString() + ':' : '' | 69 | const displayedHours = hours > 0 ? hours.toString() + ':' : '' |
67 | 70 | ||
68 | return displayedHours + minutesPadding + | 71 | return displayedHours + minutesPadding + minutes.toString() + ':' + secondsPadding + seconds.toString() |
69 | minutes.toString() + ':' + secondsPadding + seconds.toString() | ||
70 | } | 72 | } |
71 | 73 | ||
72 | constructor (hash: VideoServerModel, translations = {}) { | 74 | constructor (hash: VideoServerModel, translations = {}) { |
@@ -78,6 +80,8 @@ export class Video implements VideoServerModel { | |||
78 | this.licence = hash.licence | 80 | this.licence = hash.licence |
79 | this.language = hash.language | 81 | this.language = hash.language |
80 | this.privacy = hash.privacy | 82 | this.privacy = hash.privacy |
83 | this.waitTranscoding = hash.waitTranscoding | ||
84 | this.state = hash.state | ||
81 | this.description = hash.description | 85 | this.description = hash.description |
82 | this.duration = hash.duration | 86 | this.duration = hash.duration |
83 | this.durationLabel = Video.createDurationString(hash.duration) | 87 | this.durationLabel = Video.createDurationString(hash.duration) |
@@ -104,6 +108,8 @@ export class Video implements VideoServerModel { | |||
104 | this.licence.label = peertubeTranslate(this.licence.label, translations) | 108 | this.licence.label = peertubeTranslate(this.licence.label, translations) |
105 | this.language.label = peertubeTranslate(this.language.label, translations) | 109 | this.language.label = peertubeTranslate(this.language.label, translations) |
106 | this.privacy.label = peertubeTranslate(this.privacy.label, translations) | 110 | this.privacy.label = peertubeTranslate(this.privacy.label, translations) |
111 | |||
112 | if (this.state) this.state.label = peertubeTranslate(this.state.label, translations) | ||
107 | } | 113 | } |
108 | 114 | ||
109 | isVideoNSFWForUser (user: User, serverConfig: ServerConfig) { | 115 | isVideoNSFWForUser (user: User, serverConfig: ServerConfig) { |
diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts index 58cb52efc..d63915ad2 100644 --- a/client/src/app/shared/video/video.service.ts +++ b/client/src/app/shared/video/video.service.ts | |||
@@ -80,6 +80,7 @@ export class VideoService { | |||
80 | privacy: video.privacy, | 80 | privacy: video.privacy, |
81 | tags: video.tags, | 81 | tags: video.tags, |
82 | nsfw: video.nsfw, | 82 | nsfw: video.nsfw, |
83 | waitTranscoding: video.waitTranscoding, | ||
83 | commentsEnabled: video.commentsEnabled, | 84 | commentsEnabled: video.commentsEnabled, |
84 | thumbnailfile: video.thumbnailfile, | 85 | thumbnailfile: video.thumbnailfile, |
85 | previewfile: video.previewfile | 86 | previewfile: video.previewfile |
@@ -98,11 +99,11 @@ export class VideoService { | |||
98 | const req = new HttpRequest('POST', VideoService.BASE_VIDEO_URL + 'upload', video, { reportProgress: true }) | 99 | const req = new HttpRequest('POST', VideoService.BASE_VIDEO_URL + 'upload', video, { reportProgress: true }) |
99 | 100 | ||
100 | return this.authHttp | 101 | return this.authHttp |
101 | .request<{ video: { id: number, uuid: string} }>(req) | 102 | .request<{ video: { id: number, uuid: string } }>(req) |
102 | .pipe(catchError(this.restExtractor.handleError)) | 103 | .pipe(catchError(this.restExtractor.handleError)) |
103 | } | 104 | } |
104 | 105 | ||
105 | getMyVideos (videoPagination: ComponentPagination, sort: VideoSortField): Observable<{ videos: Video[], totalVideos: number}> { | 106 | getMyVideos (videoPagination: ComponentPagination, sort: VideoSortField): Observable<{ videos: Video[], totalVideos: number }> { |
106 | const pagination = this.restService.componentPaginationToRestPagination(videoPagination) | 107 | const pagination = this.restService.componentPaginationToRestPagination(videoPagination) |
107 | 108 | ||
108 | let params = new HttpParams() | 109 | let params = new HttpParams() |
@@ -120,7 +121,7 @@ export class VideoService { | |||
120 | account: Account, | 121 | account: Account, |
121 | videoPagination: ComponentPagination, | 122 | videoPagination: ComponentPagination, |
122 | sort: VideoSortField | 123 | sort: VideoSortField |
123 | ): Observable<{ videos: Video[], totalVideos: number}> { | 124 | ): Observable<{ videos: Video[], totalVideos: number }> { |
124 | const pagination = this.restService.componentPaginationToRestPagination(videoPagination) | 125 | const pagination = this.restService.componentPaginationToRestPagination(videoPagination) |
125 | 126 | ||
126 | let params = new HttpParams() | 127 | let params = new HttpParams() |
@@ -138,7 +139,7 @@ export class VideoService { | |||
138 | videoChannel: VideoChannel, | 139 | videoChannel: VideoChannel, |
139 | videoPagination: ComponentPagination, | 140 | videoPagination: ComponentPagination, |
140 | sort: VideoSortField | 141 | sort: VideoSortField |
141 | ): Observable<{ videos: Video[], totalVideos: number}> { | 142 | ): Observable<{ videos: Video[], totalVideos: number }> { |
142 | const pagination = this.restService.componentPaginationToRestPagination(videoPagination) | 143 | const pagination = this.restService.componentPaginationToRestPagination(videoPagination) |
143 | 144 | ||
144 | let params = new HttpParams() | 145 | let params = new HttpParams() |
@@ -156,7 +157,7 @@ export class VideoService { | |||
156 | videoPagination: ComponentPagination, | 157 | videoPagination: ComponentPagination, |
157 | sort: VideoSortField, | 158 | sort: VideoSortField, |
158 | filter?: VideoFilter | 159 | filter?: VideoFilter |
159 | ): Observable<{ videos: Video[], totalVideos: number}> { | 160 | ): Observable<{ videos: Video[], totalVideos: number }> { |
160 | const pagination = this.restService.componentPaginationToRestPagination(videoPagination) | 161 | const pagination = this.restService.componentPaginationToRestPagination(videoPagination) |
161 | 162 | ||
162 | let params = new HttpParams() | 163 | let params = new HttpParams() |
@@ -225,7 +226,7 @@ export class VideoService { | |||
225 | search: string, | 226 | search: string, |
226 | videoPagination: ComponentPagination, | 227 | videoPagination: ComponentPagination, |
227 | sort: VideoSortField | 228 | sort: VideoSortField |
228 | ): Observable<{ videos: Video[], totalVideos: number}> { | 229 | ): Observable<{ videos: Video[], totalVideos: number }> { |
229 | const url = VideoService.BASE_VIDEO_URL + 'search' | 230 | const url = VideoService.BASE_VIDEO_URL + 'search' |
230 | 231 | ||
231 | const pagination = this.restService.componentPaginationToRestPagination(videoPagination) | 232 | const pagination = this.restService.componentPaginationToRestPagination(videoPagination) |
@@ -295,18 +296,18 @@ export class VideoService { | |||
295 | 296 | ||
296 | private extractVideos (result: ResultList<VideoServerModel>) { | 297 | private extractVideos (result: ResultList<VideoServerModel>) { |
297 | return this.serverService.localeObservable | 298 | return this.serverService.localeObservable |
298 | .pipe( | 299 | .pipe( |
299 | map(translations => { | 300 | map(translations => { |
300 | const videosJson = result.data | 301 | const videosJson = result.data |
301 | const totalVideos = result.total | 302 | const totalVideos = result.total |
302 | const videos: Video[] = [] | 303 | const videos: Video[] = [] |
303 | 304 | ||
304 | for (const videoJson of videosJson) { | 305 | for (const videoJson of videosJson) { |
305 | videos.push(new Video(videoJson, translations)) | 306 | videos.push(new Video(videoJson, translations)) |
306 | } | 307 | } |
307 | 308 | ||
308 | return { videos, totalVideos } | 309 | return { videos, totalVideos } |
309 | }) | 310 | }) |
310 | ) | 311 | ) |
311 | } | 312 | } |
312 | } | 313 | } |
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 c8cd0d679..379cf7948 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 | |||
@@ -109,6 +109,16 @@ | |||
109 | <label i18n for="commentsEnabled">Enable video comments</label> | 109 | <label i18n for="commentsEnabled">Enable video comments</label> |
110 | </div> | 110 | </div> |
111 | 111 | ||
112 | <div class="form-group form-group-checkbox"> | ||
113 | <input type="checkbox" id="waitTranscoding" formControlName="waitTranscoding" /> | ||
114 | <label for="waitTranscoding"></label> | ||
115 | <label i18n for="waitTranscoding">Wait transcoding before publishing the video</label> | ||
116 | <my-help | ||
117 | tooltipPlacement="top" helpType="custom" i18n-customHtml | ||
118 | customHtml="If you decide to not wait transcoding before publishing the video, it can be unplayable until it transcoding ends." | ||
119 | ></my-help> | ||
120 | </div> | ||
121 | |||
112 | </div> | 122 | </div> |
113 | </tab> | 123 | </tab> |
114 | 124 | ||
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 61515c0b0..ee4fd5dc1 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 | |||
@@ -47,6 +47,7 @@ export class VideoEditComponent implements OnInit { | |||
47 | const defaultValues = { | 47 | const defaultValues = { |
48 | nsfw: 'false', | 48 | nsfw: 'false', |
49 | commentsEnabled: 'true', | 49 | commentsEnabled: 'true', |
50 | waitTranscoding: 'true', | ||
50 | tags: [] | 51 | tags: [] |
51 | } | 52 | } |
52 | const obj = { | 53 | const obj = { |
@@ -55,6 +56,7 @@ export class VideoEditComponent implements OnInit { | |||
55 | channelId: this.videoValidatorsService.VIDEO_CHANNEL, | 56 | channelId: this.videoValidatorsService.VIDEO_CHANNEL, |
56 | nsfw: null, | 57 | nsfw: null, |
57 | commentsEnabled: null, | 58 | commentsEnabled: null, |
59 | waitTranscoding: null, | ||
58 | category: this.videoValidatorsService.VIDEO_CATEGORY, | 60 | category: this.videoValidatorsService.VIDEO_CATEGORY, |
59 | licence: this.videoValidatorsService.VIDEO_LICENCE, | 61 | licence: this.videoValidatorsService.VIDEO_LICENCE, |
60 | language: this.videoValidatorsService.VIDEO_LANGUAGE, | 62 | language: this.videoValidatorsService.VIDEO_LANGUAGE, |
@@ -74,13 +76,13 @@ export class VideoEditComponent implements OnInit { | |||
74 | ) | 76 | ) |
75 | 77 | ||
76 | // We will update the "support" field depending on the channel | 78 | // We will update the "support" field depending on the channel |
77 | this.form.controls['channelId'] | 79 | this.form.controls[ 'channelId' ] |
78 | .valueChanges | 80 | .valueChanges |
79 | .pipe(map(res => parseInt(res.toString(), 10))) | 81 | .pipe(map(res => parseInt(res.toString(), 10))) |
80 | .subscribe( | 82 | .subscribe( |
81 | newChannelId => { | 83 | newChannelId => { |
82 | const oldChannelId = parseInt(this.form.value['channelId'], 10) | 84 | const oldChannelId = parseInt(this.form.value[ 'channelId' ], 10) |
83 | const currentSupport = this.form.value['support'] | 85 | const currentSupport = this.form.value[ 'support' ] |
84 | 86 | ||
85 | // Not initialized yet | 87 | // Not initialized yet |
86 | if (isNaN(newChannelId)) return | 88 | if (isNaN(newChannelId)) return |
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 332f757d7..85afd0caa 100644 --- a/client/src/app/videos/+video-edit/video-add.component.ts +++ b/client/src/app/videos/+video-edit/video-add.component.ts | |||
@@ -164,6 +164,7 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy | |||
164 | 164 | ||
165 | const privacy = this.firstStepPrivacyId.toString() | 165 | const privacy = this.firstStepPrivacyId.toString() |
166 | const nsfw = false | 166 | const nsfw = false |
167 | const waitTranscoding = true | ||
167 | const commentsEnabled = true | 168 | const commentsEnabled = true |
168 | const channelId = this.firstStepChannelId.toString() | 169 | const channelId = this.firstStepChannelId.toString() |
169 | 170 | ||
@@ -173,6 +174,7 @@ export class VideoAddComponent extends FormReactive implements OnInit, OnDestroy | |||
173 | formData.append('privacy', VideoPrivacy.PRIVATE.toString()) | 174 | formData.append('privacy', VideoPrivacy.PRIVATE.toString()) |
174 | formData.append('nsfw', '' + nsfw) | 175 | formData.append('nsfw', '' + nsfw) |
175 | formData.append('commentsEnabled', '' + commentsEnabled) | 176 | formData.append('commentsEnabled', '' + commentsEnabled) |
177 | formData.append('waitTranscoding', '' + waitTranscoding) | ||
176 | formData.append('channelId', '' + channelId) | 178 | formData.append('channelId', '' + channelId) |
177 | formData.append('videofile', videofile) | 179 | formData.append('videofile', videofile) |
178 | 180 | ||
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 4c650b121..8bd5c00ff 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.html +++ b/client/src/app/videos/+video-watch/video-watch.component.html | |||
@@ -3,6 +3,10 @@ | |||
3 | <div id="video-element-wrapper"> | 3 | <div id="video-element-wrapper"> |
4 | </div> | 4 | </div> |
5 | 5 | ||
6 | <div i18n id="warning-transcoding" class="alert alert-warning" *ngIf="isVideoToTranscode()"> | ||
7 | The video is being transcoded, it may not work properly. | ||
8 | </div> | ||
9 | |||
6 | <!-- Video information --> | 10 | <!-- Video information --> |
7 | <div *ngIf="video" class="margin-content video-bottom"> | 11 | <div *ngIf="video" class="margin-content video-bottom"> |
8 | <div class="video-info"> | 12 | <div class="video-info"> |
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 00e776a69..06dd75653 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.scss +++ b/client/src/app/videos/+video-watch/video-watch.component.scss | |||
@@ -28,6 +28,10 @@ | |||
28 | } | 28 | } |
29 | } | 29 | } |
30 | 30 | ||
31 | #warning-transcoding { | ||
32 | text-align: center; | ||
33 | } | ||
34 | |||
31 | #video-not-found { | 35 | #video-not-found { |
32 | height: 300px; | 36 | height: 300px; |
33 | line-height: 300px; | 37 | line-height: 300px; |
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 eefa43a73..498542fff 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { catchError } from 'rxjs/operators' | 1 | import { catchError } from 'rxjs/operators' |
2 | import { Component, ElementRef, LOCALE_ID, NgZone, OnDestroy, OnInit, ViewChild, Inject } from '@angular/core' | 2 | import { Component, ElementRef, Inject, LOCALE_ID, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core' |
3 | import { ActivatedRoute, Router } from '@angular/router' | 3 | import { ActivatedRoute, Router } from '@angular/router' |
4 | import { RedirectService } from '@app/core/routing/redirect.service' | 4 | import { RedirectService } from '@app/core/routing/redirect.service' |
5 | import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage' | 5 | import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage' |
@@ -10,7 +10,7 @@ import { Subscription } from 'rxjs' | |||
10 | import * as videojs from 'video.js' | 10 | import * as videojs from 'video.js' |
11 | import 'videojs-hotkeys' | 11 | import 'videojs-hotkeys' |
12 | import * as WebTorrent from 'webtorrent' | 12 | import * as WebTorrent from 'webtorrent' |
13 | import { UserVideoRateType, VideoRateType } from '../../../../../shared' | 13 | import { UserVideoRateType, VideoRateType, VideoState } from '../../../../../shared' |
14 | import '../../../assets/player/peertube-videojs-plugin' | 14 | import '../../../assets/player/peertube-videojs-plugin' |
15 | import { AuthService, ConfirmService } from '../../core' | 15 | import { AuthService, ConfirmService } from '../../core' |
16 | import { RestExtractor, VideoBlacklistService } from '../../shared' | 16 | import { RestExtractor, VideoBlacklistService } from '../../shared' |
@@ -21,7 +21,7 @@ import { MarkdownService } from '../shared' | |||
21 | import { VideoDownloadComponent } from './modal/video-download.component' | 21 | import { VideoDownloadComponent } from './modal/video-download.component' |
22 | import { VideoReportComponent } from './modal/video-report.component' | 22 | import { VideoReportComponent } from './modal/video-report.component' |
23 | import { VideoShareComponent } from './modal/video-share.component' | 23 | import { VideoShareComponent } from './modal/video-share.component' |
24 | import { getVideojsOptions, loadLocale, addContextMenu } from '../../../assets/player/peertube-player' | 24 | import { addContextMenu, getVideojsOptions, loadLocale } from '../../../assets/player/peertube-player' |
25 | import { ServerService } from '@app/core' | 25 | import { ServerService } from '@app/core' |
26 | import { I18n } from '@ngx-translate/i18n-polyfill' | 26 | import { I18n } from '@ngx-translate/i18n-polyfill' |
27 | import { environment } from '../../../environments/environment' | 27 | import { environment } from '../../../environments/environment' |
@@ -91,21 +91,21 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
91 | } | 91 | } |
92 | 92 | ||
93 | this.videoService.getVideos({ currentPage: 1, itemsPerPage: 5 }, '-createdAt') | 93 | this.videoService.getVideos({ currentPage: 1, itemsPerPage: 5 }, '-createdAt') |
94 | .subscribe( | 94 | .subscribe( |
95 | data => { | 95 | data => { |
96 | this.otherVideos = data.videos | 96 | this.otherVideos = data.videos |
97 | this.updateOtherVideosDisplayed() | 97 | this.updateOtherVideosDisplayed() |
98 | }, | 98 | }, |
99 | 99 | ||
100 | err => console.error(err) | 100 | err => console.error(err) |
101 | ) | 101 | ) |
102 | 102 | ||
103 | this.paramsSub = this.route.params.subscribe(routeParams => { | 103 | this.paramsSub = this.route.params.subscribe(routeParams => { |
104 | if (this.player) { | 104 | if (this.player) { |
105 | this.player.pause() | 105 | this.player.pause() |
106 | } | 106 | } |
107 | 107 | ||
108 | const uuid = routeParams['uuid'] | 108 | const uuid = routeParams[ 'uuid' ] |
109 | 109 | ||
110 | // Video did not change | 110 | // Video did not change |
111 | if (this.video && this.video.uuid === uuid) return | 111 | if (this.video && this.video.uuid === uuid) return |
@@ -113,13 +113,11 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
113 | this.videoService | 113 | this.videoService |
114 | .getVideo(uuid) | 114 | .getVideo(uuid) |
115 | .pipe(catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 404 ]))) | 115 | .pipe(catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 404 ]))) |
116 | .subscribe( | 116 | .subscribe(video => { |
117 | video => { | 117 | const startTime = this.route.snapshot.queryParams.start |
118 | const startTime = this.route.snapshot.queryParams.start | 118 | this.onVideoFetched(video, startTime) |
119 | this.onVideoFetched(video, startTime) | 119 | .catch(err => this.handleError(err)) |
120 | .catch(err => this.handleError(err)) | 120 | }) |
121 | } | ||
122 | ) | ||
123 | }) | 121 | }) |
124 | } | 122 | } |
125 | 123 | ||
@@ -157,17 +155,17 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
157 | if (res === false) return | 155 | if (res === false) return |
158 | 156 | ||
159 | this.videoBlacklistService.blacklistVideo(this.video.id) | 157 | this.videoBlacklistService.blacklistVideo(this.video.id) |
160 | .subscribe( | 158 | .subscribe( |
161 | status => { | 159 | () => { |
162 | this.notificationsService.success( | 160 | this.notificationsService.success( |
163 | this.i18n('Success'), | 161 | this.i18n('Success'), |
164 | this.i18n('Video {{videoName}} had been blacklisted.', { videoName: this.video.name }) | 162 | this.i18n('Video {{videoName}} had been blacklisted.', { videoName: this.video.name }) |
165 | ) | 163 | ) |
166 | this.redirectService.redirectToHomepage() | 164 | this.redirectService.redirectToHomepage() |
167 | }, | 165 | }, |
168 | 166 | ||
169 | error => this.notificationsService.error(this.i18n('Error'), error.message) | 167 | error => this.notificationsService.error(this.i18n('Error'), error.message) |
170 | ) | 168 | ) |
171 | } | 169 | } |
172 | 170 | ||
173 | showMoreDescription () { | 171 | showMoreDescription () { |
@@ -188,22 +186,22 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
188 | this.descriptionLoading = true | 186 | this.descriptionLoading = true |
189 | 187 | ||
190 | this.videoService.loadCompleteDescription(this.video.descriptionPath) | 188 | this.videoService.loadCompleteDescription(this.video.descriptionPath) |
191 | .subscribe( | 189 | .subscribe( |
192 | description => { | 190 | description => { |
193 | this.completeDescriptionShown = true | 191 | this.completeDescriptionShown = true |
194 | this.descriptionLoading = false | 192 | this.descriptionLoading = false |
195 | 193 | ||
196 | this.shortVideoDescription = this.video.description | 194 | this.shortVideoDescription = this.video.description |
197 | this.completeVideoDescription = description | 195 | this.completeVideoDescription = description |
198 | 196 | ||
199 | this.updateVideoDescription(this.completeVideoDescription) | 197 | this.updateVideoDescription(this.completeVideoDescription) |
200 | }, | 198 | }, |
201 | 199 | ||
202 | error => { | 200 | error => { |
203 | this.descriptionLoading = false | 201 | this.descriptionLoading = false |
204 | this.notificationsService.error(this.i18n('Error'), error.message) | 202 | this.notificationsService.error(this.i18n('Error'), error.message) |
205 | } | 203 | } |
206 | ) | 204 | ) |
207 | } | 205 | } |
208 | 206 | ||
209 | showReportModal (event: Event) { | 207 | showReportModal (event: Event) { |
@@ -259,19 +257,19 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
259 | if (res === false) return | 257 | if (res === false) return |
260 | 258 | ||
261 | this.videoService.removeVideo(this.video.id) | 259 | this.videoService.removeVideo(this.video.id) |
262 | .subscribe( | 260 | .subscribe( |
263 | status => { | 261 | status => { |
264 | this.notificationsService.success( | 262 | this.notificationsService.success( |
265 | this.i18n('Success'), | 263 | this.i18n('Success'), |
266 | this.i18n('Video {{videoName}} deleted.', { videoName: this.video.name }) | 264 | this.i18n('Video {{videoName}} deleted.', { videoName: this.video.name }) |
267 | ) | 265 | ) |
268 | 266 | ||
269 | // Go back to the video-list. | 267 | // Go back to the video-list. |
270 | this.redirectService.redirectToHomepage() | 268 | this.redirectService.redirectToHomepage() |
271 | }, | 269 | }, |
272 | 270 | ||
273 | error => this.notificationsService.error(this.i18n('Error'), error.message) | 271 | error => this.notificationsService.error(this.i18n('Error'), error.message) |
274 | ) | 272 | ) |
275 | } | 273 | } |
276 | 274 | ||
277 | acceptedPrivacyConcern () { | 275 | acceptedPrivacyConcern () { |
@@ -279,6 +277,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
279 | this.hasAlreadyAcceptedPrivacyConcern = true | 277 | this.hasAlreadyAcceptedPrivacyConcern = true |
280 | } | 278 | } |
281 | 279 | ||
280 | isVideoToTranscode () { | ||
281 | return this.video && this.video.state.id === VideoState.TO_TRANSCODE | ||
282 | } | ||
283 | |||
282 | private updateVideoDescription (description: string) { | 284 | private updateVideoDescription (description: string) { |
283 | this.video.description = description | 285 | this.video.description = description |
284 | this.setVideoDescriptionHTML() | 286 | this.setVideoDescriptionHTML() |
@@ -294,10 +296,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
294 | } | 296 | } |
295 | 297 | ||
296 | private setVideoLikesBarTooltipText () { | 298 | private setVideoLikesBarTooltipText () { |
297 | this.likesBarTooltipText = this.i18n( | 299 | this.likesBarTooltipText = this.i18n('{{likesNumber}} likes / {{dislikesNumber}} dislikes', { |
298 | '{{likesNumber}} likes / {{dislikesNumber}} dislikes', | 300 | likesNumber: this.video.likes, |
299 | { likesNumber: this.video.likes, dislikesNumber: this.video.dislikes } | 301 | dislikesNumber: this.video.dislikes |
300 | ) | 302 | }) |
301 | } | 303 | } |
302 | 304 | ||
303 | private handleError (err: any) { | 305 | private handleError (err: any) { |
@@ -320,15 +322,15 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
320 | if (this.isUserLoggedIn() === false) return | 322 | if (this.isUserLoggedIn() === false) return |
321 | 323 | ||
322 | this.videoService.getUserVideoRating(this.video.id) | 324 | this.videoService.getUserVideoRating(this.video.id) |
323 | .subscribe( | 325 | .subscribe( |
324 | ratingObject => { | 326 | ratingObject => { |
325 | if (ratingObject) { | 327 | if (ratingObject) { |
326 | this.userRating = ratingObject.rating | 328 | this.userRating = ratingObject.rating |
327 | } | 329 | } |
328 | }, | 330 | }, |
329 | 331 | ||
330 | err => this.notificationsService.error(this.i18n('Error'), err.message) | 332 | err => this.notificationsService.error(this.i18n('Error'), err.message) |
331 | ) | 333 | ) |
332 | } | 334 | } |
333 | 335 | ||
334 | private async onVideoFetched (video: VideoDetails, startTime = 0) { | 336 | private async onVideoFetched (video: VideoDetails, startTime = 0) { |
@@ -409,14 +411,15 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
409 | } | 411 | } |
410 | 412 | ||
411 | method.call(this.videoService, this.video.id) | 413 | method.call(this.videoService, this.video.id) |
412 | .subscribe( | 414 | .subscribe( |
413 | () => { | 415 | () => { |
414 | // Update the video like attribute | 416 | // Update the video like attribute |
415 | this.updateVideoRating(this.userRating, nextRating) | 417 | this.updateVideoRating(this.userRating, nextRating) |
416 | this.userRating = nextRating | 418 | this.userRating = nextRating |
417 | }, | 419 | }, |
418 | err => this.notificationsService.error(this.i18n('Error'), err.message) | 420 | |
419 | ) | 421 | err => this.notificationsService.error(this.i18n('Error'), err.message) |
422 | ) | ||
420 | } | 423 | } |
421 | 424 | ||
422 | private updateVideoRating (oldRating: UserVideoRateType, newRating: VideoRateType) { | 425 | private updateVideoRating (oldRating: UserVideoRateType, newRating: VideoRateType) { |