diff options
-rw-r--r-- | .github/actions/reusable-prepare-peertube-run/action.yml | 2 | ||||
-rw-r--r-- | server/helpers/image-utils.ts | 11 | ||||
-rw-r--r-- | server/tests/fixtures/exif.jpg | bin | 0 -> 10877 bytes | |||
-rw-r--r-- | server/tests/fixtures/exif.png | bin | 0 -> 21059 bytes | |||
-rw-r--r-- | server/tests/helpers/image.ts | 58 | ||||
-rw-r--r-- | support/doc/development/tests.md | 8 |
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 | |||
123 | function hasExif (image: Jimp) { | ||
124 | return !!(image.bitmap as any).exifBuffer | ||
125 | } | ||
126 | |||
127 | function 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' | |||
4 | import { expect } from 'chai' | 4 | import { expect } from 'chai' |
5 | import { readFile, remove } from 'fs-extra' | 5 | import { readFile, remove } from 'fs-extra' |
6 | import { join } from 'path' | 6 | import { join } from 'path' |
7 | import { execPromise } from '@server/helpers/core-utils' | ||
7 | import { buildAbsoluteFixturePath, root } from '@shared/core-utils' | 8 | import { buildAbsoluteFixturePath, root } from '@shared/core-utils' |
8 | import { processImage } from '../../../server/helpers/image-utils' | 9 | import { 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 | ||
24 | async 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 | |||
23 | describe('Image helpers', function () { | 30 | describe('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 | ||
34 | Ensure you also have these commands: | ||
35 | |||
36 | ``` | ||
37 | $ exiftool --help | ||
38 | ``` | ||
39 | |||
34 | ### Test | 40 | ### Test |
35 | 41 | ||
36 | To run all test suites: | 42 | To 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 | ||
42 | Most of tests can be runned using: | 48 | Most of tests can be run using: |
43 | 49 | ||
44 | ```bash | 50 | ```bash |
45 | TS_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 | 51 | TS_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 |