diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts
index 984470d69..bef1599fc 100644
--- a/client/src/app/app.component.ts
+++ b/client/src/app/app.component.ts
@@ -1,4 +1,4 @@
-import { Component, OnInit, ViewContainerRef } from '@angular/core'
+import { Component, OnInit } from '@angular/core'
import { Router } from '@angular/router'
import { AuthService, ServerService } from './core'
@@ -28,8 +28,7 @@ export class AppComponent implements OnInit {
constructor (
private router: Router,
private authService: AuthService,
- private serverService: ServerService,
- private userService: UserService
+ private serverService: ServerService
) {}
ngOnInit () {
@@ -45,6 +44,7 @@ export class AppComponent implements OnInit {
this.serverService.loadVideoCategories()
this.serverService.loadVideoLanguages()
this.serverService.loadVideoLicences()
+ this.serverService.loadVideoPrivacies()
// Do not display menu on small screens
if (window.innerWidth < 600) {
diff --git a/client/src/app/core/menu/menu.component.html b/client/src/app/core/menu/menu.component.html
index 2d8aace54..fcde23fdd 100644
--- a/client/src/app/core/menu/menu.component.html
+++ b/client/src/app/core/menu/menu.component.html
@@ -23,6 +23,11 @@
My account
+
+
+
+ My videos
+
+
+
+ Privacy:
+
+
+ {{ video.privacyLabel }}
+
+
+
Category:
diff --git a/client/src/app/videos/shared/video-details.model.ts b/client/src/app/videos/shared/video-details.model.ts
index 68ded5210..84f96a25f 100644
--- a/client/src/app/videos/shared/video-details.model.ts
+++ b/client/src/app/videos/shared/video-details.model.ts
@@ -5,7 +5,8 @@ import {
VideoFile,
VideoChannel,
VideoResolution,
- UserRight
+ UserRight,
+ VideoPrivacy
} from '../../../../../shared'
export class VideoDetails extends Video implements VideoDetailsServerModel {
@@ -41,10 +42,14 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
descriptionPath: string
files: VideoFile[]
channel: VideoChannel
+ privacy: VideoPrivacy
+ privacyLabel: string
constructor (hash: VideoDetailsServerModel) {
super(hash)
+ this.privacy = hash.privacy
+ this.privacyLabel = hash.privacyLabel
this.descriptionPath = hash.descriptionPath
this.files = hash.files
this.channel = hash.channel
diff --git a/client/src/app/videos/shared/video-edit.model.ts b/client/src/app/videos/shared/video-edit.model.ts
index e0b7bf130..88d23a59f 100644
--- a/client/src/app/videos/shared/video-edit.model.ts
+++ b/client/src/app/videos/shared/video-edit.model.ts
@@ -1,4 +1,5 @@
import { VideoDetails } from './video-details.model'
+import { VideoPrivacy } from '../../../../../shared/models/videos/video-privacy.enum'
export class VideoEdit {
category: number
@@ -9,6 +10,7 @@ export class VideoEdit {
tags: string[]
nsfw: boolean
channel: number
+ privacy: VideoPrivacy
uuid?: string
id?: number
@@ -23,6 +25,7 @@ export class VideoEdit {
this.tags = videoDetails.tags
this.nsfw = videoDetails.nsfw
this.channel = videoDetails.channel.id
+ this.privacy = videoDetails.privacy
}
patch (values: Object) {
@@ -40,7 +43,8 @@ export class VideoEdit {
name: this.name,
tags: this.tags,
nsfw: this.nsfw,
- channel: this.channel
+ channel: this.channel,
+ privacy: this.privacy
}
}
}
diff --git a/client/src/app/videos/shared/video.service.ts b/client/src/app/videos/shared/video.service.ts
index 7d5372334..8459aa0d3 100644
--- a/client/src/app/videos/shared/video.service.ts
+++ b/client/src/app/videos/shared/video.service.ts
@@ -19,7 +19,6 @@ import {
UserVideoRate,
VideoRateType,
VideoUpdate,
- VideoAbuseCreate,
UserVideoRateUpdate,
Video as VideoServerModel,
VideoDetails as VideoDetailsServerModel,
@@ -51,6 +50,7 @@ export class VideoService {
licence: video.licence,
language,
description: video.description,
+ privacy: video.privacy,
tags: video.tags,
nsfw: video.nsfw
}
@@ -63,22 +63,35 @@ export class VideoService {
uploadVideo (video: FormData) {
const req = new HttpRequest('POST', VideoService.BASE_VIDEO_URL + 'upload', video, { reportProgress: true })
- return this.authHttp.request(req)
- .catch(this.restExtractor.handleError)
+ return this.authHttp
+ .request(req)
+ .catch(this.restExtractor.handleError)
}
- getVideos (videoPagination: VideoPagination, sort: SortField) {
+ getMyVideos (videoPagination: VideoPagination, sort: SortField): Observable<{ videos: Video[], totalVideos: number}> {
const pagination = this.videoPaginationToRestPagination(videoPagination)
let params = new HttpParams()
params = this.restService.addRestGetParams(params, pagination, sort)
- return this.authHttp.get(VideoService.BASE_VIDEO_URL, { params })
- .map(this.extractVideos)
- .catch((res) => this.restExtractor.handleError(res))
+ return this.authHttp.get(UserService.BASE_USERS_URL + '/me/videos', { params })
+ .map(this.extractVideos)
+ .catch((res) => this.restExtractor.handleError(res))
}
- searchVideos (search: Search, videoPagination: VideoPagination, sort: SortField) {
+ getVideos (videoPagination: VideoPagination, sort: SortField): Observable<{ videos: Video[], totalVideos: number}> {
+ const pagination = this.videoPaginationToRestPagination(videoPagination)
+
+ let params = new HttpParams()
+ params = this.restService.addRestGetParams(params, pagination, sort)
+
+ return this.authHttp
+ .get(VideoService.BASE_VIDEO_URL, { params })
+ .map(this.extractVideos)
+ .catch((res) => this.restExtractor.handleError(res))
+ }
+
+ searchVideos (search: Search, videoPagination: VideoPagination, sort: SortField): Observable<{ videos: Video[], totalVideos: number}> {
const url = VideoService.BASE_VIDEO_URL + 'search/' + encodeURIComponent(search.value)
const pagination = this.videoPaginationToRestPagination(videoPagination)
@@ -88,15 +101,17 @@ export class VideoService {
if (search.field) params.set('field', search.field)
- return this.authHttp.get>(url, { params })
- .map(this.extractVideos)
- .catch((res) => this.restExtractor.handleError(res))
+ return this.authHttp
+ .get>(url, { params })
+ .map(this.extractVideos)
+ .catch((res) => this.restExtractor.handleError(res))
}
removeVideo (id: number) {
- return this.authHttp.delete(VideoService.BASE_VIDEO_URL + id)
- .map(this.restExtractor.extractDataBool)
- .catch((res) => this.restExtractor.handleError(res))
+ return this.authHttp
+ .delete(VideoService.BASE_VIDEO_URL + id)
+ .map(this.restExtractor.extractDataBool)
+ .catch((res) => this.restExtractor.handleError(res))
}
loadCompleteDescription (descriptionPath: string) {
@@ -117,8 +132,9 @@ export class VideoService {
getUserVideoRating (id: number): Observable {
const url = UserService.BASE_USERS_URL + 'me/videos/' + id + '/rating'
- return this.authHttp.get(url)
- .catch(res => this.restExtractor.handleError(res))
+ return this.authHttp
+ .get(url)
+ .catch(res => this.restExtractor.handleError(res))
}
private videoPaginationToRestPagination (videoPagination: VideoPagination) {
@@ -134,9 +150,10 @@ export class VideoService {
rating: rateType
}
- return this.authHttp.put(url, body)
- .map(this.restExtractor.extractDataBool)
- .catch(res => this.restExtractor.handleError(res))
+ return this.authHttp
+ .put(url, body)
+ .map(this.restExtractor.extractDataBool)
+ .catch(res => this.restExtractor.handleError(res))
}
private extractVideos (result: ResultList) {
diff --git a/client/src/app/videos/video-list/index.ts b/client/src/app/videos/video-list/index.ts
index a490e6bb5..ed2bb1657 100644
--- a/client/src/app/videos/video-list/index.ts
+++ b/client/src/app/videos/video-list/index.ts
@@ -1,4 +1,3 @@
-export * from './loader.component'
+export * from './my-videos.component'
export * from './video-list.component'
-export * from './video-miniature.component'
-export * from './video-sort.component'
+export * from './shared'
diff --git a/client/src/app/videos/video-list/my-videos.component.ts b/client/src/app/videos/video-list/my-videos.component.ts
new file mode 100644
index 000000000..648741a40
--- /dev/null
+++ b/client/src/app/videos/video-list/my-videos.component.ts
@@ -0,0 +1,36 @@
+import { Component, OnDestroy, OnInit } from '@angular/core'
+import { ActivatedRoute, Router } from '@angular/router'
+
+import { NotificationsService } from 'angular2-notifications'
+
+import { AbstractVideoList } from './shared'
+import { VideoService } from '../shared'
+
+@Component({
+ selector: 'my-videos',
+ styleUrls: [ './shared/abstract-video-list.scss' ],
+ templateUrl: './shared/abstract-video-list.html'
+})
+export class MyVideosComponent extends AbstractVideoList implements OnInit, OnDestroy {
+
+ constructor (
+ protected router: Router,
+ protected route: ActivatedRoute,
+ protected notificationsService: NotificationsService,
+ private videoService: VideoService
+ ) {
+ super()
+ }
+
+ ngOnInit () {
+ super.ngOnInit()
+ }
+
+ ngOnDestroy () {
+ this.subActivatedRoute.unsubscribe()
+ }
+
+ getVideosObservable () {
+ return this.videoService.getMyVideos(this.pagination, this.sort)
+ }
+}
diff --git a/client/src/app/videos/video-list/video-list.component.html b/client/src/app/videos/video-list/shared/abstract-video-list.html
similarity index 100%
rename from client/src/app/videos/video-list/video-list.component.html
rename to client/src/app/videos/video-list/shared/abstract-video-list.html
diff --git a/client/src/app/videos/video-list/video-list.component.scss b/client/src/app/videos/video-list/shared/abstract-video-list.scss
similarity index 100%
rename from client/src/app/videos/video-list/video-list.component.scss
rename to client/src/app/videos/video-list/shared/abstract-video-list.scss
diff --git a/client/src/app/videos/video-list/shared/abstract-video-list.ts b/client/src/app/videos/video-list/shared/abstract-video-list.ts
new file mode 100644
index 000000000..87d5bc48a
--- /dev/null
+++ b/client/src/app/videos/video-list/shared/abstract-video-list.ts
@@ -0,0 +1,104 @@
+import { OnDestroy, OnInit } from '@angular/core'
+import { ActivatedRoute, Router } from '@angular/router'
+import { Subscription } from 'rxjs/Subscription'
+import { BehaviorSubject } from 'rxjs/BehaviorSubject'
+import { Observable } from 'rxjs/Observable'
+
+import { NotificationsService } from 'angular2-notifications'
+
+import {
+ SortField,
+ Video,
+ VideoPagination
+} from '../../shared'
+
+export abstract class AbstractVideoList implements OnInit, OnDestroy {
+ loading: BehaviorSubject = new BehaviorSubject(false)
+ pagination: VideoPagination = {
+ currentPage: 1,
+ itemsPerPage: 25,
+ totalItems: null
+ }
+ sort: SortField
+ videos: Video[] = []
+
+ protected notificationsService: NotificationsService
+ protected router: Router
+ protected route: ActivatedRoute
+
+ protected subActivatedRoute: Subscription
+
+ abstract getVideosObservable (): Observable<{ videos: Video[], totalVideos: number}>
+
+ ngOnInit () {
+ // Subscribe to route changes
+ this.subActivatedRoute = this.route.params.subscribe(routeParams => {
+ this.loadRouteParams(routeParams)
+
+ this.getVideos()
+ })
+ }
+
+ ngOnDestroy () {
+ this.subActivatedRoute.unsubscribe()
+ }
+
+ getVideos () {
+ this.loading.next(true)
+ this.videos = []
+
+ const observable = this.getVideosObservable()
+
+ observable.subscribe(
+ ({ videos, totalVideos }) => {
+ this.videos = videos
+ this.pagination.totalItems = totalVideos
+
+ this.loading.next(false)
+ },
+ error => this.notificationsService.error('Error', error.text)
+ )
+ }
+
+ isThereNoVideo () {
+ return !this.loading.getValue() && this.videos.length === 0
+ }
+
+ onPageChanged (event: { page: number }) {
+ // Be sure the current page is set
+ this.pagination.currentPage = event.page
+
+ this.navigateToNewParams()
+ }
+
+ onSort (sort: SortField) {
+ this.sort = sort
+
+ this.navigateToNewParams()
+ }
+
+ protected buildRouteParams () {
+ // There is always a sort and a current page
+ const params = {
+ sort: this.sort,
+ page: this.pagination.currentPage
+ }
+
+ return params
+ }
+
+ protected loadRouteParams (routeParams: { [ key: string ]: any }) {
+ this.sort = routeParams['sort'] as SortField || '-createdAt'
+
+ if (routeParams['page'] !== undefined) {
+ this.pagination.currentPage = parseInt(routeParams['page'], 10)
+ } else {
+ this.pagination.currentPage = 1
+ }
+ }
+
+ protected navigateToNewParams () {
+ const routeParams = this.buildRouteParams()
+ this.router.navigate([ '/videos/list', routeParams ])
+ }
+}
diff --git a/client/src/app/videos/video-list/shared/index.ts b/client/src/app/videos/video-list/shared/index.ts
new file mode 100644
index 000000000..2c9804e6d
--- /dev/null
+++ b/client/src/app/videos/video-list/shared/index.ts
@@ -0,0 +1,4 @@
+export * from './abstract-video-list'
+export * from './loader.component'
+export * from './video-miniature.component'
+export * from './video-sort.component'
diff --git a/client/src/app/videos/video-list/loader.component.html b/client/src/app/videos/video-list/shared/loader.component.html
similarity index 100%
rename from client/src/app/videos/video-list/loader.component.html
rename to client/src/app/videos/video-list/shared/loader.component.html
diff --git a/client/src/app/videos/video-list/loader.component.ts b/client/src/app/videos/video-list/shared/loader.component.ts
similarity index 100%
rename from client/src/app/videos/video-list/loader.component.ts
rename to client/src/app/videos/video-list/shared/loader.component.ts
diff --git a/client/src/app/videos/video-list/video-miniature.component.html b/client/src/app/videos/video-list/shared/video-miniature.component.html
similarity index 100%
rename from client/src/app/videos/video-list/video-miniature.component.html
rename to client/src/app/videos/video-list/shared/video-miniature.component.html
diff --git a/client/src/app/videos/video-list/video-miniature.component.scss b/client/src/app/videos/video-list/shared/video-miniature.component.scss
similarity index 100%
rename from client/src/app/videos/video-list/video-miniature.component.scss
rename to client/src/app/videos/video-list/shared/video-miniature.component.scss
diff --git a/client/src/app/videos/video-list/video-miniature.component.ts b/client/src/app/videos/video-list/shared/video-miniature.component.ts
similarity index 82%
rename from client/src/app/videos/video-list/video-miniature.component.ts
rename to client/src/app/videos/video-list/shared/video-miniature.component.ts
index 18434dad2..e5a87907b 100644
--- a/client/src/app/videos/video-list/video-miniature.component.ts
+++ b/client/src/app/videos/video-list/shared/video-miniature.component.ts
@@ -1,7 +1,7 @@
import { Component, Input } from '@angular/core'
-import { SortField, Video } from '../shared'
-import { User } from '../../shared'
+import { SortField, Video } from '../../shared'
+import { User } from '../../../shared'
@Component({
selector: 'my-video-miniature',
diff --git a/client/src/app/videos/video-list/video-sort.component.html b/client/src/app/videos/video-list/shared/video-sort.component.html
similarity index 100%
rename from client/src/app/videos/video-list/video-sort.component.html
rename to client/src/app/videos/video-list/shared/video-sort.component.html
diff --git a/client/src/app/videos/video-list/video-sort.component.ts b/client/src/app/videos/video-list/shared/video-sort.component.ts
similarity index 95%
rename from client/src/app/videos/video-list/video-sort.component.ts
rename to client/src/app/videos/video-list/shared/video-sort.component.ts
index 64916bf16..8aa89d32b 100644
--- a/client/src/app/videos/video-list/video-sort.component.ts
+++ b/client/src/app/videos/video-list/shared/video-sort.component.ts
@@ -1,6 +1,6 @@
import { Component, EventEmitter, Input, Output } from '@angular/core'
-import { SortField } from '../shared'
+import { SortField } from '../../shared'
@Component({
selector: 'my-video-sort',
diff --git a/client/src/app/videos/video-list/video-list.component.ts b/client/src/app/videos/video-list/video-list.component.ts
index bf6f60215..784162679 100644
--- a/client/src/app/videos/video-list/video-list.component.ts
+++ b/client/src/app/videos/video-list/video-list.component.ts
@@ -1,51 +1,33 @@
import { Component, OnDestroy, OnInit } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router'
import { Subscription } from 'rxjs/Subscription'
-import { BehaviorSubject } from 'rxjs/BehaviorSubject'
import { NotificationsService } from 'angular2-notifications'
-import { AuthService } from '../../core'
-import {
- SortField,
- Video,
- VideoService,
- VideoPagination
-} from '../shared'
-import { Search, SearchField, SearchService, User } from '../../shared'
+import { VideoService } from '../shared'
+import { Search, SearchField, SearchService } from '../../shared'
+import { AbstractVideoList } from './shared'
@Component({
selector: 'my-videos-list',
- styleUrls: [ './video-list.component.scss' ],
- templateUrl: './video-list.component.html'
+ styleUrls: [ './shared/abstract-video-list.scss' ],
+ templateUrl: './shared/abstract-video-list.html'
})
-export class VideoListComponent implements OnInit, OnDestroy {
- loading: BehaviorSubject = new BehaviorSubject(false)
- pagination: VideoPagination = {
- currentPage: 1,
- itemsPerPage: 25,
- totalItems: null
- }
- sort: SortField
- user: User
- videos: Video[] = []
-
+export class VideoListComponent extends AbstractVideoList implements OnInit, OnDestroy {
private search: Search
- private subActivatedRoute: Subscription
private subSearch: Subscription
constructor (
- private authService: AuthService,
- private notificationsService: NotificationsService,
- private router: Router,
- private route: ActivatedRoute,
+ protected router: Router,
+ protected route: ActivatedRoute,
+ protected notificationsService: NotificationsService,
private videoService: VideoService,
private searchService: SearchService
- ) {}
+ ) {
+ super()
+ }
ngOnInit () {
- this.user = this.authService.getUser()
-
// Subscribe to route changes
this.subActivatedRoute = this.route.params.subscribe(routeParams => {
this.loadRouteParams(routeParams)
@@ -66,14 +48,12 @@ export class VideoListComponent implements OnInit, OnDestroy {
}
ngOnDestroy () {
- this.subActivatedRoute.unsubscribe()
+ super.ngOnDestroy()
+
this.subSearch.unsubscribe()
}
- getVideos () {
- this.loading.next(true)
- this.videos = []
-
+ getVideosObservable () {
let observable = null
if (this.search.value) {
observable = this.videoService.searchVideos(this.search, this.pagination, this.sort)
@@ -81,40 +61,11 @@ export class VideoListComponent implements OnInit, OnDestroy {
observable = this.videoService.getVideos(this.pagination, this.sort)
}
- observable.subscribe(
- ({ videos, totalVideos }) => {
- this.videos = videos
- this.pagination.totalItems = totalVideos
-
- this.loading.next(false)
- },
- error => this.notificationsService.error('Error', error.text)
- )
+ return observable
}
- isThereNoVideo () {
- return !this.loading.getValue() && this.videos.length === 0
- }
-
- onPageChanged (event: { page: number }) {
- // Be sure the current page is set
- this.pagination.currentPage = event.page
-
- this.navigateToNewParams()
- }
-
- onSort (sort: SortField) {
- this.sort = sort
-
- this.navigateToNewParams()
- }
-
- private buildRouteParams () {
- // There is always a sort and a current page
- const params = {
- sort: this.sort,
- page: this.pagination.currentPage
- }
+ protected buildRouteParams () {
+ const params = super.buildRouteParams()
// Maybe there is a search
if (this.search.value) {
@@ -125,7 +76,9 @@ export class VideoListComponent implements OnInit, OnDestroy {
return params
}
- private loadRouteParams (routeParams: { [ key: string ]: any }) {
+ protected loadRouteParams (routeParams: { [ key: string ]: any }) {
+ super.loadRouteParams(routeParams)
+
if (routeParams['search'] !== undefined) {
this.search = {
value: routeParams['search'],
@@ -137,18 +90,5 @@ export class VideoListComponent implements OnInit, OnDestroy {
field: 'name'
}
}
-
- this.sort = routeParams['sort'] as SortField || '-createdAt'
-
- if (routeParams['page'] !== undefined) {
- this.pagination.currentPage = parseInt(routeParams['page'], 10)
- } else {
- this.pagination.currentPage = 1
- }
- }
-
- private navigateToNewParams () {
- const routeParams = this.buildRouteParams()
- this.router.navigate([ '/videos/list', routeParams ])
}
}
diff --git a/client/src/app/videos/videos-routing.module.ts b/client/src/app/videos/videos-routing.module.ts
index d3869748b..3ca3e5486 100644
--- a/client/src/app/videos/videos-routing.module.ts
+++ b/client/src/app/videos/videos-routing.module.ts
@@ -3,7 +3,7 @@ import { RouterModule, Routes } from '@angular/router'
import { MetaGuard } from '@ngx-meta/core'
-import { VideoListComponent } from './video-list'
+import { VideoListComponent, MyVideosComponent } from './video-list'
import { VideosComponent } from './videos.component'
const videosRoutes: Routes = [
@@ -12,6 +12,15 @@ const videosRoutes: Routes = [
component: VideosComponent,
canActivateChild: [ MetaGuard ],
children: [
+ {
+ path: 'mine',
+ component: MyVideosComponent,
+ data: {
+ meta: {
+ title: 'My videos'
+ }
+ }
+ },
{
path: 'list',
component: VideoListComponent,
diff --git a/client/src/app/videos/videos.module.ts b/client/src/app/videos/videos.module.ts
index 3a0c3feac..ecc351b65 100644
--- a/client/src/app/videos/videos.module.ts
+++ b/client/src/app/videos/videos.module.ts
@@ -2,7 +2,13 @@ import { NgModule } from '@angular/core'
import { VideosRoutingModule } from './videos-routing.module'
import { VideosComponent } from './videos.component'
-import { LoaderComponent, VideoListComponent, VideoMiniatureComponent, VideoSortComponent } from './video-list'
+import {
+ LoaderComponent,
+ VideoListComponent,
+ MyVideosComponent,
+ VideoMiniatureComponent,
+ VideoSortComponent
+} from './video-list'
import { VideoService } from './shared'
import { SharedModule } from '../shared'
@@ -16,6 +22,7 @@ import { SharedModule } from '../shared'
VideosComponent,
VideoListComponent,
+ MyVideosComponent,
VideoMiniatureComponent,
VideoSortComponent,
diff --git a/client/src/sass/video-js-custom.scss b/client/src/sass/video-js-custom.scss
index c5f668f17..6ad21988e 100644
--- a/client/src/sass/video-js-custom.scss
+++ b/client/src/sass/video-js-custom.scss
@@ -334,71 +334,34 @@ $slider-bg-color: lighten($primary-background-color, 33%);
// Thanks: https://projects.lukehaas.me/css-loaders/
.vjs-loading-spinner {
- border: none;
- opacity: 1;
+ margin: -25px 0 0 -25px;
+ position: absolute;
+ top: 50%;
+ left: 50%;
font-size: 10px;
- text-indent: -9999em;
- width: 5em;
- height: 5em;
- border-radius: 50%;
- background: #ffffff;
- background: -moz-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
- background: -webkit-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
- background: -o-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
- background: -ms-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
- background: linear-gradient(to right, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
position: relative;
- -webkit-animation: load3 1.4s infinite linear;
- animation: load3 1.4s infinite linear;
- -webkit-transform: translateZ(0);
- -ms-transform: translateZ(0);
+ text-indent: -9999em;
+ border: 0.7em solid rgba(255, 255, 255, 0.2);
+ border-left-color: #ffffff;
transform: translateZ(0);
+ animation: spinner 1.4s infinite linear;
&:before {
- width: 50%;
- height: 50%;
- background: #ffffff;
- border-radius: 100% 0 0 0;
- position: absolute;
- top: 0;
- left: 0;
- content: '';
animation: none !important;
- margin: 0 !important;
}
&:after {
- background: #000;
- width: 75%;
- height: 75%;
border-radius: 50%;
- content: '';
- margin: auto;
- position: absolute;
- top: 0;
- left: 0;
- bottom: 0;
- right: 0;
+ width: 6em;
+ height: 6em;
animation: none !important;
}
- @-webkit-keyframes load3 {
+ @keyframes spinner {
0% {
- -webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
- -webkit-transform: rotate(360deg);
- transform: rotate(360deg);
- }
- }
- @keyframes load3 {
- 0% {
- -webkit-transform: rotate(0deg);
- transform: rotate(0deg);
- }
- 100% {
- -webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
diff --git a/server/controllers/api/remote/videos.ts b/server/controllers/api/remote/videos.ts
index 3ecc62ada..cba47f0a1 100644
--- a/server/controllers/api/remote/videos.ts
+++ b/server/controllers/api/remote/videos.ts
@@ -267,7 +267,8 @@ async function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod
views: videoToCreateData.views,
likes: videoToCreateData.likes,
dislikes: videoToCreateData.dislikes,
- remote: true
+ remote: true,
+ privacy: videoToCreateData.privacy
}
const video = db.Video.build(videoData)
@@ -334,6 +335,7 @@ async function updateRemoteVideo (videoAttributesToUpdate: RemoteVideoUpdateData
videoInstance.set('views', videoAttributesToUpdate.views)
videoInstance.set('likes', videoAttributesToUpdate.likes)
videoInstance.set('dislikes', videoAttributesToUpdate.dislikes)
+ videoInstance.set('privacy', videoAttributesToUpdate.privacy)
await videoInstance.save(sequelizeOptions)
diff --git a/server/controllers/api/users.ts b/server/controllers/api/users.ts
index fdc9b0c87..dcd407fdf 100644
--- a/server/controllers/api/users.ts
+++ b/server/controllers/api/users.ts
@@ -30,6 +30,8 @@ import {
} from '../../../shared'
import { createUserAuthorAndChannel } from '../../lib'
import { UserInstance } from '../../models'
+import { videosSortValidator } from '../../middlewares/validators/sort'
+import { setVideosSort } from '../../middlewares/sort'
const usersRouter = express.Router()
@@ -38,6 +40,15 @@ usersRouter.get('/me',
asyncMiddleware(getUserInformation)
)
+usersRouter.get('/me/videos',
+ authenticate,
+ paginationValidator,
+ videosSortValidator,
+ setVideosSort,
+ setPagination,
+ asyncMiddleware(getUserVideos)
+)
+
usersRouter.get('/me/videos/:videoId/rating',
authenticate,
usersVideoRatingValidator,
@@ -101,6 +112,13 @@ export {
// ---------------------------------------------------------------------------
+async function getUserVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
+ const user = res.locals.oauth.token.User
+ const resultList = await db.Video.listUserVideosForApi(user.id ,req.query.start, req.query.count, req.query.sort)
+
+ return res.json(getFormattedObjects(resultList.data, resultList.total))
+}
+
async function createUserRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
const options = {
arguments: [ req, res ],
@@ -146,13 +164,14 @@ async function registerUser (req: express.Request, res: express.Response, next:
}
async function getUserInformation (req: express.Request, res: express.Response, next: express.NextFunction) {
+ // We did not load channels in res.locals.user
const user = await db.User.loadByUsernameAndPopulateChannels(res.locals.oauth.token.user.username)
return res.json(user.toFormattedJSON())
}
function getUser (req: express.Request, res: express.Response, next: express.NextFunction) {
- return res.json(res.locals.user.toFormattedJSON())
+ return res.json(res.locals.oauth.token.User.toFormattedJSON())
}
async function getUserVideoRating (req: express.Request, res: express.Response, next: express.NextFunction) {
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 49f0e4630..4dd09917b 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -9,7 +9,8 @@ import {
REQUEST_VIDEO_EVENT_TYPES,
VIDEO_CATEGORIES,
VIDEO_LICENCES,
- VIDEO_LANGUAGES
+ VIDEO_LANGUAGES,
+ VIDEO_PRIVACIES
} from '../../../initializers'
import {
addEventToRemoteVideo,
@@ -43,7 +44,7 @@ import {
resetSequelizeInstance
} from '../../../helpers'
import { VideoInstance } from '../../../models'
-import { VideoCreate, VideoUpdate } from '../../../../shared'
+import { VideoCreate, VideoUpdate, VideoPrivacy } from '../../../../shared'
import { abuseVideoRouter } from './abuse'
import { blacklistRouter } from './blacklist'
@@ -84,6 +85,7 @@ videosRouter.use('/', videoChannelRouter)
videosRouter.get('/categories', listVideoCategories)
videosRouter.get('/licences', listVideoLicences)
videosRouter.get('/languages', listVideoLanguages)
+videosRouter.get('/privacies', listVideoPrivacies)
videosRouter.get('/',
paginationValidator,
@@ -149,6 +151,10 @@ function listVideoLanguages (req: express.Request, res: express.Response) {
res.json(VIDEO_LANGUAGES)
}
+function listVideoPrivacies (req: express.Request, res: express.Response) {
+ res.json(VIDEO_PRIVACIES)
+}
+
// Wrapper to video add that retry the function if there is a database error
// We need this because we run the transaction in SERIALIZABLE isolation that can fail
async function addVideoRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
@@ -179,6 +185,7 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi
language: videoInfo.language,
nsfw: videoInfo.nsfw,
description: videoInfo.description,
+ privacy: videoInfo.privacy,
duration: videoPhysicalFile['duration'], // duration was added by a previous middleware
channelId: res.locals.videoChannel.id
}
@@ -240,6 +247,8 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi
// Let transcoding job send the video to friends because the video file extension might change
if (CONFIG.TRANSCODING.ENABLED === true) return undefined
+ // Don't send video to remote pods, it is private
+ if (video.privacy === VideoPrivacy.PRIVATE) return undefined
const remoteVideo = await video.toAddRemoteJSON()
// Now we'll add the video's meta data to our friends
@@ -264,6 +273,7 @@ async function updateVideo (req: express.Request, res: express.Response) {
const videoInstance = res.locals.video
const videoFieldsSave = videoInstance.toJSON()
const videoInfoToUpdate: VideoUpdate = req.body
+ const wasPrivateVideo = videoInstance.privacy === VideoPrivacy.PRIVATE
try {
await db.sequelize.transaction(async t => {
@@ -276,6 +286,7 @@ async function updateVideo (req: express.Request, res: express.Response) {
if (videoInfoToUpdate.licence !== undefined) videoInstance.set('licence', videoInfoToUpdate.licence)
if (videoInfoToUpdate.language !== undefined) videoInstance.set('language', videoInfoToUpdate.language)
if (videoInfoToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfoToUpdate.nsfw)
+ if (videoInfoToUpdate.privacy !== undefined) videoInstance.set('privacy', videoInfoToUpdate.privacy)
if (videoInfoToUpdate.description !== undefined) videoInstance.set('description', videoInfoToUpdate.description)
await videoInstance.save(sequelizeOptions)
@@ -287,10 +298,17 @@ async function updateVideo (req: express.Request, res: express.Response) {
videoInstance.Tags = tagInstances
}
- const json = videoInstance.toUpdateRemoteJSON()
-
// Now we'll update the video's meta data to our friends
- return updateVideoToFriends(json, t)
+ if (wasPrivateVideo === false) {
+ const json = videoInstance.toUpdateRemoteJSON()
+ return updateVideoToFriends(json, t)
+ }
+
+ // Video is not private anymore, send a create action to remote pods
+ if (wasPrivateVideo === true && videoInstance.privacy !== VideoPrivacy.PRIVATE) {
+ const remoteVideo = await videoInstance.toAddRemoteJSON()
+ return addVideoToFriends(remoteVideo, t)
+ }
})
logger.info('Video with name %s and uuid %s updated.', videoInstance.name, videoInstance.uuid)
diff --git a/server/helpers/custom-validators/videos.ts b/server/helpers/custom-validators/videos.ts
index 5b9102275..f3fdcaf2d 100644
--- a/server/helpers/custom-validators/videos.ts
+++ b/server/helpers/custom-validators/videos.ts
@@ -11,6 +11,7 @@ import {
VIDEO_LICENCES,
VIDEO_LANGUAGES,
VIDEO_RATE_TYPES,
+ VIDEO_PRIVACIES,
database as db
} from '../../initializers'
import { isUserUsernameValid } from './users'
@@ -36,6 +37,15 @@ function isVideoLicenceValid (value: number) {
return VIDEO_LICENCES[value] !== undefined
}
+function isVideoPrivacyValid (value: string) {
+ return VIDEO_PRIVACIES[value] !== undefined
+}
+
+// Maybe we don't know the remote privacy setting, but that doesn't matter
+function isRemoteVideoPrivacyValid (value: string) {
+ return validator.isInt('' + value)
+}
+
// Maybe we don't know the remote licence, but that doesn't matter
function isRemoteVideoLicenceValid (value: string) {
return validator.isInt('' + value)
@@ -195,6 +205,8 @@ export {
isVideoDislikesValid,
isVideoEventCountValid,
isVideoFileSizeValid,
+ isVideoPrivacyValid,
+ isRemoteVideoPrivacyValid,
isVideoFileResolutionValid,
checkVideoExists,
isRemoteVideoCategoryValid,
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index adccb9f41..d349abaf0 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -12,10 +12,11 @@ import {
RemoteVideoRequestType,
JobState
} from '../../shared/models'
+import { VideoPrivacy } from '../../shared/models/videos/video-privacy.enum'
// ---------------------------------------------------------------------------
-const LAST_MIGRATION_VERSION = 90
+const LAST_MIGRATION_VERSION = 95
// ---------------------------------------------------------------------------
@@ -196,6 +197,12 @@ const VIDEO_LANGUAGES = {
14: 'Italian'
}
+const VIDEO_PRIVACIES = {
+ [VideoPrivacy.PUBLIC]: 'Public',
+ [VideoPrivacy.UNLISTED]: 'Unlisted',
+ [VideoPrivacy.PRIVATE]: 'Private'
+}
+
// ---------------------------------------------------------------------------
// Score a pod has when we create it as a friend
@@ -394,6 +401,7 @@ export {
THUMBNAILS_SIZE,
VIDEO_CATEGORIES,
VIDEO_LANGUAGES,
+ VIDEO_PRIVACIES,
VIDEO_LICENCES,
VIDEO_RATE_TYPES
}
diff --git a/server/initializers/migrations/0095-videos-privacy.ts b/server/initializers/migrations/0095-videos-privacy.ts
new file mode 100644
index 000000000..4c2bf91d0
--- /dev/null
+++ b/server/initializers/migrations/0095-videos-privacy.ts
@@ -0,0 +1,35 @@
+import * as Sequelize from 'sequelize'
+
+async function up (utils: {
+ transaction: Sequelize.Transaction,
+ queryInterface: Sequelize.QueryInterface,
+ sequelize: Sequelize.Sequelize,
+ db: any
+}): Promise {
+ const q = utils.queryInterface
+
+ const data = {
+ type: Sequelize.INTEGER,
+ defaultValue: null,
+ allowNull: true
+ }
+ await q.addColumn('Videos', 'privacy', data)
+
+ const query = 'UPDATE "Videos" SET "privacy" = 1'
+ const options = {
+ type: Sequelize.QueryTypes.BULKUPDATE
+ }
+ await utils.sequelize.query(query, options)
+
+ data.allowNull = false
+ await q.changeColumn('Videos', 'privacy', data)
+}
+
+function down (options) {
+ throw new Error('Not implemented.')
+}
+
+export {
+ up,
+ down
+}
diff --git a/server/middlewares/validators/videos.ts b/server/middlewares/validators/videos.ts
index 0c07404c5..e197d4606 100644
--- a/server/middlewares/validators/videos.ts
+++ b/server/middlewares/validators/videos.ts
@@ -20,9 +20,10 @@ import {
isVideoRatingTypeValid,
getDurationFromVideoFile,
checkVideoExists,
- isIdValid
+ isIdValid,
+ isVideoPrivacyValid
} from '../../helpers'
-import { UserRight } from '../../../shared'
+import { UserRight, VideoPrivacy } from '../../../shared'
const videosAddValidator = [
body('videofile').custom((value, { req }) => isVideoFile(req.files)).withMessage(
@@ -36,6 +37,7 @@ const videosAddValidator = [
body('nsfw').custom(isVideoNSFWValid).withMessage('Should have a valid NSFW attribute'),
body('description').custom(isVideoDescriptionValid).withMessage('Should have a valid description'),
body('channelId').custom(isIdValid).withMessage('Should have correct video channel id'),
+ body('privacy').custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'),
body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'),
(req: express.Request, res: express.Response, next: express.NextFunction) => {
@@ -110,6 +112,7 @@ const videosUpdateValidator = [
body('licence').optional().custom(isVideoLicenceValid).withMessage('Should have a valid licence'),
body('language').optional().custom(isVideoLanguageValid).withMessage('Should have a valid language'),
body('nsfw').optional().custom(isVideoNSFWValid).withMessage('Should have a valid NSFW attribute'),
+ body('privacy').custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'),
body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'),
body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'),
@@ -118,19 +121,27 @@ const videosUpdateValidator = [
checkErrors(req, res, () => {
checkVideoExists(req.params.id, res, () => {
+ const video = res.locals.video
+
// We need to make additional checks
- if (res.locals.video.isOwned() === false) {
+ if (video.isOwned() === false) {
return res.status(403)
.json({ error: 'Cannot update video of another pod' })
.end()
}
- if (res.locals.video.VideoChannel.Author.userId !== res.locals.oauth.token.User.id) {
+ if (video.VideoChannel.Author.userId !== res.locals.oauth.token.User.id) {
return res.status(403)
.json({ error: 'Cannot update video of another user' })
.end()
}
+ if (video.privacy !== VideoPrivacy.PRIVATE && req.body.privacy === VideoPrivacy.PRIVATE) {
+ return res.status(409)
+ .json({ error: 'Cannot set "private" a video that was not private anymore.' })
+ .end()
+ }
+
next()
})
})
diff --git a/server/models/video/video-interface.ts b/server/models/video/video-interface.ts
index 587652f45..cfe65f9aa 100644
--- a/server/models/video/video-interface.ts
+++ b/server/models/video/video-interface.ts
@@ -49,6 +49,7 @@ export namespace VideoMethods {
export type ListOwnedByAuthor = (author: string) => Promise
export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList >
+ export type ListUserVideosForApi = (userId: number, start: number, count: number, sort: string) => Promise< ResultList >
export type SearchAndPopulateAuthorAndPodAndTags = (
value: string,
field: string,
@@ -75,6 +76,7 @@ export interface VideoClass {
generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData
list: VideoMethods.List
listForApi: VideoMethods.ListForApi
+ listUserVideosForApi: VideoMethods.ListUserVideosForApi
listOwnedAndPopulateAuthorAndTags: VideoMethods.ListOwnedAndPopulateAuthorAndTags
listOwnedByAuthor: VideoMethods.ListOwnedByAuthor
load: VideoMethods.Load
@@ -97,6 +99,7 @@ export interface VideoAttributes {
nsfw: boolean
description: string
duration: number
+ privacy: number
views?: number
likes?: number
dislikes?: number
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 1877c506a..2c1bd6b6e 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -18,6 +18,7 @@ import {
isVideoNSFWValid,
isVideoDescriptionValid,
isVideoDurationValid,
+ isVideoPrivacyValid,
readFileBufferPromise,
unlinkPromise,
renamePromise,
@@ -38,10 +39,11 @@ import {
THUMBNAILS_SIZE,
PREVIEWS_SIZE,
CONSTRAINTS_FIELDS,
- API_VERSION
+ API_VERSION,
+ VIDEO_PRIVACIES
} from '../../initializers'
import { removeVideoToFriends } from '../../lib'
-import { VideoResolution } from '../../../shared'
+import { VideoResolution, VideoPrivacy } from '../../../shared'
import { VideoFileInstance, VideoFileModel } from './video-file-interface'
import { addMethodsToModel, getSort } from '../utils'
@@ -79,6 +81,7 @@ let getTruncatedDescription: VideoMethods.GetTruncatedDescription
let generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData
let list: VideoMethods.List
let listForApi: VideoMethods.ListForApi
+let listUserVideosForApi: VideoMethods.ListUserVideosForApi
let loadByHostAndUUID: VideoMethods.LoadByHostAndUUID
let listOwnedAndPopulateAuthorAndTags: VideoMethods.ListOwnedAndPopulateAuthorAndTags
let listOwnedByAuthor: VideoMethods.ListOwnedByAuthor
@@ -146,6 +149,16 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
}
}
},
+ privacy: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ validate: {
+ privacyValid: value => {
+ const res = isVideoPrivacyValid(value)
+ if (res === false) throw new Error('Video privacy is not valid.')
+ }
+ }
+ },
nsfw: {
type: DataTypes.BOOLEAN,
allowNull: false,
@@ -245,6 +258,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
generateThumbnailFromData,
list,
listForApi,
+ listUserVideosForApi,
listOwnedAndPopulateAuthorAndTags,
listOwnedByAuthor,
load,
@@ -501,7 +515,13 @@ toFormattedJSON = function (this: VideoInstance) {
toFormattedDetailsJSON = function (this: VideoInstance) {
const formattedJson = this.toFormattedJSON()
+ // Maybe our pod is not up to date and there are new privacy settings since our version
+ let privacyLabel = VIDEO_PRIVACIES[this.privacy]
+ if (!privacyLabel) privacyLabel = 'Unknown'
+
const detailsJson = {
+ privacyLabel,
+ privacy: this.privacy,
descriptionPath: this.getDescriptionPath(),
channel: this.VideoChannel.toFormattedJSON(),
files: []
@@ -555,6 +575,7 @@ toAddRemoteJSON = function (this: VideoInstance) {
views: this.views,
likes: this.likes,
dislikes: this.dislikes,
+ privacy: this.privacy,
files: []
}
@@ -587,6 +608,7 @@ toUpdateRemoteJSON = function (this: VideoInstance) {
views: this.views,
likes: this.likes,
dislikes: this.dislikes,
+ privacy: this.privacy,
files: []
}
@@ -746,8 +768,39 @@ list = function () {
return Video.findAll(query)
}
+listUserVideosForApi = function (userId: number, start: number, count: number, sort: string) {
+ const query = {
+ distinct: true,
+ offset: start,
+ limit: count,
+ order: [ getSort(sort), [ Video['sequelize'].models.Tag, 'name', 'ASC' ] ],
+ include: [
+ {
+ model: Video['sequelize'].models.VideoChannel,
+ required: true,
+ include: [
+ {
+ model: Video['sequelize'].models.Author,
+ where: {
+ userId
+ },
+ required: true
+ }
+ ]
+ },
+ Video['sequelize'].models.Tag
+ ]
+ }
+
+ return Video.findAndCountAll(query).then(({ rows, count }) => {
+ return {
+ data: rows,
+ total: count
+ }
+ })
+}
+
listForApi = function (start: number, count: number, sort: string) {
- // Exclude blacklisted videos from the list
const query = {
distinct: true,
offset: start,
@@ -768,8 +821,7 @@ listForApi = function (start: number, count: number, sort: string) {
}
]
},
- Video['sequelize'].models.Tag,
- Video['sequelize'].models.VideoFile
+ Video['sequelize'].models.Tag
],
where: createBaseVideosWhere()
}
@@ -969,10 +1021,6 @@ searchAndPopulateAuthorAndPodAndTags = function (value: string, field: string, s
model: Video['sequelize'].models.Tag
}
- const videoFileInclude: Sequelize.IncludeOptions = {
- model: Video['sequelize'].models.VideoFile
- }
-
const query: Sequelize.FindOptions = {
distinct: true,
where: createBaseVideosWhere(),
@@ -981,12 +1029,7 @@ searchAndPopulateAuthorAndPodAndTags = function (value: string, field: string, s
order: [ getSort(sort), [ Video['sequelize'].models.Tag, 'name', 'ASC' ] ]
}
- // Make an exact search with the magnet
- if (field === 'magnetUri') {
- videoFileInclude.where = {
- infoHash: magnetUtil.decode(value).infoHash
- }
- } else if (field === 'tags') {
+ if (field === 'tags') {
const escapedValue = Video['sequelize'].escape('%' + value + '%')
query.where['id'][Sequelize.Op.in] = Video['sequelize'].literal(
`(SELECT "VideoTags"."videoId"
@@ -1016,7 +1059,7 @@ searchAndPopulateAuthorAndPodAndTags = function (value: string, field: string, s
}
query.include = [
- videoChannelInclude, tagInclude, videoFileInclude
+ videoChannelInclude, tagInclude
]
return Video.findAndCountAll(query).then(({ rows, count }) => {
@@ -1035,7 +1078,8 @@ function createBaseVideosWhere () {
[Sequelize.Op.notIn]: Video['sequelize'].literal(
'(SELECT "BlacklistedVideos"."videoId" FROM "BlacklistedVideos")'
)
- }
+ },
+ privacy: VideoPrivacy.PUBLIC
}
}
diff --git a/shared/models/pods/remote-video/remote-video-create-request.model.ts b/shared/models/pods/remote-video/remote-video-create-request.model.ts
index cb20dfa03..9a382e654 100644
--- a/shared/models/pods/remote-video/remote-video-create-request.model.ts
+++ b/shared/models/pods/remote-video/remote-video-create-request.model.ts
@@ -16,6 +16,7 @@ export interface RemoteVideoCreateData {
views: number
likes: number
dislikes: number
+ privacy: number
thumbnailData: string
files: {
infoHash: string
diff --git a/shared/models/pods/remote-video/remote-video-update-request.model.ts b/shared/models/pods/remote-video/remote-video-update-request.model.ts
index 8439cfa24..924489c75 100644
--- a/shared/models/pods/remote-video/remote-video-update-request.model.ts
+++ b/shared/models/pods/remote-video/remote-video-update-request.model.ts
@@ -15,6 +15,7 @@ export interface RemoteVideoUpdateData {
views: number
likes: number
dislikes: number
+ privacy: number
files: {
infoHash: string
extname: string
diff --git a/shared/models/videos/index.ts b/shared/models/videos/index.ts
index 2a3912f06..14a10f5d8 100644
--- a/shared/models/videos/index.ts
+++ b/shared/models/videos/index.ts
@@ -8,6 +8,7 @@ export * from './video-channel-create.model'
export * from './video-channel-update.model'
export * from './video-channel.model'
export * from './video-create.model'
+export * from './video-privacy.enum'
export * from './video-rate.type'
export * from './video-resolution.enum'
export * from './video-update.model'
diff --git a/shared/models/videos/video-create.model.ts b/shared/models/videos/video-create.model.ts
index 4d0e83520..e537c38a8 100644
--- a/shared/models/videos/video-create.model.ts
+++ b/shared/models/videos/video-create.model.ts
@@ -1,3 +1,5 @@
+import { VideoPrivacy } from './video-privacy.enum'
+
export interface VideoCreate {
category: number
licence: number
@@ -7,4 +9,5 @@ export interface VideoCreate {
nsfw: boolean
name: string
tags: string[]
+ privacy: VideoPrivacy
}
diff --git a/shared/models/videos/video-privacy.enum.ts b/shared/models/videos/video-privacy.enum.ts
new file mode 100644
index 000000000..29888c7b8
--- /dev/null
+++ b/shared/models/videos/video-privacy.enum.ts
@@ -0,0 +1,5 @@
+export enum VideoPrivacy {
+ PUBLIC = 1,
+ UNLISTED = 2,
+ PRIVATE = 3
+}
diff --git a/shared/models/videos/video-update.model.ts b/shared/models/videos/video-update.model.ts
index 29a82621b..0cf38fe6e 100644
--- a/shared/models/videos/video-update.model.ts
+++ b/shared/models/videos/video-update.model.ts
@@ -1,9 +1,12 @@
+import { VideoPrivacy } from './video-privacy.enum'
+
export interface VideoUpdate {
name?: string
category?: number
licence?: number
language?: number
description?: string
+ privacy?: VideoPrivacy
tags?: string[]
nsfw?: boolean
}
diff --git a/shared/models/videos/video.model.ts b/shared/models/videos/video.model.ts
index 1490d345c..2f4ee2462 100644
--- a/shared/models/videos/video.model.ts
+++ b/shared/models/videos/video.model.ts
@@ -1,4 +1,5 @@
import { VideoChannel } from './video-channel.model'
+import { VideoPrivacy } from './video-privacy.enum'
export interface VideoFile {
magnetUri: string
@@ -37,7 +38,9 @@ export interface Video {
}
export interface VideoDetails extends Video {
- descriptionPath: string,
+ privacy: VideoPrivacy
+ privacyLabel: string
+ descriptionPath: string
channel: VideoChannel
files: VideoFile[]
}