Code Monkey home page Code Monkey logo

pyexiftool's Introduction

PyExifTool

GitHub Pages GitHub Actions PyPI Version Supported Python Versions Total PyPI Downloads PyPI Downloads this month

PyExifTool is a Python library to communicate with an instance of Phil Harvey's ExifTool command-line application.

The library provides the class exiftool.ExifTool that runs the command-line tool in batch mode and features methods to send commands to that program, including methods to extract meta-information from one or more image files. Since exiftool is run in batch mode, only a single instance needs to be launched and can be reused for many queries. This is much more efficient than launching a separate process for every single query.

Example Usage

Simple example:

import exiftool

files = ["a.jpg", "b.png", "c.tif"]
with exiftool.ExifToolHelper() as et:
    metadata = et.get_metadata(files)
    for d in metadata:
        print("{:20.20} {:20.20}".format(d["SourceFile"],
                                         d["EXIF:DateTimeOriginal"]))

Refer to documentation for more Examples and Quick Start Guide

Getting PyExifTool

PyPI

Easiest: Install a version from the official PyExifTool PyPI

python -m pip install -U pyexiftool

From Source

  1. Check out the source code from the github repository

    • git clone git://github.com/sylikc/pyexiftool.git
    • Alternatively, you can download a tarball.
  2. Run setup.py to install the module from source

    • python setup.py install [--user|--prefix=<installation-prefix>]

PyExifTool Dependencies

Python

PyExifTool runs on Python 3.6+. (If you need Python 2.6 support, please use version v0.4.x). PyExifTool has been tested on Windows and Linux, and probably also runs on other Unix-like platforms.

Phil Harvey's exiftool

For PyExifTool to function, exiftool command-line tool must exist on the system. If exiftool is not on the PATH, you can specify the full pathname to it by using ExifTool(executable=<full path>).

PyExifTool requires a minimum version of 12.15 (which was the first production version of exiftool featuring the options to allow exit status checks used in conjuction with -echo3 and -echo4 parameters).

To check your exiftool version:

exiftool -ver

Windows/Mac

Windows/Mac users can download the latest version of exiftool:

https://exiftool.org

Linux

Most current Linux distributions have a package which will install exiftool. Unfortunately, some do not have the minimum required version, in which case you will have to build from source.

  • Ubuntu

    sudo apt install libimage-exiftool-perl
    
  • CentOS/RHEL

    yum install perl-Image-ExifTool
    

Documentation

The current documentation is available at sylikc.github.io.

http://sylikc.github.io/pyexiftool/

Package Structure

PyExifTool was designed with flexibility and extensibility in mind. The library consists of a few classes, each with increasingly more features.

The base ExifTool class contains the core functionality exposed in the most rudimentary way, and each successive class inherits and adds functionality.

  • exiftool.ExifTool is the base class with core logic to interface with PH's ExifTool process. It contains only the core features with no extra fluff. The main methods provided are execute() and execute_json() which allows direct interaction with the underlying exiftool process.
    • The API is considered stable and should not change much with future releases.
  • exiftool.ExifToolHelper exposes some of the most commonly used functionality. It overloads some inherited functions to turn common errors into warnings and adds logic to make exiftool.ExifTool easier to use. For example, ExifToolHelper provides wrapper functions to get metadata, and auto-starts the exiftool instance if it's not running (instead of raising an Exception). ExifToolHelper demonstrates how to extend ExifTool to your liking if your project demands customizations not directly provided by ExifTool.
    • More methods may be added and/or slight API tweaks may occur with future releases.
  • exiftool.ExifToolAlpha further extends the ExifToolHelper and includes some community-contributed not-very-well-tested methods. These methods were formerly added ad-hoc by various community contributors, but no longer stand up to the rigor of the current design. ExifToolAlpha is not up to the rigorous testing standard of both ExifTool or ExifToolHelper. There may be old, buggy, or defunct code.
    • This is the least polished of the classes and functionality/API may be changed/added/removed on any release.
    • NOTE: The methods exposed may be changed/removed at any time.
    • If you are using any of these methods in your project, please Submit an Issue to start a discussion on making those functions more robust, and making their way into ExifToolHelper. (Think of ExifToolAlpha as ideas on how to extend ExifTool, where new functionality which may one day make it into the ExifToolHelper class.)

Brief History

PyExifTool was originally developed by Sven Marnach in 2012 to answer a stackoverflow question Call exiftool from a python script?. Over time, Sven refined the code, added tests, documentation, and a slew of improvements. While PyExifTool gained popularity, Sven never intended to maintain it as an active project. The original repository was last updated in 2014.

Over the years, numerous issues were filed and several PRs were opened on the stagnant repository. In early 2019, Martin Čarnogurský created a PyPI release from the 2014 code with some minor updates. Coincidentally in mid 2019, Kevin M (sylikc) forked the original repository and started merging the PR and issues which were reported on Sven's issues/PR page.

