format-img 0.1.2__py3-none-any.whl

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.
format_img/__init__.py ADDED
@@ -0,0 +1 @@
1
+ # format_img package
@@ -0,0 +1,84 @@
1
+ """
2
+ 格式化图片名称
3
+ """
4
+ import sys
5
+ import getopt
6
+ import exifread
7
+ import time
8
+ import os
9
+ from .my_utility import get_imgs, get_suffixes, get_format, get_file_suffix
10
+
11
+ HELP = """
12
+ format-img
13
+ [-f format] default is '%Y%m%d%H%M%S'
14
+ [-s suffixs] default is 'jpg,png,jpeg,heic'
15
+ -h help
16
+ """
17
+
18
+
19
+ def main():
20
+ """主入口函数"""
21
+ fmt = ''
22
+ suffixes = []
23
+ try:
24
+ options, args = getopt.getopt(sys.argv[1:], 'hs:f:')
25
+ for option, value in options:
26
+ if option == '-f':
27
+ fmt = value
28
+ elif option == '-s':
29
+ suffixes = value.split(',')
30
+ elif option == '-h':
31
+ print(HELP)
32
+ sys.exit(0)
33
+ except Exception as e:
34
+ sys.stderr.write(f"Error parsing options: {e}\n")
35
+ sys.exit(1)
36
+
37
+ if not fmt:
38
+ fmt = get_format()
39
+ if not suffixes:
40
+ suffixes = get_suffixes()
41
+
42
+ ok_cnt = 0
43
+ ng_cnt = 0
44
+
45
+ for img in get_imgs(suffixes):
46
+ # 遍历出图片的EXIF时间或者文件创建时间并且按照格式重新命名文件
47
+ try:
48
+ with open(img, 'rb') as f_img:
49
+ tags = exifread.process_file(f_img, stop_tag='EXIF DateTimeOriginal')
50
+ except Exception as e:
51
+ sys.stderr.write(f"Failed to read EXIF from {img}: {e}\n")
52
+ ng_cnt += 1
53
+ continue
54
+
55
+ if 'EXIF DateTimeOriginal' in tags:
56
+ try:
57
+ img_time = time.strptime(str(tags['EXIF DateTimeOriginal']), "%Y:%m:%d %H:%M:%S")
58
+ except ValueError:
59
+ img_time = time.localtime(os.path.getmtime(img))
60
+ else:
61
+ img_time = time.localtime(os.path.getmtime(img))
62
+
63
+ # 重命名文件
64
+ i = 0
65
+ img_suffix = get_file_suffix(img)
66
+ img_new_name = time.strftime(fmt, img_time) + "." + img_suffix
67
+ while os.path.exists(img_new_name):
68
+ i += 1
69
+ img_new_name = time.strftime(fmt, img_time) + "_" + str(i) + "." + img_suffix
70
+ try:
71
+ os.rename(img, img_new_name)
72
+ print("%-25s\t→\t%-25s\tsuccess" % (img, img_new_name))
73
+ ok_cnt += 1
74
+ except Exception as e:
75
+ print("%-25s\t→\t%-25s\tfailed" % (img, img_new_name))
76
+ sys.stderr.write(f"Error: {e}\n")
77
+ ng_cnt += 1
78
+
79
+ # 输出统计信息
80
+ print("\nProcess completed\nSuccess: %d\nFailed: %d" % (ok_cnt, ng_cnt))
81
+
82
+
83
+ if __name__ == '__main__':
84
+ main()
@@ -0,0 +1,93 @@
1
+ """
2
+ 编辑图片的EXIF信息
3
+ """
4
+ import sys
5
+ import getopt
6
+ from .my_utility import get_file_exif, set_file_exif
7
+
8
+ HELP = """
9
+ modify-exif
10
+ -f file -r property [-s] read property, only output value if s is set
11
+ -f file -rt [-s] read datetime_original and datetime_digitized, only output value if s is set
12
+ -f file -w property -v value write property
13
+ -f file -wt -v value write datetime_original and datetime_digitized
14
+ -h help
15
+ """
16
+
17
+
18
+ def read_property(filename, property_name, simple_out=False):
19
+ """读取属性并打印详情"""
20
+ value = get_file_exif(filename, property_name)
21
+ if simple_out:
22
+ if value != "":
23
+ print(value)
24
+ else:
25
+ print(f"read {property_name} from {filename}")
26
+ print(f"value is {value}")
27
+
28
+
29
+ def write_property(filename, property_name, value):
30
+ """写入属性并打印详情"""
31
+ print(f"write '{property_name}={value}' to {filename}")
32
+ set_file_exif(filename, property_name, value)
33
+
34
+
35
+ def main():
36
+ """主入口函数"""
37
+ process_file = ""
38
+ process_mode = ""
39
+ process_property = ""
40
+ process_value = ""
41
+ simple_out = False
42
+
43
+ try:
44
+ options, args = getopt.getopt(sys.argv[1:], 'hf:r:w::v:s')
45
+ for option, value in options:
46
+ if option == '-f':
47
+ process_file = value
48
+ elif option == '-r':
49
+ process_mode = "r"
50
+ process_property = value
51
+ elif option == '-w':
52
+ process_mode = "w"
53
+ process_property = value
54
+ elif option == '-v':
55
+ process_value = value
56
+ elif option == '-s':
57
+ simple_out = True
58
+ elif option == '-h':
59
+ print(HELP)
60
+ sys.exit(0)
61
+
62
+ if process_file == "":
63
+ raise Exception("file is empty")
64
+ if process_mode == "":
65
+ raise Exception("r or w flag is necessary")
66
+ if process_property == "":
67
+ raise Exception("property is necessary")
68
+ if process_mode == "w" and process_value == "":
69
+ raise Exception("value is necessary")
70
+ except Exception as e:
71
+ sys.stderr.write("".join(e.args) + "\n")
72
+ sys.exit(1)
73
+
74
+ try:
75
+ if process_mode == 'r':
76
+ if process_property == "t":
77
+ read_property(process_file, "datetime_original", simple_out)
78
+ read_property(process_file, "datetime_digitized", simple_out)
79
+ else:
80
+ read_property(process_file, process_property, simple_out)
81
+ else:
82
+ if process_property == "t":
83
+ write_property(process_file, "datetime_original", process_value)
84
+ write_property(process_file, "datetime_digitized", process_value)
85
+ else:
86
+ write_property(process_file, process_property, process_value)
87
+ except Exception as e:
88
+ sys.stderr.write("".join(e.args) + "\n")
89
+ sys.exit(1)
90
+
91
+
92
+ if __name__ == '__main__':
93
+ main()
@@ -0,0 +1,75 @@
1
+ """
2
+ 自定义工具集
3
+ """
4
+ from datetime import datetime
5
+ import os
6
+ import re
7
+ from exif import Image, DATETIME_STR_FORMAT
8
+
9
+
10
+ def get_imgs(suffixs=None):
11
+ """获取指定后缀的图片文件名"""
12
+ if suffixs is None:
13
+ suffixs = get_suffixes()
14
+
15
+ # 构造匹配表达式
16
+ img_pattern = '$|.*\\.'.join(suffixs)
17
+ if img_pattern != "":
18
+ img_pattern += '$'
19
+ img_pattern = '.*\\.' + img_pattern
20
+
21
+ try:
22
+ dirs = os.listdir('.')
23
+ except Exception:
24
+ dirs = []
25
+
26
+ imgs = []
27
+ for img in dirs:
28
+ if re.match(img_pattern, img, re.I):
29
+ imgs.append(img)
30
+ return imgs
31
+
32
+
33
+ def get_suffixes():
34
+ """获取默认的文件后缀"""
35
+ return ['jpg', 'png', 'jpeg', 'heic']
36
+
37
+
38
+ def get_format():
39
+ """获取默认的命名格式"""
40
+ return '%Y%m%d%H%M%S'
41
+
42
+
43
+ def get_file_suffix(filename=''):
44
+ """获取文件后缀"""
45
+ if filename != '':
46
+ tmp = filename.split('.')
47
+ if len(tmp) > 0:
48
+ return tmp.pop()
49
+ return ''
50
+
51
+
52
+ def get_file_exif(filename='', property=''):
53
+ """获取指定文件的EXIF信息,默认获取所有信息"""
54
+ image = Image(filename)
55
+ if not image.has_exif:
56
+ return ""
57
+ if property == "":
58
+ return image.list_all()
59
+ else:
60
+ return image.get(property, "")
61
+
62
+
63
+ def set_file_exif(filename='', property='datetime_original', value=''):
64
+ """设定指定文件的EXIF信息,默认设置datetime_original为当前时间"""
65
+ image = Image(filename)
66
+ if value == '':
67
+ value = datetime.now().strftime(DATETIME_STR_FORMAT)
68
+ image.set(property, value)
69
+ with open(filename, 'wb') as f:
70
+ f.write(image.get_file())
71
+
72
+
73
+ if __name__ == '__main__':
74
+ # 模块测试
75
+ pass
@@ -0,0 +1,124 @@
1
+ Metadata-Version: 2.4
2
+ Name: format-img
3
+ Version: 0.1.2
4
+ Summary: A python tool to format images based on EXIF information and edit EXIF data.
5
+ License-File: LICENSE
6
+ Requires-Python: >=3.8
7
+ Requires-Dist: exif>=1.3.5
8
+ Requires-Dist: exifread>=2.3.2
9
+ Description-Content-Type: text/markdown
10
+
11
+ # 图片名称格式化工具
12
+ ## 根据 EXIF 信息来格式化图片名字,方便管理
13
+
14
+ ## 环境与安装
15
+
16
+ 本工具使用 `uv` 管理依赖和运行环境。
17
+
18
+ ### 免克隆直接运行(推荐)
19
+
20
+ 如果不想克隆整个仓库,可以直接使用 `uvx`(即 `uv tool run`)来运行本工具:
21
+
22
+ - **format-img**:
23
+ ```bash
24
+ uvx format-img [参数]
25
+ ```
26
+ - **modify-exif**:
27
+ ```bash
28
+ uvx --from format-img modify-exif [参数]
29
+ ```
30
+
31
+ ### 克隆本地仓库运行
32
+
33
+ 如果克隆了整个仓库,需要先安装依赖并同步环境:
34
+
35
+ ```bash
36
+ uv sync
37
+ ```
38
+
39
+ ---
40
+
41
+ ## 用法
42
+
43
+ ### 1. format-img
44
+
45
+ 格式化当前目录下的图片文件名。
46
+
47
+ #### 基本用法
48
+ ```bash
49
+ uv run format-img [-f format] [-s suffixs] [-h]
50
+ ```
51
+
52
+ - `-f` 文件名的格式,默认值为 `%Y%m%d%H%M%S`。支持的占位符列表见下表。
53
+ - `-s` 要格式化文件名的后缀名列表(以逗号分隔,不区分大小写),默认值为 `jpg,png,jpeg,heic`。例如 `-s "jpg,png"`。
54
+ - `-h` 获取帮助信息。
55
+
56
+ #### 支持的格式占位符:
57
+
58
+ | 格式 | 含义 |
59
+ |---|---|
60
+ | %a | 本地(locale)简化星期名称 |
61
+ | %A | 本地完整星期名称 |
62
+ | %b | 本地简化月份名称 |
63
+ | %B | 本地完整月份名称 |
64
+ | %c | 本地相应的日期和时间表示 |
65
+ | %d | 一个月中的第几天(01 - 31) |
66
+ | %H | 一天中的第几个小时(24小时制,00 - 23) |
67
+ | %I | 第几个小时(12小时制,01 - 12) |
68
+ | %j | 一年中的第几天(001 - 366) |
69
+ | %m | 月份(01 - 12) |
70
+ | %M | 分钟数(00 - 59) |
71
+ | %p | 本地am或者pm的相应符 |
72
+ | %S | 秒(01 - 61) |
73
+ | %U | 一年中的星期数(00 - 53,星期天为一个星期的开始) |
74
+ | %w | 一个星期中的第几天(0 - 6,0是星期天) |
75
+ | %W | 和 %U 基本相同,不同的是以星期一为一个星期的开始 |
76
+ | %x | 本地相应日期 |
77
+ | %X | 本地相应时间 |
78
+ | %y | 去掉世纪的年份(00 - 99) |
79
+ | %Y | 完整的年份 |
80
+ | %Z | 时区的名字 |
81
+ | %% | '%' 字符 |
82
+
83
+ #### 示例
84
+ 把当前目录下的 `jpg` 和 `png` 文件的名称格式化成 `2017-10-01 10:17:01` 的形式:
85
+ ```bash
86
+ uv run format-img -s "jpg,png" -f "%Y-%m-%d %H:%M:%S"
87
+ ```
88
+
89
+ ---
90
+
91
+ ### 2. modify-exif
92
+
93
+ 用于读取/修改图片的 EXIF 信息。
94
+
95
+ #### 基本用法
96
+ ```bash
97
+ # 读取属性详情
98
+ uv run modify-exif -f file -r property [-s]
99
+
100
+ # 读取拍摄时间与数字化时间(only output value if -s is set)
101
+ uv run modify-exif -f file -rt [-s]
102
+
103
+ # 写入指定属性
104
+ uv run modify-exif -f file -w property -v value
105
+
106
+ # 写入拍摄时间与数字化时间
107
+ uv run modify-exif -f file -wt -v value
108
+
109
+ # 查看帮助
110
+ uv run modify-exif -h
111
+ ```
112
+
113
+ 具体示例可以参考 `scripts/update-datetime.ps1`,用于批量修改目标文件夹里面 `yyyyMMddHHmmss` 这样命名规则图片的 EXIF 时间信息。
114
+
115
+ ## 注意事项
116
+
117
+ **跨路径调用**:如果要在其他路径(即不是项目根目录)下调用本工具,建议使用 `--project` 参数指定项目路径,例如:
118
+
119
+ ```powershell
120
+ uv --project D:\Development\format-img run format-img
121
+ ```
122
+
123
+ 使用 `--project` 可以确保 `uv` 能够找到该项目的环境与依赖,同时**不会**改变当前的工作路径(cwd),从而正确地对你当前所在的命令行路径下的图片进行格式化操作。
124
+
@@ -0,0 +1,9 @@
1
+ format_img/__init__.py,sha256=nhXMbeUohi4_q7-Yzr0aaQ0ACtvW5yApaCgk-DXyY5s,21
2
+ format_img/format_img.py,sha256=MuqP5nScQNPvDmYYHrZ3Lx2Zayr0NxHUc_oT_z3bqiY,2502
3
+ format_img/modify_exif.py,sha256=UG6o7EyJ1kmuZM219J2NS_DqqHaxScR2cKMrTBIShIo,3104
4
+ format_img/my_utility.py,sha256=ZuIrO-be5PlVft5j9g-Gf4eKuK2o81yZd_lFhou6-O0,1745
5
+ format_img-0.1.2.dist-info/METADATA,sha256=Qe-fikncEZz2V_VApi0mqk0wod1SCjQg1v3TFi1ISGU,3561
6
+ format_img-0.1.2.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
7
+ format_img-0.1.2.dist-info/entry_points.txt,sha256=N_WMDdJNUwD9KPKrNeMKu5Peq340lg_F9Y2ye9pd4FY,100
8
+ format_img-0.1.2.dist-info/licenses/LICENSE,sha256=fA1Fa5i5FSCWvDaTZ0zOkvHEkLmG_5x_OrYanEG9xFU,1063
9
+ format_img-0.1.2.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ format-img = format_img.format_img:main
3
+ modify-exif = format_img.modify_exif:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Martin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.