Sobesednik / node-exiftool

A Node.js interface to exiftool command-line application.
MIT License
91 stars 15 forks source link

Line breaks and carriage returns not working properly #58

Open jihu opened 2 years ago

jihu commented 2 years ago

It seems that this tool doesn't handle line breaks and carriage returns properly.

Example when writing:

const metadata = {
    all: '', // remove all metadata at first
    'Caption-Abstract': 'test-line1\ntest-line2',
}

The output then contains:

{
  "data": null,
  "error": "Error: File not found - test-line2\r\n    1 image files updated\r\n    1 files weren't updated due to errors"
}

As you can see, the line break character causes it to believe that "test-line2" is a file argument.

I noticed this when trying to update the metadata of an image with existing line breaks (\n) and carriage returns (\r). It was part of a field that I didn't intend to modify (so I left it unchanged), but when writing the metadata I got this error:

undefined:1
======== some text [1/6]
^

SyntaxError: Unexpected token = in JSON at position 0
    at JSON.parse (<anonymous>)
    at C:\redacted\node_modules\node-exiftool\src\lib.js:146:37
    at processTicksAndRejections (node:internal/process/task_queues:96:5)

When looking at this (line 146 and the surrounding lines), I see this code:

return {
    data: res[0] ? JSON.parse(res[0]) : null,
    error: res[1] || null,
}

This means that it is expecting json formated output from exiftool in res[0].

Adding some code that prints this data just before the return, I got this:

This is the content of res[0]:
-----------------------------------------------------------------------
======== some text [1/6]
======== some more text [2/6]
======== some more text  [3/6]
======== some more text  [4/6]
======== some more text  [5/6]
======== C:/redacted/some_image.jpg [6/6]
-----------------------------------------------------------------------

The lines with "-----------------------------------------------------------------------" is not part of the output. Here you can see that for some reason exiftool doesn't output json here. Instead it outputs text strings that each was prefixed with a line break or carriage return, except for the last line that is the path to the image processed. And each output is for some reason prefixed with "======== " When outputting the content of res[1] I get a bunch of exiftool warnings about things that isn't writable (like "Warning: Sorry, ExifToolVersion is not writable"), but at the end there is an error "Error: File not found - ..." for each "some text" line above.

So, clearly, these line breaks and carriage returns is messing up the communication between node-exiftool and exiftool. Sadly I haven't been able to construct a sample jpg that causes this, and because of copyright reasons I can't share the actual image used.

Actually, maybe this is two bugs in one. One bug being the line breaks and/or carriage returns causing incorrect input data to exiftool, and one bug being that node-exiftool always asumes that the output from exiftool is json without checking.

gdethier commented 2 years ago

@jihu I have the same problem here. It comes from the fact that node-exiftool makes exiftool read an argfile from stdin (i.e. calls exiftool with arguments -@ -). In that "mode", each line is interpreted as a single argument and so, in your example, test-line2 is considered as a new argument passed to the tool.

The solution is actually here, see point (e):

Use the "#[CSTR]" feature to allow C-style escape sequences for a specific line in a -@ argfile

I patched this function so that it enables C-style escape sequences when data written to stdin contain carriage returns and/or new lines. It then looks like this:

function writeStdIn(proc, data, encoding) {
    if(/[\n\r]/.test(data)) {
        proc.stdin.write("#[CSTR]", encoding)
        proc.stdin.write(data.replaceAll(/(\r\n|\n\r|\n|\r)/g, "\\n"), encoding)
    } else {
        proc.stdin.write(data, encoding)
    }
    proc.stdin.write(EOL, encoding)
}

This solution may not be platform independent but it works on Linux at least.

gdethier commented 2 years ago

For the record, we created a fork and published a package which contains the fix (as well as type definitions for those interested). If anyone has write access to this repository and is willing to keep up maintaining it, I would be happy to create a PR.

anasshakil commented 1 year ago

Please take a look at my repo. I hope it'll solve your problem Link: https://github.com/anasshakil/metadata