从python脚本调用exiftool?

时间:2022-09-18 20:29:47

I'm looking to use exiftool to scan the EXIF tags from my photos and videos. It's a perl executable. What's the best way to inferface with this? Are there any Python libraries to do this already? Or should I directly call the executable and parse the output? (The latter seems dirty.) Thanks.

我希望使用exiftool扫描我的照片和视频中的EXIF标签。这是一个perl可执行文件。用这个来表达最好的方法是什么?有没有Python库可以做到这一点?或者我应该直接调用可执行文件并解析输出? (后者看起来很脏。)谢谢。

The reason I ask is this because I am currently using pyexiv2, which does not have support for videos. Perl's exiftool has very broad support for images and videos, and I'd like to use it.

我问的原因是因为我目前正在使用pyexiv2,它不支持视频。 Perl的exiftool对图像和视频有非常广泛的支持,我想使用它。

1 个解决方案

#1


19  

To avoid launching a new process for each image, you should start exiftool using the -stay_open flag. You can then send commands to the process via stdin, and read the output on stdout. ExifTool supports JSON output, which is probably the best option for reading the metadata.

为避免为每个图像启动新进程,您应该使用-stay_open标志启动exiftool。然后,您可以通过stdin将命令发送到进程,并读取stdout上的输出。 ExifTool支持JSON输出,这可能是读取元数据的最佳选择。

Here's a simple class that launches an exiftool process and features an execute() method to send commands to that process. I also included get_metadata() to read the metadata in JSON format:

这是一个简单的类,它启动一个exiftool进程,并具有一个execute()方法,用于向该进程发送命令。我还包括get_metadata()来读取JSON格式的元数据:

import subprocess
import os
import json

class ExifTool(object):

    sentinel = "{ready}\n"

    def __init__(self, executable="/usr/bin/exiftool"):
        self.executable = executable

    def __enter__(self):
        self.process = subprocess.Popen(
            [self.executable, "-stay_open", "True",  "-@", "-"],
            stdin=subprocess.PIPE, stdout=subprocess.PIPE)
        return self

    def  __exit__(self, exc_type, exc_value, traceback):
        self.process.stdin.write("-stay_open\nFalse\n")
        self.process.stdin.flush()

    def execute(self, *args):
        args = args + ("-execute\n",)
        self.process.stdin.write(str.join("\n", args))
        self.process.stdin.flush()
        output = ""
        fd = self.process.stdout.fileno()
        while not output.endswith(self.sentinel):
            output += os.read(fd, 4096)
        return output[:-len(self.sentinel)]

    def get_metadata(self, *filenames):
        return json.loads(self.execute("-G", "-j", "-n", *filenames))

This class is written as a context manager to ensure the process is exited if you are done. You can use it as

此类被编写为上下文管理器,以确保在您完成后退出该过程。你可以用它

with ExifTool() as e:
    metadata = e.get_metadata(*filenames)

EDIT for python 3: To get this to work in python 3 two small changes are needed. The first is an additional argument to subprocess.Popen:

编辑python 3:为了让它在python 3中工作,需要进行两个小的更改。第一个是subprocess.Popen的附加参数:

self.process = subprocess.Popen(
         [self.executable, "-stay_open", "True",  "-@", "-"],
         universal_newlines=True,
         stdin=subprocess.PIPE, stdout=subprocess.PIPE)

The second is that you have to decode the byte series returned by os.read():

第二个是你必须解码os.read()返回的字节序列:

output += os.read(fd, 4096).decode('utf-8')

EDIT for Windows: To get this working on Windows, the sentinel need to be changed into "{ready}\r\n", i.e.

EDIT for Windows:要在Windows上运行,需要将标记更改为“{ready} \ r \ n”,即

sentinel = "{ready}\r\n"

Otherwise the program will hang because the while loop inside execute() won't stop

否则程序将挂起,因为execute()内的while循环不会停止

#1


19  

To avoid launching a new process for each image, you should start exiftool using the -stay_open flag. You can then send commands to the process via stdin, and read the output on stdout. ExifTool supports JSON output, which is probably the best option for reading the metadata.

为避免为每个图像启动新进程,您应该使用-stay_open标志启动exiftool。然后,您可以通过stdin将命令发送到进程,并读取stdout上的输出。 ExifTool支持JSON输出,这可能是读取元数据的最佳选择。

Here's a simple class that launches an exiftool process and features an execute() method to send commands to that process. I also included get_metadata() to read the metadata in JSON format:

这是一个简单的类,它启动一个exiftool进程,并具有一个execute()方法,用于向该进程发送命令。我还包括get_metadata()来读取JSON格式的元数据:

import subprocess
import os
import json

class ExifTool(object):

    sentinel = "{ready}\n"

    def __init__(self, executable="/usr/bin/exiftool"):
        self.executable = executable

    def __enter__(self):
        self.process = subprocess.Popen(
            [self.executable, "-stay_open", "True",  "-@", "-"],
            stdin=subprocess.PIPE, stdout=subprocess.PIPE)
        return self

    def  __exit__(self, exc_type, exc_value, traceback):
        self.process.stdin.write("-stay_open\nFalse\n")
        self.process.stdin.flush()

    def execute(self, *args):
        args = args + ("-execute\n",)
        self.process.stdin.write(str.join("\n", args))
        self.process.stdin.flush()
        output = ""
        fd = self.process.stdout.fileno()
        while not output.endswith(self.sentinel):
            output += os.read(fd, 4096)
        return output[:-len(self.sentinel)]

    def get_metadata(self, *filenames):
        return json.loads(self.execute("-G", "-j", "-n", *filenames))

This class is written as a context manager to ensure the process is exited if you are done. You can use it as

此类被编写为上下文管理器,以确保在您完成后退出该过程。你可以用它

with ExifTool() as e:
    metadata = e.get_metadata(*filenames)

EDIT for python 3: To get this to work in python 3 two small changes are needed. The first is an additional argument to subprocess.Popen:

编辑python 3:为了让它在python 3中工作,需要进行两个小的更改。第一个是subprocess.Popen的附加参数:

self.process = subprocess.Popen(
         [self.executable, "-stay_open", "True",  "-@", "-"],
         universal_newlines=True,
         stdin=subprocess.PIPE, stdout=subprocess.PIPE)

The second is that you have to decode the byte series returned by os.read():

第二个是你必须解码os.read()返回的字节序列:

output += os.read(fd, 4096).decode('utf-8')

EDIT for Windows: To get this working on Windows, the sentinel need to be changed into "{ready}\r\n", i.e.

EDIT for Windows:要在Windows上运行,需要将标记更改为“{ready} \ r \ n”,即

sentinel = "{ready}\r\n"

Otherwise the program will hang because the while loop inside execute() won't stop

否则程序将挂起,因为execute()内的while循环不会停止