mirror of https://github.com/Chocobozzz/PeerTube
Import torrents with webtorrent
parent
ce33919c24
commit
990b6a0b0c
|
@ -5,7 +5,7 @@
|
|||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th style="width: 40px;"></th>
|
||||
<th i18n>URL</th>
|
||||
<th i18n>Target</th>
|
||||
<th i18n>Video</th>
|
||||
<th i18n style="width: 150px">State</th>
|
||||
<th i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
|
||||
|
@ -22,7 +22,10 @@
|
|||
</td>
|
||||
|
||||
<td>
|
||||
<a [href]="videoImport.targetUrl" target="_blank" rel="noopener noreferrer">{{ videoImport.targetUrl }}</a>
|
||||
<a *ngIf="videoImport.targetUrl; else torrent" [href]="videoImport.targetUrl" target="_blank" rel="noopener noreferrer">{{ videoImport.targetUrl }}</a>
|
||||
<ng-template #torrent>
|
||||
<span [title]="videoImport.torrentName || videoImport.magnetUri">{{ videoImport.torrentName || videoImport.magnetUri }}</span>
|
||||
</ng-template>
|
||||
</td>
|
||||
|
||||
<td *ngIf="isVideoImportPending(videoImport)">
|
||||
|
|
|
@ -2,8 +2,16 @@
|
|||
<div class="import-video-torrent">
|
||||
<div class="icon icon-upload"></div>
|
||||
|
||||
<div class="form-group">
|
||||
<label i18n for="magnetUri">Magnet URI</label>
|
||||
<div class="button-file">
|
||||
<span i18n>Select the torrent to import</span>
|
||||
<input #torrentfileInput type="file" name="torrentfile" id="torrentfile" accept=".torrent" (change)="fileChange()" />
|
||||
</div>
|
||||
<span class="button-file-extension">(.torrent)</span>
|
||||
|
||||
<div class="torrent-or-magnet">Or</div>
|
||||
|
||||
<div class="form-group form-group-magnet-uri">
|
||||
<label i18n for="magnetUri">Paste magnet URI</label>
|
||||
<my-help
|
||||
helpType="custom" i18n-customHtml
|
||||
customHtml="You can import any torrent file that points to a mp4 file. You should make sure you have diffusion rights over the content it points to, otherwise it could cause legal trouble to yourself and your instance."
|
||||
|
|
|
@ -20,6 +20,26 @@ $width-size: 190px;
|
|||
background-image: url('../../../../assets/images/video/upload.svg');
|
||||
}
|
||||
|
||||
.button-file {
|
||||
@include peertube-button-file(auto);
|
||||
|
||||
min-width: 190px;
|
||||
}
|
||||
|
||||
.button-file-extension {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.torrent-or-magnet {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.form-group-magnet-uri {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
@include peertube-input-text($width-size);
|
||||
display: block;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, EventEmitter, OnInit, Output } from '@angular/core'
|
||||
import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'
|
||||
import { Router } from '@angular/router'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { VideoPrivacy, VideoUpdate } from '../../../../../../shared/models/videos'
|
||||
|
@ -23,6 +23,7 @@ import { VideoImportService } from '@app/shared/video-import'
|
|||
})
|
||||
export class VideoImportTorrentComponent extends VideoSend implements OnInit, CanComponentDeactivate {
|
||||
@Output() firstStepDone = new EventEmitter<string>()
|
||||
@ViewChild('torrentfileInput') torrentfileInput
|
||||
|
||||
videoFileName: string
|
||||
magnetUri = ''
|
||||
|
@ -33,7 +34,7 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Ca
|
|||
|
||||
video: VideoEdit
|
||||
|
||||
protected readonly DEFAULT_VIDEO_PRIVACY = VideoPrivacy.PRIVATE
|
||||
protected readonly DEFAULT_VIDEO_PRIVACY = VideoPrivacy.PUBLIC
|
||||
|
||||
constructor (
|
||||
protected formValidatorService: FormValidatorService,
|
||||
|
@ -62,7 +63,14 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Ca
|
|||
return !!this.magnetUri
|
||||
}
|
||||
|
||||
importVideo () {
|
||||
fileChange () {
|
||||
const torrentfile = this.torrentfileInput.nativeElement.files[0] as File
|
||||
if (!torrentfile) return
|
||||
|
||||
this.importVideo(torrentfile)
|
||||
}
|
||||
|
||||
importVideo (torrentfile?: Blob) {
|
||||
this.isImportingVideo = true
|
||||
|
||||
const videoUpdate: VideoUpdate = {
|
||||
|
@ -74,7 +82,7 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Ca
|
|||
|
||||
this.loadingBar.start()
|
||||
|
||||
this.videoImportService.importVideoTorrent(this.magnetUri, videoUpdate).subscribe(
|
||||
this.videoImportService.importVideoTorrent(torrentfile || this.magnetUri, videoUpdate).subscribe(
|
||||
res => {
|
||||
this.loadingBar.complete()
|
||||
this.firstStepDone.emit(res.video.name)
|
||||
|
|
|
@ -33,7 +33,7 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, CanCom
|
|||
|
||||
video: VideoEdit
|
||||
|
||||
protected readonly DEFAULT_VIDEO_PRIVACY = VideoPrivacy.PRIVATE
|
||||
protected readonly DEFAULT_VIDEO_PRIVACY = VideoPrivacy.PUBLIC
|
||||
|
||||
constructor (
|
||||
protected formValidatorService: FormValidatorService,
|
||||
|
|
|
@ -49,7 +49,7 @@ $background-color: #F7F7F7;
|
|||
background-color: $background-color;
|
||||
border-radius: 3px;
|
||||
width: 100%;
|
||||
height: 440px;
|
||||
min-height: 440px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
import * as magnetUtil from 'magnet-uri'
|
||||
import * as express from 'express'
|
||||
import * as magnetUtil from 'magnet-uri'
|
||||
import 'multer'
|
||||
import { auditLoggerFactory, VideoImportAuditView } from '../../../helpers/audit-logger'
|
||||
import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoImportAddValidator } from '../../../middlewares'
|
||||
import { CONFIG, IMAGE_MIMETYPE_EXT, PREVIEWS_SIZE, sequelizeTypescript, THUMBNAILS_SIZE } from '../../../initializers'
|
||||
import {
|
||||
CONFIG,
|
||||
IMAGE_MIMETYPE_EXT,
|
||||
PREVIEWS_SIZE,
|
||||
sequelizeTypescript,
|
||||
THUMBNAILS_SIZE,
|
||||
TORRENT_MIMETYPE_EXT
|
||||
} from '../../../initializers'
|
||||
import { getYoutubeDLInfo, YoutubeDLInfo } from '../../../helpers/youtube-dl'
|
||||
import { createReqFiles } from '../../../helpers/express-utils'
|
||||
import { logger } from '../../../helpers/logger'
|
||||
|
@ -18,16 +26,20 @@ import { isArray } from '../../../helpers/custom-validators/misc'
|
|||
import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model'
|
||||
import { VideoChannelModel } from '../../../models/video/video-channel'
|
||||
import * as Bluebird from 'bluebird'
|
||||
import * as parseTorrent from 'parse-torrent'
|
||||
import { readFileBufferPromise, renamePromise } from '../../../helpers/core-utils'
|
||||
import { getSecureTorrentName } from '../../../helpers/utils'
|
||||
|
||||
const auditLogger = auditLoggerFactory('video-imports')
|
||||
const videoImportsRouter = express.Router()
|
||||
|
||||
const reqVideoFileImport = createReqFiles(
|
||||
[ 'thumbnailfile', 'previewfile' ],
|
||||
IMAGE_MIMETYPE_EXT,
|
||||
[ 'thumbnailfile', 'previewfile', 'torrentfile' ],
|
||||
Object.assign({}, TORRENT_MIMETYPE_EXT, IMAGE_MIMETYPE_EXT),
|
||||
{
|
||||
thumbnailfile: CONFIG.STORAGE.THUMBNAILS_DIR,
|
||||
previewfile: CONFIG.STORAGE.PREVIEWS_DIR
|
||||
previewfile: CONFIG.STORAGE.PREVIEWS_DIR,
|
||||
torrentfile: CONFIG.STORAGE.TORRENTS_DIR
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -49,17 +61,37 @@ export {
|
|||
function addVideoImport (req: express.Request, res: express.Response) {
|
||||
if (req.body.targetUrl) return addYoutubeDLImport(req, res)
|
||||
|
||||
if (req.body.magnetUri) return addTorrentImport(req, res)
|
||||
const file = req.files['torrentfile'][0]
|
||||
if (req.body.magnetUri || file) return addTorrentImport(req, res, file)
|
||||
}
|
||||
|
||||
async function addTorrentImport (req: express.Request, res: express.Response) {
|
||||
async function addTorrentImport (req: express.Request, res: express.Response, torrentfile: Express.Multer.File) {
|
||||
const body: VideoImportCreate = req.body
|
||||
const magnetUri = body.magnetUri
|
||||
|
||||
const parsed = magnetUtil.decode(magnetUri)
|
||||
const magnetName = isArray(parsed.name) ? parsed.name[0] : parsed.name as string
|
||||
let videoName: string
|
||||
let torrentName: string
|
||||
let magnetUri: string
|
||||
|
||||
const video = buildVideo(res.locals.videoChannel.id, body, { name: magnetName })
|
||||
if (torrentfile) {
|
||||
torrentName = torrentfile.originalname
|
||||
|
||||
// Rename the torrent to a secured name
|
||||
const newTorrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, getSecureTorrentName(torrentName))
|
||||
await renamePromise(torrentfile.path, newTorrentPath)
|
||||
torrentfile.path = newTorrentPath
|
||||
|
||||
const buf = await readFileBufferPromise(torrentfile.path)
|
||||
const parsedTorrent = parseTorrent(buf)
|
||||
|
||||
videoName = isArray(parsedTorrent.name) ? parsedTorrent.name[ 0 ] : parsedTorrent.name as string
|
||||
} else {
|
||||
magnetUri = body.magnetUri
|
||||
|
||||
const parsed = magnetUtil.decode(magnetUri)
|
||||
videoName = isArray(parsed.name) ? parsed.name[ 0 ] : parsed.name as string
|
||||
}
|
||||
|
||||
const video = buildVideo(res.locals.videoChannel.id, body, { name: videoName })
|
||||
|
||||
await processThumbnail(req, video)
|
||||
await processPreview(req, video)
|
||||
|
@ -67,13 +99,14 @@ async function addTorrentImport (req: express.Request, res: express.Response) {
|
|||
const tags = null
|
||||
const videoImportAttributes = {
|
||||
magnetUri,
|
||||
torrentName,
|
||||
state: VideoImportState.PENDING
|
||||
}
|
||||
const videoImport: VideoImportModel = await insertIntoDB(video, res.locals.videoChannel, tags, videoImportAttributes)
|
||||
|
||||
// Create job to import the video
|
||||
const payload = {
|
||||
type: 'magnet-uri' as 'magnet-uri',
|
||||
type: torrentfile ? 'torrent-file' as 'torrent-file' : 'magnet-uri' as 'magnet-uri',
|
||||
videoImportId: videoImport.id,
|
||||
magnetUri
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import * as pem from 'pem'
|
|||
import * as rimraf from 'rimraf'
|
||||
import { URL } from 'url'
|
||||
import { truncate } from 'lodash'
|
||||
import * as crypto from 'crypto'
|
||||
|
||||
function sanitizeUrl (url: string) {
|
||||
const urlObject = new URL(url)
|
||||
|
@ -95,6 +96,10 @@ function peertubeTruncate (str: string, maxLength: number) {
|
|||
return truncate(str, options)
|
||||
}
|
||||
|
||||
function sha256 (str: string) {
|
||||
return crypto.createHash('sha256').update(str).digest('hex')
|
||||
}
|
||||
|
||||
function promisify0<A> (func: (cb: (err: any, result: A) => void) => void): () => Promise<A> {
|
||||
return function promisified (): Promise<A> {
|
||||
return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
|
||||
|
@ -165,6 +170,7 @@ export {
|
|||
sanitizeHost,
|
||||
buildPath,
|
||||
peertubeTruncate,
|
||||
sha256,
|
||||
|
||||
promisify0,
|
||||
promisify1,
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import 'express-validator'
|
||||
import 'multer'
|
||||
import * as validator from 'validator'
|
||||
import { CONSTRAINTS_FIELDS, VIDEO_IMPORT_STATES } from '../../initializers'
|
||||
import { exists } from './misc'
|
||||
import { CONSTRAINTS_FIELDS, TORRENT_MIMETYPE_EXT, VIDEO_IMPORT_STATES } from '../../initializers'
|
||||
import { exists, isFileValid } from './misc'
|
||||
import * as express from 'express'
|
||||
import { VideoChannelModel } from '../../models/video/video-channel'
|
||||
import { VideoImportModel } from '../../models/video/video-import'
|
||||
|
||||
function isVideoImportTargetUrlValid (url: string) {
|
||||
|
@ -25,6 +24,12 @@ function isVideoImportStateValid (value: any) {
|
|||
return exists(value) && VIDEO_IMPORT_STATES[ value ] !== undefined
|
||||
}
|
||||
|
||||
const videoTorrentImportTypes = Object.keys(TORRENT_MIMETYPE_EXT).map(m => `(${m})`)
|
||||
const videoTorrentImportRegex = videoTorrentImportTypes.join('|')
|
||||
function isVideoImportTorrentFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) {
|
||||
return isFileValid(files, videoTorrentImportRegex, 'torrentfile', CONSTRAINTS_FIELDS.VIDEO_IMPORTS.TORRENT_FILE.FILE_SIZE.max, true)
|
||||
}
|
||||
|
||||
async function isVideoImportExist (id: number, res: express.Response) {
|
||||
const videoImport = await VideoImportModel.loadAndPopulateVideo(id)
|
||||
|
||||
|
@ -45,5 +50,6 @@ async function isVideoImportExist (id: number, res: express.Response) {
|
|||
export {
|
||||
isVideoImportStateValid,
|
||||
isVideoImportTargetUrlValid,
|
||||
isVideoImportExist
|
||||
isVideoImportExist,
|
||||
isVideoImportTorrentFile
|
||||
}
|
||||
|
|
|
@ -6,11 +6,12 @@ import { CONFIG } from '../initializers'
|
|||
import { UserModel } from '../models/account/user'
|
||||
import { ActorModel } from '../models/activitypub/actor'
|
||||
import { ApplicationModel } from '../models/application/application'
|
||||
import { pseudoRandomBytesPromise, unlinkPromise } from './core-utils'
|
||||
import { pseudoRandomBytesPromise, sha256, unlinkPromise } from './core-utils'
|
||||
import { logger } from './logger'
|
||||
import { isArray } from './custom-validators/misc'
|
||||
import * as crypto from "crypto"
|
||||
import { join } from "path"
|
||||
import { Instance as ParseTorrent } from 'parse-torrent'
|
||||
|
||||
const isCidr = require('is-cidr')
|
||||
|
||||
|
@ -183,13 +184,18 @@ async function getServerActor () {
|
|||
return Promise.resolve(serverActor)
|
||||
}
|
||||
|
||||
function generateVideoTmpPath (id: string) {
|
||||
const hash = crypto.createHash('sha256').update(id).digest('hex')
|
||||
function generateVideoTmpPath (target: string | ParseTorrent) {
|
||||
const id = typeof target === 'string' ? target : target.infoHash
|
||||
|
||||
const hash = sha256(id)
|
||||
return join(CONFIG.STORAGE.VIDEOS_DIR, hash + '-import.mp4')
|
||||
}
|
||||
|
||||
type SortType = { sortModel: any, sortValue: string }
|
||||
function getSecureTorrentName (originalName: string) {
|
||||
return sha256(originalName) + '.torrent'
|
||||
}
|
||||
|
||||
type SortType = { sortModel: any, sortValue: string }
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
@ -199,6 +205,7 @@ export {
|
|||
generateRandomString,
|
||||
getFormattedObjects,
|
||||
isSignupAllowed,
|
||||
getSecureTorrentName,
|
||||
isSignupAllowedForCurrentIP,
|
||||
computeResolutionsToTranscode,
|
||||
resetSequelizeInstance,
|
||||
|
|
|
@ -2,17 +2,22 @@ import { logger } from './logger'
|
|||
import { generateVideoTmpPath } from './utils'
|
||||
import * as WebTorrent from 'webtorrent'
|
||||
import { createWriteStream } from 'fs'
|
||||
import { Instance as ParseTorrent } from 'parse-torrent'
|
||||
import { CONFIG } from '../initializers'
|
||||
import { join } from 'path'
|
||||
|
||||
function downloadWebTorrentVideo (target: string) {
|
||||
const path = generateVideoTmpPath(target)
|
||||
function downloadWebTorrentVideo (target: { magnetUri: string, torrentName: string }) {
|
||||
const id = target.magnetUri || target.torrentName
|
||||
|
||||
logger.info('Importing torrent video %s', target)
|
||||
const path = generateVideoTmpPath(id)
|
||||
logger.info('Importing torrent video %s', id)
|
||||
|
||||
return new Promise<string>((res, rej) => {
|
||||
const webtorrent = new WebTorrent()
|
||||
|
||||
const torrent = webtorrent.add(target, torrent => {
|
||||
if (torrent.files.length !== 1) throw new Error('The number of files is not equal to 1 for ' + target)
|
||||
const torrentId = target.magnetUri || join(CONFIG.STORAGE.TORRENTS_DIR, target.torrentName)
|
||||
const torrent = webtorrent.add(torrentId, torrent => {
|
||||
if (torrent.files.length !== 1) return rej(new Error('The number of files is not equal to 1 for ' + torrentId))
|
||||
|
||||
const file = torrent.files[ 0 ]
|
||||
file.createReadStream().pipe(createWriteStream(path))
|
||||
|
|
|
@ -273,6 +273,12 @@ const CONSTRAINTS_FIELDS = {
|
|||
VIDEO_IMPORTS: {
|
||||
URL: { min: 3, max: 2000 }, // Length
|
||||
TORRENT_NAME: { min: 3, max: 255 }, // Length
|
||||
TORRENT_FILE: {
|
||||
EXTNAME: [ '.torrent' ],
|
||||
FILE_SIZE: {
|
||||
max: 1024 * 200 // 200 KB
|
||||
}
|
||||
}
|
||||
},
|
||||
VIDEOS: {
|
||||
NAME: { min: 3, max: 120 }, // Length
|
||||
|
@ -417,6 +423,10 @@ const VIDEO_CAPTIONS_MIMETYPE_EXT = {
|
|||
'application/x-subrip': '.srt'
|
||||
}
|
||||
|
||||
const TORRENT_MIMETYPE_EXT = {
|
||||
'application/x-bittorrent': '.torrent'
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const SERVER_ACTOR_NAME = 'peertube'
|
||||
|
@ -596,6 +606,7 @@ export {
|
|||
FEEDS,
|
||||
JOB_TTL,
|
||||
NSFW_POLICY_TYPES,
|
||||
TORRENT_MIMETYPE_EXT,
|
||||
STATIC_MAX_AGE,
|
||||
STATIC_PATHS,
|
||||
ACTIVITY_PUB,
|
||||
|
|
|
@ -14,6 +14,7 @@ import { JobQueue } from '../index'
|
|||
import { federateVideoIfNeeded } from '../../activitypub'
|
||||
import { VideoModel } from '../../../models/video/video'
|
||||
import { downloadWebTorrentVideo } from '../../../helpers/webtorrent'
|
||||
import { getSecureTorrentName } from '../../../helpers/utils'
|
||||
|
||||
type VideoImportYoutubeDLPayload = {
|
||||
type: 'youtube-dl'
|
||||
|
@ -25,7 +26,7 @@ type VideoImportYoutubeDLPayload = {
|
|||
}
|
||||
|
||||
type VideoImportTorrentPayload = {
|
||||
type: 'magnet-uri'
|
||||
type: 'magnet-uri' | 'torrent-file'
|
||||
videoImportId: number
|
||||
}
|
||||
|
||||
|
@ -35,7 +36,7 @@ async function processVideoImport (job: Bull.Job) {
|
|||
const payload = job.data as VideoImportPayload
|
||||
|
||||
if (payload.type === 'youtube-dl') return processYoutubeDLImport(job, payload)
|
||||
if (payload.type === 'magnet-uri') return processTorrentImport(job, payload)
|
||||
if (payload.type === 'magnet-uri' || payload.type === 'torrent-file') return processTorrentImport(job, payload)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
@ -50,6 +51,7 @@ async function processTorrentImport (job: Bull.Job, payload: VideoImportTorrentP
|
|||
logger.info('Processing torrent video import in job %d.', job.id)
|
||||
|
||||
const videoImport = await getVideoImportOrDie(payload.videoImportId)
|
||||
|
||||
const options = {
|
||||
videoImportId: payload.videoImportId,
|
||||
|
||||
|
@ -59,7 +61,11 @@ async function processTorrentImport (job: Bull.Job, payload: VideoImportTorrentP
|
|||
generateThumbnail: true,
|
||||
generatePreview: true
|
||||
}
|
||||
return processFile(() => downloadWebTorrentVideo(videoImport.magnetUri), videoImport, options)
|
||||
const target = {
|
||||
torrentName: videoImport.torrentName ? getSecureTorrentName(videoImport.torrentName) : undefined,
|
||||
magnetUri: videoImport.magnetUri
|
||||
}
|
||||
return processFile(() => downloadWebTorrentVideo(target), videoImport, options)
|
||||
}
|
||||
|
||||
async function processYoutubeDLImport (job: Bull.Job, payload: VideoImportYoutubeDLPayload) {
|
||||
|
|
|
@ -4,10 +4,11 @@ import { isIdValid } from '../../helpers/custom-validators/misc'
|
|||
import { logger } from '../../helpers/logger'
|
||||
import { areValidationErrors } from './utils'
|
||||
import { getCommonVideoAttributes } from './videos'
|
||||
import { isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports'
|
||||
import { isVideoImportTargetUrlValid, isVideoImportTorrentFile } from '../../helpers/custom-validators/video-imports'
|
||||
import { cleanUpReqFiles } from '../../helpers/utils'
|
||||
import { isVideoChannelOfAccountExist, isVideoMagnetUriValid, isVideoNameValid } from '../../helpers/custom-validators/videos'
|
||||
import { CONFIG } from '../../initializers/constants'
|
||||
import { CONSTRAINTS_FIELDS } from '../../initializers'
|
||||
|
||||
const videoImportAddValidator = getCommonVideoAttributes().concat([
|
||||
body('channelId')
|
||||
|
@ -19,6 +20,11 @@ const videoImportAddValidator = getCommonVideoAttributes().concat([
|
|||
body('magnetUri')
|
||||
.optional()
|
||||
.custom(isVideoMagnetUriValid).withMessage('Should have a valid video magnet URI'),
|
||||
body('torrentfile')
|
||||
.custom((value, { req }) => isVideoImportTorrentFile(req.files)).withMessage(
|
||||
'This torrent file is not supported or too large. Please, make sure it is of the following type: '
|
||||
+ CONSTRAINTS_FIELDS.VIDEO_IMPORTS.TORRENT_FILE.EXTNAME.join(', ')
|
||||
),
|
||||
body('name')
|
||||
.optional()
|
||||
.custom(isVideoNameValid).withMessage('Should have a valid name'),
|
||||
|
@ -40,11 +46,12 @@ const videoImportAddValidator = getCommonVideoAttributes().concat([
|
|||
if (!await isVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req)
|
||||
|
||||
// Check we have at least 1 required param
|
||||
if (!req.body.targetUrl && !req.body.magnetUri) {
|
||||
const file = req.files['torrentfile'][0]
|
||||
if (!req.body.targetUrl && !req.body.magnetUri && !file) {
|
||||
cleanUpReqFiles(req)
|
||||
|
||||
return res.status(400)
|
||||
.json({ error: 'Should have a magnetUri or a targetUrl.' })
|
||||
.json({ error: 'Should have a magnetUri or a targetUrl or a torrent file.' })
|
||||
.end()
|
||||
}
|
||||
|
||||
|
|
|
@ -171,7 +171,11 @@ export class VideoImportModel extends Model<VideoImportModel> {
|
|||
|
||||
return {
|
||||
id: this.id,
|
||||
|
||||
targetUrl: this.targetUrl,
|
||||
magnetUri: this.magnetUri,
|
||||
torrentName: this.torrentName,
|
||||
|
||||
state: {
|
||||
id: this.state,
|
||||
label: VideoImportModel.getStateLabel(this.state)
|
||||
|
|
|
@ -4,7 +4,11 @@ import { VideoImportState } from './video-import-state.enum'
|
|||
|
||||
export interface VideoImport {
|
||||
id: number
|
||||
|
||||
targetUrl: string
|
||||
magnetUri: string
|
||||
torrentName: string
|
||||
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
state: VideoConstant<VideoImportState>
|
||||
|
|
Loading…
Reference in New Issue