In late 2019 and early 2020 there was a discussion started to Provide visibility for an active fork. There was a conversation to transfer ownership of the original repository, have a coordinated plan to communicate to PyExifTool users, amongst other things, but it never materialized.

Kevin M (sylikc) made the first release to the PyPI repository in early 2021. At the same time, discussions were started, revolving around Deprecating Python 2.x compatibility and refactoring the code and classes.

The latest version is the result of all of those discussions, designs, and development. Special thanks to the community contributions, especially Jan Philip Göpfert, Seth P, and Kolen Cheung.

Licence

PyExifTool is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the licence, or (at your option) any later version, or the BSD licence.

PyExifTool is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See LICENSE for more details.

pyexiftool's People

Contributors

asielen avatar csparker247 avatar ickc avatar jangop avatar nyoungstudios avatar prutschman avatar smarnach avatar sylikc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

pyexiftool's Issues

Reading a binary tag

I need to read a tag that stores 100 bytes of binary data with a custom format that I have to parse myself. Without the -b option I'm getting the string (Binary data 100 bytes, use -b option to extract). I then use

tag = et.get_tags(["image.dng"], [TAG_NAME], ['-b'])[0]

This returns a dictionary with the name of the tag and the value of that tag as a string. However, I'd like to get the raw binary data so that I can parse it according to some external specifications. Is that possible? I think that on this line the binary data would be available, but it is automatically decoded to a string again. But additionally this line does not "just" return the value of the tag I'm interested in, but more information like SourceFile or the name of the Tag again.

Long story short: How can I get the raw binary data stored in a tag?

Edit
I forgot to mention that if I do this on the command line

exiftool.exe -TAG_NAME -b image.dng > data.dat

The file data.dat contains the binary data that I would expect.

pyexiftool tag values different from command line exiftool tag values (print conversion enabled on command line by default)

I've been using the pyexiftool (v0.5) in a Python3.9 and I've noticed that the returned tag values for QuickTime:MajorBrand is not the same as the command line version of exiftool. With pyexiftool, QuickTime:MajorBrand returns 'qt', whereas exiftool returns 'Apple QuickTime (.MOV/QT)'.

Sorry if this is the wrong forum to post, I am not sure where to inquire. Please see attached.

Thanks for your help.

Abe
pyexif-tags.txt

Exiftool location

What assumptions are made regarding the location of the Exiftool library/executable?
Is there a way specify a specific path or executable?
For Windows>
For Linux?

I have a packaged app - Gramps AIO https://gramps-project.org/blog/ - and would like to be able to use the Python interface, but under windows there seems to be no way.
I am inexperienced enough to not be able to figure that out from the Python source of this interface
Edit:
At present, Gramps marks the plugin unavailable if it cannot find the exiftool executable while it is parsing the "import exiftool" statement.
Edit 2:
For Windows, Exiftool.exe is on the PATH

High CPU consumption when using "-b" with large embedded binary data

Usually when processing a batch of raw images, I'm either IO limited, or else the external exiftool process is CPU bound. In these circumstances on my machine I can read metadata at 10-25 files per second.

I have some raw files with the "QuickTime:JpgFromRaw" tag, which contains a roughly 3MB embedded jpeg version of the raw image. When I process this with the "-b" flag enabled, my throughput drops to less than one file per second. Process Monitor shows that my python process is CPU bound.

Profiling the code shows that the vast majority of time is spent in _read_fd_endswith. I suspected the line output += os.read(fd, block_size) so I tried factoring it out into a separate function so the profiler could measure it, and indeed, it was the culprit.

For small amounts of data this isn't a problem, but repeatedly concatenating like this is "accidentally quadratic"--every time you add to the buffer, you have to copy the previous contents of the buffer.

I did a quick and dirty test of maintaining a list of buffers output_list=[b''], adding new data with output_list.append(os.read(fd,block_size)) and returning them at the end with b"".join(output_list). This did fix the slowdown. The catch is that I was only looking at the most recent data chunk when looking for the termination string. This happens to work since exiftool apparently flushes its buffers at the end of the write before printing the end sentinel. But, that relies on undocumented behvaior of exiftool, so I don't love it as a solution.

For my own current purposes, it's good enough. I'd like to contribute back, though. If I implement a version with robust logic for matching "b_endswith", would you be interested in a pull request?

How to set arguments for EXIFTool?

I'm trying to read lot of metadatas from video files. Some of them are very big size.

When I try to read metadata of big file, the message appeared like this
"SourceFile": "2023-02-05/20230923_124112(AX700).MP4",
"ExifTool:ExifToolVersion": 12.55,
"ExifTool:Warning": "End of processing at large atom (LargeFileSupport not enabled)",
"File:FileName": "20230923_124112(AX700).MP4",
"File:Directory": "2023-02-05",

I'm initializing EXIFtoolhelper like this:
from exiftool import ExifToolHelper, exceptions
e = ExifToolHelper()

