Merge branch 'develop' of framagit.org:chocobozzz/PeerTube into develop
This commit is contained in:
commit
0db1a22650
|
@ -146,3 +146,9 @@ Build the application and run the unit/integration tests:
|
||||||
$ npm run build
|
$ npm run build
|
||||||
$ npm test
|
$ npm test
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you just want to run 1 test:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ npm run mocha -- --exit --require ts-node/register/type-check --bail server/tests/api/index.ts
|
||||||
|
```
|
||||||
|
|
|
@ -62,6 +62,22 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="instanceDefaultNSFWPolicy">Policy on videos containing sensitive content</label>
|
||||||
|
<my-help helpType="custom" customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."></my-help>
|
||||||
|
|
||||||
|
<div class="peertube-select-container">
|
||||||
|
<select id="instanceDefaultNSFWPolicy" formControlName="instanceDefaultNSFWPolicy">
|
||||||
|
<option value="do_not_list">Do not list</option>
|
||||||
|
<option value="blur">Blur thumbnails</option>
|
||||||
|
<option value="display">Display</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="formErrors.instanceDefaultNSFWPolicy" class="form-error">
|
||||||
|
{{ formErrors.instanceDefaultNSFWPolicy }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="inner-form-title">Cache</div>
|
<div class="inner-form-title">Cache</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
|
@ -48,6 +48,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
||||||
instanceDescription: '',
|
instanceDescription: '',
|
||||||
instanceTerms: '',
|
instanceTerms: '',
|
||||||
instanceDefaultClientRoute: '',
|
instanceDefaultClientRoute: '',
|
||||||
|
instanceDefaultNSFWPolicy: '',
|
||||||
cachePreviewsSize: '',
|
cachePreviewsSize: '',
|
||||||
signupLimit: '',
|
signupLimit: '',
|
||||||
adminEmail: '',
|
adminEmail: '',
|
||||||
|
@ -90,6 +91,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
||||||
instanceDescription: [ '' ],
|
instanceDescription: [ '' ],
|
||||||
instanceTerms: [ '' ],
|
instanceTerms: [ '' ],
|
||||||
instanceDefaultClientRoute: [ '' ],
|
instanceDefaultClientRoute: [ '' ],
|
||||||
|
instanceDefaultNSFWPolicy: [ '' ],
|
||||||
cachePreviewsSize: [ '', CACHE_PREVIEWS_SIZE.VALIDATORS ],
|
cachePreviewsSize: [ '', CACHE_PREVIEWS_SIZE.VALIDATORS ],
|
||||||
signupEnabled: [ ],
|
signupEnabled: [ ],
|
||||||
signupLimit: [ '', SIGNUP_LIMIT.VALIDATORS ],
|
signupLimit: [ '', SIGNUP_LIMIT.VALIDATORS ],
|
||||||
|
@ -167,6 +169,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
||||||
description: this.form.value['instanceDescription'],
|
description: this.form.value['instanceDescription'],
|
||||||
terms: this.form.value['instanceTerms'],
|
terms: this.form.value['instanceTerms'],
|
||||||
defaultClientRoute: this.form.value['instanceDefaultClientRoute'],
|
defaultClientRoute: this.form.value['instanceDefaultClientRoute'],
|
||||||
|
defaultNSFWPolicy: this.form.value['instanceDefaultNSFWPolicy'],
|
||||||
customizations: {
|
customizations: {
|
||||||
javascript: this.form.value['customizationJavascript'],
|
javascript: this.form.value['customizationJavascript'],
|
||||||
css: this.form.value['customizationCSS']
|
css: this.form.value['customizationCSS']
|
||||||
|
@ -224,6 +227,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
||||||
instanceDescription: this.customConfig.instance.description,
|
instanceDescription: this.customConfig.instance.description,
|
||||||
instanceTerms: this.customConfig.instance.terms,
|
instanceTerms: this.customConfig.instance.terms,
|
||||||
instanceDefaultClientRoute: this.customConfig.instance.defaultClientRoute,
|
instanceDefaultClientRoute: this.customConfig.instance.defaultClientRoute,
|
||||||
|
instanceDefaultNSFWPolicy: this.customConfig.instance.defaultNSFWPolicy,
|
||||||
cachePreviewsSize: this.customConfig.cache.previews.size,
|
cachePreviewsSize: this.customConfig.cache.previews.size,
|
||||||
signupEnabled: this.customConfig.signup.enabled,
|
signupEnabled: this.customConfig.signup.enabled,
|
||||||
signupLimit: this.customConfig.signup.limit,
|
signupLimit: this.customConfig.signup.limit,
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
<form role="form" (ngSubmit)="updateDetails()" [formGroup]="form">
|
<form role="form" (ngSubmit)="updateDetails()" [formGroup]="form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input
|
<label for="nsfwPolicy">Default policy on videos containing sensitive content</label>
|
||||||
type="checkbox" id="displayNSFW"
|
<my-help helpType="custom" customHtml="With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video."></my-help>
|
||||||
formControlName="displayNSFW"
|
|
||||||
>
|
<div class="peertube-select-container">
|
||||||
<label for="displayNSFW"></label>
|
<select id="nsfwPolicy" formControlName="nsfwPolicy">
|
||||||
<label for="displayNSFW">Display videos that contain mature or explicit content</label>
|
<option value="do_not_list">Do not list</option>
|
||||||
|
<option value="blur">Blur thumbnails</option>
|
||||||
|
<option value="display">Display</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
|
@ -12,3 +12,9 @@ input[type=submit] {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.peertube-select-container {
|
||||||
|
@include peertube-select-container(340px);
|
||||||
|
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
|
@ -29,7 +29,7 @@ export class AccountDetailsComponent extends FormReactive implements OnInit {
|
||||||
|
|
||||||
buildForm () {
|
buildForm () {
|
||||||
this.form = this.formBuilder.group({
|
this.form = this.formBuilder.group({
|
||||||
displayNSFW: [ this.user.displayNSFW ],
|
nsfwPolicy: [ this.user.nsfwPolicy ],
|
||||||
autoPlayVideo: [ this.user.autoPlayVideo ]
|
autoPlayVideo: [ this.user.autoPlayVideo ]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -41,10 +41,10 @@ export class AccountDetailsComponent extends FormReactive implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDetails () {
|
updateDetails () {
|
||||||
const displayNSFW = this.form.value['displayNSFW']
|
const nsfwPolicy = this.form.value['nsfwPolicy']
|
||||||
const autoPlayVideo = this.form.value['autoPlayVideo']
|
const autoPlayVideo = this.form.value['autoPlayVideo']
|
||||||
const details: UserUpdateMe = {
|
const details: UserUpdateMe = {
|
||||||
displayNSFW,
|
nsfwPolicy,
|
||||||
autoPlayVideo
|
autoPlayVideo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
<div class="video-info">
|
<div class="video-info">
|
||||||
<a class="video-info-name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name">{{ video.name }}</a>
|
<a class="video-info-name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name">{{ video.name }}</a>
|
||||||
<span class="video-info-date-views">{{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span>
|
<span class="video-info-date-views">{{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span>
|
||||||
|
<div class="video-info-private">{{ video.privacy.label }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Display only once -->
|
<!-- Display only once -->
|
||||||
|
|
|
@ -79,8 +79,12 @@
|
||||||
font-weight: $font-semibold;
|
font-weight: $font-semibold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-info-date-views {
|
.video-info-date-views, .video-info-private {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|
||||||
|
&.video-info-private {
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { UserRight } from '../../../../../shared/models/users/user-right.enum'
|
||||||
// Do not use the barrel (dependency loop)
|
// Do not use the barrel (dependency loop)
|
||||||
import { hasUserRight, UserRole } from '../../../../../shared/models/users/user-role'
|
import { hasUserRight, UserRole } from '../../../../../shared/models/users/user-role'
|
||||||
import { User, UserConstructorHash } from '../../shared/users/user.model'
|
import { User, UserConstructorHash } from '../../shared/users/user.model'
|
||||||
|
import { NSFWPolicyType } from '../../../../../shared/models/videos/nsfw-policy.type'
|
||||||
|
|
||||||
export type TokenOptions = {
|
export type TokenOptions = {
|
||||||
accessToken: string
|
accessToken: string
|
||||||
|
@ -70,7 +71,7 @@ export class AuthUser extends User {
|
||||||
ROLE: 'role',
|
ROLE: 'role',
|
||||||
EMAIL: 'email',
|
EMAIL: 'email',
|
||||||
USERNAME: 'username',
|
USERNAME: 'username',
|
||||||
DISPLAY_NSFW: 'display_nsfw',
|
NSFW_POLICY: 'nsfw_policy',
|
||||||
AUTO_PLAY_VIDEO: 'auto_play_video'
|
AUTO_PLAY_VIDEO: 'auto_play_video'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +86,7 @@ export class AuthUser extends User {
|
||||||
username: peertubeLocalStorage.getItem(this.KEYS.USERNAME),
|
username: peertubeLocalStorage.getItem(this.KEYS.USERNAME),
|
||||||
email: peertubeLocalStorage.getItem(this.KEYS.EMAIL),
|
email: peertubeLocalStorage.getItem(this.KEYS.EMAIL),
|
||||||
role: parseInt(peertubeLocalStorage.getItem(this.KEYS.ROLE), 10) as UserRole,
|
role: parseInt(peertubeLocalStorage.getItem(this.KEYS.ROLE), 10) as UserRole,
|
||||||
displayNSFW: peertubeLocalStorage.getItem(this.KEYS.DISPLAY_NSFW) === 'true',
|
nsfwPolicy: peertubeLocalStorage.getItem(this.KEYS.NSFW_POLICY) as NSFWPolicyType,
|
||||||
autoPlayVideo: peertubeLocalStorage.getItem(this.KEYS.AUTO_PLAY_VIDEO) === 'true'
|
autoPlayVideo: peertubeLocalStorage.getItem(this.KEYS.AUTO_PLAY_VIDEO) === 'true'
|
||||||
},
|
},
|
||||||
Tokens.load()
|
Tokens.load()
|
||||||
|
@ -99,7 +100,7 @@ export class AuthUser extends User {
|
||||||
peertubeLocalStorage.removeItem(this.KEYS.USERNAME)
|
peertubeLocalStorage.removeItem(this.KEYS.USERNAME)
|
||||||
peertubeLocalStorage.removeItem(this.KEYS.ID)
|
peertubeLocalStorage.removeItem(this.KEYS.ID)
|
||||||
peertubeLocalStorage.removeItem(this.KEYS.ROLE)
|
peertubeLocalStorage.removeItem(this.KEYS.ROLE)
|
||||||
peertubeLocalStorage.removeItem(this.KEYS.DISPLAY_NSFW)
|
peertubeLocalStorage.removeItem(this.KEYS.NSFW_POLICY)
|
||||||
peertubeLocalStorage.removeItem(this.KEYS.AUTO_PLAY_VIDEO)
|
peertubeLocalStorage.removeItem(this.KEYS.AUTO_PLAY_VIDEO)
|
||||||
peertubeLocalStorage.removeItem(this.KEYS.EMAIL)
|
peertubeLocalStorage.removeItem(this.KEYS.EMAIL)
|
||||||
Tokens.flush()
|
Tokens.flush()
|
||||||
|
@ -136,7 +137,7 @@ export class AuthUser extends User {
|
||||||
peertubeLocalStorage.setItem(AuthUser.KEYS.USERNAME, this.username)
|
peertubeLocalStorage.setItem(AuthUser.KEYS.USERNAME, this.username)
|
||||||
peertubeLocalStorage.setItem(AuthUser.KEYS.EMAIL, this.email)
|
peertubeLocalStorage.setItem(AuthUser.KEYS.EMAIL, this.email)
|
||||||
peertubeLocalStorage.setItem(AuthUser.KEYS.ROLE, this.role.toString())
|
peertubeLocalStorage.setItem(AuthUser.KEYS.ROLE, this.role.toString())
|
||||||
peertubeLocalStorage.setItem(AuthUser.KEYS.DISPLAY_NSFW, JSON.stringify(this.displayNSFW))
|
peertubeLocalStorage.setItem(AuthUser.KEYS.NSFW_POLICY, this.nsfwPolicy.toString())
|
||||||
peertubeLocalStorage.setItem(AuthUser.KEYS.AUTO_PLAY_VIDEO, JSON.stringify(this.autoPlayVideo))
|
peertubeLocalStorage.setItem(AuthUser.KEYS.AUTO_PLAY_VIDEO, JSON.stringify(this.autoPlayVideo))
|
||||||
this.tokens.save()
|
this.tokens.save()
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import 'rxjs/add/operator/do'
|
||||||
import { ReplaySubject } from 'rxjs/ReplaySubject'
|
import { ReplaySubject } from 'rxjs/ReplaySubject'
|
||||||
import { ServerConfig } from '../../../../../shared'
|
import { ServerConfig } from '../../../../../shared'
|
||||||
import { About } from '../../../../../shared/models/server/about.model'
|
import { About } from '../../../../../shared/models/server/about.model'
|
||||||
import { ServerStats } from '../../../../../shared/models/server/server-stats.model'
|
|
||||||
import { environment } from '../../../environments/environment'
|
import { environment } from '../../../environments/environment'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -26,6 +25,7 @@ export class ServerService {
|
||||||
shortDescription: 'PeerTube, a federated (ActivityPub) video streaming platform ' +
|
shortDescription: 'PeerTube, a federated (ActivityPub) video streaming platform ' +
|
||||||
'using P2P (BitTorrent) directly in the web browser with WebTorrent and Angular.',
|
'using P2P (BitTorrent) directly in the web browser with WebTorrent and Angular.',
|
||||||
defaultClientRoute: '',
|
defaultClientRoute: '',
|
||||||
|
defaultNSFWPolicy: 'do_not_list' as 'do_not_list',
|
||||||
customizations: {
|
customizations: {
|
||||||
javascript: '',
|
javascript: '',
|
||||||
css: ''
|
css: ''
|
||||||
|
|
|
@ -13,6 +13,9 @@
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class="help-tooltip-button" containerClass="help-tooltip" title="Click to get help"
|
class="help-tooltip-button"
|
||||||
#tooltipDirective="bs-tooltip" [tooltip]="tooltipTemplate" triggers="click"
|
title="Get help"
|
||||||
|
[popover]="tooltipTemplate"
|
||||||
|
[placement]="tooltipPlacement"
|
||||||
|
[outsideClick]="true"
|
||||||
></span>
|
></span>
|
||||||
|
|
|
@ -12,20 +12,16 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/ {
|
/deep/ {
|
||||||
.help-tooltip {
|
.popover-body {
|
||||||
opacity: 1 !important;
|
text-align: left;
|
||||||
|
padding: 10px;
|
||||||
|
max-width: 300px;
|
||||||
|
|
||||||
.tooltip-inner {
|
font-size: 13px;
|
||||||
text-align: left;
|
font-family: $main-fonts;
|
||||||
padding: 10px;
|
background-color: #fff;
|
||||||
max-width: 300px;
|
color: #000;
|
||||||
|
box-shadow: 0 0 6px rgba(0, 0, 0, 0.5);
|
||||||
font-size: 13px;
|
|
||||||
font-family: $main-fonts;
|
|
||||||
background-color: #fff;
|
|
||||||
color: #000;
|
|
||||||
box-shadow: 0 0 6px rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { Component, ElementRef, HostListener, Input, OnInit, ViewChild, OnChanges } from '@angular/core'
|
import { Component, Input, OnChanges, OnInit } from '@angular/core'
|
||||||
import { MarkdownService } from '@app/videos/shared'
|
import { MarkdownService } from '@app/videos/shared'
|
||||||
import { TooltipDirective } from 'ngx-bootstrap/tooltip'
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-help',
|
selector: 'my-help',
|
||||||
|
@ -9,16 +8,14 @@ import { TooltipDirective } from 'ngx-bootstrap/tooltip'
|
||||||
})
|
})
|
||||||
|
|
||||||
export class HelpComponent implements OnInit, OnChanges {
|
export class HelpComponent implements OnInit, OnChanges {
|
||||||
@ViewChild('tooltipDirective') tooltipDirective: TooltipDirective
|
|
||||||
@Input() preHtml = ''
|
@Input() preHtml = ''
|
||||||
@Input() postHtml = ''
|
@Input() postHtml = ''
|
||||||
@Input() customHtml = ''
|
@Input() customHtml = ''
|
||||||
@Input() helpType: 'custom' | 'markdownText' | 'markdownEnhanced' = 'custom'
|
@Input() helpType: 'custom' | 'markdownText' | 'markdownEnhanced' = 'custom'
|
||||||
|
@Input() tooltipPlacement = 'right'
|
||||||
|
|
||||||
mainHtml = ''
|
mainHtml = ''
|
||||||
|
|
||||||
constructor (private elementRef: ElementRef) { }
|
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
this.init()
|
this.init()
|
||||||
}
|
}
|
||||||
|
@ -27,15 +24,6 @@ export class HelpComponent implements OnInit, OnChanges {
|
||||||
this.init()
|
this.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('document:click', ['$event.target'])
|
|
||||||
public onClick (targetElement) {
|
|
||||||
const clickedInside = this.elementRef.nativeElement.contains(targetElement)
|
|
||||||
|
|
||||||
if (this.tooltipDirective.isOpen && !clickedInside) {
|
|
||||||
this.tooltipDirective.hide()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private init () {
|
private init () {
|
||||||
if (this.helpType === 'custom') {
|
if (this.helpType === 'custom') {
|
||||||
this.mainHtml = this.customHtml
|
this.mainHtml = this.customHtml
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { hasUserRight, User as UserServerModel, UserRight, UserRole, VideoChannel } from '../../../../../shared'
|
import { hasUserRight, User as UserServerModel, UserRight, UserRole, VideoChannel } from '../../../../../shared'
|
||||||
import { Account } from '../account/account.model'
|
import { Account } from '../account/account.model'
|
||||||
|
import { NSFWPolicyType } from '../../../../../shared/models/videos/nsfw-policy.type'
|
||||||
|
|
||||||
export type UserConstructorHash = {
|
export type UserConstructorHash = {
|
||||||
id: number,
|
id: number,
|
||||||
|
@ -7,7 +8,7 @@ export type UserConstructorHash = {
|
||||||
email: string,
|
email: string,
|
||||||
role: UserRole,
|
role: UserRole,
|
||||||
videoQuota?: number,
|
videoQuota?: number,
|
||||||
displayNSFW?: boolean,
|
nsfwPolicy?: NSFWPolicyType,
|
||||||
autoPlayVideo?: boolean,
|
autoPlayVideo?: boolean,
|
||||||
createdAt?: Date,
|
createdAt?: Date,
|
||||||
account?: Account,
|
account?: Account,
|
||||||
|
@ -18,7 +19,7 @@ export class User implements UserServerModel {
|
||||||
username: string
|
username: string
|
||||||
email: string
|
email: string
|
||||||
role: UserRole
|
role: UserRole
|
||||||
displayNSFW: boolean
|
nsfwPolicy: NSFWPolicyType
|
||||||
autoPlayVideo: boolean
|
autoPlayVideo: boolean
|
||||||
videoQuota: number
|
videoQuota: number
|
||||||
account: Account
|
account: Account
|
||||||
|
@ -40,8 +41,8 @@ export class User implements UserServerModel {
|
||||||
this.videoQuota = hash.videoQuota
|
this.videoQuota = hash.videoQuota
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hash.displayNSFW !== undefined) {
|
if (hash.nsfwPolicy !== undefined) {
|
||||||
this.displayNSFW = hash.displayNSFW
|
this.nsfwPolicy = hash.nsfwPolicy
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hash.autoPlayVideo !== undefined) {
|
if (hash.autoPlayVideo !== undefined) {
|
||||||
|
|
|
@ -1,17 +1,9 @@
|
||||||
import {
|
import { UserRight, VideoChannel, VideoDetails as VideoDetailsServerModel, VideoFile } from '../../../../../shared'
|
||||||
UserRight,
|
|
||||||
VideoChannel,
|
|
||||||
VideoDetails as VideoDetailsServerModel,
|
|
||||||
VideoFile,
|
|
||||||
VideoPrivacy
|
|
||||||
} from '../../../../../shared'
|
|
||||||
import { Account } from '../../../../../shared/models/actors'
|
import { Account } from '../../../../../shared/models/actors'
|
||||||
import { VideoConstant } from '../../../../../shared/models/videos/video.model'
|
|
||||||
import { AuthUser } from '../../core'
|
import { AuthUser } from '../../core'
|
||||||
import { Video } from '../../shared/video/video.model'
|
import { Video } from '../../shared/video/video.model'
|
||||||
|
|
||||||
export class VideoDetails extends Video implements VideoDetailsServerModel {
|
export class VideoDetails extends Video implements VideoDetailsServerModel {
|
||||||
privacy: VideoConstant<VideoPrivacy>
|
|
||||||
descriptionPath: string
|
descriptionPath: string
|
||||||
support: string
|
support: string
|
||||||
channel: VideoChannel
|
channel: VideoChannel
|
||||||
|
@ -26,7 +18,6 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
|
||||||
constructor (hash: VideoDetailsServerModel) {
|
constructor (hash: VideoDetailsServerModel) {
|
||||||
super(hash)
|
super(hash)
|
||||||
|
|
||||||
this.privacy = hash.privacy
|
|
||||||
this.descriptionPath = hash.descriptionPath
|
this.descriptionPath = hash.descriptionPath
|
||||||
this.files = hash.files
|
this.files = hash.files
|
||||||
this.channel = hash.channel
|
this.channel = hash.channel
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<div class="video-miniature">
|
<div class="video-miniature">
|
||||||
<my-video-thumbnail [video]="video" [nsfw]="isVideoNSFWForThisUser()"></my-video-thumbnail>
|
<my-video-thumbnail [video]="video" [nsfw]="isVideoBlur()"></my-video-thumbnail>
|
||||||
|
|
||||||
<div class="video-miniature-information">
|
<div class="video-miniature-information">
|
||||||
<span class="video-miniature-name">
|
<span class="video-miniature-name">
|
||||||
<a
|
<a
|
||||||
class="video-miniature-name"
|
class="video-miniature-name"
|
||||||
[routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name" [ngClass]="{ 'blur-filter': isVideoNSFWForThisUser() }"
|
[routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name" [ngClass]="{ 'blur-filter': isVideoBlur() }"
|
||||||
>
|
>
|
||||||
{{ video.name }}
|
{{ video.name }}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Component, Input } from '@angular/core'
|
import { Component, Input } from '@angular/core'
|
||||||
import { User } from '../users'
|
import { User } from '../users'
|
||||||
import { Video } from './video.model'
|
import { Video } from './video.model'
|
||||||
|
import { ServerService } from '@app/core'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-miniature',
|
selector: 'my-video-miniature',
|
||||||
|
@ -11,7 +12,9 @@ export class VideoMiniatureComponent {
|
||||||
@Input() user: User
|
@Input() user: User
|
||||||
@Input() video: Video
|
@Input() video: Video
|
||||||
|
|
||||||
isVideoNSFWForThisUser () {
|
constructor (private serverService: ServerService) { }
|
||||||
return this.video.isVideoNSFWForUser(this.user)
|
|
||||||
|
isVideoBlur () {
|
||||||
|
return this.video.isVideoNSFWForUser(this.user, this.serverService.getConfig())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { Account } from '@app/shared/account/account.model'
|
import { Account } from '@app/shared/account/account.model'
|
||||||
import { User } from '../'
|
import { User } from '../'
|
||||||
import { Video as VideoServerModel } from '../../../../../shared'
|
import { Video as VideoServerModel, VideoPrivacy } from '../../../../../shared'
|
||||||
import { Avatar } from '../../../../../shared/models/avatars/avatar.model'
|
import { Avatar } from '../../../../../shared/models/avatars/avatar.model'
|
||||||
import { VideoConstant } from '../../../../../shared/models/videos/video.model'
|
import { VideoConstant } from '../../../../../shared/models/videos/video.model'
|
||||||
import { getAbsoluteAPIUrl } from '../misc/utils'
|
import { getAbsoluteAPIUrl } from '../misc/utils'
|
||||||
|
import { ServerConfig } from '../../../../../shared/models'
|
||||||
|
|
||||||
export class Video implements VideoServerModel {
|
export class Video implements VideoServerModel {
|
||||||
by: string
|
by: string
|
||||||
|
@ -13,6 +14,7 @@ export class Video implements VideoServerModel {
|
||||||
category: VideoConstant<number>
|
category: VideoConstant<number>
|
||||||
licence: VideoConstant<number>
|
licence: VideoConstant<number>
|
||||||
language: VideoConstant<number>
|
language: VideoConstant<number>
|
||||||
|
privacy: VideoConstant<VideoPrivacy>
|
||||||
description: string
|
description: string
|
||||||
duration: number
|
duration: number
|
||||||
durationLabel: string
|
durationLabel: string
|
||||||
|
@ -61,6 +63,7 @@ export class Video implements VideoServerModel {
|
||||||
this.category = hash.category
|
this.category = hash.category
|
||||||
this.licence = hash.licence
|
this.licence = hash.licence
|
||||||
this.language = hash.language
|
this.language = hash.language
|
||||||
|
this.privacy = hash.privacy
|
||||||
this.description = hash.description
|
this.description = hash.description
|
||||||
this.duration = hash.duration
|
this.duration = hash.duration
|
||||||
this.durationLabel = Video.createDurationString(hash.duration)
|
this.durationLabel = Video.createDurationString(hash.duration)
|
||||||
|
@ -83,8 +86,14 @@ export class Video implements VideoServerModel {
|
||||||
this.by = Account.CREATE_BY_STRING(hash.account.name, hash.account.host)
|
this.by = Account.CREATE_BY_STRING(hash.account.name, hash.account.host)
|
||||||
}
|
}
|
||||||
|
|
||||||
isVideoNSFWForUser (user: User) {
|
isVideoNSFWForUser (user: User, serverConfig: ServerConfig) {
|
||||||
// If the video is NSFW and the user is not logged in, or the user does not want to display NSFW videos...
|
// Video is not NSFW, skip
|
||||||
return (this.nsfw && (!user || user.displayNSFW === false))
|
if (this.nsfw === false) return false
|
||||||
|
|
||||||
|
// Return user setting if logged in
|
||||||
|
if (user) return user.nsfwPolicy !== 'display'
|
||||||
|
|
||||||
|
// Return default instance config
|
||||||
|
return serverConfig.instance.defaultNSFWPolicy !== 'display'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,7 @@
|
||||||
<input type="checkbox" id="nsfw" formControlName="nsfw" />
|
<input type="checkbox" id="nsfw" formControlName="nsfw" />
|
||||||
<label for="nsfw"></label>
|
<label for="nsfw"></label>
|
||||||
<label for="nsfw">This video contains mature or explicit content</label>
|
<label for="nsfw">This video contains mature or explicit content</label>
|
||||||
|
<my-help tooltipPlacement="top" helpType="custom" customHtml="Some instances do not list NSFW videos by default."></my-help>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group form-group-checkbox">
|
<div class="form-group form-group-checkbox">
|
||||||
|
|
|
@ -9,6 +9,10 @@
|
||||||
@include peertube-select-disabled-container(auto);
|
@include peertube-select-disabled-container(auto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-group-checkbox {
|
||||||
|
my-help { margin-left: 5px }
|
||||||
|
}
|
||||||
|
|
||||||
.video-edit {
|
.video-edit {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import { VideoDownloadComponent } from './modal/video-download.component'
|
||||||
import { VideoReportComponent } from './modal/video-report.component'
|
import { VideoReportComponent } from './modal/video-report.component'
|
||||||
import { VideoShareComponent } from './modal/video-share.component'
|
import { VideoShareComponent } from './modal/video-share.component'
|
||||||
import { getVideojsOptions } from '../../../assets/player/peertube-player'
|
import { getVideojsOptions } from '../../../assets/player/peertube-player'
|
||||||
|
import { ServerService } from '@app/core'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-watch',
|
selector: 'my-video-watch',
|
||||||
|
@ -66,6 +67,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
private confirmService: ConfirmService,
|
private confirmService: ConfirmService,
|
||||||
private metaService: MetaService,
|
private metaService: MetaService,
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
|
private serverService: ServerService,
|
||||||
private notificationsService: NotificationsService,
|
private notificationsService: NotificationsService,
|
||||||
private markdownService: MarkdownService,
|
private markdownService: MarkdownService,
|
||||||
private zone: NgZone,
|
private zone: NgZone,
|
||||||
|
@ -335,7 +337,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
this.updateOtherVideosDisplayed()
|
this.updateOtherVideosDisplayed()
|
||||||
|
|
||||||
if (this.video.isVideoNSFWForUser(this.user)) {
|
if (this.video.isVideoNSFWForUser(this.user, this.serverService.getConfig())) {
|
||||||
const res = await this.confirmService.confirm(
|
const res = await this.confirmService.confirm(
|
||||||
'This video contains mature or explicit content. Are you sure you want to watch it?',
|
'This video contains mature or explicit content. Are you sure you want to watch it?',
|
||||||
'Mature or explicit content'
|
'Mature or explicit content'
|
||||||
|
|
|
@ -11,6 +11,12 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
<div id="error-block">
|
||||||
|
<h1 id="error-title">Sorry</h1>
|
||||||
|
|
||||||
|
<div id="error-content"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<video id="video-container" class="video-js vjs-peertube-skin">
|
<video id="video-container" class="video-js vjs-peertube-skin">
|
||||||
</video>
|
</video>
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,16 @@
|
||||||
@import '~videojs-dock/dist/videojs-dock.css';
|
@import '~videojs-dock/dist/videojs-dock.css';
|
||||||
@import '../../sass/video-js-custom.scss';
|
@import '../../sass/video-js-custom.scss';
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: $main-fonts;
|
||||||
|
font-weight: $font-regular;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
video {
|
video {
|
||||||
width: 99%;
|
width: 99%;
|
||||||
}
|
}
|
||||||
|
@ -43,3 +53,38 @@ html, body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#error-block {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
flex-direction: column;
|
||||||
|
align-content: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #141313;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
color: white;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: sans-serif;
|
||||||
|
|
||||||
|
#error-title {
|
||||||
|
font-size: 45px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#error-content {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 300px) {
|
||||||
|
#error-block {
|
||||||
|
font-size: 36px;
|
||||||
|
|
||||||
|
#error-content {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,19 +9,53 @@ function getVideoUrl (id: string) {
|
||||||
return window.location.origin + '/api/v1/videos/' + id
|
return window.location.origin + '/api/v1/videos/' + id
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadVideoInfo (videoId: string): Promise<VideoDetails> {
|
function loadVideoInfo (videoId: string): Promise<Response> {
|
||||||
const response = await fetch(getVideoUrl(videoId))
|
return fetch(getVideoUrl(videoId))
|
||||||
return response.json()
|
}
|
||||||
|
|
||||||
|
function removeElement (element: HTMLElement) {
|
||||||
|
element.parentElement.removeChild(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayError (videoElement: HTMLVideoElement, text: string) {
|
||||||
|
// Remove video element
|
||||||
|
removeElement(videoElement)
|
||||||
|
|
||||||
|
document.title = 'Sorry - ' + text
|
||||||
|
|
||||||
|
const errorBlock = document.getElementById('error-block')
|
||||||
|
errorBlock.style.display = 'flex'
|
||||||
|
|
||||||
|
const errorText = document.getElementById('error-content')
|
||||||
|
errorText.innerHTML = text
|
||||||
|
}
|
||||||
|
|
||||||
|
function videoNotFound (videoElement: HTMLVideoElement) {
|
||||||
|
const text = 'This video does not exist.'
|
||||||
|
displayError(videoElement, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
function videoFetchError (videoElement: HTMLVideoElement) {
|
||||||
|
const text = 'We cannot fetch the video. Please try again later.'
|
||||||
|
displayError(videoElement, text)
|
||||||
}
|
}
|
||||||
|
|
||||||
const urlParts = window.location.href.split('/')
|
const urlParts = window.location.href.split('/')
|
||||||
const videoId = urlParts[urlParts.length - 1]
|
const videoId = urlParts[urlParts.length - 1]
|
||||||
|
|
||||||
loadVideoInfo(videoId)
|
loadVideoInfo(videoId)
|
||||||
.then(videoInfo => {
|
.then(async response => {
|
||||||
const videoContainerId = 'video-container'
|
const videoContainerId = 'video-container'
|
||||||
|
|
||||||
const videoElement = document.getElementById(videoContainerId) as HTMLVideoElement
|
const videoElement = document.getElementById(videoContainerId) as HTMLVideoElement
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
if (response.status === 404) return videoNotFound(videoElement)
|
||||||
|
|
||||||
|
return videoFetchError(videoElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
const videoInfo: VideoDetails = await response.json()
|
||||||
|
|
||||||
let autoplay = false
|
let autoplay = false
|
||||||
let startTime = 0
|
let startTime = 0
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,9 @@ instance:
|
||||||
description: 'Welcome to this PeerTube instance!' # Support markdown
|
description: 'Welcome to this PeerTube instance!' # Support markdown
|
||||||
terms: 'No terms for now.' # Support markdown
|
terms: 'No terms for now.' # Support markdown
|
||||||
default_client_route: '/videos/trending'
|
default_client_route: '/videos/trending'
|
||||||
|
# By default, "do_not_list" or "blur" or "display" NSFW videos
|
||||||
|
# Could be overridden per user with a setting
|
||||||
|
default_nsfw_policy: 'do_not_list'
|
||||||
customizations:
|
customizations:
|
||||||
javascript: '' # Directly your JavaScript code (without <script> tags). Will be eval at runtime
|
javascript: '' # Directly your JavaScript code (without <script> tags). Will be eval at runtime
|
||||||
css: '' # Directly your CSS code (without <style> tags). Will be injected at runtime
|
css: '' # Directly your CSS code (without <style> tags). Will be injected at runtime
|
||||||
|
|
|
@ -100,6 +100,9 @@ instance:
|
||||||
description: '' # Support markdown
|
description: '' # Support markdown
|
||||||
terms: '' # Support markdown
|
terms: '' # Support markdown
|
||||||
default_client_route: '/videos/trending'
|
default_client_route: '/videos/trending'
|
||||||
|
# By default, "do_not_list" or "blur" or "display" NSFW videos
|
||||||
|
# Could be overridden per user with a setting
|
||||||
|
default_nsfw_policy: 'do_not_list'
|
||||||
customizations:
|
customizations:
|
||||||
javascript: '' # Directly your JavaScript code (without <script> tags). Will be eval at runtime
|
javascript: '' # Directly your JavaScript code (without <script> tags). Will be eval at runtime
|
||||||
css: '' # Directly your CSS code (without <style> tags). Will be injected at runtime
|
css: '' # Directly your CSS code (without <style> tags). Will be injected at runtime
|
||||||
|
|
|
@ -32,3 +32,6 @@ transcoding:
|
||||||
480p: true
|
480p: true
|
||||||
720p: true
|
720p: true
|
||||||
1080p: true
|
1080p: true
|
||||||
|
|
||||||
|
instance:
|
||||||
|
default_nsfw_policy: 'display'
|
|
@ -47,6 +47,7 @@
|
||||||
"nodemon": "nodemon",
|
"nodemon": "nodemon",
|
||||||
"ts-node": "ts-node",
|
"ts-node": "ts-node",
|
||||||
"tslint": "tslint",
|
"tslint": "tslint",
|
||||||
|
"mocha": "mocha",
|
||||||
"travis": "scripty",
|
"travis": "scripty",
|
||||||
"release": "scripty",
|
"release": "scripty",
|
||||||
"client-report": "scripty"
|
"client-report": "scripty"
|
||||||
|
|
|
@ -46,6 +46,7 @@ async function getConfig (req: express.Request, res: express.Response, next: exp
|
||||||
name: CONFIG.INSTANCE.NAME,
|
name: CONFIG.INSTANCE.NAME,
|
||||||
shortDescription: CONFIG.INSTANCE.SHORT_DESCRIPTION,
|
shortDescription: CONFIG.INSTANCE.SHORT_DESCRIPTION,
|
||||||
defaultClientRoute: CONFIG.INSTANCE.DEFAULT_CLIENT_ROUTE,
|
defaultClientRoute: CONFIG.INSTANCE.DEFAULT_CLIENT_ROUTE,
|
||||||
|
defaultNSFWPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
|
||||||
customizations: {
|
customizations: {
|
||||||
javascript: CONFIG.INSTANCE.CUSTOMIZATIONS.JAVASCRIPT,
|
javascript: CONFIG.INSTANCE.CUSTOMIZATIONS.JAVASCRIPT,
|
||||||
css: CONFIG.INSTANCE.CUSTOMIZATIONS.CSS
|
css: CONFIG.INSTANCE.CUSTOMIZATIONS.CSS
|
||||||
|
@ -128,6 +129,7 @@ async function updateCustomConfig (req: express.Request, res: express.Response,
|
||||||
toUpdateJSON.user['video_quota'] = toUpdate.user.videoQuota
|
toUpdateJSON.user['video_quota'] = toUpdate.user.videoQuota
|
||||||
toUpdateJSON.instance['default_client_route'] = toUpdate.instance.defaultClientRoute
|
toUpdateJSON.instance['default_client_route'] = toUpdate.instance.defaultClientRoute
|
||||||
toUpdateJSON.instance['short_description'] = toUpdate.instance.shortDescription
|
toUpdateJSON.instance['short_description'] = toUpdate.instance.shortDescription
|
||||||
|
toUpdateJSON.instance['default_nsfw_policy'] = toUpdate.instance.defaultNSFWPolicy
|
||||||
|
|
||||||
await writeFilePromise(CONFIG.CUSTOM_FILE, JSON.stringify(toUpdateJSON, undefined, 2))
|
await writeFilePromise(CONFIG.CUSTOM_FILE, JSON.stringify(toUpdateJSON, undefined, 2))
|
||||||
|
|
||||||
|
@ -153,6 +155,7 @@ function customConfig (): CustomConfig {
|
||||||
description: CONFIG.INSTANCE.DESCRIPTION,
|
description: CONFIG.INSTANCE.DESCRIPTION,
|
||||||
terms: CONFIG.INSTANCE.TERMS,
|
terms: CONFIG.INSTANCE.TERMS,
|
||||||
defaultClientRoute: CONFIG.INSTANCE.DEFAULT_CLIENT_ROUTE,
|
defaultClientRoute: CONFIG.INSTANCE.DEFAULT_CLIENT_ROUTE,
|
||||||
|
defaultNSFWPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
|
||||||
customizations: {
|
customizations: {
|
||||||
css: CONFIG.INSTANCE.CUSTOMIZATIONS.CSS,
|
css: CONFIG.INSTANCE.CUSTOMIZATIONS.CSS,
|
||||||
javascript: CONFIG.INSTANCE.CUSTOMIZATIONS.JAVASCRIPT
|
javascript: CONFIG.INSTANCE.CUSTOMIZATIONS.JAVASCRIPT
|
||||||
|
|
|
@ -42,6 +42,7 @@ import { AccountVideoRateModel } from '../../models/account/account-video-rate'
|
||||||
import { UserModel } from '../../models/account/user'
|
import { UserModel } from '../../models/account/user'
|
||||||
import { OAuthTokenModel } from '../../models/oauth/oauth-token'
|
import { OAuthTokenModel } from '../../models/oauth/oauth-token'
|
||||||
import { VideoModel } from '../../models/video/video'
|
import { VideoModel } from '../../models/video/video'
|
||||||
|
import { VideoSortField } from '../../../client/src/app/shared/video/sort-field.type'
|
||||||
|
|
||||||
const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.AVATARS_DIR })
|
const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.AVATARS_DIR })
|
||||||
const loginRateLimiter = new RateLimit({
|
const loginRateLimiter = new RateLimit({
|
||||||
|
@ -161,7 +162,13 @@ export {
|
||||||
|
|
||||||
async function getUserVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
|
async function getUserVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
const user = res.locals.oauth.token.User as UserModel
|
const user = res.locals.oauth.token.User as UserModel
|
||||||
const resultList = await VideoModel.listAccountVideosForApi(user.Account.id ,req.query.start, req.query.count, req.query.sort)
|
const resultList = await VideoModel.listAccountVideosForApi(
|
||||||
|
user.Account.id,
|
||||||
|
req.query.start as number,
|
||||||
|
req.query.count as number,
|
||||||
|
req.query.sort as VideoSortField,
|
||||||
|
false // Display my NSFW videos
|
||||||
|
)
|
||||||
|
|
||||||
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
||||||
}
|
}
|
||||||
|
@ -188,7 +195,7 @@ async function createUser (req: express.Request) {
|
||||||
username: body.username,
|
username: body.username,
|
||||||
password: body.password,
|
password: body.password,
|
||||||
email: body.email,
|
email: body.email,
|
||||||
displayNSFW: false,
|
nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
|
||||||
autoPlayVideo: true,
|
autoPlayVideo: true,
|
||||||
role: body.role,
|
role: body.role,
|
||||||
videoQuota: body.videoQuota
|
videoQuota: body.videoQuota
|
||||||
|
@ -219,7 +226,7 @@ async function registerUser (req: express.Request) {
|
||||||
username: body.username,
|
username: body.username,
|
||||||
password: body.password,
|
password: body.password,
|
||||||
email: body.email,
|
email: body.email,
|
||||||
displayNSFW: false,
|
nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
|
||||||
autoPlayVideo: true,
|
autoPlayVideo: true,
|
||||||
role: UserRole.USER,
|
role: UserRole.USER,
|
||||||
videoQuota: CONFIG.USER.VIDEO_QUOTA
|
videoQuota: CONFIG.USER.VIDEO_QUOTA
|
||||||
|
@ -286,7 +293,7 @@ async function updateMe (req: express.Request, res: express.Response, next: expr
|
||||||
|
|
||||||
if (body.password !== undefined) user.password = body.password
|
if (body.password !== undefined) user.password = body.password
|
||||||
if (body.email !== undefined) user.email = body.email
|
if (body.email !== undefined) user.email = body.email
|
||||||
if (body.displayNSFW !== undefined) user.displayNSFW = body.displayNSFW
|
if (body.nsfwPolicy !== undefined) user.nsfwPolicy = body.nsfwPolicy
|
||||||
if (body.autoPlayVideo !== undefined) user.autoPlayVideo = body.autoPlayVideo
|
if (body.autoPlayVideo !== undefined) user.autoPlayVideo = body.autoPlayVideo
|
||||||
|
|
||||||
await sequelizeTypescript.transaction(async t => {
|
await sequelizeTypescript.transaction(async t => {
|
||||||
|
|
|
@ -19,13 +19,18 @@ import {
|
||||||
VIDEO_MIMETYPE_EXT,
|
VIDEO_MIMETYPE_EXT,
|
||||||
VIDEO_PRIVACIES
|
VIDEO_PRIVACIES
|
||||||
} from '../../../initializers'
|
} from '../../../initializers'
|
||||||
import { fetchRemoteVideoDescription, getVideoActivityPubUrl, shareVideoByServerAndChannel } from '../../../lib/activitypub'
|
import {
|
||||||
|
fetchRemoteVideoDescription,
|
||||||
|
getVideoActivityPubUrl,
|
||||||
|
shareVideoByServerAndChannel
|
||||||
|
} from '../../../lib/activitypub'
|
||||||
import { sendCreateVideo, sendCreateView, sendUpdateVideo } from '../../../lib/activitypub/send'
|
import { sendCreateVideo, sendCreateView, sendUpdateVideo } from '../../../lib/activitypub/send'
|
||||||
import { JobQueue } from '../../../lib/job-queue'
|
import { JobQueue } from '../../../lib/job-queue'
|
||||||
import { Redis } from '../../../lib/redis'
|
import { Redis } from '../../../lib/redis'
|
||||||
import {
|
import {
|
||||||
asyncMiddleware,
|
asyncMiddleware,
|
||||||
authenticate,
|
authenticate,
|
||||||
|
optionalAuthenticate,
|
||||||
paginationValidator,
|
paginationValidator,
|
||||||
setDefaultPagination,
|
setDefaultPagination,
|
||||||
setDefaultSort,
|
setDefaultSort,
|
||||||
|
@ -44,6 +49,9 @@ import { blacklistRouter } from './blacklist'
|
||||||
import { videoChannelRouter } from './channel'
|
import { videoChannelRouter } from './channel'
|
||||||
import { videoCommentRouter } from './comment'
|
import { videoCommentRouter } from './comment'
|
||||||
import { rateVideoRouter } from './rate'
|
import { rateVideoRouter } from './rate'
|
||||||
|
import { User } from '../../../../shared/models/users'
|
||||||
|
import { VideoFilter } from '../../../../shared/models/videos/video-query.type'
|
||||||
|
import { VideoSortField } from '../../../../client/src/app/shared/video/sort-field.type'
|
||||||
|
|
||||||
const videosRouter = express.Router()
|
const videosRouter = express.Router()
|
||||||
|
|
||||||
|
@ -81,6 +89,7 @@ videosRouter.get('/',
|
||||||
videosSortValidator,
|
videosSortValidator,
|
||||||
setDefaultSort,
|
setDefaultSort,
|
||||||
setDefaultPagination,
|
setDefaultPagination,
|
||||||
|
optionalAuthenticate,
|
||||||
asyncMiddleware(listVideos)
|
asyncMiddleware(listVideos)
|
||||||
)
|
)
|
||||||
videosRouter.get('/search',
|
videosRouter.get('/search',
|
||||||
|
@ -89,6 +98,7 @@ videosRouter.get('/search',
|
||||||
videosSortValidator,
|
videosSortValidator,
|
||||||
setDefaultSort,
|
setDefaultSort,
|
||||||
setDefaultPagination,
|
setDefaultPagination,
|
||||||
|
optionalAuthenticate,
|
||||||
asyncMiddleware(searchVideos)
|
asyncMiddleware(searchVideos)
|
||||||
)
|
)
|
||||||
videosRouter.put('/:id',
|
videosRouter.put('/:id',
|
||||||
|
@ -391,7 +401,13 @@ async function getVideoDescription (req: express.Request, res: express.Response)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
|
async function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
const resultList = await VideoModel.listForApi(req.query.start, req.query.count, req.query.sort, req.query.filter)
|
const resultList = await VideoModel.listForApi(
|
||||||
|
req.query.start as number,
|
||||||
|
req.query.count as number,
|
||||||
|
req.query.sort as VideoSortField,
|
||||||
|
isNSFWHidden(res),
|
||||||
|
req.query.filter as VideoFilter
|
||||||
|
)
|
||||||
|
|
||||||
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
||||||
}
|
}
|
||||||
|
@ -419,11 +435,21 @@ async function removeVideo (req: express.Request, res: express.Response) {
|
||||||
|
|
||||||
async function searchVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
|
async function searchVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
const resultList = await VideoModel.searchAndPopulateAccountAndServer(
|
const resultList = await VideoModel.searchAndPopulateAccountAndServer(
|
||||||
req.query.search,
|
req.query.search as string,
|
||||||
req.query.start,
|
req.query.start as number,
|
||||||
req.query.count,
|
req.query.count as number,
|
||||||
req.query.sort
|
req.query.sort as VideoSortField,
|
||||||
|
isNSFWHidden(res)
|
||||||
)
|
)
|
||||||
|
|
||||||
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isNSFWHidden (res: express.Response) {
|
||||||
|
if (res.locals.oauth) {
|
||||||
|
const user: User = res.locals.oauth.token.User
|
||||||
|
if (user) return user.nsfwPolicy === 'do_not_list'
|
||||||
|
}
|
||||||
|
|
||||||
|
return CONFIG.INSTANCE.DEFAULT_NSFW_POLICY === 'do_not_list'
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import * as Feed from 'pfeed'
|
||||||
import { ResultList } from '../../shared/models'
|
import { ResultList } from '../../shared/models'
|
||||||
import { AccountModel } from '../models/account/account'
|
import { AccountModel } from '../models/account/account'
|
||||||
import { cacheRoute } from '../middlewares/cache'
|
import { cacheRoute } from '../middlewares/cache'
|
||||||
|
import { VideoSortField } from '../../client/src/app/shared/video/sort-field.type'
|
||||||
|
|
||||||
const feedsRouter = express.Router()
|
const feedsRouter = express.Router()
|
||||||
|
|
||||||
|
@ -31,20 +32,22 @@ async function generateFeed (req: express.Request, res: express.Response, next:
|
||||||
|
|
||||||
let resultList: ResultList<VideoModel>
|
let resultList: ResultList<VideoModel>
|
||||||
const account: AccountModel = res.locals.account
|
const account: AccountModel = res.locals.account
|
||||||
|
const hideNSFW = CONFIG.INSTANCE.DEFAULT_NSFW_POLICY === 'do_not_list'
|
||||||
|
|
||||||
if (account) {
|
if (account) {
|
||||||
resultList = await VideoModel.listAccountVideosForApi(
|
resultList = await VideoModel.listAccountVideosForApi(
|
||||||
account.id,
|
account.id,
|
||||||
start,
|
start,
|
||||||
FEEDS.COUNT,
|
FEEDS.COUNT,
|
||||||
req.query.sort,
|
req.query.sort as VideoSortField,
|
||||||
true
|
hideNSFW
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
resultList = await VideoModel.listForApi(
|
resultList = await VideoModel.listForApi(
|
||||||
start,
|
start,
|
||||||
FEEDS.COUNT,
|
FEEDS.COUNT,
|
||||||
req.query.sort,
|
req.query.sort as VideoSortField,
|
||||||
|
hideNSFW,
|
||||||
req.query.filter,
|
req.query.filter,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import 'express-validator'
|
import 'express-validator'
|
||||||
import * as validator from 'validator'
|
import * as validator from 'validator'
|
||||||
import { UserRole } from '../../../shared'
|
import { UserRole } from '../../../shared'
|
||||||
import { CONSTRAINTS_FIELDS } from '../../initializers'
|
import { CONSTRAINTS_FIELDS, NSFW_POLICY_TYPES } from '../../initializers'
|
||||||
|
|
||||||
import { exists, isFileValid } from './misc'
|
import { exists, isFileValid } from './misc'
|
||||||
|
import { values } from 'lodash'
|
||||||
|
|
||||||
const USERS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.USERS
|
const USERS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.USERS
|
||||||
|
|
||||||
|
@ -29,8 +30,9 @@ function isBoolean (value: any) {
|
||||||
return typeof value === 'boolean' || (typeof value === 'string' && validator.isBoolean(value))
|
return typeof value === 'boolean' || (typeof value === 'string' && validator.isBoolean(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
function isUserDisplayNSFWValid (value: any) {
|
const nsfwPolicies = values(NSFW_POLICY_TYPES)
|
||||||
return isBoolean(value)
|
function isUserNSFWPolicyValid (value: any) {
|
||||||
|
return exists(value) && nsfwPolicies.indexOf(value) !== -1
|
||||||
}
|
}
|
||||||
|
|
||||||
function isUserAutoPlayVideoValid (value: any) {
|
function isUserAutoPlayVideoValid (value: any) {
|
||||||
|
@ -56,7 +58,7 @@ export {
|
||||||
isUserRoleValid,
|
isUserRoleValid,
|
||||||
isUserVideoQuotaValid,
|
isUserVideoQuotaValid,
|
||||||
isUserUsernameValid,
|
isUserUsernameValid,
|
||||||
isUserDisplayNSFWValid,
|
isUserNSFWPolicyValid,
|
||||||
isUserAutoPlayVideoValid,
|
isUserAutoPlayVideoValid,
|
||||||
isUserDescriptionValid,
|
isUserDescriptionValid,
|
||||||
isAvatarFile
|
isAvatarFile
|
||||||
|
|
|
@ -5,12 +5,12 @@ import { ApplicationModel } from '../models/application/application'
|
||||||
import { OAuthClientModel } from '../models/oauth/oauth-client'
|
import { OAuthClientModel } from '../models/oauth/oauth-client'
|
||||||
|
|
||||||
// Some checks on configuration files
|
// Some checks on configuration files
|
||||||
|
// Return an error message, or null if everything is okay
|
||||||
function checkConfig () {
|
function checkConfig () {
|
||||||
if (config.has('webserver.host')) {
|
const defaultNSFWPolicy = config.get<string>('instance.default_nsfw_policy')
|
||||||
let errorMessage = '`host` config key was renamed to `hostname` but it seems you still have a `host` key in your configuration files!'
|
|
||||||
errorMessage += ' Please ensure to rename your `host` configuration to `hostname`.'
|
|
||||||
|
|
||||||
return errorMessage
|
if ([ 'do_not_list', 'blur', 'display' ].indexOf(defaultNSFWPolicy) === -1) {
|
||||||
|
return 'NSFW policy setting should be "do_not_list" or "blur" or "display" instead of ' + defaultNSFWPolicy
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
@ -28,7 +28,8 @@ function checkMissedConfig () {
|
||||||
'log.level',
|
'log.level',
|
||||||
'user.video_quota',
|
'user.video_quota',
|
||||||
'cache.previews.size', 'admin.email', 'signup.enabled', 'signup.limit', 'transcoding.enabled', 'transcoding.threads',
|
'cache.previews.size', 'admin.email', 'signup.enabled', 'signup.limit', 'transcoding.enabled', 'transcoding.threads',
|
||||||
'instance.name', 'instance.short_description', 'instance.description', 'instance.terms', 'instance.default_client_route'
|
'instance.name', 'instance.short_description', 'instance.description', 'instance.terms', 'instance.default_client_route',
|
||||||
|
'instance.default_nsfw_policy'
|
||||||
]
|
]
|
||||||
const miss: string[] = []
|
const miss: string[] = []
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,14 @@ import { FollowState } from '../../shared/models/actors'
|
||||||
import { VideoPrivacy } from '../../shared/models/videos'
|
import { VideoPrivacy } from '../../shared/models/videos'
|
||||||
// Do not use barrels, remain constants as independent as possible
|
// Do not use barrels, remain constants as independent as possible
|
||||||
import { buildPath, isTestInstance, root, sanitizeHost, sanitizeUrl } from '../helpers/core-utils'
|
import { buildPath, isTestInstance, root, sanitizeHost, sanitizeUrl } from '../helpers/core-utils'
|
||||||
|
import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type'
|
||||||
|
|
||||||
// Use a variable to reload the configuration if we need
|
// Use a variable to reload the configuration if we need
|
||||||
let config: IConfig = require('config')
|
let config: IConfig = require('config')
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const LAST_MIGRATION_VERSION = 200
|
const LAST_MIGRATION_VERSION = 205
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -167,6 +168,7 @@ const CONFIG = {
|
||||||
get DESCRIPTION () { return config.get<string>('instance.description') },
|
get DESCRIPTION () { return config.get<string>('instance.description') },
|
||||||
get TERMS () { return config.get<string>('instance.terms') },
|
get TERMS () { return config.get<string>('instance.terms') },
|
||||||
get DEFAULT_CLIENT_ROUTE () { return config.get<string>('instance.default_client_route') },
|
get DEFAULT_CLIENT_ROUTE () { return config.get<string>('instance.default_client_route') },
|
||||||
|
get DEFAULT_NSFW_POLICY () { return config.get<NSFWPolicyType>('instance.default_nsfw_policy') },
|
||||||
CUSTOMIZATIONS: {
|
CUSTOMIZATIONS: {
|
||||||
get JAVASCRIPT () { return config.get<string>('instance.customizations.javascript') },
|
get JAVASCRIPT () { return config.get<string>('instance.customizations.javascript') },
|
||||||
get CSS () { return config.get<string>('instance.customizations.css') }
|
get CSS () { return config.get<string>('instance.customizations.css') }
|
||||||
|
@ -378,6 +380,12 @@ const BCRYPT_SALT_SIZE = 10
|
||||||
|
|
||||||
const USER_PASSWORD_RESET_LIFETIME = 60000 * 5 // 5 minutes
|
const USER_PASSWORD_RESET_LIFETIME = 60000 * 5 // 5 minutes
|
||||||
|
|
||||||
|
const NSFW_POLICY_TYPES: { [ id: string]: NSFWPolicyType } = {
|
||||||
|
DO_NOT_LIST: 'do_not_list',
|
||||||
|
BLUR: 'blur',
|
||||||
|
DISPLAY: 'display'
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
// Express static paths (router)
|
// Express static paths (router)
|
||||||
|
@ -474,6 +482,7 @@ export {
|
||||||
PRIVATE_RSA_KEY_SIZE,
|
PRIVATE_RSA_KEY_SIZE,
|
||||||
SORTABLE_COLUMNS,
|
SORTABLE_COLUMNS,
|
||||||
FEEDS,
|
FEEDS,
|
||||||
|
NSFW_POLICY_TYPES,
|
||||||
STATIC_MAX_AGE,
|
STATIC_MAX_AGE,
|
||||||
STATIC_PATHS,
|
STATIC_PATHS,
|
||||||
ACTIVITY_PUB,
|
ACTIVITY_PUB,
|
||||||
|
|
|
@ -120,6 +120,7 @@ async function createOAuthAdminIfNotExist () {
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
role,
|
role,
|
||||||
|
nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
|
||||||
videoQuota: -1
|
videoQuota: -1
|
||||||
}
|
}
|
||||||
const user = new UserModel(userData)
|
const user = new UserModel(userData)
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
import * as Sequelize from 'sequelize'
|
||||||
|
|
||||||
|
async function up (utils: {
|
||||||
|
transaction: Sequelize.Transaction,
|
||||||
|
queryInterface: Sequelize.QueryInterface,
|
||||||
|
sequelize: Sequelize.Sequelize
|
||||||
|
}): Promise<void> {
|
||||||
|
|
||||||
|
{
|
||||||
|
const data = {
|
||||||
|
type: Sequelize.ENUM('do_not_list', 'blur', 'display'),
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: null
|
||||||
|
}
|
||||||
|
await utils.queryInterface.addColumn('user', 'nsfwPolicy', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const query = 'UPDATE "user" SET "nsfwPolicy" = \'do_not_list\''
|
||||||
|
await utils.sequelize.query(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const query = 'UPDATE "user" SET "nsfwPolicy" = \'display\' WHERE "displayNSFW" = true'
|
||||||
|
await utils.sequelize.query(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const query = 'ALTER TABLE "user" ALTER COLUMN "nsfwPolicy" SET NOT NULL'
|
||||||
|
await utils.sequelize.query(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
await utils.queryInterface.removeColumn('user', 'displayNSFW')
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function down (options) {
|
||||||
|
throw new Error('Not implemented.')
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
up,
|
||||||
|
down
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ import * as express from 'express'
|
||||||
import * as OAuthServer from 'express-oauth-server'
|
import * as OAuthServer from 'express-oauth-server'
|
||||||
import 'express-validator'
|
import 'express-validator'
|
||||||
import { OAUTH_LIFETIME } from '../initializers'
|
import { OAUTH_LIFETIME } from '../initializers'
|
||||||
|
import { logger } from '../helpers/logger'
|
||||||
|
|
||||||
const oAuthServer = new OAuthServer({
|
const oAuthServer = new OAuthServer({
|
||||||
useErrorHandler: true,
|
useErrorHandler: true,
|
||||||
|
@ -13,6 +14,8 @@ const oAuthServer = new OAuthServer({
|
||||||
function authenticate (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function authenticate (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
oAuthServer.authenticate()(req, res, err => {
|
oAuthServer.authenticate()(req, res, err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
logger.warn('Cannot authenticate.', { err })
|
||||||
|
|
||||||
return res.status(err.status)
|
return res.status(err.status)
|
||||||
.json({
|
.json({
|
||||||
error: 'Token is invalid.',
|
error: 'Token is invalid.',
|
||||||
|
@ -25,6 +28,12 @@ function authenticate (req: express.Request, res: express.Response, next: expres
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function optionalAuthenticate (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
|
if (req.header('authorization')) return authenticate(req, res, next)
|
||||||
|
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
|
||||||
function token (req: express.Request, res: express.Response, next: express.NextFunction) {
|
function token (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
return oAuthServer.token()(req, res, err => {
|
return oAuthServer.token()(req, res, err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -44,5 +53,6 @@ function token (req: express.Request, res: express.Response, next: express.NextF
|
||||||
|
|
||||||
export {
|
export {
|
||||||
authenticate,
|
authenticate,
|
||||||
|
optionalAuthenticate,
|
||||||
token
|
token
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import { body } from 'express-validator/check'
|
import { body } from 'express-validator/check'
|
||||||
import { isUserVideoQuotaValid } from '../../helpers/custom-validators/users'
|
import { isUserNSFWPolicyValid, isUserVideoQuotaValid } from '../../helpers/custom-validators/users'
|
||||||
import { logger } from '../../helpers/logger'
|
import { logger } from '../../helpers/logger'
|
||||||
import { areValidationErrors } from './utils'
|
import { areValidationErrors } from './utils'
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ const customConfigUpdateValidator = [
|
||||||
body('instance.description').exists().withMessage('Should have a valid instance description'),
|
body('instance.description').exists().withMessage('Should have a valid instance description'),
|
||||||
body('instance.terms').exists().withMessage('Should have a valid instance terms'),
|
body('instance.terms').exists().withMessage('Should have a valid instance terms'),
|
||||||
body('instance.defaultClientRoute').exists().withMessage('Should have a valid instance default client route'),
|
body('instance.defaultClientRoute').exists().withMessage('Should have a valid instance default client route'),
|
||||||
|
body('instance.defaultNSFWPolicy').custom(isUserNSFWPolicyValid).withMessage('Should have a valid NSFW policy'),
|
||||||
body('instance.customizations.css').exists().withMessage('Should have a valid instance CSS customization'),
|
body('instance.customizations.css').exists().withMessage('Should have a valid instance CSS customization'),
|
||||||
body('instance.customizations.javascript').exists().withMessage('Should have a valid instance JavaScript customization'),
|
body('instance.customizations.javascript').exists().withMessage('Should have a valid instance JavaScript customization'),
|
||||||
body('cache.previews.size').isInt().withMessage('Should have a valid previews size'),
|
body('cache.previews.size').isInt().withMessage('Should have a valid previews size'),
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
isAvatarFile,
|
isAvatarFile,
|
||||||
isUserAutoPlayVideoValid,
|
isUserAutoPlayVideoValid,
|
||||||
isUserDescriptionValid,
|
isUserDescriptionValid,
|
||||||
isUserDisplayNSFWValid,
|
isUserNSFWPolicyValid,
|
||||||
isUserPasswordValid,
|
isUserPasswordValid,
|
||||||
isUserRoleValid,
|
isUserRoleValid,
|
||||||
isUserUsernameValid,
|
isUserUsernameValid,
|
||||||
|
@ -101,7 +101,7 @@ const usersUpdateMeValidator = [
|
||||||
body('description').optional().custom(isUserDescriptionValid).withMessage('Should have a valid description'),
|
body('description').optional().custom(isUserDescriptionValid).withMessage('Should have a valid description'),
|
||||||
body('password').optional().custom(isUserPasswordValid).withMessage('Should have a valid password'),
|
body('password').optional().custom(isUserPasswordValid).withMessage('Should have a valid password'),
|
||||||
body('email').optional().isEmail().withMessage('Should have a valid email attribute'),
|
body('email').optional().isEmail().withMessage('Should have a valid email attribute'),
|
||||||
body('displayNSFW').optional().custom(isUserDisplayNSFWValid).withMessage('Should have a valid display Not Safe For Work attribute'),
|
body('nsfwPolicy').optional().custom(isUserNSFWPolicyValid).withMessage('Should have a valid display Not Safe For Work policy'),
|
||||||
body('autoPlayVideo').optional().custom(isUserAutoPlayVideoValid).withMessage('Should have a valid automatically plays video attribute'),
|
body('autoPlayVideo').optional().custom(isUserAutoPlayVideoValid).withMessage('Should have a valid automatically plays video attribute'),
|
||||||
|
|
||||||
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
|
|
|
@ -21,7 +21,7 @@ import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared'
|
||||||
import { User, UserRole } from '../../../shared/models/users'
|
import { User, UserRole } from '../../../shared/models/users'
|
||||||
import {
|
import {
|
||||||
isUserAutoPlayVideoValid,
|
isUserAutoPlayVideoValid,
|
||||||
isUserDisplayNSFWValid,
|
isUserNSFWPolicyValid,
|
||||||
isUserPasswordValid,
|
isUserPasswordValid,
|
||||||
isUserRoleValid,
|
isUserRoleValid,
|
||||||
isUserUsernameValid,
|
isUserUsernameValid,
|
||||||
|
@ -32,6 +32,9 @@ import { OAuthTokenModel } from '../oauth/oauth-token'
|
||||||
import { getSort, throwIfNotValid } from '../utils'
|
import { getSort, throwIfNotValid } from '../utils'
|
||||||
import { VideoChannelModel } from '../video/video-channel'
|
import { VideoChannelModel } from '../video/video-channel'
|
||||||
import { AccountModel } from './account'
|
import { AccountModel } from './account'
|
||||||
|
import { NSFWPolicyType } from '../../../shared/models/videos/nsfw-policy.type'
|
||||||
|
import { values } from 'lodash'
|
||||||
|
import { NSFW_POLICY_TYPES } from '../../initializers'
|
||||||
|
|
||||||
@DefaultScope({
|
@DefaultScope({
|
||||||
include: [
|
include: [
|
||||||
|
@ -83,10 +86,9 @@ export class UserModel extends Model<UserModel> {
|
||||||
email: string
|
email: string
|
||||||
|
|
||||||
@AllowNull(false)
|
@AllowNull(false)
|
||||||
@Default(false)
|
@Is('UserNSFWPolicy', value => throwIfNotValid(value, isUserNSFWPolicyValid, 'NSFW policy'))
|
||||||
@Is('UserDisplayNSFW', value => throwIfNotValid(value, isUserDisplayNSFWValid, 'display NSFW boolean'))
|
@Column(DataType.ENUM(values(NSFW_POLICY_TYPES)))
|
||||||
@Column
|
nsfwPolicy: NSFWPolicyType
|
||||||
displayNSFW: boolean
|
|
||||||
|
|
||||||
@AllowNull(false)
|
@AllowNull(false)
|
||||||
@Default(true)
|
@Default(true)
|
||||||
|
@ -265,7 +267,7 @@ export class UserModel extends Model<UserModel> {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
username: this.username,
|
username: this.username,
|
||||||
email: this.email,
|
email: this.email,
|
||||||
displayNSFW: this.displayNSFW,
|
nsfwPolicy: this.nsfwPolicy,
|
||||||
autoPlayVideo: this.autoPlayVideo,
|
autoPlayVideo: this.autoPlayVideo,
|
||||||
role: this.role,
|
role: this.role,
|
||||||
roleLabel: USER_ROLE_LABELS[ this.role ],
|
roleLabel: USER_ROLE_LABELS[ this.role ],
|
||||||
|
|
|
@ -95,7 +95,7 @@ enum ScopeNames {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Scopes({
|
@Scopes({
|
||||||
[ScopeNames.AVAILABLE_FOR_LIST]: (actorId: number, filter?: VideoFilter, withFiles?: boolean) => {
|
[ScopeNames.AVAILABLE_FOR_LIST]: (actorId: number, hideNSFW: boolean, filter?: VideoFilter, withFiles?: boolean) => {
|
||||||
const query: IFindOptions<VideoModel> = {
|
const query: IFindOptions<VideoModel> = {
|
||||||
where: {
|
where: {
|
||||||
id: {
|
id: {
|
||||||
|
@ -161,6 +161,11 @@ enum ScopeNames {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hide nsfw videos?
|
||||||
|
if (hideNSFW === true) {
|
||||||
|
query.where['nsfw'] = false
|
||||||
|
}
|
||||||
|
|
||||||
return query
|
return query
|
||||||
},
|
},
|
||||||
[ScopeNames.WITH_ACCOUNT_DETAILS]: {
|
[ScopeNames.WITH_ACCOUNT_DETAILS]: {
|
||||||
|
@ -640,7 +645,7 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static listAccountVideosForApi (accountId: number, start: number, count: number, sort: string, withFiles = false) {
|
static listAccountVideosForApi (accountId: number, start: number, count: number, sort: string, hideNSFW: boolean, withFiles = false) {
|
||||||
const query: IFindOptions<VideoModel> = {
|
const query: IFindOptions<VideoModel> = {
|
||||||
offset: start,
|
offset: start,
|
||||||
limit: count,
|
limit: count,
|
||||||
|
@ -669,6 +674,12 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hideNSFW === true) {
|
||||||
|
query.where = {
|
||||||
|
nsfw: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return VideoModel.findAndCountAll(query).then(({ rows, count }) => {
|
return VideoModel.findAndCountAll(query).then(({ rows, count }) => {
|
||||||
return {
|
return {
|
||||||
data: rows,
|
data: rows,
|
||||||
|
@ -677,7 +688,7 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static async listForApi (start: number, count: number, sort: string, filter?: VideoFilter, withFiles = false) {
|
static async listForApi (start: number, count: number, sort: string, hideNSFW: boolean, filter?: VideoFilter, withFiles = false) {
|
||||||
const query = {
|
const query = {
|
||||||
offset: start,
|
offset: start,
|
||||||
limit: count,
|
limit: count,
|
||||||
|
@ -685,8 +696,7 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const serverActor = await getServerActor()
|
const serverActor = await getServerActor()
|
||||||
|
return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST, serverActor.id, hideNSFW, filter, withFiles ] })
|
||||||
return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST, serverActor.id, filter, withFiles ] })
|
|
||||||
.findAndCountAll(query)
|
.findAndCountAll(query)
|
||||||
.then(({ rows, count }) => {
|
.then(({ rows, count }) => {
|
||||||
return {
|
return {
|
||||||
|
@ -696,7 +706,7 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static async searchAndPopulateAccountAndServer (value: string, start: number, count: number, sort: string) {
|
static async searchAndPopulateAccountAndServer (value: string, start: number, count: number, sort: string, hideNSFW: boolean) {
|
||||||
const query: IFindOptions<VideoModel> = {
|
const query: IFindOptions<VideoModel> = {
|
||||||
offset: start,
|
offset: start,
|
||||||
limit: count,
|
limit: count,
|
||||||
|
@ -724,7 +734,7 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
|
|
||||||
const serverActor = await getServerActor()
|
const serverActor = await getServerActor()
|
||||||
|
|
||||||
return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST, serverActor.id ] })
|
return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST, serverActor.id, hideNSFW ] })
|
||||||
.findAndCountAll(query)
|
.findAndCountAll(query)
|
||||||
.then(({ rows, count }) => {
|
.then(({ rows, count }) => {
|
||||||
return {
|
return {
|
||||||
|
@ -874,6 +884,13 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
return languageLabel
|
return languageLabel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static getPrivacyLabel (id: number) {
|
||||||
|
let privacyLabel = VIDEO_PRIVACIES[id]
|
||||||
|
if (!privacyLabel) privacyLabel = 'Unknown'
|
||||||
|
|
||||||
|
return privacyLabel
|
||||||
|
}
|
||||||
|
|
||||||
getOriginalFile () {
|
getOriginalFile () {
|
||||||
if (Array.isArray(this.VideoFiles) === false) return undefined
|
if (Array.isArray(this.VideoFiles) === false) return undefined
|
||||||
|
|
||||||
|
@ -927,8 +944,11 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
return join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile))
|
return join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile))
|
||||||
}
|
}
|
||||||
|
|
||||||
createTorrentAndSetInfoHash = async function (videoFile: VideoFileModel) {
|
async createTorrentAndSetInfoHash (videoFile: VideoFileModel) {
|
||||||
const options = {
|
const options = {
|
||||||
|
// Keep the extname, it's used by the client to stream the file inside a web browser
|
||||||
|
name: `${this.name} ${videoFile.resolution}p${videoFile.extname}`,
|
||||||
|
createdBy: 'PeerTube',
|
||||||
announceList: [
|
announceList: [
|
||||||
[ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT + '/tracker/socket' ],
|
[ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT + '/tracker/socket' ],
|
||||||
[ CONFIG.WEBSERVER.URL + '/tracker/announce' ]
|
[ CONFIG.WEBSERVER.URL + '/tracker/announce' ]
|
||||||
|
@ -980,6 +1000,10 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
id: this.language,
|
id: this.language,
|
||||||
label: VideoModel.getLanguageLabel(this.language)
|
label: VideoModel.getLanguageLabel(this.language)
|
||||||
},
|
},
|
||||||
|
privacy: {
|
||||||
|
id: this.privacy,
|
||||||
|
label: VideoModel.getPrivacyLabel(this.privacy)
|
||||||
|
},
|
||||||
nsfw: this.nsfw,
|
nsfw: this.nsfw,
|
||||||
description: this.getTruncatedDescription(),
|
description: this.getTruncatedDescription(),
|
||||||
isLocal: this.isOwned(),
|
isLocal: this.isOwned(),
|
||||||
|
@ -1006,15 +1030,7 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
toFormattedDetailsJSON (): VideoDetails {
|
toFormattedDetailsJSON (): VideoDetails {
|
||||||
const formattedJson = this.toFormattedJSON()
|
const formattedJson = this.toFormattedJSON()
|
||||||
|
|
||||||
// Maybe our server 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 = {
|
const detailsJson = {
|
||||||
privacy: {
|
|
||||||
id: this.privacy,
|
|
||||||
label: privacyLabel
|
|
||||||
},
|
|
||||||
support: this.support,
|
support: this.support,
|
||||||
descriptionPath: this.getDescriptionPath(),
|
descriptionPath: this.getDescriptionPath(),
|
||||||
channel: this.VideoChannel.toFormattedJSON(),
|
channel: this.VideoChannel.toFormattedJSON(),
|
||||||
|
@ -1227,7 +1243,7 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
return peertubeTruncate(this.description, maxLength)
|
return peertubeTruncate(this.description, maxLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
optimizeOriginalVideofile = async function () {
|
async optimizeOriginalVideofile () {
|
||||||
const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
|
const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
|
||||||
const newExtname = '.mp4'
|
const newExtname = '.mp4'
|
||||||
const inputVideoFile = this.getOriginalFile()
|
const inputVideoFile = this.getOriginalFile()
|
||||||
|
@ -1264,7 +1280,7 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transcodeOriginalVideofile = async function (resolution: VideoResolution, isPortraitMode: boolean) {
|
async transcodeOriginalVideofile (resolution: VideoResolution, isPortraitMode: boolean) {
|
||||||
const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
|
const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
|
||||||
const extname = '.mp4'
|
const extname = '.mp4'
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { CustomConfig } from '../../../../shared/models/server/custom-config.mod
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createUser, flushTests, killallServers, makeDeleteRequest, makeGetRequest, makePutBodyRequest, runServer, ServerInfo,
|
createUser, flushTests, killallServers, makeDeleteRequest, makeGetRequest, makePutBodyRequest, runServer, ServerInfo,
|
||||||
setAccessTokensToServers, userLogin
|
setAccessTokensToServers, userLogin, immutableAssign
|
||||||
} from '../../utils'
|
} from '../../utils'
|
||||||
|
|
||||||
describe('Test config API validators', function () {
|
describe('Test config API validators', function () {
|
||||||
|
@ -20,6 +20,7 @@ describe('Test config API validators', function () {
|
||||||
description: 'my super description',
|
description: 'my super description',
|
||||||
terms: 'my super terms',
|
terms: 'my super terms',
|
||||||
defaultClientRoute: '/videos/recently-added',
|
defaultClientRoute: '/videos/recently-added',
|
||||||
|
defaultNSFWPolicy: 'blur',
|
||||||
customizations: {
|
customizations: {
|
||||||
javascript: 'alert("coucou")',
|
javascript: 'alert("coucou")',
|
||||||
css: 'body { background-color: red; }'
|
css: 'body { background-color: red; }'
|
||||||
|
@ -122,6 +123,22 @@ describe('Test config API validators', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Should fail with a bad default NSFW policy', async function () {
|
||||||
|
const newUpdateParams = immutableAssign(updateParams, {
|
||||||
|
instance: {
|
||||||
|
defaultNSFWPolicy: 'hello'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
await makePutBodyRequest({
|
||||||
|
url: server.url,
|
||||||
|
path,
|
||||||
|
fields: newUpdateParams,
|
||||||
|
token: server.accessToken,
|
||||||
|
statusCodeExpected: 400
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it('Should success with the correct parameters', async function () {
|
it('Should success with the correct parameters', async function () {
|
||||||
await makePutBodyRequest({
|
await makePutBodyRequest({
|
||||||
url: server.url,
|
url: server.url,
|
||||||
|
|
|
@ -231,9 +231,9 @@ describe('Test users API validators', function () {
|
||||||
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
|
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should fail with an invalid display NSFW attribute', async function () {
|
it('Should fail with an invalid NSFW policy attribute', async function () {
|
||||||
const fields = {
|
const fields = {
|
||||||
displayNSFW: -1
|
nsfwPolicy: 'hello'
|
||||||
}
|
}
|
||||||
|
|
||||||
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
|
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
|
||||||
|
@ -266,7 +266,7 @@ describe('Test users API validators', function () {
|
||||||
it('Should succeed with the correct params', async function () {
|
it('Should succeed with the correct params', async function () {
|
||||||
const fields = {
|
const fields = {
|
||||||
password: 'my super password',
|
password: 'my super password',
|
||||||
displayNSFW: true,
|
nsfwPolicy: 'blur',
|
||||||
autoPlayVideo: false,
|
autoPlayVideo: false,
|
||||||
email: 'super_email@example.com'
|
email: 'super_email@example.com'
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import './videos/video-abuse'
|
||||||
import './videos/video-blacklist'
|
import './videos/video-blacklist'
|
||||||
import './videos/video-blacklist-management'
|
import './videos/video-blacklist-management'
|
||||||
import './videos/video-description'
|
import './videos/video-description'
|
||||||
|
import './videos/video-nsfw'
|
||||||
import './videos/video-privacy'
|
import './videos/video-privacy'
|
||||||
import './videos/services'
|
import './videos/services'
|
||||||
import './server/email'
|
import './server/email'
|
||||||
|
|
|
@ -59,6 +59,7 @@ describe('Test config', function () {
|
||||||
expect(data.instance.description).to.equal('Welcome to this PeerTube instance!')
|
expect(data.instance.description).to.equal('Welcome to this PeerTube instance!')
|
||||||
expect(data.instance.terms).to.equal('No terms for now.')
|
expect(data.instance.terms).to.equal('No terms for now.')
|
||||||
expect(data.instance.defaultClientRoute).to.equal('/videos/trending')
|
expect(data.instance.defaultClientRoute).to.equal('/videos/trending')
|
||||||
|
expect(data.instance.defaultNSFWPolicy).to.equal('display')
|
||||||
expect(data.instance.customizations.css).to.be.empty
|
expect(data.instance.customizations.css).to.be.empty
|
||||||
expect(data.instance.customizations.javascript).to.be.empty
|
expect(data.instance.customizations.javascript).to.be.empty
|
||||||
expect(data.cache.previews.size).to.equal(1)
|
expect(data.cache.previews.size).to.equal(1)
|
||||||
|
@ -83,6 +84,7 @@ describe('Test config', function () {
|
||||||
description: 'my super description',
|
description: 'my super description',
|
||||||
terms: 'my super terms',
|
terms: 'my super terms',
|
||||||
defaultClientRoute: '/videos/recently-added',
|
defaultClientRoute: '/videos/recently-added',
|
||||||
|
defaultNSFWPolicy: 'blur' as 'blur',
|
||||||
customizations: {
|
customizations: {
|
||||||
javascript: 'alert("coucou")',
|
javascript: 'alert("coucou")',
|
||||||
css: 'body { background-color: red; }'
|
css: 'body { background-color: red; }'
|
||||||
|
@ -125,6 +127,7 @@ describe('Test config', function () {
|
||||||
expect(data.instance.description).to.equal('my super description')
|
expect(data.instance.description).to.equal('my super description')
|
||||||
expect(data.instance.terms).to.equal('my super terms')
|
expect(data.instance.terms).to.equal('my super terms')
|
||||||
expect(data.instance.defaultClientRoute).to.equal('/videos/recently-added')
|
expect(data.instance.defaultClientRoute).to.equal('/videos/recently-added')
|
||||||
|
expect(data.instance.defaultNSFWPolicy).to.equal('blur')
|
||||||
expect(data.instance.customizations.javascript).to.equal('alert("coucou")')
|
expect(data.instance.customizations.javascript).to.equal('alert("coucou")')
|
||||||
expect(data.instance.customizations.css).to.equal('body { background-color: red; }')
|
expect(data.instance.customizations.css).to.equal('body { background-color: red; }')
|
||||||
expect(data.cache.previews.size).to.equal(2)
|
expect(data.cache.previews.size).to.equal(2)
|
||||||
|
@ -156,6 +159,7 @@ describe('Test config', function () {
|
||||||
expect(data.instance.description).to.equal('my super description')
|
expect(data.instance.description).to.equal('my super description')
|
||||||
expect(data.instance.terms).to.equal('my super terms')
|
expect(data.instance.terms).to.equal('my super terms')
|
||||||
expect(data.instance.defaultClientRoute).to.equal('/videos/recently-added')
|
expect(data.instance.defaultClientRoute).to.equal('/videos/recently-added')
|
||||||
|
expect(data.instance.defaultNSFWPolicy).to.equal('blur')
|
||||||
expect(data.instance.customizations.javascript).to.equal('alert("coucou")')
|
expect(data.instance.customizations.javascript).to.equal('alert("coucou")')
|
||||||
expect(data.instance.customizations.css).to.equal('body { background-color: red; }')
|
expect(data.instance.customizations.css).to.equal('body { background-color: red; }')
|
||||||
expect(data.cache.previews.size).to.equal(2)
|
expect(data.cache.previews.size).to.equal(2)
|
||||||
|
@ -198,6 +202,7 @@ describe('Test config', function () {
|
||||||
expect(data.instance.description).to.equal('Welcome to this PeerTube instance!')
|
expect(data.instance.description).to.equal('Welcome to this PeerTube instance!')
|
||||||
expect(data.instance.terms).to.equal('No terms for now.')
|
expect(data.instance.terms).to.equal('No terms for now.')
|
||||||
expect(data.instance.defaultClientRoute).to.equal('/videos/trending')
|
expect(data.instance.defaultClientRoute).to.equal('/videos/trending')
|
||||||
|
expect(data.instance.defaultNSFWPolicy).to.equal('display')
|
||||||
expect(data.instance.customizations.css).to.be.empty
|
expect(data.instance.customizations.css).to.be.empty
|
||||||
expect(data.instance.customizations.javascript).to.be.empty
|
expect(data.instance.customizations.javascript).to.be.empty
|
||||||
expect(data.cache.previews.size).to.equal(1)
|
expect(data.cache.previews.size).to.equal(1)
|
||||||
|
|
|
@ -168,7 +168,7 @@ describe('Test users', function () {
|
||||||
|
|
||||||
expect(user.username).to.equal('user_1')
|
expect(user.username).to.equal('user_1')
|
||||||
expect(user.email).to.equal('user_1@example.com')
|
expect(user.email).to.equal('user_1@example.com')
|
||||||
expect(user.displayNSFW).to.be.false
|
expect(user.nsfwPolicy).to.equal('display')
|
||||||
expect(user.videoQuota).to.equal(2 * 1024 * 1024)
|
expect(user.videoQuota).to.equal(2 * 1024 * 1024)
|
||||||
expect(user.roleLabel).to.equal('User')
|
expect(user.roleLabel).to.equal('User')
|
||||||
expect(user.id).to.be.a('number')
|
expect(user.id).to.be.a('number')
|
||||||
|
@ -215,12 +215,12 @@ describe('Test users', function () {
|
||||||
const user = users[ 0 ]
|
const user = users[ 0 ]
|
||||||
expect(user.username).to.equal('user_1')
|
expect(user.username).to.equal('user_1')
|
||||||
expect(user.email).to.equal('user_1@example.com')
|
expect(user.email).to.equal('user_1@example.com')
|
||||||
expect(user.displayNSFW).to.be.false
|
expect(user.nsfwPolicy).to.equal('display')
|
||||||
|
|
||||||
const rootUser = users[ 1 ]
|
const rootUser = users[ 1 ]
|
||||||
expect(rootUser.username).to.equal('root')
|
expect(rootUser.username).to.equal('root')
|
||||||
expect(rootUser.email).to.equal('admin1@example.com')
|
expect(rootUser.email).to.equal('admin1@example.com')
|
||||||
expect(rootUser.displayNSFW).to.be.false
|
expect(user.nsfwPolicy).to.equal('display')
|
||||||
|
|
||||||
userId = user.id
|
userId = user.id
|
||||||
})
|
})
|
||||||
|
@ -239,7 +239,7 @@ describe('Test users', function () {
|
||||||
expect(user.username).to.equal('root')
|
expect(user.username).to.equal('root')
|
||||||
expect(user.email).to.equal('admin1@example.com')
|
expect(user.email).to.equal('admin1@example.com')
|
||||||
expect(user.roleLabel).to.equal('Administrator')
|
expect(user.roleLabel).to.equal('Administrator')
|
||||||
expect(user.displayNSFW).to.be.false
|
expect(user.nsfwPolicy).to.equal('display')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should list only the first user by username desc', async function () {
|
it('Should list only the first user by username desc', async function () {
|
||||||
|
@ -254,7 +254,7 @@ describe('Test users', function () {
|
||||||
const user = users[ 0 ]
|
const user = users[ 0 ]
|
||||||
expect(user.username).to.equal('user_1')
|
expect(user.username).to.equal('user_1')
|
||||||
expect(user.email).to.equal('user_1@example.com')
|
expect(user.email).to.equal('user_1@example.com')
|
||||||
expect(user.displayNSFW).to.be.false
|
expect(user.nsfwPolicy).to.equal('display')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should list only the second user by createdAt desc', async function () {
|
it('Should list only the second user by createdAt desc', async function () {
|
||||||
|
@ -269,7 +269,7 @@ describe('Test users', function () {
|
||||||
const user = users[ 0 ]
|
const user = users[ 0 ]
|
||||||
expect(user.username).to.equal('user_1')
|
expect(user.username).to.equal('user_1')
|
||||||
expect(user.email).to.equal('user_1@example.com')
|
expect(user.email).to.equal('user_1@example.com')
|
||||||
expect(user.displayNSFW).to.be.false
|
expect(user.nsfwPolicy).to.equal('display')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should list all the users by createdAt asc', async function () {
|
it('Should list all the users by createdAt asc', async function () {
|
||||||
|
@ -283,11 +283,11 @@ describe('Test users', function () {
|
||||||
|
|
||||||
expect(users[ 0 ].username).to.equal('root')
|
expect(users[ 0 ].username).to.equal('root')
|
||||||
expect(users[ 0 ].email).to.equal('admin1@example.com')
|
expect(users[ 0 ].email).to.equal('admin1@example.com')
|
||||||
expect(users[ 0 ].displayNSFW).to.be.false
|
expect(users[ 0 ].nsfwPolicy).to.equal('display')
|
||||||
|
|
||||||
expect(users[ 1 ].username).to.equal('user_1')
|
expect(users[ 1 ].username).to.equal('user_1')
|
||||||
expect(users[ 1 ].email).to.equal('user_1@example.com')
|
expect(users[ 1 ].email).to.equal('user_1@example.com')
|
||||||
expect(users[ 1 ].displayNSFW).to.be.false
|
expect(users[ 1 ].nsfwPolicy).to.equal('display')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should update my password', async function () {
|
it('Should update my password', async function () {
|
||||||
|
@ -305,7 +305,7 @@ describe('Test users', function () {
|
||||||
await updateMyUser({
|
await updateMyUser({
|
||||||
url: server.url,
|
url: server.url,
|
||||||
accessToken: accessTokenUser,
|
accessToken: accessTokenUser,
|
||||||
displayNSFW: true
|
nsfwPolicy: 'do_not_list'
|
||||||
})
|
})
|
||||||
|
|
||||||
const res = await getMyUserInformation(server.url, accessTokenUser)
|
const res = await getMyUserInformation(server.url, accessTokenUser)
|
||||||
|
@ -313,7 +313,7 @@ describe('Test users', function () {
|
||||||
|
|
||||||
expect(user.username).to.equal('user_1')
|
expect(user.username).to.equal('user_1')
|
||||||
expect(user.email).to.equal('user_1@example.com')
|
expect(user.email).to.equal('user_1@example.com')
|
||||||
expect(user.displayNSFW).to.be.ok
|
expect(user.nsfwPolicy).to.equal('do_not_list')
|
||||||
expect(user.videoQuota).to.equal(2 * 1024 * 1024)
|
expect(user.videoQuota).to.equal(2 * 1024 * 1024)
|
||||||
expect(user.id).to.be.a('number')
|
expect(user.id).to.be.a('number')
|
||||||
expect(user.account.description).to.be.null
|
expect(user.account.description).to.be.null
|
||||||
|
@ -344,7 +344,7 @@ describe('Test users', function () {
|
||||||
|
|
||||||
expect(user.username).to.equal('user_1')
|
expect(user.username).to.equal('user_1')
|
||||||
expect(user.email).to.equal('updated@example.com')
|
expect(user.email).to.equal('updated@example.com')
|
||||||
expect(user.displayNSFW).to.be.ok
|
expect(user.nsfwPolicy).to.equal('do_not_list')
|
||||||
expect(user.videoQuota).to.equal(2 * 1024 * 1024)
|
expect(user.videoQuota).to.equal(2 * 1024 * 1024)
|
||||||
expect(user.id).to.be.a('number')
|
expect(user.id).to.be.a('number')
|
||||||
expect(user.account.description).to.be.null
|
expect(user.account.description).to.be.null
|
||||||
|
@ -377,7 +377,7 @@ describe('Test users', function () {
|
||||||
|
|
||||||
expect(user.username).to.equal('user_1')
|
expect(user.username).to.equal('user_1')
|
||||||
expect(user.email).to.equal('updated@example.com')
|
expect(user.email).to.equal('updated@example.com')
|
||||||
expect(user.displayNSFW).to.be.ok
|
expect(user.nsfwPolicy).to.equal('do_not_list')
|
||||||
expect(user.videoQuota).to.equal(2 * 1024 * 1024)
|
expect(user.videoQuota).to.equal(2 * 1024 * 1024)
|
||||||
expect(user.id).to.be.a('number')
|
expect(user.id).to.be.a('number')
|
||||||
expect(user.account.description).to.equal('my super description updated')
|
expect(user.account.description).to.equal('my super description updated')
|
||||||
|
@ -398,7 +398,7 @@ describe('Test users', function () {
|
||||||
|
|
||||||
expect(user.username).to.equal('user_1')
|
expect(user.username).to.equal('user_1')
|
||||||
expect(user.email).to.equal('updated2@example.com')
|
expect(user.email).to.equal('updated2@example.com')
|
||||||
expect(user.displayNSFW).to.be.ok
|
expect(user.nsfwPolicy).to.equal('do_not_list')
|
||||||
expect(user.videoQuota).to.equal(42)
|
expect(user.videoQuota).to.equal(42)
|
||||||
expect(user.roleLabel).to.equal('Moderator')
|
expect(user.roleLabel).to.equal('Moderator')
|
||||||
expect(user.id).to.be.a('number')
|
expect(user.id).to.be.a('number')
|
||||||
|
|
|
@ -0,0 +1,197 @@
|
||||||
|
/* tslint:disable:no-unused-expression */
|
||||||
|
|
||||||
|
import * as chai from 'chai'
|
||||||
|
import 'mocha'
|
||||||
|
import { flushTests, getVideosList, killallServers, ServerInfo, setAccessTokensToServers, uploadVideo } from '../../utils/index'
|
||||||
|
import { userLogin } from '../../utils/users/login'
|
||||||
|
import { createUser } from '../../utils/users/users'
|
||||||
|
import { getMyVideos } from '../../utils/videos/videos'
|
||||||
|
import {
|
||||||
|
getConfig, getCustomConfig,
|
||||||
|
getMyUserInformation,
|
||||||
|
getVideosListWithToken,
|
||||||
|
runServer,
|
||||||
|
searchVideo,
|
||||||
|
searchVideoWithToken, updateCustomConfig,
|
||||||
|
updateMyUser
|
||||||
|
} from '../../utils'
|
||||||
|
import { ServerConfig } from '../../../../shared/models'
|
||||||
|
import { CustomConfig } from '../../../../shared/models/server/custom-config.model'
|
||||||
|
|
||||||
|
const expect = chai.expect
|
||||||
|
|
||||||
|
describe('Test video NSFW policy', function () {
|
||||||
|
let server: ServerInfo
|
||||||
|
let userAccessToken: string
|
||||||
|
let customConfig: CustomConfig
|
||||||
|
|
||||||
|
before(async function () {
|
||||||
|
this.timeout(50000)
|
||||||
|
|
||||||
|
await flushTests()
|
||||||
|
server = await runServer(1)
|
||||||
|
|
||||||
|
// Get the access tokens
|
||||||
|
await setAccessTokensToServers([ server ])
|
||||||
|
|
||||||
|
{
|
||||||
|
const attributes = { name: 'nsfw', nsfw: true }
|
||||||
|
await uploadVideo(server.url, server.accessToken, attributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const attributes = { name: 'normal', nsfw: false }
|
||||||
|
await uploadVideo(server.url, server.accessToken, attributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const res = await getCustomConfig(server.url, server.accessToken)
|
||||||
|
customConfig = res.body
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Instance default NSFW policy', function () {
|
||||||
|
it('Should display NSFW videos with display default NSFW policy', async function () {
|
||||||
|
const resConfig = await getConfig(server.url)
|
||||||
|
const serverConfig: ServerConfig = resConfig.body
|
||||||
|
expect(serverConfig.instance.defaultNSFWPolicy).to.equal('display')
|
||||||
|
|
||||||
|
for (const res of [ await getVideosList(server.url), await searchVideo(server.url, 'n') ]) {
|
||||||
|
expect(res.body.total).to.equal(2)
|
||||||
|
|
||||||
|
const videos = res.body.data
|
||||||
|
expect(videos).to.have.lengthOf(2)
|
||||||
|
expect(videos[ 0 ].name).to.equal('normal')
|
||||||
|
expect(videos[ 1 ].name).to.equal('nsfw')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should not display NSFW videos with do_not_list default NSFW policy', async function () {
|
||||||
|
customConfig.instance.defaultNSFWPolicy = 'do_not_list'
|
||||||
|
await updateCustomConfig(server.url, server.accessToken, customConfig)
|
||||||
|
|
||||||
|
const resConfig = await getConfig(server.url)
|
||||||
|
const serverConfig: ServerConfig = resConfig.body
|
||||||
|
expect(serverConfig.instance.defaultNSFWPolicy).to.equal('do_not_list')
|
||||||
|
|
||||||
|
for (const res of [ await getVideosList(server.url), await searchVideo(server.url, 'n') ]) {
|
||||||
|
expect(res.body.total).to.equal(1)
|
||||||
|
|
||||||
|
const videos = res.body.data
|
||||||
|
expect(videos).to.have.lengthOf(1)
|
||||||
|
expect(videos[ 0 ].name).to.equal('normal')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should display NSFW videos with blur default NSFW policy', async function () {
|
||||||
|
customConfig.instance.defaultNSFWPolicy = 'blur'
|
||||||
|
await updateCustomConfig(server.url, server.accessToken, customConfig)
|
||||||
|
|
||||||
|
const resConfig = await getConfig(server.url)
|
||||||
|
const serverConfig: ServerConfig = resConfig.body
|
||||||
|
expect(serverConfig.instance.defaultNSFWPolicy).to.equal('blur')
|
||||||
|
|
||||||
|
for (const res of [ await getVideosList(server.url), await searchVideo(server.url, 'n') ]) {
|
||||||
|
expect(res.body.total).to.equal(2)
|
||||||
|
|
||||||
|
const videos = res.body.data
|
||||||
|
expect(videos).to.have.lengthOf(2)
|
||||||
|
expect(videos[ 0 ].name).to.equal('normal')
|
||||||
|
expect(videos[ 1 ].name).to.equal('nsfw')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('User NSFW policy', function () {
|
||||||
|
|
||||||
|
it('Should create a user having the default nsfw policy', async function () {
|
||||||
|
const username = 'user1'
|
||||||
|
const password = 'my super password'
|
||||||
|
await createUser(server.url, server.accessToken, username, password)
|
||||||
|
|
||||||
|
userAccessToken = await userLogin(server, { username, password })
|
||||||
|
|
||||||
|
const res = await getMyUserInformation(server.url, userAccessToken)
|
||||||
|
const user = res.body
|
||||||
|
|
||||||
|
expect(user.nsfwPolicy).to.equal('blur')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should display NSFW videos with blur user NSFW policy', async function () {
|
||||||
|
const results = [
|
||||||
|
await getVideosListWithToken(server.url, userAccessToken),
|
||||||
|
await searchVideoWithToken(server.url, 'n', userAccessToken)
|
||||||
|
]
|
||||||
|
|
||||||
|
for (const res of results) {
|
||||||
|
expect(res.body.total).to.equal(2)
|
||||||
|
|
||||||
|
const videos = res.body.data
|
||||||
|
expect(videos).to.have.lengthOf(2)
|
||||||
|
expect(videos[ 0 ].name).to.equal('normal')
|
||||||
|
expect(videos[ 1 ].name).to.equal('nsfw')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should display NSFW videos with display user NSFW policy', async function () {
|
||||||
|
await updateMyUser({
|
||||||
|
url: server.url,
|
||||||
|
accessToken: server.accessToken,
|
||||||
|
nsfwPolicy: 'display'
|
||||||
|
})
|
||||||
|
|
||||||
|
const results = [
|
||||||
|
await getVideosListWithToken(server.url, server.accessToken),
|
||||||
|
await searchVideoWithToken(server.url, 'n', server.accessToken)
|
||||||
|
]
|
||||||
|
|
||||||
|
for (const res of results) {
|
||||||
|
expect(res.body.total).to.equal(2)
|
||||||
|
|
||||||
|
const videos = res.body.data
|
||||||
|
expect(videos).to.have.lengthOf(2)
|
||||||
|
expect(videos[ 0 ].name).to.equal('normal')
|
||||||
|
expect(videos[ 1 ].name).to.equal('nsfw')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should not display NSFW videos with do_not_list user NSFW policy', async function () {
|
||||||
|
await updateMyUser({
|
||||||
|
url: server.url,
|
||||||
|
accessToken: server.accessToken,
|
||||||
|
nsfwPolicy: 'do_not_list'
|
||||||
|
})
|
||||||
|
|
||||||
|
const results = [
|
||||||
|
await getVideosListWithToken(server.url, server.accessToken),
|
||||||
|
await searchVideoWithToken(server.url, 'n', server.accessToken)
|
||||||
|
]
|
||||||
|
for (const res of results) {
|
||||||
|
expect(res.body.total).to.equal(1)
|
||||||
|
|
||||||
|
const videos = res.body.data
|
||||||
|
expect(videos).to.have.lengthOf(1)
|
||||||
|
expect(videos[ 0 ].name).to.equal('normal')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should be able to see my NSFW videos even with do_not_list user NSFW policy', async function () {
|
||||||
|
const res = await getMyVideos(server.url, server.accessToken, 0, 5)
|
||||||
|
expect(res.body.total).to.equal(2)
|
||||||
|
|
||||||
|
const videos = res.body.data
|
||||||
|
expect(videos).to.have.lengthOf(2)
|
||||||
|
expect(videos[ 0 ].name).to.equal('normal')
|
||||||
|
expect(videos[ 1 ].name).to.equal('nsfw')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
after(async function () {
|
||||||
|
killallServers([ server ])
|
||||||
|
|
||||||
|
// Keep the logs if the test failed
|
||||||
|
if (this['ok']) {
|
||||||
|
await flushTests()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
|
@ -3,6 +3,7 @@ import * as request from 'supertest'
|
||||||
import { makePostBodyRequest, makeUploadRequest, makePutBodyRequest } from '../'
|
import { makePostBodyRequest, makeUploadRequest, makePutBodyRequest } from '../'
|
||||||
|
|
||||||
import { UserRole } from '../../../../shared/index'
|
import { UserRole } from '../../../../shared/index'
|
||||||
|
import { NSFWPolicyType } from '../../../../shared/models/videos/nsfw-policy.type'
|
||||||
|
|
||||||
function createUser (
|
function createUser (
|
||||||
url: string,
|
url: string,
|
||||||
|
@ -128,7 +129,7 @@ function updateMyUser (options: {
|
||||||
url: string
|
url: string
|
||||||
accessToken: string,
|
accessToken: string,
|
||||||
newPassword?: string,
|
newPassword?: string,
|
||||||
displayNSFW?: boolean,
|
nsfwPolicy?: NSFWPolicyType,
|
||||||
email?: string,
|
email?: string,
|
||||||
autoPlayVideo?: boolean
|
autoPlayVideo?: boolean
|
||||||
description?: string
|
description?: string
|
||||||
|
@ -137,7 +138,7 @@ function updateMyUser (options: {
|
||||||
|
|
||||||
const toSend = {}
|
const toSend = {}
|
||||||
if (options.newPassword !== undefined && options.newPassword !== null) toSend['password'] = options.newPassword
|
if (options.newPassword !== undefined && options.newPassword !== null) toSend['password'] = options.newPassword
|
||||||
if (options.displayNSFW !== undefined && options.displayNSFW !== null) toSend['displayNSFW'] = options.displayNSFW
|
if (options.nsfwPolicy !== undefined && options.nsfwPolicy !== null) toSend['nsfwPolicy'] = options.nsfwPolicy
|
||||||
if (options.autoPlayVideo !== undefined && options.autoPlayVideo !== null) toSend['autoPlayVideo'] = options.autoPlayVideo
|
if (options.autoPlayVideo !== undefined && options.autoPlayVideo !== null) toSend['autoPlayVideo'] = options.autoPlayVideo
|
||||||
if (options.email !== undefined && options.email !== null) toSend['email'] = options.email
|
if (options.email !== undefined && options.email !== null) toSend['email'] = options.email
|
||||||
if (options.description !== undefined && options.description !== null) toSend['description'] = options.description
|
if (options.description !== undefined && options.description !== null) toSend['description'] = options.description
|
||||||
|
|
|
@ -128,6 +128,18 @@ function getVideosList (url: string) {
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getVideosListWithToken (url: string, token: string) {
|
||||||
|
const path = '/api/v1/videos'
|
||||||
|
|
||||||
|
return request(url)
|
||||||
|
.get(path)
|
||||||
|
.set('Authorization', 'Bearer ' + token)
|
||||||
|
.query({ sort: 'name' })
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
}
|
||||||
|
|
||||||
function getLocalVideos (url: string) {
|
function getLocalVideos (url: string) {
|
||||||
const path = '/api/v1/videos'
|
const path = '/api/v1/videos'
|
||||||
|
|
||||||
|
@ -202,6 +214,18 @@ function searchVideo (url: string, search: string) {
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function searchVideoWithToken (url: string, search: string, token: string) {
|
||||||
|
const path = '/api/v1/videos'
|
||||||
|
const req = request(url)
|
||||||
|
.get(path + '/search')
|
||||||
|
.set('Authorization', 'Bearer ' + token)
|
||||||
|
.query({ search })
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
|
||||||
|
return req.expect(200)
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
}
|
||||||
|
|
||||||
function searchVideoWithPagination (url: string, search: string, start: number, count: number, sort?: string) {
|
function searchVideoWithPagination (url: string, search: string, start: number, count: number, sort?: string) {
|
||||||
const path = '/api/v1/videos'
|
const path = '/api/v1/videos'
|
||||||
|
|
||||||
|
@ -418,6 +442,8 @@ async function completeVideoCheck (
|
||||||
expect(video.licence.label).to.equal(VIDEO_LICENCES[attributes.licence] || 'Unknown')
|
expect(video.licence.label).to.equal(VIDEO_LICENCES[attributes.licence] || 'Unknown')
|
||||||
expect(video.language.id).to.equal(attributes.language)
|
expect(video.language.id).to.equal(attributes.language)
|
||||||
expect(video.language.label).to.equal(VIDEO_LANGUAGES[attributes.language] || 'Unknown')
|
expect(video.language.label).to.equal(VIDEO_LANGUAGES[attributes.language] || 'Unknown')
|
||||||
|
expect(video.privacy.id).to.deep.equal(attributes.privacy)
|
||||||
|
expect(video.privacy.label).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy])
|
||||||
expect(video.nsfw).to.equal(attributes.nsfw)
|
expect(video.nsfw).to.equal(attributes.nsfw)
|
||||||
expect(video.description).to.equal(attributes.description)
|
expect(video.description).to.equal(attributes.description)
|
||||||
expect(video.account.host).to.equal(attributes.account.host)
|
expect(video.account.host).to.equal(attributes.account.host)
|
||||||
|
@ -435,8 +461,6 @@ async function completeVideoCheck (
|
||||||
|
|
||||||
expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
|
expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
|
||||||
expect(videoDetails.tags).to.deep.equal(attributes.tags)
|
expect(videoDetails.tags).to.deep.equal(attributes.tags)
|
||||||
expect(videoDetails.privacy.id).to.deep.equal(attributes.privacy)
|
|
||||||
expect(videoDetails.privacy.label).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy])
|
|
||||||
expect(videoDetails.account.name).to.equal(attributes.account.name)
|
expect(videoDetails.account.name).to.equal(attributes.account.name)
|
||||||
expect(videoDetails.account.host).to.equal(attributes.account.host)
|
expect(videoDetails.account.host).to.equal(attributes.account.host)
|
||||||
expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled)
|
expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled)
|
||||||
|
@ -490,6 +514,7 @@ export {
|
||||||
getVideoPrivacies,
|
getVideoPrivacies,
|
||||||
getVideoLanguages,
|
getVideoLanguages,
|
||||||
getMyVideos,
|
getMyVideos,
|
||||||
|
searchVideoWithToken,
|
||||||
getVideo,
|
getVideo,
|
||||||
getVideoWithToken,
|
getVideoWithToken,
|
||||||
getVideosList,
|
getVideosList,
|
||||||
|
@ -499,6 +524,7 @@ export {
|
||||||
searchVideo,
|
searchVideo,
|
||||||
searchVideoWithPagination,
|
searchVideoWithPagination,
|
||||||
searchVideoWithSort,
|
searchVideoWithSort,
|
||||||
|
getVideosListWithToken,
|
||||||
uploadVideo,
|
uploadVideo,
|
||||||
updateVideo,
|
updateVideo,
|
||||||
rateVideo,
|
rateVideo,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { NSFWPolicyType } from '../videos/nsfw-policy.type'
|
||||||
|
|
||||||
export interface CustomConfig {
|
export interface CustomConfig {
|
||||||
instance: {
|
instance: {
|
||||||
name: string
|
name: string
|
||||||
|
@ -5,6 +7,7 @@ export interface CustomConfig {
|
||||||
description: string
|
description: string
|
||||||
terms: string
|
terms: string
|
||||||
defaultClientRoute: string
|
defaultClientRoute: string
|
||||||
|
defaultNSFWPolicy: NSFWPolicyType
|
||||||
customizations: {
|
customizations: {
|
||||||
javascript?: string
|
javascript?: string
|
||||||
css?: string
|
css?: string
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { NSFWPolicyType } from '../videos/nsfw-policy.type'
|
||||||
|
|
||||||
export interface ServerConfig {
|
export interface ServerConfig {
|
||||||
serverVersion: string
|
serverVersion: string
|
||||||
|
|
||||||
|
@ -5,6 +7,7 @@ export interface ServerConfig {
|
||||||
name: string
|
name: string
|
||||||
shortDescription: string
|
shortDescription: string
|
||||||
defaultClientRoute: string
|
defaultClientRoute: string
|
||||||
|
defaultNSFWPolicy: NSFWPolicyType
|
||||||
customizations: {
|
customizations: {
|
||||||
javascript: string
|
javascript: string
|
||||||
css: string
|
css: string
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import { NSFWPolicyType } from '../videos/nsfw-policy.type'
|
||||||
|
|
||||||
export interface UserUpdateMe {
|
export interface UserUpdateMe {
|
||||||
description?: string
|
description?: string
|
||||||
displayNSFW?: boolean
|
nsfwPolicy?: NSFWPolicyType
|
||||||
autoPlayVideo?: boolean
|
autoPlayVideo?: boolean
|
||||||
email?: string
|
email?: string
|
||||||
password?: string
|
password?: string
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import { Account } from '../actors'
|
import { Account } from '../actors'
|
||||||
import { VideoChannel } from '../videos/video-channel.model'
|
import { VideoChannel } from '../videos/video-channel.model'
|
||||||
import { UserRole } from './user-role'
|
import { UserRole } from './user-role'
|
||||||
|
import { NSFWPolicyType } from '../videos/nsfw-policy.type'
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
id: number
|
id: number
|
||||||
username: string
|
username: string
|
||||||
email: string
|
email: string
|
||||||
displayNSFW: boolean
|
nsfwPolicy: NSFWPolicyType
|
||||||
autoPlayVideo: boolean
|
autoPlayVideo: boolean
|
||||||
role: UserRole
|
role: UserRole
|
||||||
videoQuota: number
|
videoQuota: number
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
export type NSFWPolicyType = 'do_not_list' | 'blur' | 'display'
|
|
@ -26,6 +26,7 @@ export interface Video {
|
||||||
category: VideoConstant<number>
|
category: VideoConstant<number>
|
||||||
licence: VideoConstant<number>
|
licence: VideoConstant<number>
|
||||||
language: VideoConstant<number>
|
language: VideoConstant<number>
|
||||||
|
privacy: VideoConstant<VideoPrivacy>
|
||||||
description: string
|
description: string
|
||||||
duration: number
|
duration: number
|
||||||
isLocal: boolean
|
isLocal: boolean
|
||||||
|
@ -48,7 +49,6 @@ export interface Video {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VideoDetails extends Video {
|
export interface VideoDetails extends Video {
|
||||||
privacy: VideoConstant<VideoPrivacy>
|
|
||||||
descriptionPath: string
|
descriptionPath: string
|
||||||
support: string
|
support: string
|
||||||
channel: VideoChannel
|
channel: VideoChannel
|
||||||
|
|
Loading…
Reference in New Issue