aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.github/actions/reusable-prepare-peertube-run/action.yml2
-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
-rw-r--r--support/doc/development/tests.md8
6 files changed, 67 insertions, 12 deletions
diff --git a/.github/actions/reusable-prepare-peertube-run/action.yml b/.github/actions/reusable-prepare-peertube-run/action.yml
index 1a6cd2cfd..aa5b897c9 100644
--- a/.github/actions/reusable-prepare-peertube-run/action.yml
+++ b/.github/actions/reusable-prepare-peertube-run/action.yml
@@ -8,7 +8,7 @@ runs:
8 - name: Setup system dependencies 8 - name: Setup system dependencies
9 shell: bash 9 shell: bash
10 run: | 10 run: |
11 sudo apt-get install postgresql-client-common redis-tools parallel 11 sudo apt-get install postgresql-client-common redis-tools parallel libimage-exiftool-perl
12 wget --quiet --no-check-certificate "https://download.cpy.re/ffmpeg/ffmpeg-release-4.3.1-64bit-static.tar.xz" 12 wget --quiet --no-check-certificate "https://download.cpy.re/ffmpeg/ffmpeg-release-4.3.1-64bit-static.tar.xz"
13 tar xf ffmpeg-release-4.3.1-64bit-static.tar.xz 13 tar xf ffmpeg-release-4.3.1-64bit-static.tar.xz
14 mkdir -p $HOME/bin 14 mkdir -p $HOME/bin
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})
diff --git a/support/doc/development/tests.md b/support/doc/development/tests.md
index 02fc41147..47602156c 100644
--- a/support/doc/development/tests.md
+++ b/support/doc/development/tests.md
@@ -31,6 +31,12 @@ $ sudo docker run -p 9444:9000 chocobozzz/s3-ninja
31$ sudo docker run -p 10389:10389 chocobozzz/docker-test-openldap 31$ sudo docker run -p 10389:10389 chocobozzz/docker-test-openldap
32``` 32```
33 33
34Ensure you also have these commands:
35
36```
37$ exiftool --help
38```
39
34### Test 40### Test
35 41
36To run all test suites: 42To run all test suites:
@@ -39,7 +45,7 @@ To run all test suites:
39$ npm run test # See scripts/test.sh to run a particular suite 45$ npm run test # See scripts/test.sh to run a particular suite
40``` 46```
41 47
42Most of tests can be runned using: 48Most of tests can be run using:
43 49
44```bash 50```bash
45TS_NODE_TRANSPILE_ONLY=true npm run mocha -- --timeout 30000 --exit -r ts-node/register -r tsconfig-paths/register --bail server/tests/api/videos/video-transcoder.ts 51TS_NODE_TRANSPILE_ONLY=true npm run mocha -- --timeout 30000 --exit -r ts-node/register -r tsconfig-paths/register --bail server/tests/api/videos/video-transcoder.ts