Then I can get metadata with m = e.get_metadata(file).

How can I initialize EXIFtoolHelper with argument LargeFileSupport?

Thank you.

datetime print format via `-d FMT` option ignored when `-n` option present

Hey sylikc,

By default, exiftool uses : as the separator for the date aspect of all datetimes, eg: 2021:03:20 08:58:12. The CLI tool exposes a flag so that you can change how datetimes are formatted. I can add the below flag when running the CLI tool to render datetimes differently.

$ exiftool my-img.jpg -json -d "%Y-%m-%d %H:%M:%S%z"

[{
  "SourceFile": "my-img.jpg",
  ....
  "CreateDate": "2021-03-20 08:58:12-0400"
  ....
}]

Unfortunately, I am seeing different results when using the same flag and same file with pyexiftool. I have tried passing the datetime formatting parameter in via exiftool.get_metadata as well as exiftool.execute_json. I have also tried rendering the parameters in multiple ways. Regardless of how I render them, the only exifdata that is returned is "SourceFile". In the below example, if I omit passing in params, then the expected exifdata is returned, but uses the default rendering format of datetimes (as I would expect).

Python version 3.9.0

import exiftool

def extract_imagery_exifdata(source_filepath):
    with exiftool.ExifTool() as et:
        params = [exiftool.fsencode('-d "%Y-%m-%d %H:%M:%S%z"')]
        print(params)
        md = et.get_metadata(source_filepath, params=params)
        print(md)

>>> extract_imagery_exifdata("./my-img.jpg")

# note, that the params are being rendered correctly 
[b'-d "%Y-%m-%d %H:%M:%S%z"']

{'SourceFile': 'my-img.jpg'}

I haven't dug into the source code yet so I'm not sure if the issue is on my end or a small bug in the code, but would appreciate any guidance you may have. Thanks!

Decoding error when try to open Korean language file

I'm using your code really well, but I've noticed one odd thing.
If the name of the file contains certain Korean characters, I get a decoding error and the metadata of the file becomes unreadable. If I rename the file to something else, I can read the metadata normally.

def getFilesFromDirectory(directory):       # directory 변수로부터 해당 경로 내에 있는 모든 파일 목록 가져와 반환하기
    fileList = []       # fileList 빈 배열 만들기
    myName = Path(sys.argv[0]).name
    for filename in directory.iterdir():    # directory 경로명에 있는 모든 객체들을 filename으로서 탐색
        if filename.is_file() and filename.name != myName:      # filename이 만약 폴더가 아닌 파일이라면
            fileList.append(filename)   # fileList 배열 마지막에 삽입하기
    return fileList     # fileList 배열을 getFilesFromDirectory를 호출했던 곳에 반환하기

mainDirectory = "./2023-02-05"

files = getFilesFromDirectory(Path(mainDirectory))     # 테스트용 폴더 "./2023-02-05"에서 모든 파일 목록 가져와 files 변수에 저장하기
fileList = files[:]
e = ExifToolHelper()    # ExifToolHelper를 e로서 초기화하기
for i in range(len(files)):     # files 배열의 개수만큼 아래 내용 반복하기(i=0부터 files의 개수만큼 변경)
    f = files[i]        # f에 i번째 files 배열 값 저장하기
    files[i] = {}
    try:
        files[i]['dirName'] = os.path.dirname(os.path.abspath(f))
        files[i]['filename'] = os.path.splitext(os.path.basename(f))[0]
        files[i]['fullName'] = os.path.basename(f)
        files[i]['fullPath'] = f
        files[i]['ext'] = os.path.splitext(f)[1][1:]
        try:
            **m = e.get_metadata(f)       # f의 EXIF 메타데이터 가져와 m에 저장하기**
            if "ExifTool:Warning" in m[0]:
                if m[0]["ExifTool:Warning"] == "End of processing at large atom (LargeFileSupport not enabled)":
                    m = e.get_metadata(f, params=['-api', 'largefilesupport=1'])
        except Exception as err:
            print(f"파일 : {f}")      # f의 파일명 출력하기
            print(f"e.get_metadata 실패. EXIF 정보를 불러올 수 없는 파일: 에러 내용 ({err})")
            continue
    except exceptions.ExifToolExecuteError as err:  # 만약 EXIF 데이터를 가져오는 도중 에러가 발생하면 해당 에러 출력해주기(modd 파일은 괜찮은데 moff 파일의 경우 ExifTool로 메타데이터 가져오기 시도 시 에러 발생)
        print(f"파일 : {f}")      # f의 파일명 출력하기
        print(f"EXIF 정보를 불러올 수 없는 파일: 에러 내용 ({err}")
        continue

This code works well for the most part, but the bolded part, m = e.get_metadata(f), often results in an error. This happens when the filename contains certain Korean characters, which I've found to be the case for the filename "ㅍ휸ㅇ류.JPG".

The error content is as follows

'cp949' codec can't decode byte 0xb7 in position 37: illegal multibyte sequence

I looked up this error, and it says that I just need to specify UTF-8 as the encoding option when opening the file in Python. However, Python is opening the file fine, and it's doing a good job of displaying other properties of the file. Is there any option in EXIFTool to control the encoding related part?

Add versioning information directly to module

@jangop I see an issue opened with the original source smarnach#18 , and I've read through the PEP but wasn't sure how to do it exactly...

Where does __version__ go? Does it go into __init__.py or does it go like into the top of the different py files... or does it go into the class level? like in the ExifTool class?

I think the PEP 396 referenced is outdated (it's marked as Rejected)... latest I believe is PEP 440

It's unclear where I would put the version information... any clues?

Library documentation URL is out of date

Hey there - thanks so much for forking the original library and keeping this up-to-date. This work is quite valuable.

I noticed that the URL for the library documentation no longer works. *.github.com TLD has been deprecated and replaced by *.github.io. I have submitted a PR to fix this issue.

Fix can be found in PR #16

UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d

I tested a picture from: https://commons.wikimedia.org/wiki/File:Metadata_test_file_-_includes_data_in_IIM,_XMP,_and_Exif.jpg with code:

        with exiftool.ExifToolHelper(executable=ExifTool(), logger=logger) as et:
            meta = et.get_metadata(self.photo, params=["-s"])

And there is an error raised:

File "C:\Work\share\EF\venv\lib\site-packages\exiftool\helper.py", line 293, in get_metadata
    return self.get_tags(files, None, params=params)
  File "C:\Work\share\EF\venv\lib\site-packages\exiftool\helper.py", line 378, in get_tags
    ret = self.execute_json(*exec_params)
  File "C:\Work\share\EF\venv\lib\site-packages\exiftool\exiftool.py", line 1127, in execute_json
    result = self.execute("-j", *params)  # stdout
  File "C:\Work\share\EF\venv\lib\site-packages\exiftool\helper.py", line 132, in execute
    result: Union[str, bytes] = super().execute(*str_bytes_params, **kwargs)
  File "C:\Work\share\EF\venv\lib\site-packages\exiftool\exiftool.py", line 1018, in execute
    raw_stdout = raw_stdout.decode(self._encoding)
  File "C:\SDK\Python\Python39\lib\encodings\cp1252.py", line 15, in decode
    return codecs.charmap_decode(input,errors,decoding_table)
UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d in position 16621: character maps to <undefined>

But in the terminal: exiftool -G -s downloaded.jpg work well. All of the meta information is output correctly.

How to avoid Unicode errors like the result including 0x9d or 0x81, etc...
Thanks.

UTF-8 and local codepage

Hi,
As I remember, you added a _encoding agr to support non-unicodes.
Today, I find that a tag with “utf-8" but wide-character value is unsupported:

UnicodeDecodeError: 'gbk' codec can't decode byte 0xad in position 6326: illegal multibyte sequence
('gbk' (Chinese) is my windows's current code page.)

If I modify row 1018 in file 'exiftool.py' as fellowing, everything goes right.

#raw_stdout = raw_stdout.decode(self._encoding)
raw_stdout = raw_stdout.decode("utf-8")

Well, the result of pyexiftool is abtained from json, but json only accecpt vaild 'utf-8'.
If an invaild value (of local codeding) is passed to json, the value will be modified ( but garbled), even you use "self._encoding" (local codeding) to decode the value, you can still no longer get the original value. I did some tests about it:
https://exiftool.org/forum/index.php?topic=13473

On the other hand, json returns 'utf-8' and you have to use 'utf-8' to decode it, otherwise, it can't even support valid unicode "utf-8" tag value.
I think non-unicode tag value support could not be done, as long as you use json to get the results.
I don't know whether other local codeding rather than Chinese could be support by the ‘trik’ to decode json with local encoding, but in my exprience, it just can't. The only way to get it around is use '-b' option and decode it:

    def Base64_to_Str(base64_str: str, encoding=None) -> str:
        if base64_str == None or not base64_str.startswith('base64:'):
            return None
        b: bytes = base64.b64decode(base64_str[7:])
        if encoding == None:
            encoding = locale.getpreferredencoding(False)  # 'cp936' same as 'gb2312'?
        fixed: str = b.decode(encoding)
        return fixed

That's how I deal with filenames, which are always local encoded for windows command line. (Alought now windows support to set the enconding to 'utf-8', but we can't suppose every users turn that option on). I will keep tracking in exiftool fourm, to see whether this problem could be fixed on exiftool side.

In terms of embed tags, I think morden softwares tend to set the value in 'utf-8', and that should be supported.

So, plz decode json with 'utf-8'. And non-unicon values are just not supported by json (at least json in exiftool), you can't help.

any idea on how to strip all metadata?

how would I do the equivalent of: exiftool -all= path_to_file

I can't find it and on the doc it says that the execute function should not be used by developers.

thanks

Reading all tags from no files

The test

def test_read_all_from_no_file(self):
fails. This used to be (I think) because
def get_metadata(self, files, params=None):
returned None instead of [] but, looking at the source, apparently it (or rather get_tags) now raises TypeError if files evaluates to False (which both None and [] do).

Should that (raising TypeError) continue to be the expected behavior? If, so we should modify the test accordingly and adjust the docstring of get_metadata.

v0.5.0 AttributeError: 'ExifTool' object has no attribute 'get_metadata_batch' and 'get_metadata'

After update to 0.5.0 (Successfully installed pyexiftool-0.5.0)
I get two problems:
first one the parameter executable_ from Exiftool doesn' t exist I have to replace by executable
second one methods 'get_metadata_batch' or 'get_metadata' doesn't exist and if I have a look at the documentation there are always here
AttributeError: 'ExifTool' object has no attribute 'get_metadata_batch'
AttributeError: 'ExifTool' object has no attribute 'get_metadata'

Could you help?

Writing lists of values

I'm trying to use pyexiftool to update Keywords and Subject, which are both 'list' tags. From the exiftool docs, it looks like the choice is to use one of these approaches:

 exiftool -Keywords=keyword1 -Keywords=keyword2 -Keywords=keyword3 file.jpg
 exiftool -sep ", " -Keywords="keyword1, keyword2, keyword3" file.jpg

I can't see a way to get this to work from pyexiftool. For option 1, the set_tags_batch function takes a dictionary of tags and values, and so obviously can't contain multiple instances of the same tag as keys. For option 2, I've tried setting:

extl = exiftool.ExifTool(common_args=['-sep', '", "'])
extl.start()
extl.set_tags({'Keywords': "keyword1, keyword2, keyword3" }, 'file.jpg')

That doesn't work either - when you read the file it is a single string and not a list of three elements. I've tried using a list as the values:

extl.set_tags({'Keywords': ["keyword1", "keyword2", "keyword3"]}, 'file.jpg')

But that just ends up using the repr of the list. Any suggestions?

Windows hang

I'm hoping to use this in Linux, so hopefully not an issue but makes dev in two different environments hard. Most of the time it just hangs at program exit, but sometimes I get an Exception as below:

Exception ignored in: <function ExifTool.__del__ at 0x0000022E9FDFA840>
Traceback (most recent call last):
  File "C:\Users\Simon\AppData\Local\Programs\Python\Python311\Lib\site-packages\exiftool\exiftool.py", line 327, in __del__
    if self.running:
       ^^^^^^^^^^^^
  File "C:\Users\Simon\AppData\Local\Programs\Python\Python311\Lib\site-packages\exiftool\exiftool.py", line 557, in running
    if self._process.poll() is not None:
       ^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Simon\AppData\Local\Programs\Python\Python311\Lib\subprocess.py", line 1234, in poll
    return self._internal_poll()
           ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Simon\AppData\Local\Programs\Python\Python311\Lib\subprocess.py", line 1530, in _internal_poll
    if _WaitForSingleObject(self._handle, 0) == _WAIT_OBJECT_0:
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
OSError: [WinError 6] The handle is invalid

variable inside exiftoolhelper.execute()

Hi,
if have written a small program to test some functionality. I can't seem to figure out how to pass a variable as an argument for .execute().
this is my prgram:

from exiftool import ExifToolHelper
import logging
logging.basicConfig(level=logging.DEBUG)

# Define a list of file paths for which you want to retrieve metadata
path = "C:\Users\Arthu\Pictures"

# Create an ExifToolHelper instance
with ExifToolHelper(logger=logging.getLogger(__name__)) as et:
    # Execute the ExifTool command
    result = et.execute('-Directory<CreateDate',  '-d "%Y/%m"',  '-r',  f'{path}')

# 'result' contains the output of the ExifTool command
print(result)

I would like to pass a path from a file or directory to the exiftool command, wich should basicly comme down to exiftool "-Directory<CreateDate" -d "%Y/%m" -r "C:\Users\Arthu\Pictures\test exif tool" .

Could you please teach me how to do this propperly?

delete metadata

Tell me how to correctly delete all metadata in a file and overwrite it?

I tried this:

with exiftool.ExifTool() as et:
    print(et.execute(*["-all:all="] + ['/path/file']))
    #or print(et.execute(*["-all="] + ['/path/file']))

but the result:

0 image files updated
    1 files weren't updated due to errors

and metadata is not cleared

by the way, as a result it writes: 0 image. This is not entirely correct, because I’m trying to clean up the .docx file

execute_json should set the ExifTool param -struct too

Currently calling execute_json sets only -j as ExifTool parameter - but it does not set the -struct parameter. That's a dangerous for metadata properties with structured values and multiple values.

Example: the IPTC Photo Metadata Standard defines a property Location Shown in the Image which has a structure of City, State/Province, Country Name, Country Code, Sublocation and more. And it may have multiple values = multiple structures.

Using the -j (JSON) parameter without the -struct parameter returns such a result:

"XMP:LocationShownCity": ["City (Location shown2) (ref2021.1)"],
"XMP:LocationShownCountryCode": ["ABC","ABC"],
"XMP:LocationShownCountryName": ["CountryName (Location shown1) (ref2021.1)","CountryName (Location shown2) (ref2021.1)"],
"XMP:LocationShownSublocation": ["Sublocation (Location shown1) (ref2021.1)"],

Using the -j (JSON) parameter WITH the -struct parameter returns such a result:

"XMP:LocationShown": [{
    "CountryCode": "ABC",
    "CountryName": "CountryName (Location shown1) (ref2021.1)",
    "Sublocation": "Sublocation (Location shown1) (ref2021.1)"
  },{
    "City": "City (Location shown2) (ref2021.1)",
    "CountryCode": "ABC",
    "CountryName": "CountryName (Location shown2) (ref2021.1)"
  }],

The essential difference: the XMP:LocationShownCity and the XMP:LocationShownSublocation of the result without -struct have only a single value in the array, but knowbody knows if this is the City name or Sublocation name of the first location of of the second location. While the XMP:LocationShown has a JSON object for each location and the first object has no City but a Sublocation, the second object has a City but no Sublocation. Which location has what structured data is crystal clear.

(Note: the results above are taken from the IPTC Photo Metadata reference image, it has values telling to which property it belongs.)

With this semantic issue as background I suggest to set the -struct parameter with the -j parameter in the execute_json method.

why XMP tag not add to IPTC Please donnot ignore?

As I am using this code and the tag name is from XMP but i have mentioned IPTC

from exiftool import ExifTool
with ExifTool() as et:
    print(et.execute(*["-IPTC:Subject=hi"] + ["skyblue.png"]))

Thanks please write your precious answer

Program hangs when using get_metadata function on Windows.

Hello.

My program is so simple. I'm just trying basic function of pyexiftool but having problem.

import os
from exiftool import ExifToolHelper

def get_files_in_directory(directory):
    file_list = []
    for filename in os.listdir(directory):
        file_path = os.path.join(directory, filename)
        if os.path.isfile(file_path):
            file_list.append(file_path)
    return file_list

files = get_files_in_directory("directory path")
for i in range(len(files)):
    print(files[i])
    **for d in ExifToolHelper().get_metadata(files[i]):**
        for k, v in d.items():
            print(f"Dict: {k} =- {v}")

My program hangs at bold part.

If I press Ctrl + C in the command window, the metadata of the file is displayed normally with an error. But you can't type Ctrl+C one by one. Why stop there?

Hangs on Windows if exiftool.exe is in C:\Windows\system32 (upstream PH ExifTool issue)

Thank you for providing this package!

I've installed exiftool.exe into C:\Windows\system32. The following example then hangs:

import exiftool
with exiftool.ExifToolHelper() as et:
    metadata = et.get_metadata("some\image.png")

The problem is that this while loop in _read_fd_endswith does not terminate because output actually does not change:

while not output[-endswith_count:].strip().endswith(b_endswith):
	if constants.PLATFORM_WINDOWS:
		# windows does not support select() for anything except sockets
		# https://docs.python.org/3.7/library/select.html
		output += os.read(fd, block_size)

This might be happening because to access exiftool.exe in system32 one requires Admin rights? I'm not sure if this is a bug, and the workaround is pretty easy: just use ExifToolHelper(executable="path\\to\\exiftool.exe") with a path the current user has access to. If it's not a bug (or something that's not easily fixable), it might be good to note it in the documentation?

Periodically receive tempfile already exists error when using pyexiftool

As in title. I have a script that modifies an image on command, using the following code, and periodically exiftool (not pyexiftool) will fail with the message that the tempfile already exists. However, this is nondeterministic, and the tempfile is always removed after success or failure, so it's very difficult to dig into what exactly is happening.

def set_exif_data(filename, source_link):
     comment = source_link
     print("comment:", comment)
     with exiftool.ExifToolHelper(['-G', '-n', '-overwrite_original']) as et:
         et.set_tags(filename, {"UserComment": comment})
Error: Temporary file already exists: images-saved/temp-file.mp4_exiftool_tmp

I added the 'overwrite_original' to try and prevent this issue from cropping up, but it keeps cropping up anyway. It occurred before I added it.

ExifToolHelper Hangs in pytest, in wine

Trying to debug a pyexiftool issue on Windows, to which I don't have access, I'm basically trying to run this, in tobix/pywine:

def test_exiftool_works_on_all_images():
    with ExifToolHelper(executable="./exiftool.exe") as et:
        for d in et.get_metadata("./test.tiff"):
             assert len(d) > 0

    # Also test ExifTool.execute_json
    with ExifTool(executable="./exiftool.exe") as eftl:
        d = eftl.execute_json("./test.tiff")
        assert len(d) > 0

I run this in the following command:

xvfb-run sh -c "wine pytest"

With various prints and pytest options, I can see that the get_metadata call works, and that assert len(d) > 0 passes, but the code never goes past the end of the with block. In other words, it hangs in .terminate(), and never goes to test ExifTool.

Now. If I add a import pdb; pdb.set_trace() as first line in the function, and hit c when prompted, it works. What am I doing wrong?

Newline in field causes execute error

The following command on command line:

exiftool -comment="post contents:
 new line" images-saved/temp-file.jpg

gives the following output:

    1 image files updated

However, when I try to use pyexiftool to do the same, even using shlex to parse the exact same string:

>>> import shlex, exiftool
>>> with exiftool.ExifToolHelper() as et:
...     params = shlex.split("""-comment="post contents:
...  new line" images-saved/temp-file.jpg""")
...     et.execute(*params)
... 

I get this output

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
  File "/home/pi/.local/lib/python3.11/site-packages/exiftool/helper.py", line 135, in execute
    raise ExifToolExecuteError(self._last_status, self._last_stdout, self._last_stderr, str_bytes_params)
exiftool.exceptions.ExifToolExecuteError: execute returned a non-zero exit status: 1

Given that using shlex to determine the correct parsing is the only thing I have found to do if pyexiftool's output doesn't match exiftool's, I'm not sure what else I can do here.

loss of precision caused by json

Hi,

After I write a value e.g. 1.10 to the tag Exif:UserComment and read it by pyExifTool using method involving json (exiftoolhelper.get_tags()), the return value becomes 1.1, the precision just lost. (1.10 means 10th January for me, not equal to 1.1.)

According to exiftool doc:
exiftool OPTIONS

ExifTool quotes JSON values only if they don't look like numbers (regardless of the original storage format or the relevant metadata specification).

Additionally, the original outputs of exiftool do keep zeros at the end:

[{
  "SourceFile": "./test.jpg",
  "UserComment": 1.10
}]

So, I guess parse float as str is nessary. And the fellowing code could fix the problem:

//exiftool.py line 1159
parsed = json.loads(result, parse_float=str)

I think It's a good idea to add a option to let users to parse float as str without modifing the lib of pyexiftool.

execute hangs in Windows

I am unable to use the execute function (get_metadata works flawlessly). My code is basically

import exiftool
with exiftool.ExifToolHelper() as et:
   params = ['-TagsFromFile D:\\somepath\\ND800_0007854.NEF.xmp', '-XMP:Subject', '-XMP:HierarchicalSubject', 'D:\\somepath\\ND800_0007854.jpg']
   et.execute(*params)

and it hangs in the execute function until I kill it. The traceback points at

  File "C:\ProgramData\Miniconda3\envs\phototools\lib\site-packages\exiftool\exiftool.py", line 135, in _read_fd_endswith
    while not _get_buffer_end(output_list, endswith_count).strip().endswith(b_endswith):

I am able to run the command in the terminal:

exiftool -TagsFromFile D:\somepath\ND800_0007854.NEF.xmp -XMP:Subject -XMP:HierarchicalSubject D:\somepath\ND800_0007854.jpg
Warning: [minor] Ignored empty rdf:Seq list for darktable:masks_history - D:/somepath/ND800_0007854.jpg
    1 image files updated

Specifying Latin encoding for filenames due to accents on folder names

Hi there,
Firstly thanks for this wrapper. Exiftool is so powerful and combined with Python is even better. I am using it to rename my entire digital photo collection dating back 20 years.
The problem I have atm is that in a folder full of photos from France, I have some folder names that have accents on some letters. With the active page set to CHCP 65001 (utf8), when I run exiftool normally in the cmd prompt, I get funky characters in the directory name (which i'm using in the rename) and a filename encoding not specified warning.
image
When I specify the charset to be Latin, it works properly.
exiftool -charset filename=Latin "D:\Processing\Paris Adventures\Day 03\Église St-Etienne-du-Mont\IMG_9251.jpg"
image
So my question is, how do i implement that equivalent in the python code when reading the initial metadata.
with exiftool.ExifToolHelper() as et: print(et.get_metadata(r"D:\Processing\Paris Adventures\Day 03\Église St-Etienne-du-Mont\IMG_9252.jpg", "File:Directory"))
I have tried adding the 'encoding' option to the exiftool executable line
with exiftool.ExifToolHelper(encoding='Latin') as et: print(et.get_metadata(r"D:\Processing\Paris Adventures\Day 03\Église St-Etienne-du-Mont\IMG_9252.jpg"))
but when it runs the Directory still has the funky character in it and is not decoded. I'm terrible at interpreting API documentation, so I"m not sure what I'm doing wrong exactly.
Am I better off using the execute_json method so i can specify the 'charset' argument instead of the 'get_metadata' method (I'm self-taught python so my terminology could be incorrect)?
Any help would be greatly appreciated and apologies if this is not the right place for this question or if further information is required.
Thank you so much in advance.

Sian Doherty

'-CreateDate<filename' doesn't appear to work via et.execute

You can use exiftool '-CreateDate<filename' to attempt to parse the file name as a date and set that to the CreateDate tag, on the command line. However, I am running into difficulties with doing this via pyexiftool.

with exiftool.ExifToolHelper(config_file="./exiftool.config", logger = logging.getLogger()) as et:
     et.execute("-CreateDate<filename", f)

I'm seeing 1 image files updated when I print the return state but after running exiftool -time:all on that file, the date has not been updated. There are also no warnings/errors in the debug log.

INFO 2022-09-04 17:31:21,743 - Method 'run': Exiftool version '12.42' (pid 42558) launched with args '['/usr/local/bin/exiftool', '-config', './exiftool.config', '-stay_open', 'True', '-@', '-', '-common_args', '-G', '-n']'
INFO 2022-09-04 17:31:21,743 - Method 'execute': Command sent = [b'-CreateDate<filename', b'redacted/20090506121211.png', b'-echo4', b'=${status}=post694948']
DEBUG 2022-09-04 17:31:21,928 - ExifToolHelper.execute: IN  params = ('-CreateDate<filename', 'redacted/20090506121211.png')
DEBUG 2022-09-04 17:31:21,928 - ExifToolHelper.execute: OUT stdout = "1 image files updated

Could you assist? Thanks

Can output formats be specified using exiftool.ExifToolHelper()?

Can output formats be specified using exiftool.ExifToolHelper()? I tried the formatting date for CreateDate and it did not work.

  • the tagname was returned together with a groupname.
  • I wanted to get the actual value of the TAG using get_tags() but the returned values included sourcefile and groupname:tagname.
  • I previously used the older version of pyexiftool >> xx.get_tag('CreateDate', FileName) << will return only value of CreateDate without sourcefile and tagnames.

import exiftool

with exiftool.ExifToolHelper() as et:
... md=et.get_tags('./misc/20190105_153439.mp4','CreateDate',['-r','-s','-d','%Y-%m-%d'])
... print(md)
...
[{'SourceFile': './misc/20190105_153439.mp4', 'QuickTime:CreateDate': '2019:01:21 22:08:12'}]


Note: using exiftool.ExifTool() applies the date format specified in the common_args list
and tagname does not contain groupname.


import exiftool

with exiftool.ExifTool(common_args= ['-r','-s','-d','%Y-%m-%d','-CreateDate']) as et:
... md = et.execute_json("./misc/20190105_153439.mp4")
...
print(md)
[{'SourceFile': './misc/20190105_153439.mp4', 'CreateDate': '2019-01-21'}]

Thanks again for your help.

Start instance automatically

There is a check in execute to see if an instance is running, but it raises an exception instead of launching an instance:

raise ValueError("ExifTool instance not running.")

What is the reasoning behind requiring the user to start an instance manually? Are there situations in which doing this lazily would cause problems?

reading metadata of empty file

Use Exiftool in command line to read metadata of a empty file:

PS H:\dev\git\ExifToolGUI\.samples\empty file> exiftool -j ./empty.txt
[{
  "SourceFile": "./empty.txt",
  "ExifToolVersion": 12.64,
  "FileName": "empty.txt",
  "Directory": ".",
  "FileSize": "0 bytes",
  "FileModifyDate": "2023:07:17 23:38:56+08:00",
  "FileAccessDate": "2023:07:17 23:38:56+08:00",
  "FileCreateDate": "2023:07:17 23:38:56+08:00",
  "FilePermissions": "-rw-rw-rw-",
  "Error": "File is empty"
}]

It returns at least the file system metadata, but pyExiftool returns nothing but throws a error.

Documentation of .encoding - UTF-8 vs utf-8

The documentation of propertyexiftool.ExifTool.encoding tells:

Default to UTF-8 if nothing is returned ...

But the code of the @encoding.setter is:

ENCODING_UTF8: str = "utf-8"
... and ...
self._encoding = new_encoding or (locale.getpreferredencoding(do_setlocale=False) or ENCODING_UTF8)

Therefore the documentation should tell:

Defaults to utf-8 if nothing is returned ...

... else debugging people like me may be wondering why the self._encoding value is not 'UTF-8'.

Report progress during batch processing

Hi,

I've been playing with the module and so far it's proven to be really useful, thanks for your work.

I'm renaming some files based on their date of creation. Sometimes, I have to process folders with a couple thousand files. I used to do a "for file in files: process(file)" but that took hours, and found that ExifTool is capable of doing batch processing itself, WAY FASTER (minutes), so I use the execute_json to do the batch processing for me.

Nevertheless, it still takes minutes, and was wondering if there's a way to report progress during processing, or at least an "spinner".

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.