1
0
Fork 0
mirror of https://github.com/Chocobozzz/PeerTube.git synced 2025-10-06 03:50:26 +02:00

Merge branch 'release/4.1.0' into develop

This commit is contained in:
Chocobozzz 2022-03-08 11:28:32 +01:00
commit 7b51ede977
No known key found for this signature in database
GPG key ID: 583A612D890159BE
15 changed files with 132 additions and 44 deletions

View file

@ -118,6 +118,8 @@ async function autoResize (options: {
const sourceIsPortrait = sourceImage.getWidth() < sourceImage.getHeight()
const destIsPortraitOrSquare = newSize.width <= newSize.height
removeExif(sourceImage)
if (sourceIsPortrait && !destIsPortraitOrSquare) {
const baseImage = sourceImage.cloneQuiet().cover(newSize.width, newSize.height)
.color([ { apply: 'shade', params: [ 50 ] } ])
@ -144,6 +146,7 @@ function skipProcessing (options: {
const { sourceImage, newSize, imageBytes, inputExt, outputExt } = options
const { width, height } = newSize
if (hasExif(sourceImage)) return false
if (sourceImage.getWidth() > width || sourceImage.getHeight() > height) return false
if (inputExt !== outputExt) return false
@ -154,3 +157,11 @@ function skipProcessing (options: {
return imageBytes <= 15 * kB
}
function hasExif (image: Jimp) {
return !!(image.bitmap as any).exifBuffer
}
function removeExif (image: Jimp) {
(image.bitmap as any).exifBuffer = null
}

View file

@ -7,8 +7,13 @@ const sanitizeHtml = require('sanitize-html')
const markdownItEmoji = require('markdown-it-emoji/light')
const MarkdownItClass = require('markdown-it')
const markdownItWithHTML = new MarkdownItClass('default', { linkify: true, breaks: true, html: true })
const markdownItWithoutHTML = new MarkdownItClass('default', { linkify: false, breaks: true, html: false })
const markdownItForSafeHtml = new MarkdownItClass('default', { linkify: true, breaks: true, html: true })
.enable(TEXT_WITH_HTML_RULES)
.use(markdownItEmoji)
const markdownItForPlainText = new MarkdownItClass('default', { linkify: false, breaks: true, html: false })
.use(markdownItEmoji)
.use(plainTextPlugin)
const toSafeHtml = (text: string) => {
if (!text) return ''
@ -17,9 +22,7 @@ const toSafeHtml = (text: string) => {
const textWithLineFeed = text.replace(/<br.?\/?>/g, '\r\n')
// Convert possible markdown (emojis, emphasis and lists) to html
const html = markdownItWithHTML.enable(TEXT_WITH_HTML_RULES)
.use(markdownItEmoji)
.render(textWithLineFeed)
const html = markdownItForSafeHtml.render(textWithLineFeed)
// Convert to safe Html
return sanitizeHtml(html, defaultSanitizeOptions)
@ -28,12 +31,10 @@ const toSafeHtml = (text: string) => {
const mdToOneLinePlainText = (text: string) => {
if (!text) return ''
markdownItWithoutHTML.use(markdownItEmoji)
.use(plainTextPlugin)
.render(text)
markdownItForPlainText.render(text)
// Convert to safe Html
return sanitizeHtml(markdownItWithoutHTML.plainText, textOnlySanitizeOptions)
return sanitizeHtml(markdownItForPlainText.plainText, textOnlySanitizeOptions)
}
// ---------------------------------------------------------------------------
@ -47,30 +48,38 @@ export {
// Thanks: https://github.com/wavesheep/markdown-it-plain-text
function plainTextPlugin (markdownIt: any) {
let lastSeparator = ''
function plainTextRule (state: any) {
const text = scan(state.tokens)
markdownIt.plainText = text.replace(/\s+/g, ' ')
markdownIt.plainText = text
}
function scan (tokens: any[]) {
let lastSeparator = ''
let text = ''
for (const token of tokens) {
if (token.children !== null) {
text += scan(token.children)
continue
}
function buildSeparator (token: any) {
if (token.type === 'list_item_close') {
lastSeparator = ', '
} else if (token.type.endsWith('_close')) {
}
if (token.tag === 'br' || token.type === 'paragraph_close') {
lastSeparator = ' '
} else if (token.content) {
text += lastSeparator
text += token.content
}
}
for (const token of tokens) {
buildSeparator(token)
if (token.type !== 'inline') continue
for (const child of token.children) {
buildSeparator(child)
if (!child.content) continue
text += lastSeparator + child.content
lastSeparator = ''
}
}

View file

@ -37,7 +37,7 @@ const createTranscodingValidator = [
// Prefer using job info table instead of video state because before 4.0 failed transcoded video were stuck in "TO_TRANSCODE" state
const info = await VideoJobInfoModel.load(video.id)
if (info && info.pendingTranscode !== 0) {
if (info && info.pendingTranscode > 0) {
return res.fail({
status: HttpStatusCode.CONFLICT_409,
message: 'This video is already being transcoded'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Before After
Before After

BIN
server/tests/fixtures/exif.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
server/tests/fixtures/exif.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View file

@ -4,6 +4,7 @@ import 'mocha'
import { expect } from 'chai'
import { readFile, remove } from 'fs-extra'
import { join } from 'path'
import { execPromise } from '@server/helpers/core-utils'
import { buildAbsoluteFixturePath, root } from '@shared/core-utils'
import { processImage } from '../../../server/helpers/image-utils'
@ -20,40 +21,77 @@ async function checkBuffers (path1: string, path2: string, equals: boolean) {
}
}
async function hasTitleExif (path: string) {
const result = JSON.parse(await execPromise(`exiftool -json ${path}`))
return result[0]?.Title === 'should be removed'
}
describe('Image helpers', function () {
const imageDestDir = join(root(), 'test-images')
const imageDest = join(imageDestDir, 'test.jpg')
const imageDestJPG = join(imageDestDir, 'test.jpg')
const imageDestPNG = join(imageDestDir, 'test.png')
const thumbnailSize = { width: 223, height: 122 }
it('Should skip processing if the source image is okay', async function () {
const input = buildAbsoluteFixturePath('thumbnail.jpg')
await processImage(input, imageDest, thumbnailSize, true)
await processImage(input, imageDestJPG, thumbnailSize, true)
await checkBuffers(input, imageDest, true)
await checkBuffers(input, imageDestJPG, true)
})
it('Should not skip processing if the source image does not have the appropriate extension', async function () {
const input = buildAbsoluteFixturePath('thumbnail.png')
await processImage(input, imageDest, thumbnailSize, true)
await processImage(input, imageDestJPG, thumbnailSize, true)
await checkBuffers(input, imageDest, false)
await checkBuffers(input, imageDestJPG, false)
})
it('Should not skip processing if the source image does not have the appropriate size', async function () {
const input = buildAbsoluteFixturePath('preview.jpg')
await processImage(input, imageDest, thumbnailSize, true)
await processImage(input, imageDestJPG, thumbnailSize, true)
await checkBuffers(input, imageDest, false)
await checkBuffers(input, imageDestJPG, false)
})
it('Should not skip processing if the source image does not have the appropriate size', async function () {
const input = buildAbsoluteFixturePath('thumbnail-big.jpg')
await processImage(input, imageDest, thumbnailSize, true)
await processImage(input, imageDestJPG, thumbnailSize, true)
await checkBuffers(input, imageDest, false)
await checkBuffers(input, imageDestJPG, false)
})
it('Should strip exif for a jpg file that can not be copied', async function () {
const input = buildAbsoluteFixturePath('exif.jpg')
expect(await hasTitleExif(input)).to.be.true
await processImage(input, imageDestJPG, { width: 100, height: 100 }, true)
await checkBuffers(input, imageDestJPG, false)
expect(await hasTitleExif(imageDestJPG)).to.be.false
})
it('Should strip exif for a jpg file that could be copied', async function () {
const input = buildAbsoluteFixturePath('exif.jpg')
expect(await hasTitleExif(input)).to.be.true
await processImage(input, imageDestJPG, thumbnailSize, true)
await checkBuffers(input, imageDestJPG, false)
expect(await hasTitleExif(imageDestJPG)).to.be.false
})
it('Should strip exif for png', async function () {
const input = buildAbsoluteFixturePath('exif.png')
expect(await hasTitleExif(input)).to.be.true
await processImage(input, imageDestPNG, thumbnailSize, true)
expect(await hasTitleExif(imageDestPNG)).to.be.false
})
after(async function () {
await remove(imageDest)
await remove(imageDestDir)
})
})

View file

@ -30,5 +30,11 @@ describe('Markdown helpers', function () {
expect(result).to.equal('Hello coucou')
})
it('Should convert tags to plain text', function () {
const result = mdToOneLinePlainText(`#déconversion\n#newage\n#histoire`)
expect(result).to.equal('#déconversion #newage #histoire')
})
})
})

View file

@ -25,21 +25,21 @@ async function expectLogDoesNotContain (server: PeerTubeServer, str: string) {
expect(content.toString()).to.not.contain(str)
}
async function testImage (url: string, imageName: string, imagePath: string, extension = '.jpg') {
async function testImage (url: string, imageName: string, imageHTTPPath: string, extension = '.jpg') {
const res = await makeGetRequest({
url,
path: imagePath,
path: imageHTTPPath,
expectedStatus: HttpStatusCode.OK_200
})
const body = res.body
const data = await readFile(join(root(), 'server', 'tests', 'fixtures', imageName + extension))
const minLength = body.length - ((30 * body.length) / 100)
const maxLength = body.length + ((30 * body.length) / 100)
const minLength = data.length - ((40 * data.length) / 100)
const maxLength = data.length + ((40 * data.length) / 100)
expect(data.length).to.be.above(minLength, 'the generated image is way smaller than the recorded fixture')
expect(data.length).to.be.below(maxLength, 'the generated image is way larger than the recorded fixture')
expect(body.length).to.be.above(minLength, 'the generated image is way smaller than the recorded fixture')
expect(body.length).to.be.below(maxLength, 'the generated image is way larger than the recorded fixture')
}
async function testFileExistsOrNot (server: PeerTubeServer, directory: string, filePath: string, exist: boolean) {