Enable video upload and edit
authorclementbrizard <clementbrizard53@gmail.com>
Sat, 12 Jan 2019 13:45:23 +0000 (13:45 +0000)
committerclementbrizard <clementbrizard53@gmail.com>
Sat, 12 Jan 2019 13:45:23 +0000 (13:45 +0000)
client/src/app/shared/forms/form-validators/video-validators.service.ts
client/src/app/shared/video-import/video-import.service.ts
client/src/app/shared/video/video.service.ts
client/src/app/videos/+video-edit/shared/video-edit.component.html
client/src/app/videos/+video-edit/shared/video-edit.component.ts
server/controllers/api/videos/index.ts
server/helpers/custom-validators/videos.ts
server/middlewares/validators/videos/videos.ts

index 81ed0666f11f41693c741ee80a3d6e5b3be0fc0a..e3f7a0969dc5ce3ee1809f0450cfdad03c325d69 100644 (file)
@@ -16,6 +16,7 @@ export class VideoValidatorsService {
   readonly VIDEO_TAGS: BuildFormValidator
   readonly VIDEO_SUPPORT: BuildFormValidator
   readonly VIDEO_SCHEDULE_PUBLICATION_AT: BuildFormValidator
+  readonly VIDEO_ORIGINALLY_PUBLISHED_AT: BuildFormValidator
 
   constructor (private i18n: I18n) {
 
@@ -92,5 +93,10 @@ export class VideoValidatorsService {
         'required': this.i18n('A date is required to schedule video update.')
       }
     }
+
+    this.VIDEO_ORIGINALLY_PUBLISHED_AT = {
+      VALIDATORS: [ ],
+      MESSAGES: {}
+    }
   }
 }
