吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1511|回复: 37
上一主题 下一主题
收起左侧

[Python 原创] 整理大量照片的小工具

  [复制链接]
跳转到指定楼层
楼主
ssdreamC 发表于 2024-11-27 15:50 回帖奖励
本帖最后由 ssdreamC 于 2024-11-28 10:55 编辑

2024年11月28日更新一下,一个是加上了md5校验选项,一个是禁用视频、音频的选项,还有说让我提供一个可执行文件的,我也用pyinstaller打包了一份,说实话我是第一次打包python的,,,
打包用的是
pyinstaller --onefile --windowed --add-binary "favicon.ico;." --icon "favicon.ico" media_organizer.py
# 说实话我还是觉得python的东西直接执行就行,尤其是我这种第三方库也只用了一个pillow的小工具

打包好的:https://www.alipan.com/s/vWq5wacxRNh
通过网盘分享的文件:media_organizer.exe链接: https://pan.baidu.com/s/1gpScadlQZH7EGJUuz__mgQ?pwd=52pj 提取码: 52pj
再次声明:我还是觉得python的东西直接执行就行,尤其是我这种第三方库也只用了一个pillow的小工具
好嘞,今天上午的鱼就摸到这里啦


-----


这两天用华为手机助手把手机上的几千张照片导出到了电脑上,想整理一下,但整理起来心态就不太好了,,,
于是拿 python 做了个小工具,按“年月”整理到不同文件夹中,自我感觉还挺好,发上来请大佬们给指导指导{:1_893:}


昨天写的,后来想着加一下视频和音频的,现在懒了。。。
import os, sys
from shutil import move
from hashlib import md5
from PIL import Image
from PIL.ExifTags import TAGS
from tkinter import Tk, Button, Label, filedialog, Checkbutton, Text, Scrollbar, BooleanVar, Frame
from tkinter import SUNKEN, BOTTOM, E, X, END

