aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2022-03-07 17:16:54 +0100
committerChocobozzz <me@florianbigard.com>2022-03-07 17:23:12 +0100
commit0c058f256a195b92f124be10109c95d1fbe93ad8 (patch)
tree259ec36671b5b5b46e9bd7f4f5eb308ea65e93ff /server
parent2c7d736bd3089503117edb101d5a7d0f8fa3ee7e (diff)
downloadPeerTube-0c058f256a195b92f124be10109c95d1fbe93ad8.tar.gz
PeerTube-0c058f256a195b92f124be10109c95d1fbe93ad8.tar.zst
PeerTube-0c058f256a195b92f124be10109c95d1fbe93ad8.zip
Remove exif tags when processing images
Diffstat (limited to 'server')
-rw-r--r--server/helpers/image-utils.ts11
-rw-r--r--server/tests/fixtures/exif.jpgbin0 -> 10877 bytes
-rw-r--r--server/tests/fixtures/exif.pngbin0 -> 21059 bytes
-rw-r--r--server/tests/helpers/image.ts58
4 files changed, 59 insertions, 10 deletions
diff --git a/server/helpers/image-utils.ts b/server/helpers/image-utils.ts
index b174ae436..28d8fff4c 100644
--- a/server/helpers/image-utils.ts
+++ b/server/helpers/image-utils.ts
@@ -80,6 +80,8 @@ async function autoResize (options: {
80 const sourceIsPortrait = sourceImage.getWidth() < sourceImage.getHeight() 80 const sourceIsPortrait = sourceImage.getWidth() < sourceImage.getHeight()
81 const destIsPortraitOrSquare = newSize.width <= newSize.height 81 const destIsPortraitOrSquare = newSize.width <= newSize.height
82 82
83 removeExif(sourceImage)
84
83 if (sourceIsPortrait && !destIsPortraitOrSquare) { 85 if (sourceIsPortrait && !destIsPortraitOrSquare) {
84 const baseImage = sourceImage.cloneQuiet().cover(newSize.width, newSize.height) 86 const baseImage = sourceImage.cloneQuiet().cover(newSize.width, newSize.height)
85 .color([ { apply: 'shade', params: [ 50 ] } ]) 87 .color([ { apply: 'shade', params: [ 50 ] } ])
@@ -106,6 +108,7 @@ function skipProcessing (options: {
106 const { sourceImage, newSize, imageBytes, inputExt, outputExt } = options 108 const { sourceImage, newSize, imageBytes, inputExt, outputExt } = options
107 const { width, height } = newSize 109 const { width, height } = newSize
108 110
111 if (hasExif(sourceImage)) return false
109 if (sourceImage.getWidth() > width || sourceImage.getHeight() > height) return false 112 if (sourceImage.getWidth() > width || sourceImage.getHeight() > height) return false
110 if (inputExt !== outputExt) return false 113 if (inputExt !== outputExt) return false
111 114
@@ -116,3 +119,11 @@ function skipProcessing (options: {
116 119
117 return imageBytes <= 15 * kB 120 return imageBytes <= 15 * kB
118} 121}
122
123function hasExif (image: Jimp) {
124 return !!(image.bitmap as any).exifBuffer
125}
126
127function removeExif (image: Jimp) {
128 (image.bitmap as any).exifBuffer = null
129}
diff --git a/server/tests/fixtures/exif.jpg b/server/tests/fixtures/exif.jpg
new file mode 100644
index 000000000..2997b38e9
--- /dev/null
+++ b/server/tests/fixtures/exif.jpg
Binary files differ
diff --git a/server/tests/fixtures/exif.png b/server/tests/fixtures/exif.png
new file mode 100644
index 000000000..a1a0113f8
--- /dev/null
+++ b/server/tests/fixtures/exif.png
Binary files differ
diff --git a/server/tests/helpers/image.ts b/server/tests/helpers/image.ts
index 64bd373cc..475ca8fb2 100644
--- a/server/tests/helpers/image.ts
+++ b/server/tests/helpers/image.ts
@@ -4,6 +4,7 @@ import 'mocha'
4import { expect } from 'chai' 4import { expect } from 'chai'
5import { readFile, remove } from 'fs-extra' 5import { readFile, remove } from 'fs-extra'
6import { join } from 'path' 6import { join } from 'path'
7import { execPromise } from '@server/helpers/core-utils'
7import { buildAbsoluteFixturePath, root } from '@shared/core-utils' 8import { buildAbsoluteFixturePath, root } from '@shared/core-utils'
8import { processImage } from '../../../server/helpers/image-utils' 9import { processImage } from '../../../server/helpers/image-utils'
9 10
@@ -20,40 +21,77 @@ async function checkBuffers (path1: string, path2: string, equals: boolean) {
20 } 21 }
21} 22}
22 23
24async function hasTitleExif (path: string) {
25 const result = JSON.parse(await execPromise(`exiftool -json ${path}`))
26
27 return result[0]?.Title === 'should be removed'
28}
29
23describe('Image helpers', function () { 30describe('Image helpers', function () {
24 const imageDestDir = join(root(), 'test-images') 31 const imageDestDir = join(root(), 'test-images')
25 const imageDest = join(imageDestDir, 'test.jpg') 32
33 const imageDestJPG = join(imageDestDir, 'test.jpg')
34 const imageDestPNG = join(imageDestDir, 'test.png')
35
26 const thumbnailSize = { width: 223, height: 122 } 36 const thumbnailSize = { width: 223, height: 122 }
27 37
28 it('Should skip processing if the source image is okay', async function () { 38 it('Should skip processing if the source image is okay', async function () {
29 const input = buildAbsoluteFixturePath('thumbnail.jpg') 39 const input = buildAbsoluteFixturePath('thumbnail.jpg')
30 await processImage(input, imageDest, thumbnailSize, true) 40 await processImage(input, imageDestJPG, thumbnailSize, true)
31 41
32 await checkBuffers(input, imageDest, true) 42 await checkBuffers(input, imageDestJPG, true)
33 }) 43 })
34 44
35 it('Should not skip processing if the source image does not have the appropriate extension', async function () { 45 it('Should not skip processing if the source image does not have the appropriate extension', async function () {
36 const input = buildAbsoluteFixturePath('thumbnail.png') 46 const input = buildAbsoluteFixturePath('thumbnail.png')
37 await processImage(input, imageDest, thumbnailSize, true) 47 await processImage(input, imageDestJPG, thumbnailSize, true)
38 48
39 await checkBuffers(input, imageDest, false) 49 await checkBuffers(input, imageDestJPG, false)
40 }) 50 })
41 51
42 it('Should not skip processing if the source image does not have the appropriate size', async function () { 52 it('Should not skip processing if the source image does not have the appropriate size', async function () {
43 const input = buildAbsoluteFixturePath('preview.jpg') 53 const input = buildAbsoluteFixturePath('preview.jpg')
44 await processImage(input, imageDest, thumbnailSize, true) 54 await processImage(input, imageDestJPG, thumbnailSize, true)
45 55
46 await checkBuffers(input, imageDest, false) 56 await checkBuffers(input, imageDestJPG, false)
47 }) 57 })
48 58
49 it('Should not skip processing if the source image does not have the appropriate size', async function () { 59 it('Should not skip processing if the source image does not have the appropriate size', async function () {
50 const input = buildAbsoluteFixturePath('thumbnail-big.jpg') 60 const input = buildAbsoluteFixturePath('thumbnail-big.jpg')
51 await processImage(input, imageDest, thumbnailSize, true) 61 await processImage(input, imageDestJPG, thumbnailSize, true)
62
63 await checkBuffers(input, imageDestJPG, false)
64 })
65
66 it('Should strip exif for a jpg file that can not be copied', async function () {
67 const input = buildAbsoluteFixturePath('exif.jpg')
68 expect(await hasTitleExif(input)).to.be.true
69
70 await processImage(input, imageDestJPG, { width: 100, height: 100 }, true)
71 await checkBuffers(input, imageDestJPG, false)
72
73 expect(await hasTitleExif(imageDestJPG)).to.be.false
74 })
75
76 it('Should strip exif for a jpg file that could be copied', async function () {
77 const input = buildAbsoluteFixturePath('exif.jpg')
78 expect(await hasTitleExif(input)).to.be.true
79
80 await processImage(input, imageDestJPG, thumbnailSize, true)
81 await checkBuffers(input, imageDestJPG, false)
82
83 expect(await hasTitleExif(imageDestJPG)).to.be.false
84 })
85
86 it('Should strip exif for png', async function () {
87 const input = buildAbsoluteFixturePath('exif.png')
88 expect(await hasTitleExif(input)).to.be.true
52 89
53 await checkBuffers(input, imageDest, false) 90 await processImage(input, imageDestPNG, thumbnailSize, true)
91 expect(await hasTitleExif(imageDestPNG)).to.be.false
54 }) 92 })
55 93
56 after(async function () { 94 after(async function () {
57 await remove(imageDest) 95 await remove(imageDestDir)
58 }) 96 })
59}) 97})