index 7ae66ddfcdf23a447e1fcb3ab982cda5039b57c7..11a7694c89f4bb35ff42da30484229db4c62bb89 100644 (file)
@@ -67,6 +67,7 @@ export class VideoImportService {
     const description = video.description || null
     const support = video.support || null
     const scheduleUpdate = video.scheduleUpdate || null
+    const originallyPublishedAt = video.originallyPublishedAt || null
 
     return {
       name: video.name,
@@ -83,7 +84,8 @@ export class VideoImportService {
       commentsEnabled: video.commentsEnabled,
       thumbnailfile: video.thumbnailfile,
       previewfile: video.previewfile,
-      scheduleUpdate
+      scheduleUpdate,
+      originallyPublishedAt
     }
   }
 
index 55844f9881971d2e708916ee966a007044f7ab41..5d258891f8d74521e78d506620e1ff76bafd8d70 100644 (file)
@@ -81,6 +81,7 @@ export class VideoService implements VideosProvider {
     const description = video.description || null
     const support = video.support || null
     const scheduleUpdate = video.scheduleUpdate || null
+    const originallyPublishedAt = video.originallyPublishedAt || null
 
     const body: VideoUpdate = {
       name: video.name,
@@ -97,7 +98,8 @@ export class VideoService implements VideosProvider {
       commentsEnabled: video.commentsEnabled,
       thumbnailfile: video.thumbnailfile,
       previewfile: video.previewfile,
-      scheduleUpdate
+      scheduleUpdate,
+      originallyPublishedAt
     }
 
     const data = objectToFormData(body)
index bd52d686a76fb11799070f4eca75d05c4837a4aa..23a71a0685feea8b9a115fe13bf3c80dbeba7a70 100644 (file)
               </div>
             </div>
 
+            <div class="form-group">
+              <label i18n for="originallyPublishedAt">Original publication date</label>
+              <my-help i18n-preHtml preHtml="This is the date when the content was originally published (e.g. the release date for a film)"></my-help>
+              <p-calendar
+                id="originallyPublishedAt" formControlName="originallyPublishedAt" [dateFormat]="calendarDateFormat"
+                [locale]="calendarLocale" [showTime]="true" [hideOnDateTimeSelect]="true" [monthNavigator]="true" [yearNavigator]="true" [yearRange]="myYearRange"
+              >
+              </p-calendar>
+
+              <div *ngIf="formErrors.originallyPublishedAt" class="form-error">
+                {{ formErrors.originallyPublishedAt }}
+              </div>
+            </div>
+
             <my-peertube-checkbox
               inputName="nsfw" formControlName="nsfw"
               i18n-labelText labelText="This video contains mature or explicit content"
index 85e01590108957db131dc9cc747e9f75e77299ec..d02f18ee946b38c15e8992e53a3a7876fef6aff2 100644 (file)
@@ -45,6 +45,7 @@ export class VideoEditComponent implements OnInit, OnDestroy {
 
   calendarLocale: any = {}
   minScheduledDate = new Date()
+  myYearRange = '1880:' + (new Date()).getFullYear()
 
   calendarTimezone: string
   calendarDateFormat: string
@@ -99,7 +100,8 @@ export class VideoEditComponent implements OnInit, OnDestroy {
       thumbnailfile: null,
       previewfile: null,
       support: this.videoValidatorsService.VIDEO_SUPPORT,
-      schedulePublicationAt: this.videoValidatorsService.VIDEO_SCHEDULE_PUBLICATION_AT
+      schedulePublicationAt: this.videoValidatorsService.VIDEO_SCHEDULE_PUBLICATION_AT,
+      originallyPublishedAt: this.videoValidatorsService.VIDEO_ORIGINALLY_PUBLISHED_AT
     }
 
     this.formValidatorService.updateForm(
index 33521a8c1386eb67e6bfe157338c69d553133895..b26dcabe17cf496d19f05acec7f396163b18f3d2 100644 (file)
@@ -188,7 +188,8 @@ async function addVideo (req: express.Request, res: express.Response) {
     support: videoInfo.support,
     privacy: videoInfo.privacy,
     duration: videoPhysicalFile['duration'], // duration was added by a previous middleware
-    channelId: res.locals.videoChannel.id
+    channelId: res.locals.videoChannel.id,
+    originallyPublishedAt: videoInfo.originallyPublishedAt
   }
   const video = new VideoModel(videoData)
   video.url = getVideoActivityPubUrl(video) // We use the UUID, so set the URL after building the object
@@ -325,6 +326,11 @@ async function updateVideo (req: express.Request, res: express.Response) {
       if (videoInfoToUpdate.support !== undefined) videoInstance.set('support', videoInfoToUpdate.support)
       if (videoInfoToUpdate.description !== undefined) videoInstance.set('description', videoInfoToUpdate.description)
       if (videoInfoToUpdate.commentsEnabled !== undefined) videoInstance.set('commentsEnabled', videoInfoToUpdate.commentsEnabled)
+      if (videoInfoToUpdate.originallyPublishedAt !== undefined &&
+          videoInfoToUpdate.originallyPublishedAt !== null) {
+        videoInstance.set('originallyPublishedAt', videoInfoToUpdate.originallyPublishedAt)
+      }
+
       if (videoInfoToUpdate.privacy !== undefined) {
         const newPrivacy = parseInt(videoInfoToUpdate.privacy.toString(), 10)
         videoInstance.set('privacy', newPrivacy)
index e6f22e6c52a3afe8e6edd44ba99f4bfd49e0a149..ce4492a30602f36e2bf7473961118fb148b48073 100644 (file)
@@ -13,7 +13,7 @@ import {
   VIDEO_STATES
 } from '../../initializers'
 import { VideoModel } from '../../models/video/video'
-import { exists, isArray, isFileValid } from './misc'
+import { exists, isArray, isDateValid, isFileValid } from './misc'
 import { VideoChannelModel } from '../../models/video/video-channel'
 import { UserModel } from '../../models/account/user'
 import * as magnetUtil from 'magnet-uri'
@@ -115,6 +115,10 @@ function isScheduleVideoUpdatePrivacyValid (value: number) {
     )
 }
 
+function isVideoOriginallyPublishedAtValid (value: string | null) {
+  return value === null || isDateValid(value)
+}
+
 function isVideoFileInfoHashValid (value: string | null | undefined) {
   return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.INFO_HASH)
 }
@@ -220,6 +224,7 @@ export {
   isVideoTagsValid,
   isVideoFPSResolutionValid,
   isScheduleVideoUpdatePrivacyValid,
+  isVideoOriginallyPublishedAtValid,
   isVideoFile,
   isVideoMagnetUriValid,
   isVideoStateValid,
index 051a19e16f832e46ad3229eafde1e10027fea1dd..194d12c6e5668d242f5fcd781ff9cf810356d0c4 100644 (file)
@@ -14,6 +14,7 @@ import {
 } from '../../../helpers/custom-validators/misc'
 import {
   checkUserCanManageVideo,
+  isVideoOriginallyPublishedAtValid,
   isScheduleVideoUpdatePrivacyValid,
   isVideoCategoryValid,
   isVideoChannelOfAccountExist,
@@ -340,6 +341,10 @@ function getCommonVideoAttributes () {
       .optional()
       .toBoolean()
       .custom(isBooleanValid).withMessage('Should have comments enabled boolean'),
+    body('originallyPublishedAt')
+      .optional()
+      .customSanitizer(toValueOrNull)
+      .custom(isVideoOriginallyPublishedAtValid).withMessage('Should have a valid original publication date'),
 
     body('scheduleUpdate')
       .optional()