class MediaOrganizer:
    def __init__(self, root):
        self.root = root
        self.root.title("照片整理简易工具 v0.2")
        if hasattr(sys, '_MEIPASS'):
            base_path = sys._MEIPASS
        else:
            base_path = os.path.abspath(".")
        icon_path = os.path.join(base_path, "favicon.ico")
        self.root.iconbitmap(icon_path)
        self.selected_folder = None
        self.tmp = ""
        self.md5_hashes = set()  # 存储已处理文件的MD5值

        self.create_widgets()

    def create_widgets(self):
        # 创建Frame来组织复选框和按钮
        control_frame = Frame(self.root)
        control_frame.pack(pady=10)
        # 创建选择文件夹的按钮
        select_button = Button(control_frame, text="选择文件夹", command=self.select_folder)
        select_button.pack(side='left', padx=5)

        # 创建复选框
        self.audio_var = BooleanVar()
        self.image_var = BooleanVar()
        self.video_var = BooleanVar()
        self.md5_var = BooleanVar()

        self.audio_check = Checkbutton(control_frame, text="声音", variable=self.audio_var, state='disabled')
        self.image_check = Checkbutton(control_frame, text="图片", variable=self.image_var)
        self.video_check = Checkbutton(control_frame, text="视频", variable=self.video_var, state='disabled')
        self.md5_check = Checkbutton(control_frame, text = 'MD5校验', variable= self.md5_var)

        self.audio_check.pack(side='left', padx=5)
        self.image_check.pack(side='left', padx=5)
        self.video_check.pack(side='left', padx=5)
        self.md5_check.pack(side='left', padx=5)

        # 绑定鼠标事件
        self.audio_check.bind("<Enter>", lambda event: self.show_tooltip(event, self.audio_check))
        self.audio_check.bind("<Leave>", self.hide_tooltip)
        self.image_check.bind("<Enter>", lambda event: self.show_tooltip(event, self.image_check))
        self.image_check.bind("<Leave>", self.hide_tooltip)
        self.video_check.bind("<Enter>", lambda event: self.show_tooltip(event, self.video_check))
        self.video_check.bind("<Leave>", self.hide_tooltip)
        self.md5_check.bind("<Enter>", lambda event: self.show_tooltip(event, self.md5_check))
        self.md5_check.bind("<Leave>", self.hide_tooltip)

        # 创建开始按钮
        start_button = Button(control_frame, text="开始整理", command=self.start_organizing)
        start_button.pack(side='left', padx=5)

        # 创建状态标签
        self.status_label = Label(self.root, text="将所选文件夹中的媒体文件按照年月分别整理到不同文件夹(例如 202411)中\n需要媒体文件包含EXIF信息,仅整理所选文件夹中的文件,不包含子文件夹", height=2)
        self.status_label.pack(pady=10)

        # 创建文本框
        log_frame = Frame(self.root)
        log_frame.pack(pady=10, fill='both', expand=True)

        self.log_text = Text(log_frame, height=20, width=80)
        self.log_text.pack(side='left', fill='both', expand=True)

        scrollbar = Scrollbar(log_frame, command=self.log_text.yview)
        scrollbar.pack(side='right', fill='y')

        self.log_text.config(yscrollcommand=scrollbar.set)

        # 状态栏
        self.statusbar = Label(self.root, text="ssdreamC 版权所有,52PoJie 免费提供 ", bd=1, relief=SUNKEN, anchor=E)
        self.statusbar.pack(side=BOTTOM, fill=X)

    def check_md5(self, file_path):
        """ 计算文件的MD5值并检查是否已存在 """
        with open(file_path, 'rb') as f:
            md5_hash = md5(f.read()).hexdigest()
        if self.md5_var.get():  # 如果启用了MD5校验
            if md5_hash in self.md5_hashes:
                self.log_text.insert(END, f"跳过重复文件:{file_path}\n")
                self.log_text.see(END)
                self.root.update_idletasks()
                return False
            self.md5_hashes.add(md5_hash)
        return True

    def get_exif_data(self, image_path):
        try:
            image = Image.open(image_path)
            exif_data = image._getexif()
            if exif_data is None:
                return None
            exif = {
                TAGS[k]: v
                for k, v in exif_data.items()
                if k in TAGS
            }
            return exif
        except Exception as e:
            self.log_text.insert(END, f"读取EXIF数据失败: {e}\n")

            self.log_text.see(END)
            self.root.update_idletasks()  # 强制更新界面
            return None

    def get_date_taken(self, exif_data):
        return exif_data.get('DateTimeOriginal', None)

    def organize_photos(self, folder_path, file_types):
        for filename in os.listdir(folder_path):
            if any(filename.lower().endswith(ext) for ext in file_types):
                file_path = os.path.join(folder_path, filename)
                if not self.check_md5(file_path):
                    continue
                exif_data = self.get_exif_data(file_path)
                if exif_data:
                    date_taken = self.get_date_taken(exif_data)
                    if date_taken:
                        year_month = date_taken[:7].replace(':', '')
                        year_month_folder = os.path.join(folder_path, year_month)
                        if not os.path.exists(year_month_folder):
                            os.makedirs(year_month_folder)
                        move(file_path, os.path.join(year_month_folder, filename))
                        self.log_text.insert(END, f"移动 {filename} 到 {year_month_folder}\n")

                        self.log_text.see(END)
                        self.root.update_idletasks()  # 强制更新界面
                    else:
                        self.log_text.insert(END, f"没有日期数据的文件: {filename}\n")

                        self.log_text.see(END)
                        self.root.update_idletasks()  # 强制更新界面
                else:
                    self.log_text.insert(END, f"没有EXIF的文件: {filename}\n")

                    self.log_text.see(END)
                    self.root.update_idletasks()  # 强制更新界面

    def select_folder(self):
        self.selected_folder = filedialog.askdirectory()
        if self.selected_folder:
            self.status_label.config(text=f"选择了文件夹:\n{self.selected_folder}")

    def start_organizing(self):
        if self.selected_folder:
            file_types = []
            if self.audio_var.get():
                file_types.extend(['.mp3', '.wav', '.flac', '.aac'])
            if self.image_var.get():
                file_types.extend(['.png', '.jpg', '.jpeg', '.tiff', '.bmp', '.gif'])
            if self.video_var.get():
                file_types.extend(['.mp4', '.avi', '.mov', '.mkv'])

            self.organize_photos(self.selected_folder, file_types)
            self.status_label.config(text=f"{self.selected_folder}\n中的文件整理好了。")
        else:
            self.status_label.config(text="请选择一个文件夹。")

    def show_tooltip(self, event, widget):
        self.tmp = self.status_label.cget("text")
        if widget == self.audio_check:
            self.status_label.config(text="声音文件包括: .mp3, .wav, .flac, .aac")
        elif widget == self.image_check:
            self.status_label.config(text="图片文件包括: .png, .jpg, .jpeg, .tiff, .bmp, .gif")
        elif widget == self.video_check:
            self.status_label.config(text="视频文件包括: .mp4, .avi, .mov, .mkv")
        elif widget == self.md5_check:
            self.status_label.config(text="启用MD5校验以避免重复文件,启用后整理速度会变慢")

    def hide_tooltip(self, event):
        self.status_label.config(text=self.tmp)

