Add ability to list video imports
This commit is contained in:
parent
299474e827
commit
ed31c05985
|
@ -8,6 +8,7 @@ import { MyAccountVideosComponent } from './my-account-videos/my-account-videos.
|
||||||
import { MyAccountVideoChannelsComponent } from '@app/+my-account/my-account-video-channels/my-account-video-channels.component'
|
import { MyAccountVideoChannelsComponent } from '@app/+my-account/my-account-video-channels/my-account-video-channels.component'
|
||||||
import { MyAccountVideoChannelCreateComponent } from '@app/+my-account/my-account-video-channels/my-account-video-channel-create.component'
|
import { MyAccountVideoChannelCreateComponent } from '@app/+my-account/my-account-video-channels/my-account-video-channel-create.component'
|
||||||
import { MyAccountVideoChannelUpdateComponent } from '@app/+my-account/my-account-video-channels/my-account-video-channel-update.component'
|
import { MyAccountVideoChannelUpdateComponent } from '@app/+my-account/my-account-video-channels/my-account-video-channel-update.component'
|
||||||
|
import { MyAccountVideoImportsComponent } from '@app/+my-account/my-account-video-imports/my-account-video-imports.component'
|
||||||
|
|
||||||
const myAccountRoutes: Routes = [
|
const myAccountRoutes: Routes = [
|
||||||
{
|
{
|
||||||
|
@ -64,6 +65,15 @@ const myAccountRoutes: Routes = [
|
||||||
title: 'Account videos'
|
title: 'Account videos'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'video-imports',
|
||||||
|
component: MyAccountVideoImportsComponent,
|
||||||
|
data: {
|
||||||
|
meta: {
|
||||||
|
title: 'Account video imports'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
<p-table
|
||||||
|
[value]="videoImports" [lazy]="true" [paginator]="true" [totalRecords]="totalRecords" [rows]="rowsPerPage"
|
||||||
|
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)"
|
||||||
|
>
|
||||||
|
<ng-template pTemplate="header">
|
||||||
|
<tr>
|
||||||
|
<th i18n>URL</th>
|
||||||
|
<th i18n>Video</th>
|
||||||
|
<th i18n style="width: 150px">State</th>
|
||||||
|
<th i18n pSortableColumn="createdAt">Created <p-sortIcon field="createdAt"></p-sortIcon></th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template pTemplate="body" let-videoImport>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a [href]="videoImport.targetUrl" target="_blank" rel="noopener noreferrer">{{ videoImport.targetUrl }}</a>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td *ngIf="isVideoImportPending(videoImport)">
|
||||||
|
{{ videoImport.video.name }}
|
||||||
|
</td>
|
||||||
|
<td *ngIf="isVideoImportSuccess(videoImport)">
|
||||||
|
<a [href]="getVideoUrl(videoImport.video)" target="_blank" rel="noopener noreferrer">{{ videoImport.video.name }}</a>
|
||||||
|
</td>
|
||||||
|
<td *ngIf="isVideoImportFailed(videoImport)"></td>
|
||||||
|
|
||||||
|
<td>{{ videoImport.state.label }}</td>
|
||||||
|
<td>{{ videoImport.createdAt }}</td>
|
||||||
|
|
||||||
|
<td class="action-cell">
|
||||||
|
<my-edit-button *ngIf="isVideoImportSuccess(videoImport)" [routerLink]="getEditVideoUrl(videoImport.video)"></my-edit-button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
</p-table>
|
|
@ -0,0 +1,2 @@
|
||||||
|
@import '_variables';
|
||||||
|
@import '_mixins';
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { Component, OnInit } from '@angular/core'
|
||||||
|
import { RestPagination, RestTable } from '@app/shared'
|
||||||
|
import { SortMeta } from 'primeng/components/common/sortmeta'
|
||||||
|
import { NotificationsService } from 'angular2-notifications'
|
||||||
|
import { ConfirmService } from '@app/core'
|
||||||
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
|
import { VideoImport, VideoImportState } from '../../../../../shared/models/videos'
|
||||||
|
import { VideoImportService } from '@app/shared/video-import'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-account-video-imports',
|
||||||
|
templateUrl: './my-account-video-imports.component.html',
|
||||||
|
styleUrls: [ './my-account-video-imports.component.scss' ]
|
||||||
|
})
|
||||||
|
export class MyAccountVideoImportsComponent extends RestTable implements OnInit {
|
||||||
|
videoImports: VideoImport[] = []
|
||||||
|
totalRecords = 0
|
||||||
|
rowsPerPage = 10
|
||||||
|
sort: SortMeta = { field: 'createdAt', order: 1 }
|
||||||
|
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
private notificationsService: NotificationsService,
|
||||||
|
private confirmService: ConfirmService,
|
||||||
|
private videoImportService: VideoImportService,
|
||||||
|
private i18n: I18n
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit () {
|
||||||
|
this.loadSort()
|
||||||
|
}
|
||||||
|
|
||||||
|
isVideoImportSuccess (videoImport: VideoImport) {
|
||||||
|
return videoImport.state.id === VideoImportState.SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
isVideoImportPending (videoImport: VideoImport) {
|
||||||
|
return videoImport.state.id === VideoImportState.PENDING
|
||||||
|
}
|
||||||
|
|
||||||
|
isVideoImportFailed (videoImport: VideoImport) {
|
||||||
|
return videoImport.state.id === VideoImportState.FAILED
|
||||||
|
}
|
||||||
|
|
||||||
|
getVideoUrl (video: { uuid: string }) {
|
||||||
|
return '/videos/watch/' + video.uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
getEditVideoUrl (video: { uuid: string }) {
|
||||||
|
return '/videos/update/' + video.uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
protected loadData () {
|
||||||
|
this.videoImportService.getMyVideoImports(this.pagination, this.sort)
|
||||||
|
.subscribe(
|
||||||
|
resultList => {
|
||||||
|
this.videoImports = resultList.data
|
||||||
|
this.totalRecords = resultList.total
|
||||||
|
},
|
||||||
|
|
||||||
|
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -145,6 +145,8 @@ export class MyAccountVideosComponent extends AbstractVideoList implements OnIni
|
||||||
suffix = this.i18n('Waiting transcoding')
|
suffix = this.i18n('Waiting transcoding')
|
||||||
} else if (video.state.id === VideoState.TO_TRANSCODE) {
|
} else if (video.state.id === VideoState.TO_TRANSCODE) {
|
||||||
suffix = this.i18n('To transcode')
|
suffix = this.i18n('To transcode')
|
||||||
|
} else if (video.state.id === VideoState.TO_IMPORT) {
|
||||||
|
suffix = this.i18n('To import')
|
||||||
} else {
|
} else {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
<a i18n routerLink="/my-account/video-channels" routerLinkActive="active" class="title-page">My video channels</a>
|
<a i18n routerLink="/my-account/video-channels" routerLinkActive="active" class="title-page">My video channels</a>
|
||||||
|
|
||||||
<a i18n routerLink="/my-account/videos" routerLinkActive="active" class="title-page">My videos</a>
|
<a i18n routerLink="/my-account/videos" routerLinkActive="active" class="title-page">My videos</a>
|
||||||
|
|
||||||
|
<a i18n routerLink="/my-account/video-imports" routerLinkActive="active" class="title-page">My video imports</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="margin-content">
|
<div class="margin-content">
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { TableModule } from 'primeng/table'
|
||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
import { SharedModule } from '../shared'
|
import { SharedModule } from '../shared'
|
||||||
import { MyAccountRoutingModule } from './my-account-routing.module'
|
import { MyAccountRoutingModule } from './my-account-routing.module'
|
||||||
|
@ -11,11 +12,13 @@ import { MyAccountVideoChannelsComponent } from '@app/+my-account/my-account-vid
|
||||||
import { MyAccountVideoChannelCreateComponent } from '@app/+my-account/my-account-video-channels/my-account-video-channel-create.component'
|
import { MyAccountVideoChannelCreateComponent } from '@app/+my-account/my-account-video-channels/my-account-video-channel-create.component'
|
||||||
import { MyAccountVideoChannelUpdateComponent } from '@app/+my-account/my-account-video-channels/my-account-video-channel-update.component'
|
import { MyAccountVideoChannelUpdateComponent } from '@app/+my-account/my-account-video-channels/my-account-video-channel-update.component'
|
||||||
import { ActorAvatarInfoComponent } from '@app/+my-account/shared/actor-avatar-info.component'
|
import { ActorAvatarInfoComponent } from '@app/+my-account/shared/actor-avatar-info.component'
|
||||||
|
import { MyAccountVideoImportsComponent } from '@app/+my-account/my-account-video-imports/my-account-video-imports.component'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
MyAccountRoutingModule,
|
MyAccountRoutingModule,
|
||||||
SharedModule
|
SharedModule,
|
||||||
|
TableModule
|
||||||
],
|
],
|
||||||
|
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -28,7 +31,8 @@ import { ActorAvatarInfoComponent } from '@app/+my-account/shared/actor-avatar-i
|
||||||
MyAccountVideoChannelsComponent,
|
MyAccountVideoChannelsComponent,
|
||||||
MyAccountVideoChannelCreateComponent,
|
MyAccountVideoChannelCreateComponent,
|
||||||
MyAccountVideoChannelUpdateComponent,
|
MyAccountVideoChannelUpdateComponent,
|
||||||
ActorAvatarInfoComponent
|
ActorAvatarInfoComponent,
|
||||||
|
MyAccountVideoImportsComponent
|
||||||
],
|
],
|
||||||
|
|
||||||
exports: [
|
exports: [
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { catchError } from 'rxjs/operators'
|
import { catchError, map, switchMap } from 'rxjs/operators'
|
||||||
import { HttpClient } from '@angular/common/http'
|
import { HttpClient, HttpParams } from '@angular/common/http'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { Observable } from 'rxjs'
|
import { Observable } from 'rxjs'
|
||||||
import { VideoImport } from '../../../../../shared'
|
import { VideoImport } from '../../../../../shared'
|
||||||
|
@ -8,6 +8,12 @@ import { RestExtractor, RestService } from '../rest'
|
||||||
import { VideoImportCreate } from '../../../../../shared/models/videos/video-import-create.model'
|
import { VideoImportCreate } from '../../../../../shared/models/videos/video-import-create.model'
|
||||||
import { objectToFormData } from '@app/shared/misc/utils'
|
import { objectToFormData } from '@app/shared/misc/utils'
|
||||||
import { VideoUpdate } from '../../../../../shared/models/videos'
|
import { VideoUpdate } from '../../../../../shared/models/videos'
|
||||||
|
import { ResultList } from '../../../../../shared/models/result-list.model'
|
||||||
|
import { UserService } from '@app/shared/users/user.service'
|
||||||
|
import { SortMeta } from 'primeng/components/common/sortmeta'
|
||||||
|
import { RestPagination } from '@app/shared/rest'
|
||||||
|
import { ServerService } from '@app/core'
|
||||||
|
import { peertubeTranslate } from '@app/shared/i18n/i18n-utils'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class VideoImportService {
|
export class VideoImportService {
|
||||||
|
@ -16,7 +22,8 @@ export class VideoImportService {
|
||||||
constructor (
|
constructor (
|
||||||
private authHttp: HttpClient,
|
private authHttp: HttpClient,
|
||||||
private restService: RestService,
|
private restService: RestService,
|
||||||
private restExtractor: RestExtractor
|
private restExtractor: RestExtractor,
|
||||||
|
private serverService: ServerService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
importVideo (targetUrl: string, video: VideoUpdate): Observable<VideoImport> {
|
importVideo (targetUrl: string, video: VideoUpdate): Observable<VideoImport> {
|
||||||
|
@ -53,4 +60,29 @@ export class VideoImportService {
|
||||||
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
.pipe(catchError(res => this.restExtractor.handleError(res)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMyVideoImports (pagination: RestPagination, sort: SortMeta): Observable<ResultList<VideoImport>> {
|
||||||
|
let params = new HttpParams()
|
||||||
|
params = this.restService.addRestGetParams(params, pagination, sort)
|
||||||
|
|
||||||
|
return this.authHttp
|
||||||
|
.get<ResultList<VideoImport>>(UserService.BASE_USERS_URL + '/me/videos/imports', { params })
|
||||||
|
.pipe(
|
||||||
|
switchMap(res => this.extractVideoImports(res)),
|
||||||
|
map(res => this.restExtractor.convertResultListDateToHuman(res)),
|
||||||
|
catchError(err => this.restExtractor.handleError(err))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private extractVideoImports (result: ResultList<VideoImport>): Observable<ResultList<VideoImport>> {
|
||||||
|
return this.serverService.localeObservable
|
||||||
|
.pipe(
|
||||||
|
map(translations => {
|
||||||
|
result.data.forEach(d =>
|
||||||
|
d.state.label = peertubeTranslate(d.state.label, translations)
|
||||||
|
)
|
||||||
|
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,7 +152,7 @@ app.use(function (err, req, res, next) {
|
||||||
error = err.stack || err.message || err
|
error = err.stack || err.message || err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.error('Error in controller.', { error })
|
logger.error('Error in controller.', { err: error })
|
||||||
return res.status(err.status || 500).end()
|
return res.status(err.status || 500).end()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,12 @@ import {
|
||||||
usersUpdateValidator,
|
usersUpdateValidator,
|
||||||
usersVideoRatingValidator
|
usersVideoRatingValidator
|
||||||
} from '../../middlewares'
|
} from '../../middlewares'
|
||||||
import { usersAskResetPasswordValidator, usersResetPasswordValidator, videosSortValidator } from '../../middlewares/validators'
|
import {
|
||||||
|
usersAskResetPasswordValidator,
|
||||||
|
usersResetPasswordValidator,
|
||||||
|
videoImportsSortValidator,
|
||||||
|
videosSortValidator
|
||||||
|
} from '../../middlewares/validators'
|
||||||
import { AccountVideoRateModel } from '../../models/account/account-video-rate'
|
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'
|
||||||
|
@ -40,6 +45,7 @@ import { UserVideoQuota } from '../../../shared/models/users/user-video-quota.mo
|
||||||
import { updateAvatarValidator } from '../../middlewares/validators/avatar'
|
import { updateAvatarValidator } from '../../middlewares/validators/avatar'
|
||||||
import { updateActorAvatarFile } from '../../lib/avatar'
|
import { updateActorAvatarFile } from '../../lib/avatar'
|
||||||
import { auditLoggerFactory, UserAuditView } from '../../helpers/audit-logger'
|
import { auditLoggerFactory, UserAuditView } from '../../helpers/audit-logger'
|
||||||
|
import { VideoImportModel } from '../../models/video/video-import'
|
||||||
|
|
||||||
const auditLogger = auditLoggerFactory('users')
|
const auditLogger = auditLoggerFactory('users')
|
||||||
|
|
||||||
|
@ -62,6 +68,16 @@ usersRouter.get('/me/video-quota-used',
|
||||||
asyncMiddleware(getUserVideoQuotaUsed)
|
asyncMiddleware(getUserVideoQuotaUsed)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
usersRouter.get('/me/videos/imports',
|
||||||
|
authenticate,
|
||||||
|
paginationValidator,
|
||||||
|
videoImportsSortValidator,
|
||||||
|
setDefaultSort,
|
||||||
|
setDefaultPagination,
|
||||||
|
asyncMiddleware(getUserVideoImports)
|
||||||
|
)
|
||||||
|
|
||||||
usersRouter.get('/me/videos',
|
usersRouter.get('/me/videos',
|
||||||
authenticate,
|
authenticate,
|
||||||
paginationValidator,
|
paginationValidator,
|
||||||
|
@ -178,6 +194,18 @@ async function getUserVideos (req: express.Request, res: express.Response, next:
|
||||||
return res.json(getFormattedObjects(resultList.data, resultList.total, { additionalAttributes }))
|
return res.json(getFormattedObjects(resultList.data, resultList.total, { additionalAttributes }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getUserVideoImports (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
|
const user = res.locals.oauth.token.User as UserModel
|
||||||
|
const resultList = await VideoImportModel.listUserVideoImportsForApi(
|
||||||
|
user.Account.id,
|
||||||
|
req.query.start as number,
|
||||||
|
req.query.count as number,
|
||||||
|
req.query.sort
|
||||||
|
)
|
||||||
|
|
||||||
|
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
||||||
|
}
|
||||||
|
|
||||||
async function createUser (req: express.Request, res: express.Response) {
|
async function createUser (req: express.Request, res: express.Response) {
|
||||||
const body: UserCreate = req.body
|
const body: UserCreate = req.body
|
||||||
const userToCreate = new UserModel({
|
const userToCreate = new UserModel({
|
||||||
|
|
|
@ -95,7 +95,7 @@ function titleTruncation (title: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function descriptionTruncation (description: string) {
|
function descriptionTruncation (description: string) {
|
||||||
if (!description) return undefined
|
if (!description || description.length < CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.min) return undefined
|
||||||
|
|
||||||
return truncate(description, {
|
return truncate(description, {
|
||||||
'length': CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max,
|
'length': CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max,
|
||||||
|
|
|
@ -37,6 +37,7 @@ const SORTABLE_COLUMNS = {
|
||||||
VIDEO_ABUSES: [ 'id', 'createdAt' ],
|
VIDEO_ABUSES: [ 'id', 'createdAt' ],
|
||||||
VIDEO_CHANNELS: [ 'id', 'name', 'updatedAt', 'createdAt' ],
|
VIDEO_CHANNELS: [ 'id', 'name', 'updatedAt', 'createdAt' ],
|
||||||
VIDEOS: [ 'name', 'duration', 'createdAt', 'publishedAt', 'views', 'likes' ],
|
VIDEOS: [ 'name', 'duration', 'createdAt', 'publishedAt', 'views', 'likes' ],
|
||||||
|
VIDEO_IMPORTS: [ 'createdAt' ],
|
||||||
VIDEO_COMMENT_THREADS: [ 'createdAt' ],
|
VIDEO_COMMENT_THREADS: [ 'createdAt' ],
|
||||||
BLACKLISTS: [ 'id', 'name', 'duration', 'views', 'likes', 'dislikes', 'uuid', 'createdAt' ],
|
BLACKLISTS: [ 'id', 'name', 'duration', 'views', 'likes', 'dislikes', 'uuid', 'createdAt' ],
|
||||||
FOLLOWERS: [ 'createdAt' ],
|
FOLLOWERS: [ 'createdAt' ],
|
||||||
|
|
|
@ -35,7 +35,7 @@ async function processVideoImport (job: Bull.Job) {
|
||||||
|
|
||||||
// Get information about this video
|
// Get information about this video
|
||||||
const { videoFileResolution } = await getVideoFileResolution(tempVideoPath)
|
const { videoFileResolution } = await getVideoFileResolution(tempVideoPath)
|
||||||
const fps = await getVideoFileFPS(tempVideoPath)
|
const fps = await getVideoFileFPS(tempVideoPath + 's')
|
||||||
const stats = await statPromise(tempVideoPath)
|
const stats = await statPromise(tempVideoPath)
|
||||||
const duration = await getDurationFromVideoFile(tempVideoPath)
|
const duration = await getDurationFromVideoFile(tempVideoPath)
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ const SORTABLE_JOBS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.JOBS)
|
||||||
const SORTABLE_VIDEO_ABUSES_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_ABUSES)
|
const SORTABLE_VIDEO_ABUSES_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_ABUSES)
|
||||||
const SORTABLE_VIDEOS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEOS)
|
const SORTABLE_VIDEOS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEOS)
|
||||||
const SORTABLE_VIDEOS_SEARCH_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEOS_SEARCH)
|
const SORTABLE_VIDEOS_SEARCH_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEOS_SEARCH)
|
||||||
|
const SORTABLE_VIDEO_IMPORTS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_IMPORTS)
|
||||||
const SORTABLE_VIDEO_COMMENT_THREADS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_COMMENT_THREADS)
|
const SORTABLE_VIDEO_COMMENT_THREADS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_COMMENT_THREADS)
|
||||||
const SORTABLE_BLACKLISTS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.BLACKLISTS)
|
const SORTABLE_BLACKLISTS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.BLACKLISTS)
|
||||||
const SORTABLE_VIDEO_CHANNELS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_CHANNELS)
|
const SORTABLE_VIDEO_CHANNELS_COLUMNS = createSortableColumns(SORTABLE_COLUMNS.VIDEO_CHANNELS)
|
||||||
|
@ -19,6 +20,7 @@ const accountsSortValidator = checkSort(SORTABLE_ACCOUNTS_COLUMNS)
|
||||||
const jobsSortValidator = checkSort(SORTABLE_JOBS_COLUMNS)
|
const jobsSortValidator = checkSort(SORTABLE_JOBS_COLUMNS)
|
||||||
const videoAbusesSortValidator = checkSort(SORTABLE_VIDEO_ABUSES_COLUMNS)
|
const videoAbusesSortValidator = checkSort(SORTABLE_VIDEO_ABUSES_COLUMNS)
|
||||||
const videosSortValidator = checkSort(SORTABLE_VIDEOS_COLUMNS)
|
const videosSortValidator = checkSort(SORTABLE_VIDEOS_COLUMNS)
|
||||||
|
const videoImportsSortValidator = checkSort(SORTABLE_VIDEO_IMPORTS_COLUMNS)
|
||||||
const videosSearchSortValidator = checkSort(SORTABLE_VIDEOS_SEARCH_COLUMNS)
|
const videosSearchSortValidator = checkSort(SORTABLE_VIDEOS_SEARCH_COLUMNS)
|
||||||
const videoCommentThreadsSortValidator = checkSort(SORTABLE_VIDEO_COMMENT_THREADS_COLUMNS)
|
const videoCommentThreadsSortValidator = checkSort(SORTABLE_VIDEO_COMMENT_THREADS_COLUMNS)
|
||||||
const blacklistSortValidator = checkSort(SORTABLE_BLACKLISTS_COLUMNS)
|
const blacklistSortValidator = checkSort(SORTABLE_BLACKLISTS_COLUMNS)
|
||||||
|
@ -32,6 +34,7 @@ export {
|
||||||
usersSortValidator,
|
usersSortValidator,
|
||||||
videoAbusesSortValidator,
|
videoAbusesSortValidator,
|
||||||
videoChannelsSortValidator,
|
videoChannelsSortValidator,
|
||||||
|
videoImportsSortValidator,
|
||||||
videosSearchSortValidator,
|
videosSearchSortValidator,
|
||||||
videosSortValidator,
|
videosSortValidator,
|
||||||
blacklistSortValidator,
|
blacklistSortValidator,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
AfterUpdate,
|
||||||
AllowNull,
|
AllowNull,
|
||||||
BelongsTo,
|
BelongsTo,
|
||||||
Column,
|
Column,
|
||||||
|
@ -12,13 +13,14 @@ import {
|
||||||
Table,
|
Table,
|
||||||
UpdatedAt
|
UpdatedAt
|
||||||
} from 'sequelize-typescript'
|
} from 'sequelize-typescript'
|
||||||
import { CONSTRAINTS_FIELDS } from '../../initializers'
|
import { CONSTRAINTS_FIELDS, VIDEO_IMPORT_STATES } from '../../initializers'
|
||||||
import { throwIfNotValid } from '../utils'
|
import { getSort, throwIfNotValid } from '../utils'
|
||||||
import { VideoModel } from './video'
|
import { VideoModel } from './video'
|
||||||
import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports'
|
import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports'
|
||||||
import { VideoImport, VideoImportState } from '../../../shared'
|
import { VideoImport, VideoImportState } from '../../../shared'
|
||||||
import { VideoChannelModel } from './video-channel'
|
import { VideoChannelModel } from './video-channel'
|
||||||
import { AccountModel } from '../account/account'
|
import { AccountModel } from '../account/account'
|
||||||
|
import { TagModel } from './tag'
|
||||||
|
|
||||||
@DefaultScope({
|
@DefaultScope({
|
||||||
include: [
|
include: [
|
||||||
|
@ -35,6 +37,10 @@ import { AccountModel } from '../account/account'
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: () => TagModel,
|
||||||
|
required: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -79,27 +85,89 @@ export class VideoImportModel extends Model<VideoImportModel> {
|
||||||
|
|
||||||
@BelongsTo(() => VideoModel, {
|
@BelongsTo(() => VideoModel, {
|
||||||
foreignKey: {
|
foreignKey: {
|
||||||
allowNull: false
|
allowNull: true
|
||||||
},
|
},
|
||||||
onDelete: 'CASCADE'
|
onDelete: 'set null'
|
||||||
})
|
})
|
||||||
Video: VideoModel
|
Video: VideoModel
|
||||||
|
|
||||||
|
@AfterUpdate
|
||||||
|
static deleteVideoIfFailed (instance: VideoImportModel, options) {
|
||||||
|
if (instance.state === VideoImportState.FAILED) {
|
||||||
|
return instance.Video.destroy({ transaction: options.transaction })
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
static loadAndPopulateVideo (id: number) {
|
static loadAndPopulateVideo (id: number) {
|
||||||
return VideoImportModel.findById(id)
|
return VideoImportModel.findById(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static listUserVideoImportsForApi (accountId: number, start: number, count: number, sort: string) {
|
||||||
|
const query = {
|
||||||
|
offset: start,
|
||||||
|
limit: count,
|
||||||
|
order: getSort(sort),
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: VideoModel,
|
||||||
|
required: true,
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: VideoChannelModel,
|
||||||
|
required: true,
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: AccountModel,
|
||||||
|
required: true,
|
||||||
|
where: {
|
||||||
|
id: accountId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: TagModel,
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return VideoImportModel.unscoped()
|
||||||
|
.findAndCountAll(query)
|
||||||
|
.then(({ rows, count }) => {
|
||||||
|
return {
|
||||||
|
data: rows,
|
||||||
|
total: count
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
toFormattedJSON (): VideoImport {
|
toFormattedJSON (): VideoImport {
|
||||||
const videoFormatOptions = {
|
const videoFormatOptions = {
|
||||||
additionalAttributes: { state: true, waitTranscoding: true, scheduledUpdate: true }
|
additionalAttributes: { state: true, waitTranscoding: true, scheduledUpdate: true }
|
||||||
}
|
}
|
||||||
const video = Object.assign(this.Video.toFormattedJSON(videoFormatOptions), {
|
const video = this.Video
|
||||||
tags: this.Video.Tags.map(t => t.name)
|
? Object.assign(this.Video.toFormattedJSON(videoFormatOptions), {
|
||||||
})
|
tags: this.Video.Tags.map(t => t.name)
|
||||||
|
})
|
||||||
|
: undefined
|
||||||
|
|
||||||
return {
|
return {
|
||||||
targetUrl: this.targetUrl,
|
targetUrl: this.targetUrl,
|
||||||
|
state: {
|
||||||
|
id: this.state,
|
||||||
|
label: VideoImportModel.getStateLabel(this.state)
|
||||||
|
},
|
||||||
|
updatedAt: this.updatedAt.toISOString(),
|
||||||
|
createdAt: this.createdAt.toISOString(),
|
||||||
video
|
video
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private static getStateLabel (id: number) {
|
||||||
|
return VIDEO_IMPORT_STATES[id] || 'Unknown'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1569,21 +1569,25 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
removeThumbnail () {
|
removeThumbnail () {
|
||||||
const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName())
|
const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName())
|
||||||
return unlinkPromise(thumbnailPath)
|
return unlinkPromise(thumbnailPath)
|
||||||
|
.catch(err => logger.warn('Cannot delete thumbnail %s.', thumbnailPath, { err }))
|
||||||
}
|
}
|
||||||
|
|
||||||
removePreview () {
|
removePreview () {
|
||||||
// Same name than video thumbnail
|
const previewPath = join(CONFIG.STORAGE.PREVIEWS_DIR + this.getPreviewName())
|
||||||
return unlinkPromise(CONFIG.STORAGE.PREVIEWS_DIR + this.getPreviewName())
|
return unlinkPromise(previewPath)
|
||||||
|
.catch(err => logger.warn('Cannot delete preview %s.', previewPath, { err }))
|
||||||
}
|
}
|
||||||
|
|
||||||
removeFile (videoFile: VideoFileModel) {
|
removeFile (videoFile: VideoFileModel) {
|
||||||
const filePath = join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile))
|
const filePath = join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile))
|
||||||
return unlinkPromise(filePath)
|
return unlinkPromise(filePath)
|
||||||
|
.catch(err => logger.warn('Cannot delete file %s.', filePath, { err }))
|
||||||
}
|
}
|
||||||
|
|
||||||
removeTorrent (videoFile: VideoFileModel) {
|
removeTorrent (videoFile: VideoFileModel) {
|
||||||
const torrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
|
const torrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
|
||||||
return unlinkPromise(torrentPath)
|
return unlinkPromise(torrentPath)
|
||||||
|
.catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err }))
|
||||||
}
|
}
|
||||||
|
|
||||||
getActivityStreamDuration () {
|
getActivityStreamDuration () {
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import { Video } from './video.model'
|
import { Video } from './video.model'
|
||||||
|
import { VideoConstant } from './video-constant.model'
|
||||||
|
import { VideoImportState } from '../../index'
|
||||||
|
|
||||||
export interface VideoImport {
|
export interface VideoImport {
|
||||||
targetUrl: string
|
targetUrl: string
|
||||||
|
createdAt: string
|
||||||
|
updatedAt: string
|
||||||
|
state: VideoConstant<VideoImportState>
|
||||||
|
|
||||||
video: Video & { tags: string[] }
|
video?: Video & { tags: string[] }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue