IPTCInfo3 2.1.4__tar.gz → 2.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iptcinfo3-2.3.0/.gitignore +21 -0
- iptcinfo3-2.3.0/CHANGELOG.md +107 -0
- iptcinfo3-2.3.0/LICENSE +20 -0
- iptcinfo3-2.3.0/PKG-INFO +103 -0
- {IPTCInfo3-2.1.4 → iptcinfo3-2.3.0}/README.rst +20 -2
- {IPTCInfo3-2.1.4 → iptcinfo3-2.3.0}/iptcinfo3.py +109 -36
- iptcinfo3-2.3.0/pyproject.toml +80 -0
- IPTCInfo3-2.1.4/IPTCInfo3.egg-info/PKG-INFO +0 -72
- IPTCInfo3-2.1.4/IPTCInfo3.egg-info/SOURCES.txt +0 -8
- IPTCInfo3-2.1.4/IPTCInfo3.egg-info/dependency_links.txt +0 -1
- IPTCInfo3-2.1.4/IPTCInfo3.egg-info/top_level.txt +0 -1
- IPTCInfo3-2.1.4/PKG-INFO +0 -72
- IPTCInfo3-2.1.4/setup.cfg +0 -15
- IPTCInfo3-2.1.4/setup.py +0 -63
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
## 2.3.0 (2025-12-02)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
- **Issue #50**: Fixed keywords and other list fields to prevent duplicate values (deduplicates on append and save)
|
|
5
|
+
- **Issue #45**: Removed outdated VERSION = 1.9 comment from code
|
|
6
|
+
|
|
7
|
+
### New Features
|
|
8
|
+
- **Issue #44**: Added LICENSE file with dual Artistic-1.0 / GPL-1.0+ license
|
|
9
|
+
- Added `UniqueList` class for list fields that automatically prevents duplicates
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 2.2.0 (2025-10-02)
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
- **Issue #40**: Clarified license statements - now consistently states "Artistic-1.0 OR GPL-1.0-or-later"
|
|
17
|
+
- **Issue #24**: Changed "Marker scan hit start of image data" to INFO level when `force=True` is used
|
|
18
|
+
- **Issue #32**: Fixed charset recognition for ISO 2022 escape sequences (UTF-8 as `\x1b%G`)
|
|
19
|
+
- **Issue #26**: Added validation for float/NaN values in `packedIIMData()` to prevent TypeError
|
|
20
|
+
|
|
21
|
+
### New Features
|
|
22
|
+
- **Issue #35**: Added 'credit line' field support per IPTC Core 1.1 (backward compatible with 'credit')
|
|
23
|
+
- **Issue #42**: Added 'destination' field as alias for 'original transmission reference'
|
|
24
|
+
|
|
25
|
+
### Improvements
|
|
26
|
+
- **Issue #15**: Enhanced IPTC tag collection with better field mappings
|
|
27
|
+
- **Issue #38**: Verified backup file behavior (use `options={'overwrite': True}` to avoid ~ files)
|
|
28
|
+
- Better error handling and logging throughout
|
|
29
|
+
|
|
30
|
+
### Build System Modernization
|
|
31
|
+
- Migrated from legacy `setup.py` to modern `pyproject.toml`
|
|
32
|
+
- Now uses `uv` and `hatchling` for building (PEP 517/518 compliant)
|
|
33
|
+
- Simplified setup.py to minimal backward compatibility shim
|
|
34
|
+
- Added comprehensive publishing guide (`PUBLISHING.md`)
|
|
35
|
+
- Supports Python 3.8 through 3.13
|
|
36
|
+
|
|
37
|
+
### Notes
|
|
38
|
+
- **Issue #39, #41**: Ready for PyPI release with all fixes from master branch
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
Updating builds to target 3.9.7
|
|
43
|
+
|
|
44
|
+
2.1: Fixes merged to save modified IPTC info images
|
|
45
|
+
|
|
46
|
+
1.9.5-8: https://bitbucket.org/gthomas/iptcinfo/issue/4/file-permissions-for-changed-files-are-not - copy original file's permission bits on save/saveAs
|
|
47
|
+
|
|
48
|
+
1.9.5-7: https://bitbucket.org/gthomas/iptcinfo/issue/3/images-w-o-iptc-data-should-not-log-errors - have silencable parse errors.
|
|
49
|
+
|
|
50
|
+
1.9.5-6: to have a nice new upload (seems easy_install grabs an old version).
|
|
51
|
+
|
|
52
|
+
1.9.5-5: fix some issues with "super"
|
|
53
|
+
|
|
54
|
+
1.9.5-3: use logging module.
|
|
55
|
+
|
|
56
|
+
1.9.5-2: Emil Stenström pinpointed some bugs/misleading (un)comments
|
|
57
|
+
Also a new (mis)feature is implemented: if you don't specify inp_charset
|
|
58
|
+
(and the image misses such information, too) than no conversion is made
|
|
59
|
+
to unicode, everything stays bytestring!
|
|
60
|
+
This way you don't need to deal with charsets, BUT it is your risk to make
|
|
61
|
+
the modifications with the SAME charset as it is in the image!
|
|
62
|
+
|
|
63
|
+
1.9.5-1: getting in sync with the Perl version 1.9.5
|
|
64
|
+
|
|
65
|
+
1.9.2-rc8:
|
|
66
|
+
charset recognition loosened (failed with some image out of
|
|
67
|
+
Adobe Lightroom).
|
|
68
|
+
|
|
69
|
+
1.9.2-rc7: NOT READY
|
|
70
|
+
IPTCInfo now accepts 'inp_charset' keyword for setting input charset.
|
|
71
|
+
|
|
72
|
+
1.9.2-rc6: just PyLint-ed out some errors.
|
|
73
|
+
|
|
74
|
+
1.9.2-rc5: Amos Latteier sent me a patch which releases the requirement of the
|
|
75
|
+
file objects to be file objects (he uses this on jpeg files stored in
|
|
76
|
+
databases as strings).
|
|
77
|
+
It modifies the module in order to look for a read method on the file
|
|
78
|
+
object. If one exists it assumes the argument is a file object, otherwise it
|
|
79
|
+
assumes it's a filename.
|
|
80
|
+
|
|
81
|
+
1.9.2-rc4: on Windows systems, tmpfile may not work correctly - now I use
|
|
82
|
+
cStringIO on file save (to save the file without truncating it on Exception).
|
|
83
|
+
|
|
84
|
+
1.9.2-rc3: some little bug fixes, some safety enhancements (now iptcinfo.py
|
|
85
|
+
will overwrite the original image file (info.save()) only if everything goes
|
|
86
|
+
fine (so if an exception is thrown at writing, it won't cut your original
|
|
87
|
+
file).
|
|
88
|
+
|
|
89
|
+
This is a pre-release version: needs some testing, and has an unfound bug
|
|
90
|
+
(yet): some pictures can be enhanced with iptc data, and iptcinfo.py is able
|
|
91
|
+
to read them, but some other iptc data readers will spit on it.
|
|
92
|
+
|
|
93
|
+
1.9.1: a first release with some little encoding support
|
|
94
|
+
|
|
95
|
+
The class IPTCInfo now has an inp_charset and an out_charset attribute - the
|
|
96
|
+
first is the read image's charset (defaults to the system default charset),
|
|
97
|
+
the second is the charset the writer will use (defaults to inp_charset).
|
|
98
|
+
|
|
99
|
+
Reader will find the charset included in IPTC data (if any, defaults to the
|
|
100
|
+
system's default charset), and use it to read to unicode strings. Writer will
|
|
101
|
+
write using IPTCinfo.out_charset (if it is not set, will not write charset
|
|
102
|
+
IPTC record).
|
|
103
|
+
|
|
104
|
+
With this, it is possible to read and write i18n strings correctly.
|
|
105
|
+
|
|
106
|
+
I haven't tested this functionality thoroughly, and that little test was only
|
|
107
|
+
on my WinXP box only, with the only other IPTC reader: IrfanView.
|
iptcinfo3-2.3.0/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
IPTCInfo3 - IPTC metadata extraction and modification for Python
|
|
2
|
+
|
|
3
|
+
Copyright (C) 2000-2004 Josh Carter <josh@multipart-mixed.com>
|
|
4
|
+
Copyright (C) 2004-2008 Tamas Gulacsi <gthomas@gthomas.hu>
|
|
5
|
+
Copyright (C) 2008-2025 James Campbell
|
|
6
|
+
|
|
7
|
+
DUAL LICENSE
|
|
8
|
+
|
|
9
|
+
This software is dual-licensed. You may choose either:
|
|
10
|
+
|
|
11
|
+
1. The Artistic License 1.0
|
|
12
|
+
https://opensource.org/licenses/Artistic-1.0
|
|
13
|
+
|
|
14
|
+
2. The GNU General Public License v1.0 or later
|
|
15
|
+
https://www.gnu.org/licenses/old-licenses/gpl-1.0.en.html
|
|
16
|
+
|
|
17
|
+
You may use, modify, and distribute this software under the terms of
|
|
18
|
+
either license, at your option.
|
|
19
|
+
|
|
20
|
+
Full license texts available at the URLs above.
|
iptcinfo3-2.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: IPTCInfo3
|
|
3
|
+
Version: 2.3.0
|
|
4
|
+
Summary: Extract and modify IPTC metadata from JPEG images
|
|
5
|
+
Project-URL: Homepage, https://github.com/jamesacampbell/iptcinfo3
|
|
6
|
+
Project-URL: Repository, https://github.com/jamesacampbell/iptcinfo3
|
|
7
|
+
Project-URL: Issues, https://github.com/jamesacampbell/iptcinfo3/issues
|
|
8
|
+
Author-email: Tamas Gulacsi <gthomas@fw.hu>
|
|
9
|
+
Maintainer-email: James Campbell <jc@normail.co>
|
|
10
|
+
License: Artistic-1.0 OR GPL-1.0-or-later
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: exif,iptc,jpeg,metadata,photography
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: Artistic License
|
|
16
|
+
Classifier: License :: OSI Approved :: GNU General Public License (GPL)
|
|
17
|
+
Classifier: Programming Language :: Python
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
26
|
+
Classifier: Topic :: Utilities
|
|
27
|
+
Requires-Python: >=3.8
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest-watch>=4.2; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
32
|
+
Description-Content-Type: text/x-rst
|
|
33
|
+
|
|
34
|
+
``IPTCINFO 3``
|
|
35
|
+
==============
|
|
36
|
+
|
|
37
|
+
Like IPTCInfo but finally compatible for Python 3
|
|
38
|
+
-------------------------------------------------
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
Ported from Josh Carter's Perl IPTCInfo-1.9.pm by Tamas Gulacsi
|
|
42
|
+
|
|
43
|
+
Ever wish you add information to your photos like a caption, the place
|
|
44
|
+
you took it, the date, and perhaps even keywords and categories? You
|
|
45
|
+
already can. The International Press Telecommunications Council (IPTC)
|
|
46
|
+
defines a format for exchanging meta-information in news content, and
|
|
47
|
+
that includes photographs. You can embed all kinds of information in
|
|
48
|
+
your images. The trick is putting it to use.
|
|
49
|
+
|
|
50
|
+
That's where this IPTCInfo Python module comes into play. You can embed
|
|
51
|
+
information using many programs, including Adobe Photoshop, and
|
|
52
|
+
IPTCInfo will let your web server -- and other automated server
|
|
53
|
+
programs -- pull it back out. You can use the information directly in
|
|
54
|
+
Python programs, export it to XML, or even export SQL statements ready
|
|
55
|
+
to be fed into a database.
|
|
56
|
+
|
|
57
|
+
Installation
|
|
58
|
+
------------
|
|
59
|
+
|
|
60
|
+
Install from PyPI::
|
|
61
|
+
|
|
62
|
+
pip install IPTCInfo3
|
|
63
|
+
|
|
64
|
+
Or with uv::
|
|
65
|
+
|
|
66
|
+
uv pip install IPTCInfo3
|
|
67
|
+
|
|
68
|
+
Requirements
|
|
69
|
+
------------
|
|
70
|
+
|
|
71
|
+
- Python 3.8 or higher
|
|
72
|
+
- No external dependencies required
|
|
73
|
+
|
|
74
|
+
Usage
|
|
75
|
+
-----
|
|
76
|
+
|
|
77
|
+
``from iptcinfo3 import IPTCInfo``
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
Create new info object
|
|
81
|
+
``info = IPTCInfo('doge.jpg')``
|
|
82
|
+
|
|
83
|
+
Print list of keywords, supplemental categories, contacts
|
|
84
|
+
``print(info['keywords'])``
|
|
85
|
+
``print(info['supplementalCategories'])``
|
|
86
|
+
``print(info['contacts'])``
|
|
87
|
+
|
|
88
|
+
Get specific attributes...
|
|
89
|
+
``caption = info['caption/abstract']``
|
|
90
|
+
|
|
91
|
+
Create object for file that may not have IPTC data
|
|
92
|
+
``info = IPTCInfo('such_iptc.jpg', force=True)``
|
|
93
|
+
|
|
94
|
+
Add/change an attribute
|
|
95
|
+
``info['caption/abstract'] = 'Witty caption here'``
|
|
96
|
+
``info['supplemental category'] = ['portrait']``
|
|
97
|
+
|
|
98
|
+
Lists for keywords, so you can just append!
|
|
99
|
+
``info['keywords']).append('cool')``
|
|
100
|
+
|
|
101
|
+
Save new info to file
|
|
102
|
+
``info.save()``
|
|
103
|
+
``info.save_as('very_meta.jpg')``
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
``IPTCINFO 3``
|
|
2
2
|
==============
|
|
3
3
|
|
|
4
|
-
`Build Status <https://travis-ci.org/crccheck/iptcinfo3.svg?branch=master>`_
|
|
5
|
-
|
|
6
4
|
Like IPTCInfo but finally compatible for Python 3
|
|
7
5
|
-------------------------------------------------
|
|
8
6
|
|
|
@@ -23,6 +21,23 @@ programs -- pull it back out. You can use the information directly in
|
|
|
23
21
|
Python programs, export it to XML, or even export SQL statements ready
|
|
24
22
|
to be fed into a database.
|
|
25
23
|
|
|
24
|
+
Installation
|
|
25
|
+
------------
|
|
26
|
+
|
|
27
|
+
Install from PyPI::
|
|
28
|
+
|
|
29
|
+
pip install IPTCInfo3
|
|
30
|
+
|
|
31
|
+
Or with uv::
|
|
32
|
+
|
|
33
|
+
uv pip install IPTCInfo3
|
|
34
|
+
|
|
35
|
+
Requirements
|
|
36
|
+
------------
|
|
37
|
+
|
|
38
|
+
- Python 3.8 or higher
|
|
39
|
+
- No external dependencies required
|
|
40
|
+
|
|
26
41
|
Usage
|
|
27
42
|
-----
|
|
28
43
|
|
|
@@ -47,6 +62,9 @@ Add/change an attribute
|
|
|
47
62
|
``info['caption/abstract'] = 'Witty caption here'``
|
|
48
63
|
``info['supplemental category'] = ['portrait']``
|
|
49
64
|
|
|
65
|
+
Lists for keywords, so you can just append!
|
|
66
|
+
``info['keywords']).append('cool')``
|
|
67
|
+
|
|
50
68
|
Save new info to file
|
|
51
69
|
``info.save()``
|
|
52
70
|
``info.save_as('very_meta.jpg')``
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
# All rights reserved.
|
|
12
12
|
#
|
|
13
13
|
# This program is free software; you can redistribute it and/or modify
|
|
14
|
-
# it under the
|
|
14
|
+
# it under the terms of the Artistic License or the GNU General Public
|
|
15
|
+
# License (GPL). You may choose either license.
|
|
15
16
|
#
|
|
16
|
-
# VERSION = '1.9';
|
|
17
17
|
"""
|
|
18
18
|
IPTCInfo - Python module for extracting and modifying IPTC image meta-data
|
|
19
19
|
"""
|
|
@@ -24,8 +24,9 @@ import shutil
|
|
|
24
24
|
import sys
|
|
25
25
|
import tempfile
|
|
26
26
|
from struct import pack, unpack
|
|
27
|
+
import json
|
|
27
28
|
|
|
28
|
-
__version__ = '2.
|
|
29
|
+
__version__ = '2.3.0'
|
|
29
30
|
__author__ = 'Gulácsi, Tamás'
|
|
30
31
|
__updated_by__ = 'Campbell, James'
|
|
31
32
|
|
|
@@ -177,7 +178,7 @@ def jpeg_get_variable_length(fh):
|
|
|
177
178
|
|
|
178
179
|
# Length includes itself, so must be at least 2
|
|
179
180
|
if length < 2:
|
|
180
|
-
logger.
|
|
181
|
+
logger.warning("jpeg_get_variable_length: erroneous JPEG marker length")
|
|
181
182
|
return 0
|
|
182
183
|
return length - 2
|
|
183
184
|
|
|
@@ -192,7 +193,7 @@ def jpeg_next_marker(fh):
|
|
|
192
193
|
try:
|
|
193
194
|
byte = read_exactly(fh, 1)
|
|
194
195
|
while ord3(byte) != 0xff:
|
|
195
|
-
# logger.
|
|
196
|
+
# logger.warning("jpeg_next_marker: bogus stuff in Jpeg file at: ')
|
|
196
197
|
byte = read_exactly(fh, 1)
|
|
197
198
|
|
|
198
199
|
# Now skip any extra 0xffs, which are valid padding.
|
|
@@ -360,7 +361,7 @@ def jpeg_debug_scan(filename): # pragma: no cover
|
|
|
360
361
|
break
|
|
361
362
|
|
|
362
363
|
if ord3(marker) == 0:
|
|
363
|
-
logger.
|
|
364
|
+
logger.warning("Marker scan failed")
|
|
364
365
|
break
|
|
365
366
|
|
|
366
367
|
elif ord3(marker) == 0xd9:
|
|
@@ -368,7 +369,7 @@ def jpeg_debug_scan(filename): # pragma: no cover
|
|
|
368
369
|
break
|
|
369
370
|
|
|
370
371
|
if not jpeg_skip_variable(fh):
|
|
371
|
-
logger.
|
|
372
|
+
logger.warning("jpeg_skip_variable failed")
|
|
372
373
|
return None
|
|
373
374
|
|
|
374
375
|
|
|
@@ -480,7 +481,7 @@ c_datasets = {
|
|
|
480
481
|
101: 'country/primary location name',
|
|
481
482
|
103: 'original transmission reference',
|
|
482
483
|
105: 'headline',
|
|
483
|
-
110: 'credit',
|
|
484
|
+
110: 'credit line', # Updated from 'credit' to 'credit line' per IPTC Core 1.1
|
|
484
485
|
115: 'source',
|
|
485
486
|
116: 'copyright notice',
|
|
486
487
|
118: 'contact',
|
|
@@ -522,6 +523,17 @@ c_charset = {100: 'iso8859_1', 101: 'iso8859_2', 109: 'iso8859_3',
|
|
|
522
523
|
c_charset_r = {v: k for k, v in c_charset.items()}
|
|
523
524
|
|
|
524
525
|
|
|
526
|
+
class UniqueList(list):
|
|
527
|
+
"""A list that prevents duplicate values on append/extend."""
|
|
528
|
+
def append(self, value):
|
|
529
|
+
if value not in self:
|
|
530
|
+
super().append(value)
|
|
531
|
+
|
|
532
|
+
def extend(self, values):
|
|
533
|
+
for v in values:
|
|
534
|
+
self.append(v)
|
|
535
|
+
|
|
536
|
+
|
|
525
537
|
class IPTCData(dict):
|
|
526
538
|
"""Dict with int/string keys from c_listdatanames"""
|
|
527
539
|
def __init__(self, diction={}, *args, **kwds):
|
|
@@ -536,6 +548,12 @@ class IPTCData(dict):
|
|
|
536
548
|
return key
|
|
537
549
|
elif isinstance(key, str) and key.lower() in c_datasets_r:
|
|
538
550
|
return c_datasets_r[key.lower()]
|
|
551
|
+
# Backward compatibility: 'credit' is now 'credit line' per IPTC Core 1.1
|
|
552
|
+
elif isinstance(key, str) and key.lower() == 'credit':
|
|
553
|
+
return 110
|
|
554
|
+
# Alias for compatibility with gThumb/exiftool
|
|
555
|
+
elif isinstance(key, str) and key.lower() == 'destination':
|
|
556
|
+
return 103 # Maps to 'original transmission reference'
|
|
539
557
|
elif key.startswith(cls.c_cust_pre) and key[len(cls.c_cust_pre):].isdigit():
|
|
540
558
|
# example: nonstandard_69 -> 69
|
|
541
559
|
return int(key[len(cls.c_cust_pre):])
|
|
@@ -553,6 +571,13 @@ class IPTCData(dict):
|
|
|
553
571
|
else:
|
|
554
572
|
raise KeyError("Key %s is not in %s!" % (key, list(c_datasets.keys())))
|
|
555
573
|
|
|
574
|
+
def __contains__(self, name):
|
|
575
|
+
try:
|
|
576
|
+
key = self._key_as_int(name)
|
|
577
|
+
except KeyError:
|
|
578
|
+
return False
|
|
579
|
+
return super().__contains__(key)
|
|
580
|
+
|
|
556
581
|
def __getitem__(self, name):
|
|
557
582
|
return self.get(self._key_as_int(name), None)
|
|
558
583
|
|
|
@@ -593,11 +618,12 @@ class IPTCInfo:
|
|
|
593
618
|
|
|
594
619
|
def __init__(self, fobj, force=False, inp_charset=None, out_charset=None):
|
|
595
620
|
self._data = IPTCData({
|
|
596
|
-
'supplemental category':
|
|
597
|
-
'keywords':
|
|
598
|
-
'contact':
|
|
621
|
+
'supplemental category': UniqueList(),
|
|
622
|
+
'keywords': UniqueList(),
|
|
623
|
+
'contact': UniqueList(),
|
|
599
624
|
})
|
|
600
625
|
self._fobj = fobj
|
|
626
|
+
self._force = force
|
|
601
627
|
if duck_typed(fobj, 'read'): # DELETEME
|
|
602
628
|
self._filename = None
|
|
603
629
|
else:
|
|
@@ -613,7 +639,7 @@ class IPTCInfo:
|
|
|
613
639
|
if datafound:
|
|
614
640
|
self.collectIIMInfo(fh)
|
|
615
641
|
else:
|
|
616
|
-
logger.
|
|
642
|
+
logger.warning('No IPTC data found in %s', fobj)
|
|
617
643
|
|
|
618
644
|
def _filepos(self, fh):
|
|
619
645
|
"""For debugging, return what position in the file we are."""
|
|
@@ -630,7 +656,7 @@ class IPTCInfo:
|
|
|
630
656
|
"""Saves Jpeg with IPTC data to a given file name."""
|
|
631
657
|
with smart_open(self._fobj, 'rb') as fh:
|
|
632
658
|
if not file_is_jpeg(fh):
|
|
633
|
-
logger.error('Source file %s is not a Jpeg.' % self.
|
|
659
|
+
logger.error('Source file %s is not a Jpeg.' % self._fobj)
|
|
634
660
|
return None
|
|
635
661
|
|
|
636
662
|
jpeg_parts = jpeg_collect_file_parts(fh)
|
|
@@ -686,8 +712,10 @@ class IPTCInfo:
|
|
|
686
712
|
os.unlink(tmpfn)
|
|
687
713
|
else:
|
|
688
714
|
tmpfh.close()
|
|
689
|
-
if os.path.exists(newfile):
|
|
690
|
-
|
|
715
|
+
if os.path.exists(newfile) and options is not None and 'overwrite' in options:
|
|
716
|
+
os.unlink(newfile)
|
|
717
|
+
elif os.path.exists(newfile):
|
|
718
|
+
shutil.move(newfile, "{file}~".format(file=newfile))
|
|
691
719
|
shutil.move(tmpfn, newfile)
|
|
692
720
|
return True
|
|
693
721
|
|
|
@@ -699,6 +727,9 @@ class IPTCInfo:
|
|
|
699
727
|
def __len__(self):
|
|
700
728
|
return len(self._data)
|
|
701
729
|
|
|
730
|
+
def __contains__(self, key):
|
|
731
|
+
return key in self._data
|
|
732
|
+
|
|
702
733
|
def __getitem__(self, key):
|
|
703
734
|
return self._data[key]
|
|
704
735
|
|
|
@@ -716,7 +747,7 @@ class IPTCInfo:
|
|
|
716
747
|
logger.info("File is JPEG, proceeding with JpegScan")
|
|
717
748
|
return self.jpegScan(fh)
|
|
718
749
|
else:
|
|
719
|
-
logger.
|
|
750
|
+
logger.warning("File not a JPEG, trying blindScan")
|
|
720
751
|
return self.blindScan(fh)
|
|
721
752
|
|
|
722
753
|
c_marker_err = {0: "Marker scan failed",
|
|
@@ -752,14 +783,18 @@ class IPTCInfo:
|
|
|
752
783
|
err = "jpeg_skip_variable failed"
|
|
753
784
|
if err is not None:
|
|
754
785
|
self.error = err
|
|
755
|
-
|
|
786
|
+
# When force=True, log as INFO instead of WARNING since we expect no IPTC data
|
|
787
|
+
if self._force:
|
|
788
|
+
logger.info(err)
|
|
789
|
+
else:
|
|
790
|
+
logger.warning(err)
|
|
756
791
|
return None
|
|
757
792
|
|
|
758
793
|
# If were's here, we must have found the right marker.
|
|
759
794
|
# Now blindScan through the data.
|
|
760
795
|
return self.blindScan(fh, MAX=jpeg_get_variable_length(fh))
|
|
761
796
|
|
|
762
|
-
def blindScan(self, fh, MAX=
|
|
797
|
+
def blindScan(self, fh, MAX=819200):
|
|
763
798
|
"""Scans blindly to first IIM Record 2 tag in the file. This
|
|
764
799
|
method may or may not work on any arbitrary file type, but it
|
|
765
800
|
doesn't hurt to check. We expect to see this tag within the first
|
|
@@ -767,7 +802,7 @@ class IPTCInfo:
|
|
|
767
802
|
depending on how other programs choose to store IIM.)"""
|
|
768
803
|
|
|
769
804
|
offset = 0
|
|
770
|
-
# keep within first
|
|
805
|
+
# keep within first 819200 bytes
|
|
771
806
|
# NOTE: this may need to change
|
|
772
807
|
logger.debug('blindScan: starting scan, max length %d', MAX)
|
|
773
808
|
|
|
@@ -776,7 +811,7 @@ class IPTCInfo:
|
|
|
776
811
|
try:
|
|
777
812
|
temp = read_exactly(fh, 1)
|
|
778
813
|
except EOFException:
|
|
779
|
-
logger.
|
|
814
|
+
logger.warning("BlindScan: hit EOF while scanning")
|
|
780
815
|
return None
|
|
781
816
|
# look for tag identifier 0x1c
|
|
782
817
|
if ord3(temp) == 0x1c:
|
|
@@ -787,15 +822,32 @@ class IPTCInfo:
|
|
|
787
822
|
# found character set's record!
|
|
788
823
|
try:
|
|
789
824
|
temp = read_exactly(fh, jpeg_get_variable_length(fh))
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
825
|
+
cs = None
|
|
826
|
+
# Check for ISO 2022 escape sequence (starts with ESC 0x1b)
|
|
827
|
+
if len(temp) >= 3 and ord3(temp[0]) == 0x1b:
|
|
828
|
+
# Parse ISO 2022 escape sequences
|
|
829
|
+
# ESC % G = UTF-8
|
|
830
|
+
if temp == b'\x1b%G':
|
|
831
|
+
self.inp_charset = 'utf_8'
|
|
832
|
+
# ESC % / @ = UTF-16 (not commonly used)
|
|
833
|
+
elif temp == b'\x1b%/@':
|
|
834
|
+
self.inp_charset = 'utf_16'
|
|
835
|
+
else:
|
|
836
|
+
logger.debug(
|
|
837
|
+
"BlindScan: unknown ISO 2022 charset escape sequence %r",
|
|
838
|
+
temp)
|
|
839
|
+
else:
|
|
840
|
+
# Try legacy numeric charset encoding
|
|
841
|
+
try:
|
|
842
|
+
cs = unpack('!H', temp)[0]
|
|
843
|
+
if cs in c_charset:
|
|
844
|
+
self.inp_charset = c_charset[cs]
|
|
845
|
+
except Exception:
|
|
846
|
+
logger.debug('BlindScan: could not parse charset from %r', temp)
|
|
847
|
+
|
|
848
|
+
if self.inp_charset:
|
|
849
|
+
logger.info("BlindScan: found character set '%s' at offset %d",
|
|
850
|
+
self.inp_charset, offset)
|
|
799
851
|
except EOFException:
|
|
800
852
|
pass
|
|
801
853
|
|
|
@@ -845,7 +897,7 @@ class IPTCInfo:
|
|
|
845
897
|
try:
|
|
846
898
|
value = str(value, encoding=self.inp_charset, errors='strict')
|
|
847
899
|
except Exception: # TODO better exception
|
|
848
|
-
logger.
|
|
900
|
+
logger.warning('Data "%r" is not in encoding %s!', value, self.inp_charset)
|
|
849
901
|
value = str(value, encoding=self.inp_charset, errors='replace')
|
|
850
902
|
|
|
851
903
|
# try to extract first into _listdata (keywords, categories)
|
|
@@ -889,11 +941,22 @@ class IPTCInfo:
|
|
|
889
941
|
LOGDBG.debug('out=%s', hex_dump(out))
|
|
890
942
|
# Iterate over data sets
|
|
891
943
|
for dataset, value in self._data.items():
|
|
892
|
-
|
|
944
|
+
# Skip None, empty strings, empty lists, and NaN values
|
|
945
|
+
if value is None:
|
|
946
|
+
continue
|
|
947
|
+
# Handle float/int that might be NaN
|
|
948
|
+
if isinstance(value, (float, int)):
|
|
949
|
+
import math
|
|
950
|
+
if isinstance(value, float) and math.isnan(value):
|
|
951
|
+
continue
|
|
952
|
+
# Convert numeric values to strings
|
|
953
|
+
value = str(value)
|
|
954
|
+
# Check length for strings and lists
|
|
955
|
+
if hasattr(value, '__len__') and len(value) == 0:
|
|
893
956
|
continue
|
|
894
957
|
|
|
895
958
|
if not (isinstance(dataset, int) and dataset in c_datasets):
|
|
896
|
-
logger.
|
|
959
|
+
logger.warning("packedIIMData: illegal dataname '%s' (%d)", dataset, dataset)
|
|
897
960
|
continue
|
|
898
961
|
|
|
899
962
|
logger.debug('packedIIMData %02X: %r -> %r', dataset, value, self._enc(value))
|
|
@@ -903,10 +966,11 @@ class IPTCInfo:
|
|
|
903
966
|
out.append(pack("!BBBH", tag, record, dataset, len(value)))
|
|
904
967
|
out.append(value)
|
|
905
968
|
else:
|
|
969
|
+
seen = set()
|
|
906
970
|
for v in map(bytes, value):
|
|
907
|
-
if v is None or len(v) == 0:
|
|
971
|
+
if v is None or len(v) == 0 or v in seen:
|
|
908
972
|
continue
|
|
909
|
-
|
|
973
|
+
seen.add(v)
|
|
910
974
|
out.append(pack("!BBBH", tag, record, dataset, len(v)))
|
|
911
975
|
out.append(v)
|
|
912
976
|
|
|
@@ -944,7 +1008,16 @@ class IPTCInfo:
|
|
|
944
1008
|
|
|
945
1009
|
|
|
946
1010
|
if __name__ == '__main__': # pragma: no cover
|
|
947
|
-
logging.basicConfig(level=logging.
|
|
1011
|
+
logging.basicConfig(level=logging.ERROR)
|
|
948
1012
|
if len(sys.argv) > 1:
|
|
949
1013
|
info = IPTCInfo(sys.argv[1])
|
|
950
|
-
|
|
1014
|
+
if info.__dict__ != '':
|
|
1015
|
+
for k, v in info.__dict__.items():
|
|
1016
|
+
if k == '_data':
|
|
1017
|
+
print(k)
|
|
1018
|
+
for key, value in v.items():
|
|
1019
|
+
if type(value) == list:
|
|
1020
|
+
print(key, [x.decode() for x in value])
|
|
1021
|
+
[print(x.decode()) for x in value]
|
|
1022
|
+
print(key, value)
|
|
1023
|
+
print(k, v)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "IPTCInfo3"
|
|
7
|
+
version = "2.3.0"
|
|
8
|
+
description = "Extract and modify IPTC metadata from JPEG images"
|
|
9
|
+
readme = "README.rst"
|
|
10
|
+
license = { text = "Artistic-1.0 OR GPL-1.0-or-later" }
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Tamas Gulacsi", email = "gthomas@fw.hu" },
|
|
13
|
+
]
|
|
14
|
+
maintainers = [
|
|
15
|
+
{ name = "James Campbell", email = "jc@normail.co" },
|
|
16
|
+
]
|
|
17
|
+
keywords = ["iptc", "metadata", "jpeg", "exif", "photography"]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 4 - Beta",
|
|
20
|
+
"License :: OSI Approved :: Artistic License",
|
|
21
|
+
"License :: OSI Approved :: GNU General Public License (GPL)",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"Programming Language :: Python",
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"Programming Language :: Python :: 3.8",
|
|
26
|
+
"Programming Language :: Python :: 3.9",
|
|
27
|
+
"Programming Language :: Python :: 3.10",
|
|
28
|
+
"Programming Language :: Python :: 3.11",
|
|
29
|
+
"Programming Language :: Python :: 3.12",
|
|
30
|
+
"Programming Language :: Python :: 3.13",
|
|
31
|
+
"Topic :: Multimedia :: Graphics",
|
|
32
|
+
"Topic :: Utilities",
|
|
33
|
+
]
|
|
34
|
+
requires-python = ">=3.8"
|
|
35
|
+
dependencies = []
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://github.com/jamesacampbell/iptcinfo3"
|
|
39
|
+
Repository = "https://github.com/jamesacampbell/iptcinfo3"
|
|
40
|
+
Issues = "https://github.com/jamesacampbell/iptcinfo3/issues"
|
|
41
|
+
|
|
42
|
+
[project.optional-dependencies]
|
|
43
|
+
dev = [
|
|
44
|
+
"pytest>=7.0",
|
|
45
|
+
"pytest-cov>=4.0",
|
|
46
|
+
"pytest-watch>=4.2",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
[tool.hatch.build.targets.sdist]
|
|
50
|
+
include = [
|
|
51
|
+
"iptcinfo3.py",
|
|
52
|
+
"README.rst",
|
|
53
|
+
"CHANGELOG.md",
|
|
54
|
+
"LICENSE",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
[tool.hatch.build.targets.wheel]
|
|
58
|
+
packages = ["."]
|
|
59
|
+
only-include = ["iptcinfo3.py"]
|
|
60
|
+
|
|
61
|
+
[tool.pytest.ini_options]
|
|
62
|
+
testpaths = ["tests"]
|
|
63
|
+
python_files = ["test_*.py", "*_test.py"]
|
|
64
|
+
|
|
65
|
+
[tool.coverage.run]
|
|
66
|
+
branch = true
|
|
67
|
+
source = ["."]
|
|
68
|
+
|
|
69
|
+
[tool.coverage.report]
|
|
70
|
+
show_missing = true
|
|
71
|
+
skip_covered = true
|
|
72
|
+
|
|
73
|
+
[tool.ruff]
|
|
74
|
+
line-length = 100
|
|
75
|
+
target-version = "py38"
|
|
76
|
+
|
|
77
|
+
[tool.ruff.lint]
|
|
78
|
+
select = ["E", "F", "W", "I", "N", "UP"]
|
|
79
|
+
ignore = ["E501"] # Line length handled by formatter
|
|
80
|
+
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 1.2
|
|
2
|
-
Name: IPTCInfo3
|
|
3
|
-
Version: 2.1.4
|
|
4
|
-
Summary: A great way to get IPTCInfo
|
|
5
|
-
Home-page: https://github.com/jamesacampbell/iptcinfo3
|
|
6
|
-
Author: Tamas Gulacsi
|
|
7
|
-
Author-email: gthomas@fw.hu
|
|
8
|
-
Maintainer: James Campbell
|
|
9
|
-
Maintainer-email: jc@normail.co
|
|
10
|
-
License: http://www.opensource.org/licenses/gpl-license.php
|
|
11
|
-
Download-URL: https://github.com/jamesacampbell/iptcinfo3
|
|
12
|
-
Description: ``IPTCINFO 3``
|
|
13
|
-
==============
|
|
14
|
-
|
|
15
|
-
`Build Status <https://travis-ci.org/crccheck/iptcinfo3.svg?branch=master>`_
|
|
16
|
-
|
|
17
|
-
Like IPTCInfo but finally compatible for Python 3
|
|
18
|
-
-------------------------------------------------
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
Ported from Josh Carter's Perl IPTCInfo-1.9.pm by Tamas Gulacsi
|
|
22
|
-
|
|
23
|
-
Ever wish you add information to your photos like a caption, the place
|
|
24
|
-
you took it, the date, and perhaps even keywords and categories? You
|
|
25
|
-
already can. The International Press Telecommunications Council (IPTC)
|
|
26
|
-
defines a format for exchanging meta-information in news content, and
|
|
27
|
-
that includes photographs. You can embed all kinds of information in
|
|
28
|
-
your images. The trick is putting it to use.
|
|
29
|
-
|
|
30
|
-
That's where this IPTCInfo Python module comes into play. You can embed
|
|
31
|
-
information using many programs, including Adobe Photoshop, and
|
|
32
|
-
IPTCInfo will let your web server -- and other automated server
|
|
33
|
-
programs -- pull it back out. You can use the information directly in
|
|
34
|
-
Python programs, export it to XML, or even export SQL statements ready
|
|
35
|
-
to be fed into a database.
|
|
36
|
-
|
|
37
|
-
Usage
|
|
38
|
-
-----
|
|
39
|
-
|
|
40
|
-
``from iptcinfo3 import IPTCInfo``
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
Create new info object
|
|
44
|
-
``info = IPTCInfo('doge.jpg')``
|
|
45
|
-
|
|
46
|
-
Print list of keywords, supplemental categories, contacts
|
|
47
|
-
``print(info['keywords'])``
|
|
48
|
-
``print(info['supplementalCategories'])``
|
|
49
|
-
``print(info['contacts'])``
|
|
50
|
-
|
|
51
|
-
Get specific attributes...
|
|
52
|
-
``caption = info['caption/abstract']``
|
|
53
|
-
|
|
54
|
-
Create object for file that may not have IPTC data
|
|
55
|
-
``info = IPTCInfo('such_iptc.jpg', force=True)``
|
|
56
|
-
|
|
57
|
-
Add/change an attribute
|
|
58
|
-
``info['caption/abstract'] = 'Witty caption here'``
|
|
59
|
-
``info['supplemental category'] = ['portrait']``
|
|
60
|
-
|
|
61
|
-
Save new info to file
|
|
62
|
-
``info.save()``
|
|
63
|
-
``info.save_as('very_meta.jpg')``
|
|
64
|
-
|
|
65
|
-
Platform: any
|
|
66
|
-
Classifier: Development Status :: 3 - Alpha
|
|
67
|
-
Classifier: License :: OSI Approved :: Artistic License
|
|
68
|
-
Classifier: License :: OSI Approved :: GNU General Public License (GPL)
|
|
69
|
-
Classifier: Intended Audience :: Developers
|
|
70
|
-
Classifier: Programming Language :: Python
|
|
71
|
-
Classifier: Topic :: Multimedia :: Graphics
|
|
72
|
-
Classifier: Topic :: Utilities
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
iptcinfo3
|
IPTCInfo3-2.1.4/PKG-INFO
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 1.2
|
|
2
|
-
Name: IPTCInfo3
|
|
3
|
-
Version: 2.1.4
|
|
4
|
-
Summary: A great way to get IPTCInfo
|
|
5
|
-
Home-page: https://github.com/jamesacampbell/iptcinfo3
|
|
6
|
-
Author: Tamas Gulacsi
|
|
7
|
-
Author-email: gthomas@fw.hu
|
|
8
|
-
Maintainer: James Campbell
|
|
9
|
-
Maintainer-email: jc@normail.co
|
|
10
|
-
License: http://www.opensource.org/licenses/gpl-license.php
|
|
11
|
-
Download-URL: https://github.com/jamesacampbell/iptcinfo3
|
|
12
|
-
Description: ``IPTCINFO 3``
|
|
13
|
-
==============
|
|
14
|
-
|
|
15
|
-
`Build Status <https://travis-ci.org/crccheck/iptcinfo3.svg?branch=master>`_
|
|
16
|
-
|
|
17
|
-
Like IPTCInfo but finally compatible for Python 3
|
|
18
|
-
-------------------------------------------------
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
Ported from Josh Carter's Perl IPTCInfo-1.9.pm by Tamas Gulacsi
|
|
22
|
-
|
|
23
|
-
Ever wish you add information to your photos like a caption, the place
|
|
24
|
-
you took it, the date, and perhaps even keywords and categories? You
|
|
25
|
-
already can. The International Press Telecommunications Council (IPTC)
|
|
26
|
-
defines a format for exchanging meta-information in news content, and
|
|
27
|
-
that includes photographs. You can embed all kinds of information in
|
|
28
|
-
your images. The trick is putting it to use.
|
|
29
|
-
|
|
30
|
-
That's where this IPTCInfo Python module comes into play. You can embed
|
|
31
|
-
information using many programs, including Adobe Photoshop, and
|
|
32
|
-
IPTCInfo will let your web server -- and other automated server
|
|
33
|
-
programs -- pull it back out. You can use the information directly in
|
|
34
|
-
Python programs, export it to XML, or even export SQL statements ready
|
|
35
|
-
to be fed into a database.
|
|
36
|
-
|
|
37
|
-
Usage
|
|
38
|
-
-----
|
|
39
|
-
|
|
40
|
-
``from iptcinfo3 import IPTCInfo``
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
Create new info object
|
|
44
|
-
``info = IPTCInfo('doge.jpg')``
|
|
45
|
-
|
|
46
|
-
Print list of keywords, supplemental categories, contacts
|
|
47
|
-
``print(info['keywords'])``
|
|
48
|
-
``print(info['supplementalCategories'])``
|
|
49
|
-
``print(info['contacts'])``
|
|
50
|
-
|
|
51
|
-
Get specific attributes...
|
|
52
|
-
``caption = info['caption/abstract']``
|
|
53
|
-
|
|
54
|
-
Create object for file that may not have IPTC data
|
|
55
|
-
``info = IPTCInfo('such_iptc.jpg', force=True)``
|
|
56
|
-
|
|
57
|
-
Add/change an attribute
|
|
58
|
-
``info['caption/abstract'] = 'Witty caption here'``
|
|
59
|
-
``info['supplemental category'] = ['portrait']``
|
|
60
|
-
|
|
61
|
-
Save new info to file
|
|
62
|
-
``info.save()``
|
|
63
|
-
``info.save_as('very_meta.jpg')``
|
|
64
|
-
|
|
65
|
-
Platform: any
|
|
66
|
-
Classifier: Development Status :: 3 - Alpha
|
|
67
|
-
Classifier: License :: OSI Approved :: Artistic License
|
|
68
|
-
Classifier: License :: OSI Approved :: GNU General Public License (GPL)
|
|
69
|
-
Classifier: Intended Audience :: Developers
|
|
70
|
-
Classifier: Programming Language :: Python
|
|
71
|
-
Classifier: Topic :: Multimedia :: Graphics
|
|
72
|
-
Classifier: Topic :: Utilities
|
IPTCInfo3-2.1.4/setup.cfg
DELETED
IPTCInfo3-2.1.4/setup.py
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
"""SETUP THIS STUFF."""
|
|
2
|
-
import sys
|
|
3
|
-
from distutils.command.sdist import sdist as _sdist
|
|
4
|
-
from os import path
|
|
5
|
-
|
|
6
|
-
from setuptools import setup
|
|
7
|
-
classifiers = """
|
|
8
|
-
Development Status :: 3 - Alpha
|
|
9
|
-
License :: OSI Approved :: Artistic License
|
|
10
|
-
License :: OSI Approved :: GNU General Public License (GPL)
|
|
11
|
-
Intended Audience :: Developers
|
|
12
|
-
Programming Language :: Python
|
|
13
|
-
Topic :: Multimedia :: Graphics
|
|
14
|
-
Topic :: Utilities
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
this_directory = path.abspath(path.dirname(__file__))
|
|
18
|
-
with open(path.join(this_directory, 'README.rst')) as f:
|
|
19
|
-
long_description = f.read()
|
|
20
|
-
|
|
21
|
-
if sys.version_info < (2, 3):
|
|
22
|
-
_setup = setup
|
|
23
|
-
|
|
24
|
-
def setup(**kwargs):
|
|
25
|
-
if "classifiers" in kwargs:
|
|
26
|
-
del kwargs["classifiers"]
|
|
27
|
-
_setup(**kwargs)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class sdist(_sdist, object):
|
|
31
|
-
def run(self):
|
|
32
|
-
import os
|
|
33
|
-
res = _sdist.run(self)
|
|
34
|
-
print((self.get_archive_files()))
|
|
35
|
-
for fn in self.get_archive_files():
|
|
36
|
-
os.system('scp -p %s gtmainbox:/var/www/html/python/' % fn)
|
|
37
|
-
return res
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def openfile(fname):
|
|
41
|
-
import os
|
|
42
|
-
return open(os.path.join(os.path.dirname(__file__), fname))
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
version = next((row.split('=', 1)[-1].strip().strip("'").strip('"')
|
|
46
|
-
for row in open('iptcinfo3.py', 'r')
|
|
47
|
-
if row.startswith('__version__')))
|
|
48
|
-
setup( # cmdclass={'sdist': sdist},
|
|
49
|
-
name='IPTCInfo3',
|
|
50
|
-
version=version,
|
|
51
|
-
url='https://github.com/jamesacampbell/iptcinfo3',
|
|
52
|
-
download_url='https://github.com/jamesacampbell/iptcinfo3',
|
|
53
|
-
author='Tamas Gulacsi',
|
|
54
|
-
author_email='gthomas@fw.hu',
|
|
55
|
-
maintainer='James Campbell',
|
|
56
|
-
maintainer_email='jc@normail.co',
|
|
57
|
-
long_description=long_description,
|
|
58
|
-
license='http://www.opensource.org/licenses/gpl-license.php',
|
|
59
|
-
platforms=['any'],
|
|
60
|
-
description="""A great way to get IPTCInfo""",
|
|
61
|
-
classifiers=[_f for _f in classifiers.split('\n') if _f],
|
|
62
|
-
py_modules=['iptcinfo3'],
|
|
63
|
-
)
|