if __name__ == "__main__":
    root = Tk()
    app = MediaOrganizer(root)
    root.mainloop()

免费评分

参与人数 9吾爱币 +13 热心值 +8 收起 理由
lilihuakai + 1 + 1 谢谢@Thanks!
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
dlz0114 + 1 用心讨论,共获提升!
up100x + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
nekolzc + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
zoohotelforever + 1 鼓励转贴优秀软件安全工具和文档!
ldty + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
kerrychu + 1 + 1 谢谢@Thanks!
helh0275 + 1 + 1 这是一个有思想的懒人的好工具,哈哈哈...

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
 楼主| ssdreamC 发表于 2024-11-28 09:46 |楼主
classSTU00 发表于 2024-11-27 21:28
感觉是我需要的,楼主,可以拿去使用下不?

随便用呗~
推荐
 楼主| ssdreamC 发表于 2024-11-29 10:26 |楼主
Weiba017 发表于 2024-11-29 09:44
隐藏的照片可以找到吗

没有对隐藏文件特殊处理,所以选定文件夹下如果有隐藏文件应该也会被整理,如果要想跳过隐藏文件可以加一次判断:
[Python] 纯文本查看 复制代码
 def is_hidden_file(self, file_path):
        if os.name == 'nt':  # Windows,文件是否隐藏在属性中设置
            import ctypes
            FILE_ATTRIBUTE_HIDDEN = 0x02
            return ctypes.windll.kernel32.GetFileAttributesW(file_path) & FILE_ATTRIBUTE_HIDDEN
        else:  # *nix,识别是否'.'开头
            return os.path.basename(file_path).startswith('.')
沙发
classSTU00 发表于 2024-11-27 21:28
感觉是我需要的,楼主,可以拿去使用下不?
3#
WJia 发表于 2024-11-27 21:47
成品呢有成品吗
4#
wang821399672 发表于 2024-11-27 22:19
好用,有成品可以试用下吗
5#
CooK1e 发表于 2024-11-27 22:26
可以的&#128077;
6#
gdfs59310 发表于 2024-11-27 22:42
能分享吗?
7#
yzb99312 发表于 2024-11-27 22:54
不错,值得
8#
LQYLHM 发表于 2024-11-27 23:27
正时急需。
9#
pyy52pojie 发表于 2024-11-28 02:12
有没有可能加个重复照片检测,自动删除其中一份的功能。
就怕有的图片是重复的但是md5又不一样,以为压缩过
10#
kerrychu 发表于 2024-11-28 07:10
挺好的,谢谢楼主分享。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-1-7 16:22

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表