使用群晖DSM系统来备份我们的照片和视频是非常方便的一件事情,但是有些时候,个人的电子设备越来越多,备份后的照片目录也越来越多,有些时候手动导入一些资源后也使得后面整理起来比较麻烦。到一定时间后再去整理和归档相关备份的资料就是一个重复和机械的操作。

群晖DSM系统对于照片和视频的归档方式是按照时间年份以及月份去归档备份资料的,对于这种方式个人觉得比较简单好用,最近正好在整理自己的照片与视频备份,于是写了一个 python 脚本来处理这个重复的工作。

基本逻辑:

读取一个目录下的所有文件,解析图片和视频中的拍摄信息,按照年月进行从命名以及归档操作,值得说明一点的是苹果特有的HEIC图片格式需要单独的处理。

Python脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# https://pypi.org/project/pyheif/
# macos
# brew install libffi libheif
# linux
# apt install libffi libheif-dev libde265-dev
# yum install libffi libheif-devel libde265-devel
# pip install git+https://github.com/carsales/pyheif.git
# pip3 install exifread whatimage pyheif Pillow piexif

import os
import io
import re
import time
import shutil
import exifread
import whatimage
import pyheif
import piexif
from PIL import Image
from types import coroutine

# 苹果 HEIC 图片转换成 JPEG
def heic_to_jpg(heic_img_path):
with open(heic_img_path, 'rb') as f:
heic_img = f.read()

img_format = whatimage.identify_image(heic_img)
print('heic_to_jpg', img_format)
if img_format in ['heic', 'heif']:
img = pyheif.read_heif(heic_img)
pi = Image.frombytes(mode=img.mode, size=img.size, data=img.data)
pi.save(heic_img_path[:-5]+".jpg", format="jpeg")

# get_file_date
def get_file_date(filepath, format):
'''
获取文件的时间戳,这里主要返回视频的时间,原时间一般为修改时间
st_atime (访问时间), st_mtime (修改时间), st_ctime(创建时间)
'''
statinfo = os.stat(filepath)
temp_time = time.localtime(statinfo.st_mtime)
return time.strftime(format, temp_time)

# get_exif_date
def get_exif_date(filepath):
# 获取照片的拍摄日期,利用exifread模块
FIELD = 'EXIF DateTimeOriginal'
fd = open(filepath, 'rb')
tags = exifread.process_file(fd)
fd.close()

if FIELD in tags:
temp_time = str(tags[FIELD])
new_time = temp_time.replace(':', '').replace(' ', '_')
return new_time
else:
new_time = ''
return new_time

# get_heic_exif_date
def get_heic_exif_date(filepath):
with open(filepath, 'rb') as f:
heic_img = f.read()

img_format = whatimage.identify_image(heic_img)
temp_time = ""
print('heic_to_jpg', img_format)
if img_format in ['heic', 'heif']:
img = pyheif.read_heif(heic_img)
for metadata in img.metadata or []:
if metadata['type'] == 'Exif':
# DATETIME 36867 时间
# DATETIMEDIGITIZED 36868
# LENSMAKE 42035
# GPSLONGITUDE 0000 经度
# GPSLATITUDE 0000 维度
FIELD = 36867
exif_dict = piexif.load(metadata['data'])
exif_dict = exif_dict['Exif']
temp_time = (exif_dict[FIELD]).decode('utf-8')
print("时间: " + temp_time)
# for key in exif_dict.keys():
# print("exif Key: " + str(key) + " value: "+ str(exif_dict[key]))

new_time = temp_time.replace(':', '').replace(' ', '_')
return new_time

# get_filetype
def get_filetype(basename):
'''
利用正则表达式判断文件后缀,照片返回0,视频返回1,其他返回2
'''
img_reg = r'(\.JPG|\.jpg|\.JPEG|\.jpeg|\.BMP|\.bmp|\.PNG|\.png)'
vedio_reg = r'(\.MP4|\.mp4|\.MOV|\.mov)'

if re.search(img_reg, basename): # 匹配照片
return 0
elif re.search(vedio_reg, basename): # 匹配视频
return 1
else:
if re.search(r'(\.HEIC|\.heic|\.HEIF|\.heif)', basename): # 匹配HEIC照片
return 2
else:
return 3

# rename_filename
def rename_filename(path, filename, format):
filepath = os.path.join(path, file_name)
newname = filename
print("当前文件: " + filepath)
# 过滤不必要的文件夹
if filename == ".DS_Store":
return

num = get_filetype(filename) # 判定文件类型
# 获取文件时间
if num == 0:
exif_time = str(get_exif_date(filepath)) # 获取照片拍摄时间
if exif_time != '':
str_time = exif_time
else:
str_time = str(get_file_date(filepath, format)) # 获取文件的修改时间
elif num == 1:
str_time = str(get_file_date(filepath, format))
elif num == 2:
exif_time = str(get_heic_exif_date(filepath)) # 获取照片拍摄时间
if exif_time != '':
str_time = exif_time
else:
str_time = str(get_file_date(filepath, format)) # 获取文件的修改时间
else:
print("- 文件格式不正确!")
return newname

# 重命名
file_suffix = os.path.splitext(filename)[1] # 后缀名
try:
newname = str_time+file_suffix
os.rename(filepath, os.path.join(path, newname))
print(filename + " newname: " + newname + ' 重命名成功!')
except Exception as e:
if e.args[0] == 17: # 重名
newname = str_time+'-1'+file_suffix
os.rename(filepath, os.path.join(path, newname))
else:
print(e)
return newname

# move_file
def move_file(path, file_name, todir):
if file_name == None:
return
oldpath = os.path.join(path, file_name)
new_dir = os.path.join(todir, "/Other")
try:
new_dir = os.path.join(todir, file_name[:4] + "/" + file_name[4: 6])
except Exception as e:
print("Exception: " + e)

if not os.path.exists(new_dir):
os.makedirs(new_dir)
# if not os.path.exists(file_name):
# #调用系统命令行来创建文件
# os.system(r"touch {}".format(path))
newpath = os.path.join(new_dir, file_name)
shutil.move(oldpath, newpath)
print("移动文件:" + newpath)

# move_file
def simple_move_file(path, file_name, todir):
if file_name == ".DS_Store":
return
else:
shutil.move(os.path.join(path, file_name),
os.path.join(todir, file_name))

# 获取所有子目录
def get_allsubdir(path):
print("所有子目录: ")
g = os.walk(path)
for path, dir_list, file_list in g:
for dir_name in dir_list:
print(os.path.join(path, dir_name))

# 获取所有文件
def get_allfiles(path):
print("所有文件: ")
g = os.walk(path)
for path, dir_list, file_list in g:
for file_name in file_list:
print(os.path.join(path, file_name))

# main
if __name__ == "__main__":
fromdir = '/Users/vvusu/Downloads'
todir = '/Volumes/homes/suixin/Photos/MobileBackup/Sue/Other'
# 时间格式
format = '%Y%m%d_%H%M%S'
print("时间格式: " + format)
g = os.walk(fromdir)
print("所有文件: ")
for path, dir_list, file_list in g:
for file_name in file_list:
new_name = rename_filename(path, file_name, format)
# move_file(path, new_name, todir)
# simple_move_file(path, file_name, todir)

Python脚本运行:

Xnip2022-07-10_22-57-33.png

整理之前的目录:

Xnip2022-07-10_22-48-58.png

整理之后的目录:

Xnip2022-07-10_22-49-39.png