-
-
-
Video abuses list
-
-
-
-
-
-
-
-
-
- {{ videoAbuse.videoId }}
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ {{ videoAbuse.videoName }}
+
+
+
diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.scss b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.scss
new file mode 100644
index 000000000..6a4762650
--- /dev/null
+++ b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.scss
@@ -0,0 +1,6 @@
+/deep/ a {
+
+ &, &:hover, &:active, &:focus {
+ color: #000;
+ }
+}
diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts
index 654603d01..b4d3bbd24 100644
--- a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts
+++ b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts
@@ -8,7 +8,8 @@ import { VideoAbuse } from '../../../../../../shared'
@Component({
selector: 'my-video-abuse-list',
- templateUrl: './video-abuse-list.component.html'
+ templateUrl: './video-abuse-list.component.html',
+ styleUrls: [ './video-abuse-list.component.scss']
})
export class VideoAbuseListComponent extends RestTable implements OnInit {
videoAbuses: VideoAbuse[] = []
diff --git a/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html b/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html
index 05d116798..1d813fa07 100644
--- a/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html
+++ b/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html
@@ -18,7 +18,7 @@
-
+
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts
index 0db197f02..e887dde1f 100644
--- a/client/src/app/core/auth/auth.service.ts
+++ b/client/src/app/core/auth/auth.service.ts
@@ -169,19 +169,15 @@ export class AuthService {
return this.http.post
(AuthService.BASE_TOKEN_URL, body, { headers })
.map(res => this.handleRefreshToken(res))
- .catch(res => {
- // The refresh token is invalid?
- if (res.status === 400 && res.error.error === 'invalid_grant') {
- console.error('Cannot refresh token -> logout...')
- this.logout()
- this.router.navigate(['/login'])
+ .catch(err => {
+ console.error(err)
+ console.log('Cannot refresh token -> logout...')
+ this.logout()
+ this.router.navigate(['/login'])
- return Observable.throw({
- error: 'You need to reconnect.'
- })
- }
-
- return this.restExtractor.handleError(res)
+ return Observable.throw({
+ error: 'You need to reconnect.'
+ })
})
}
diff --git a/client/src/app/shared/video/abstract-video-list.ts b/client/src/app/shared/video/abstract-video-list.ts
index ee1ed2cb2..ba1635a18 100644
--- a/client/src/app/shared/video/abstract-video-list.ts
+++ b/client/src/app/shared/video/abstract-video-list.ts
@@ -62,7 +62,7 @@ export abstract class AbstractVideoList implements OnInit {
observable.subscribe(
({ videos, totalVideos }) => {
// Paging is too high, return to the first one
- if (totalVideos <= ((this.pagination.currentPage - 1) * this.pagination.itemsPerPage)) {
+ if (this.pagination.currentPage > 1 && totalVideos <= ((this.pagination.currentPage - 1) * this.pagination.itemsPerPage)) {
this.pagination.currentPage = 1
this.setNewRouteParams()
return this.reloadVideos()
@@ -82,6 +82,10 @@ export abstract class AbstractVideoList implements OnInit {
}
protected hasMoreVideos () {
+ // No results
+ if (this.pagination.totalItems === 0) return false
+
+ // Not loaded yet
if (!this.pagination.totalItems) return true
const maxPage = this.pagination.totalItems / this.pagination.itemsPerPage
diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.html b/client/src/app/videos/+video-edit/shared/video-description.component.html
index da66a9753..5d05467be 100644
--- a/client/src/app/videos/+video-edit/shared/video-description.component.html
+++ b/client/src/app/videos/+video-edit/shared/video-description.component.html
@@ -1,6 +1,6 @@
diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.scss b/client/src/app/videos/+video-edit/shared/video-description.component.scss
index 8155cbca7..2a4c8d189 100644
--- a/client/src/app/videos/+video-edit/shared/video-description.component.scss
+++ b/client/src/app/videos/+video-edit/shared/video-description.component.scss
@@ -4,4 +4,21 @@ textarea {
padding: 5px 15px;
font-size: 15px;
height: 150px;
+ margin-bottom: 15px;
}
+
+/deep/ {
+ .nav-link {
+ display: flex !important;
+ align-items: center;
+ height: 30px !important;
+ padding: 0 15px !important;
+ }
+
+ .tab-content {
+ min-height: 75px;
+ padding: 15px;
+ font-size: 15px;
+ }
+}
+
diff --git a/client/src/app/videos/+video-edit/shared/video-description.component.ts b/client/src/app/videos/+video-edit/shared/video-description.component.ts
index 8dfb74b2a..9b77a27e6 100644
--- a/client/src/app/videos/+video-edit/shared/video-description.component.ts
+++ b/client/src/app/videos/+video-edit/shared/video-description.component.ts
@@ -60,6 +60,8 @@ export class VideoDescriptionComponent implements ControlValueAccessor, OnInit {
}
private updateDescriptionPreviews () {
+ if (!this.description) return
+
this.truncatedDescriptionHTML = this.markdownService.markdownToHTML(truncate(this.description, { length: 250 }))
this.descriptionHTML = this.markdownService.markdownToHTML(this.description)
}
diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss
index ecbb8dac5..5a4aa4cd9 100644
--- a/client/src/sass/application.scss
+++ b/client/src/sass/application.scss
@@ -165,7 +165,7 @@ p-datatable {
td {
border: 1px solid #E5E5E5 !important;
- padding: 15px;
+ padding-left: 15px !important;
}
tr {
@@ -185,6 +185,10 @@ p-datatable {
&:first-child td {
border-top: none !important;
}
+
+ &:last-child td {
+ border-bottom: none !important;
+ }
}
th {
@@ -198,6 +202,7 @@ p-datatable {
&.ui-state-active, &.ui-sortable-column:hover {
background-color: #f0f0f0 !important;
border: 1px solid #f0f0f0 !important;
+ border-width: 0 1px !important;
}
}
@@ -208,17 +213,10 @@ p-datatable {
}
p-paginator {
- overflow: hidden;
- display: block;
- padding-top: 2px;
- border: 1px solid #f0f0f0 !important;
- border-top: none !important;
-
.ui-paginator-bottom {
position: relative;
border: none !important;
- border-top: 1px solid #f0f0f0 !important;
- box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.16);
+ border: 1px solid #f0f0f0 !important;
height: 40px;
display: flex;
justify-content: center;
@@ -298,11 +296,6 @@ p-datatable {
font-weight: $font-semibold !important;
}
}
-
- .tab-content {
- min-height: 75px;
- padding: 15px;
- }
}
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 0f71a7f7f..63de662a7 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -280,7 +280,7 @@ async function updateVideo (req: express.Request, res: express.Response) {
if (videoInfoToUpdate.licence !== undefined) videoInstance.set('licence', videoInfoToUpdate.licence)
if (videoInfoToUpdate.language !== undefined) videoInstance.set('language', videoInfoToUpdate.language)
if (videoInfoToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfoToUpdate.nsfw)
- if (videoInfoToUpdate.privacy !== undefined) videoInstance.set('privacy', videoInfoToUpdate.privacy)
+ if (videoInfoToUpdate.privacy !== undefined) videoInstance.set('privacy', parseInt(videoInfoToUpdate.privacy.toString(), 10))
if (videoInfoToUpdate.description !== undefined) videoInstance.set('description', videoInfoToUpdate.description)
const videoInstanceUpdated = await videoInstance.save(sequelizeOptions)
@@ -298,9 +298,9 @@ async function updateVideo (req: express.Request, res: express.Response) {
}
// Video is not private anymore, send a create action to remote servers
- if (wasPrivateVideo === true && videoInstance.privacy !== VideoPrivacy.PRIVATE) {
- await sendAddVideo(videoInstance, t)
- await shareVideoByServer(videoInstance, t)
+ if (wasPrivateVideo === true && videoInstanceUpdated.privacy !== VideoPrivacy.PRIVATE) {
+ await sendAddVideo(videoInstanceUpdated, t)
+ await shareVideoByServer(videoInstanceUpdated, t)
}
})
diff --git a/server/helpers/custom-validators/activitypub/videos.ts b/server/helpers/custom-validators/activitypub/videos.ts
index 12c672fd2..2ed2988f5 100644
--- a/server/helpers/custom-validators/activitypub/videos.ts
+++ b/server/helpers/custom-validators/activitypub/videos.ts
@@ -49,14 +49,14 @@ function isVideoTorrentObjectValid (video: any) {
isActivityPubVideoDurationValid(video.duration) &&
isUUIDValid(video.uuid) &&
setValidRemoteTags(video) &&
- isRemoteIdentifierValid(video.category) &&
- isRemoteIdentifierValid(video.licence) &&
+ (!video.category || isRemoteIdentifierValid(video.category)) &&
+ (!video.licence || isRemoteIdentifierValid(video.licence)) &&
(!video.language || isRemoteIdentifierValid(video.language)) &&
isVideoViewsValid(video.views) &&
isVideoNSFWValid(video.nsfw) &&
isDateValid(video.published) &&
isDateValid(video.updated) &&
- isRemoteVideoContentValid(video.mediaType, video.content) &&
+ (!video.content || isRemoteVideoContentValid(video.mediaType, video.content)) &&
isRemoteVideoIconValid(video.icon) &&
setValidRemoteVideoUrls(video) &&
video.url.length !== 0
diff --git a/server/helpers/custom-validators/videos.ts b/server/helpers/custom-validators/videos.ts
index f13178c54..4fc460699 100644
--- a/server/helpers/custom-validators/videos.ts
+++ b/server/helpers/custom-validators/videos.ts
@@ -9,16 +9,17 @@ import { VIDEO_PRIVACIES } from '../../initializers/constants'
import { database as db } from '../../initializers/database'
import { VideoInstance } from '../../models/video/video-interface'
import { exists, isArray } from './misc'
+import isInt = require('validator/lib/isInt')
const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES
function isVideoCategoryValid (value: number) {
- return VIDEO_CATEGORIES[value] !== undefined
+ return value === null || VIDEO_CATEGORIES[value] !== undefined
}
function isVideoLicenceValid (value: number) {
- return VIDEO_LICENCES[value] !== undefined
+ return value === null || VIDEO_LICENCES[value] !== undefined
}
function isVideoLanguageValid (value: number) {
@@ -38,7 +39,7 @@ function isVideoTruncatedDescriptionValid (value: string) {
}
function isVideoDescriptionValid (value: string) {
- return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION)
+ return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION))
}
function isVideoNameValid (value: string) {
@@ -84,7 +85,7 @@ function isVideoFile (files: { [ fieldname: string ]: Express.Multer.File[] } |
}
function isVideoPrivacyValid (value: string) {
- return VIDEO_PRIVACIES[value] !== undefined
+ return validator.isInt(value + '') && VIDEO_PRIVACIES[value] !== undefined
}
function isVideoFileInfoHashValid (value: string) {
diff --git a/server/lib/activitypub/process/misc.ts b/server/lib/activitypub/process/misc.ts
index f20e588ab..0baa22c26 100644
--- a/server/lib/activitypub/process/misc.ts
+++ b/server/lib/activitypub/process/misc.ts
@@ -41,15 +41,30 @@ async function videoActivityObjectToDBAttributes (
language = parseInt(videoObject.language.identifier, 10)
}
+ let category = null
+ if (videoObject.category) {
+ category = parseInt(videoObject.category.identifier, 10)
+ }
+
+ let licence = null
+ if (videoObject.licence) {
+ licence = parseInt(videoObject.licence.identifier, 10)
+ }
+
+ let description = null
+ if (videoObject.content) {
+ description = videoObject.content
+ }
+
const videoData: VideoAttributes = {
name: videoObject.name,
uuid: videoObject.uuid,
url: videoObject.id,
- category: parseInt(videoObject.category.identifier, 10),
- licence: parseInt(videoObject.licence.identifier, 10),
+ category,
+ licence,
language,
+ description,
nsfw: videoObject.nsfw,
- description: videoObject.content,
channelId: videoChannel.id,
duration: parseInt(duration, 10),
createdAt: new Date(videoObject.published),
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 8b1eb1f96..d46fdeebe 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -564,6 +564,22 @@ toActivityPubObject = function (this: VideoInstance) {
}
}
+ let category
+ if (this.category) {
+ category = {
+ identifier: this.category + '',
+ name: this.getCategoryLabel()
+ }
+ }
+
+ let licence
+ if (this.licence) {
+ licence = {
+ identifier: this.licence + '',
+ name: this.getLicenceLabel()
+ }
+ }
+
let likesObject
let dislikesObject
@@ -635,14 +651,8 @@ toActivityPubObject = function (this: VideoInstance) {
duration: 'PT' + this.duration + 'S',
uuid: this.uuid,
tag,
- category: {
- identifier: this.category + '',
- name: this.getCategoryLabel()
- },
- licence: {
- identifier: this.licence + '',
- name: this.getLicenceLabel()
- },
+ category,
+ licence,
language,
views: this.views,
nsfw: this.nsfw,
diff --git a/server/tests/api/multiple-servers.ts b/server/tests/api/multiple-servers.ts
index c7f19f261..c9f74cc8c 100644
--- a/server/tests/api/multiple-servers.ts
+++ b/server/tests/api/multiple-servers.ts
@@ -2,6 +2,8 @@
import 'mocha'
import * as chai from 'chai'
+import { join } from "path"
+import * as request from 'supertest'
import {
dateIsValid,
@@ -707,6 +709,50 @@ describe('Test multiple servers', function () {
})
})
+ describe('With minimum parameters', function () {
+ it('Should upload and propagate the video', async function () {
+ this.timeout(50000)
+
+ const path = '/api/v1/videos/upload'
+
+ const req = request(servers[1].url)
+ .post(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + servers[1].accessToken)
+ .field('name', 'minimum parameters')
+ .field('privacy', '1')
+ .field('nsfw', 'false')
+ .field('channelId', '1')
+
+ const filePath = join(__dirname, '..', 'api', 'fixtures', 'video_short.webm')
+
+ await req.attach('videofile', filePath)
+ .expect(200)
+
+ await wait(25000)
+
+ for (const server of servers) {
+ const res = await getVideosList(server.url)
+ const video = res.body.data.find(v => v.name === 'minimum parameters')
+
+ expect(video.name).to.equal('minimum parameters')
+ expect(video.category).to.equal(null)
+ expect(video.categoryLabel).to.equal('Misc')
+ expect(video.licence).to.equal(null)
+ expect(video.licenceLabel).to.equal('Unknown')
+ expect(video.language).to.equal(null)
+ expect(video.languageLabel).to.equal('Unknown')
+ expect(video.nsfw).to.not.be.ok
+ expect(video.description).to.equal(null)
+ expect(video.serverHost).to.equal('localhost:9002')
+ expect(video.accountName).to.equal('root')
+ expect(video.tags).to.deep.equal([ ])
+ expect(dateIsValid(video.createdAt)).to.be.true
+ expect(dateIsValid(video.updatedAt)).to.be.true
+ }
+ })
+ })
+
after(async function () {
killallServers(servers)
diff --git a/server/tests/api/single-server.ts b/server/tests/api/single-server.ts
index e99955ef4..91460c7ae 100644
--- a/server/tests/api/single-server.ts
+++ b/server/tests/api/single-server.ts
@@ -694,43 +694,6 @@ describe('Test a single server', function () {
expect(video.dislikes).to.equal(1)
})
- it('Should upload a video with minimum parameters', async function () {
- const path = '/api/v1/videos/upload'
-
- const req = request(server.url)
- .post(path)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + server.accessToken)
- .field('name', 'minimum parameters')
- .field('privacy', '1')
- .field('nsfw', 'false')
- .field('channelId', '1')
-
- const filePath = join(__dirname, '..', 'api', 'fixtures', 'video_short.webm')
-
- await req.attach('videofile', filePath)
- .expect(200)
-
- const res = await getVideosList(server.url)
- const video = res.body.data.find(v => v.name === 'minimum parameters')
-
- expect(video.name).to.equal('minimum parameters')
- expect(video.category).to.equal(null)
- expect(video.categoryLabel).to.equal('Misc')
- expect(video.licence).to.equal(null)
- expect(video.licenceLabel).to.equal('Unknown')
- expect(video.language).to.equal(null)
- expect(video.languageLabel).to.equal('Unknown')
- expect(video.nsfw).to.not.be.ok
- expect(video.description).to.equal(null)
- expect(video.serverHost).to.equal('localhost:9001')
- expect(video.accountName).to.equal('root')
- expect(video.isLocal).to.be.true
- expect(video.tags).to.deep.equal([ ])
- expect(dateIsValid(video.createdAt)).to.be.true
- expect(dateIsValid(video.updatedAt)).to.be.true
- })
-
after(async function () {
killallServers([ server ])