mirror of https://github.com/Chocobozzz/PeerTube
Update data in DB when regenerate thumbnails
parent
ca87329289
commit
a0eeb45f14
|
@ -3,12 +3,13 @@ registerTSPaths()
|
||||||
|
|
||||||
import * as Bluebird from 'bluebird'
|
import * as Bluebird from 'bluebird'
|
||||||
import * as program from 'commander'
|
import * as program from 'commander'
|
||||||
import { pathExists } from 'fs-extra'
|
import { pathExists, remove } from 'fs-extra'
|
||||||
import { processImage } from '@server/helpers/image-utils'
|
import { processImage } from '@server/helpers/image-utils'
|
||||||
import { THUMBNAILS_SIZE } from '@server/initializers/constants'
|
import { THUMBNAILS_SIZE } from '@server/initializers/constants'
|
||||||
import { VideoModel } from '@server/models/video/video'
|
import { VideoModel } from '@server/models/video/video'
|
||||||
import { MVideo } from '@server/types/models'
|
import { MVideo } from '@server/types/models'
|
||||||
import { initDatabaseModels } from '@server/initializers/database'
|
import { initDatabaseModels } from '@server/initializers/database'
|
||||||
|
import { ActorImageModel } from '@server/models/account/actor-image'
|
||||||
|
|
||||||
program
|
program
|
||||||
.description('Regenerate local thumbnails using preview files')
|
.description('Regenerate local thumbnails using preview files')
|
||||||
|
@ -37,13 +38,8 @@ async function processVideo (videoArg: MVideo) {
|
||||||
const thumbnail = video.getMiniature()
|
const thumbnail = video.getMiniature()
|
||||||
const preview = video.getPreview()
|
const preview = video.getPreview()
|
||||||
|
|
||||||
const thumbnailPath = thumbnail.getPath()
|
|
||||||
const previewPath = preview.getPath()
|
const previewPath = preview.getPath()
|
||||||
|
|
||||||
if (!await pathExists(thumbnailPath)) {
|
|
||||||
throw new Error(`Thumbnail ${thumbnailPath} does not exist on disk`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!await pathExists(previewPath)) {
|
if (!await pathExists(previewPath)) {
|
||||||
throw new Error(`Preview ${previewPath} does not exist on disk`)
|
throw new Error(`Preview ${previewPath} does not exist on disk`)
|
||||||
}
|
}
|
||||||
|
@ -52,5 +48,22 @@ async function processVideo (videoArg: MVideo) {
|
||||||
width: THUMBNAILS_SIZE.width,
|
width: THUMBNAILS_SIZE.width,
|
||||||
height: THUMBNAILS_SIZE.height
|
height: THUMBNAILS_SIZE.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const oldPath = thumbnail.getPath()
|
||||||
|
|
||||||
|
// Update thumbnail
|
||||||
|
thumbnail.filename = ActorImageModel.generateFilename()
|
||||||
|
thumbnail.width = size.width
|
||||||
|
thumbnail.height = size.height
|
||||||
|
|
||||||
|
const thumbnailPath = thumbnail.getPath()
|
||||||
await processImage(previewPath, thumbnailPath, size, true)
|
await processImage(previewPath, thumbnailPath, size, true)
|
||||||
|
|
||||||
|
// Save new attributes
|
||||||
|
await thumbnail.save()
|
||||||
|
|
||||||
|
// Remove old thumbnail
|
||||||
|
await remove(oldPath)
|
||||||
|
|
||||||
|
// Don't federate, remote instances will refresh the thumbnails after a while
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { maxBy, minBy } from 'lodash'
|
||||||
import * as magnetUtil from 'magnet-uri'
|
import * as magnetUtil from 'magnet-uri'
|
||||||
import { basename, join } from 'path'
|
import { basename, join } from 'path'
|
||||||
import { Transaction } from 'sequelize/types'
|
import { Transaction } from 'sequelize/types'
|
||||||
|
import { ActorImageModel } from '@server/models/account/actor-image'
|
||||||
import { TrackerModel } from '@server/models/server/tracker'
|
import { TrackerModel } from '@server/models/server/tracker'
|
||||||
import { VideoLiveModel } from '@server/models/video/video-live'
|
import { VideoLiveModel } from '@server/models/video/video-live'
|
||||||
import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
|
import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
|
||||||
|
@ -899,7 +900,7 @@ function getPreviewFromIcons (videoObject: VideoObject) {
|
||||||
function getPreviewUrl (previewIcon: ActivityIconObject, video: MVideoWithHost) {
|
function getPreviewUrl (previewIcon: ActivityIconObject, video: MVideoWithHost) {
|
||||||
return previewIcon
|
return previewIcon
|
||||||
? previewIcon.url
|
? previewIcon.url
|
||||||
: buildRemoteVideoBaseUrl(video, join(LAZY_STATIC_PATHS.PREVIEWS, video.generatePreviewName()))
|
: buildRemoteVideoBaseUrl(video, join(LAZY_STATIC_PATHS.PREVIEWS, ActorImageModel.generateFilename()))
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTrackerUrls (object: VideoObject, video: MVideoWithHost) {
|
function getTrackerUrls (object: VideoObject, video: MVideoWithHost) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
|
import { ActorImageModel } from '@server/models/account/actor-image'
|
||||||
import { ThumbnailType } from '../../shared/models/videos/thumbnail.type'
|
import { ThumbnailType } from '../../shared/models/videos/thumbnail.type'
|
||||||
import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils'
|
import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils'
|
||||||
import { processImage } from '../helpers/image-utils'
|
import { processImage } from '../helpers/image-utils'
|
||||||
|
@ -200,7 +201,7 @@ function buildMetadataFromVideo (video: MVideoThumbnail, type: ThumbnailType, si
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
if (type === ThumbnailType.MINIATURE) {
|
if (type === ThumbnailType.MINIATURE) {
|
||||||
const filename = video.generateThumbnailName()
|
const filename = ActorImageModel.generateFilename()
|
||||||
const basePath = CONFIG.STORAGE.THUMBNAILS_DIR
|
const basePath = CONFIG.STORAGE.THUMBNAILS_DIR
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -214,7 +215,7 @@ function buildMetadataFromVideo (video: MVideoThumbnail, type: ThumbnailType, si
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === ThumbnailType.PREVIEW) {
|
if (type === ThumbnailType.PREVIEW) {
|
||||||
const filename = video.generatePreviewName()
|
const filename = ActorImageModel.generateFilename()
|
||||||
const basePath = CONFIG.STORAGE.PREVIEWS_DIR
|
const basePath = CONFIG.STORAGE.PREVIEWS_DIR
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { remove } from 'fs-extra'
|
import { remove } from 'fs-extra'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { AfterDestroy, AllowNull, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
|
import { AfterDestroy, AllowNull, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
|
||||||
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { MActorImageFormattable } from '@server/types/models'
|
import { MActorImageFormattable } from '@server/types/models'
|
||||||
import { ActorImageType } from '@shared/models'
|
import { ActorImageType } from '@shared/models'
|
||||||
import { ActorImage } from '../../../shared/models/actors/actor-image.model'
|
import { ActorImage } from '../../../shared/models/actors/actor-image.model'
|
||||||
|
@ -53,6 +54,10 @@ export class ActorImageModel extends Model {
|
||||||
.catch(err => logger.error('Cannot remove actor image file %s.', instance.filename, err))
|
.catch(err => logger.error('Cannot remove actor image file %s.', instance.filename, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static generateFilename () {
|
||||||
|
return uuidv4() + '.jpg'
|
||||||
|
}
|
||||||
|
|
||||||
static loadByName (filename: string) {
|
static loadByName (filename: string) {
|
||||||
const query = {
|
const query = {
|
||||||
where: {
|
where: {
|
||||||
|
|
|
@ -24,7 +24,6 @@ import {
|
||||||
Table,
|
Table,
|
||||||
UpdatedAt
|
UpdatedAt
|
||||||
} from 'sequelize-typescript'
|
} from 'sequelize-typescript'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
|
||||||
import { buildNSFWFilter } from '@server/helpers/express-utils'
|
import { buildNSFWFilter } from '@server/helpers/express-utils'
|
||||||
import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video'
|
import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video'
|
||||||
import { LiveManager } from '@server/lib/live-manager'
|
import { LiveManager } from '@server/lib/live-manager'
|
||||||
|
@ -1871,20 +1870,12 @@ export class VideoModel extends Model {
|
||||||
this.Thumbnails.push(savedThumbnail)
|
this.Thumbnails.push(savedThumbnail)
|
||||||
}
|
}
|
||||||
|
|
||||||
generateThumbnailName () {
|
|
||||||
return uuidv4() + '.jpg'
|
|
||||||
}
|
|
||||||
|
|
||||||
getMiniature () {
|
getMiniature () {
|
||||||
if (Array.isArray(this.Thumbnails) === false) return undefined
|
if (Array.isArray(this.Thumbnails) === false) return undefined
|
||||||
|
|
||||||
return this.Thumbnails.find(t => t.type === ThumbnailType.MINIATURE)
|
return this.Thumbnails.find(t => t.type === ThumbnailType.MINIATURE)
|
||||||
}
|
}
|
||||||
|
|
||||||
generatePreviewName () {
|
|
||||||
return uuidv4() + '.jpg'
|
|
||||||
}
|
|
||||||
|
|
||||||
hasPreview () {
|
hasPreview () {
|
||||||
return !!this.getPreview()
|
return !!this.getPreview()
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import 'mocha'
|
||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { writeFile } from 'fs-extra'
|
import { writeFile } from 'fs-extra'
|
||||||
import { basename, join } from 'path'
|
import { basename, join } from 'path'
|
||||||
import { Video } from '@shared/models'
|
import { Video, VideoDetails } from '@shared/models'
|
||||||
import {
|
import {
|
||||||
buildServerDirectory,
|
buildServerDirectory,
|
||||||
cleanupTests,
|
cleanupTests,
|
||||||
|
@ -19,6 +19,17 @@ import {
|
||||||
} from '../../../shared/extra-utils'
|
} from '../../../shared/extra-utils'
|
||||||
import { HttpStatusCode } from '@shared/core-utils'
|
import { HttpStatusCode } from '@shared/core-utils'
|
||||||
|
|
||||||
|
async function testThumbnail (server: ServerInfo, videoId: number | string) {
|
||||||
|
const res = await getVideo(server.url, videoId)
|
||||||
|
const video: VideoDetails = res.body
|
||||||
|
|
||||||
|
const res1 = await makeRawRequest(join(server.url, video.thumbnailPath), HttpStatusCode.OK_200)
|
||||||
|
expect(res1.body).to.not.have.lengthOf(0)
|
||||||
|
|
||||||
|
const res2 = await makeRawRequest(join(server.url, video.thumbnailPath), HttpStatusCode.OK_200)
|
||||||
|
expect(res2.body).to.not.have.lengthOf(0)
|
||||||
|
}
|
||||||
|
|
||||||
describe('Test regenerate thumbnails script', function () {
|
describe('Test regenerate thumbnails script', function () {
|
||||||
let servers: ServerInfo[]
|
let servers: ServerInfo[]
|
||||||
|
|
||||||
|
@ -84,18 +95,21 @@ describe('Test regenerate thumbnails script', function () {
|
||||||
await execCLI(`${env} npm run regenerate-thumbnails`)
|
await execCLI(`${env} npm run regenerate-thumbnails`)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should have regenerated local thumbnails', async function () {
|
it('Should have generated new thumbnail files', async function () {
|
||||||
{
|
await testThumbnail(servers[0], video1.uuid)
|
||||||
const res1 = await makeRawRequest(join(servers[0].url, video1.thumbnailPath), HttpStatusCode.OK_200)
|
await testThumbnail(servers[0], video2.uuid)
|
||||||
expect(res1.body).to.not.have.lengthOf(0)
|
|
||||||
|
|
||||||
const res2 = await makeRawRequest(join(servers[0].url, video1.previewPath), HttpStatusCode.OK_200)
|
const res = await makeRawRequest(join(servers[0].url, remoteVideo.thumbnailPath), HttpStatusCode.OK_200)
|
||||||
expect(res2.body).to.not.have.lengthOf(0)
|
expect(res.body).to.have.lengthOf(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should have deleted old thumbnail files', async function () {
|
||||||
|
{
|
||||||
|
await makeRawRequest(join(servers[0].url, video1.thumbnailPath), HttpStatusCode.NOT_FOUND_404)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const res = await makeRawRequest(join(servers[0].url, video2.thumbnailPath), HttpStatusCode.OK_200)
|
await makeRawRequest(join(servers[0].url, video2.thumbnailPath), HttpStatusCode.NOT_FOUND_404)
|
||||||
expect(res.body).to.not.have.lengthOf(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue