Skip to content

idhyt/mmfplace

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

145 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

What

尤其是家庭环境下,同一个照片会存在好几个设备中,每次备份会产生大量的重复照片和合并冲突(重名文件),同时,

同一个照片进行复制,传输,移动等操作后,其文件属性时间会被修改或丢失,

大部分图片处理工具会将最新的修改时间作为照片创建日期,最终的结果就是多年前拍的照片被放在了今年的目录中。

这个工具将获取所有的文件时间信息,并取最早时间作为照片的创建日期,进行分类处理,同时确保对同一文件的每次处理结果都保持一致。

日期数据来自于 文件元数据文件属性 看到的时间,并取最早时间作为最终结果。

基于该工具,当前我的照片备份方案

graph LR
    A[手机/电脑/平板等设备] --> B{同步}
    B -->|自动| C[NAS服务器]
    C --> D[挂载到电脑]
    D --> E[ALL_PHOTOS
├── PhoneA
├── PhoneB
├── Others.]
    B -->|手动| F[增量同步到电脑<br>(如 FreeFileSync)]
    F --> E
    E --> G[增量处理<br>mmfplace place -i ALL_PHOTOS -o 备份磁盘]
    G --> H[备份磁盘]
Loading

经过几次的实际验证和优化,已经帮我处理了超过10W+照片(处理完大概300G) 🎉

整理之前的目录:

❯ tree tests
tests
├── 10x12x16bit-CMYK.psd
├── 16color-10x10.bmp
├── 24bpp-10x10.bmp
├── 256color-10x10.bmp
├── 8x4x8bit-Grayscale.eps
├── 8x4x8bit-Grayscale.psd
├── adobeJpeg1.eps
├── adobeJpeg1.jpg
├── adobeJpeg1.jpg.app2
├── crash01.jpg
├── dotnet-256x256-alpha-palette.png
├── gimp-8x12-greyscale-alpha-time-background.png
├── iccDataInvalid1.jpg.app2
├── invalid-iCCP-missing-adler32-checksum.png
├── manuallyAddedThumbnail.jpg
├── mspaint-10x10.gif
├── mspaint-8x10.png
├── nikonMakernoteType1.jpg
├── nikonMakernoteType2b.jpg
...

整理之后的目录:

❯ tree tests_output
├── 1996
│   ├── 11
│   │   ├── withuncompressedycbcrthumbnail3.jpg
│   │   └── withuncompressedycbcrthumbnail.jpg
│   └── 12
│       └── withuncompressedycbcrthumbnail2.jpg
├── 2000
│   ├── 01
│   │   └── withiptc.jpg
│   └── 10
│       └── withuncompressedrgbthumbnail.jpg
├── 2001
│   ├── 01
│   │   └── withexif.jpg
│   └── 04
│       └── nikonmakernotetype1.jpg
├── 2002
│   ├── 05
│   │   └── crash01.jpg
│   ├── 06
│   │   └── withiptcexifgps.jpg
│   ├── 08
│   │   └── nikonmakernotetype2b.jpg
│   └── 11
│       ├── manuallyaddedthumbnail.jpg
│       ├── simple.jpg
│       └── simple.png
├── 2003
│   └── 11
│       └── adobejpeg1.jpg
├── 2004
│   └── 04
│       └── windowsxpfields.jpg
├── 2010
│   └── 06
│       └── withpanasonicfaces.jpg
├── 2012
│   ├── 05
│   │   ├── 10x12x16bit-cmyk.psd
│   │   └── 8x4x8bit-grayscale.psd
│   └── 12
│       └── photoshop-8x12-rgb24-all-metadata.png
├── 2013
│   └── 01
│       └── gimp-8x12-greyscale-alpha-time-background.png
...

默认情况下文件会复制到 %Y/%m/xxx 并会保留原始文件名, 如 simple.jpg -> 2002/11/simple.jpg

可以通过参数 --rename-with-ymd 将文件重命名,如 simple.jpg -> 2025/11/2025-11-16.jpg

❯ tree tests_output
├── 1996
│   ├── 11
│   │   ├── 1996-11-04.jpg
│   │   └── 1996-11-10.jpg
│   └── 12
│       └── 1996-12-20.jpg
├── 2000
│   ├── 01
│   │   └── 2000-01-01.jpg
│   └── 10
│       └── 2000-10-26.jpg
├── 2001
│   ├── 01
│   │   └── 2001-01-28.jpg
│   └── 04
│       └── 2001-04-06.jpg
├── 2002
│   ├── 05
│   │   └── 2002-05-08.jpg
│   ├── 06
│   │   └── 2002-06-20.jpg
│   ├── 08
│   │   └── 2002-08-29.jpg
│   └── 11
│       ├── 2002-11-16.jpg
│       ├── 2002-11-16_01.jpg
│       └── 2002-11-27.jpg
├── 2003
│   └── 11
│       └── 2003-11-17.jpg
├── 2004
...

Build

release 直接下载二进制文件

或者本地构建

cd builder
cargo build -- release

或者采用交叉编译

╰─ ./xbuild
1) x86_64-unknown-linux-musl
2) aarch64-unknown-linux-musl
3) x86_64-apple-darwin
4) aarch64-apple-darwin
5) x86_64-pc-windows-gnu
选择目标平台的编号:

交叉编译后的文件存放在 dist 文件夹

╰─ tree dist
dist
├── mmfplace.aarch64-apple-darwin.tar.gz
├── mmfplace.aarch64-unknown-linux-musl.tar.gz
├── mmfplace.x86_64-apple-darwin.tar.gz
├── mmfplace.x86_64-pc-windows-gnu.tar.gz
└── mmfplace.x86_64-unknown-linux-musl.tar.gz

Usage

如果在主机运行,使用前请确保系统中已经安装 java 运行环境,当前测试基于 java-11 环境,其他版本请自行验证。

程序执行后会在同级目录下释放必要的依赖文件(请勿删除)

├── config.toml                 # 配置文件,可配置并发数、java路径、数据库路径以及解析规则
├── mmfplace.exe                # 主程序
├── place.db                    # 同步数据库,请勿删除,否则无法实现增量同步
└── tools                       # 依赖工具包
    ├── metadata-extractor-2.19.0.jar
    └── xmpcore-6.1.11.jar

Test

可运行多次测试样例查看处理结果是否一致

> rm place.db
> mmfplace.exe place -i .\tests -o .\tests.output1
> rm place.db
> mmfplace.exe place -i .\tests -o .\tests.output2
> diff -r .\tests.output1 .\tests.output2

执行测试样例完整的日志

> mmfplace.exe place -i .\tests -o .\tests.output -l .\log.ansi
2025-04-01T07:43:13.004857Z  INFO place::process: start process input=".\\tests" total=53 output=".\\tests.output" test=false
2025-04-01T07:43:13.005258Z  WARN config: 🚨 The first run creates a default config file at D:\Working\MySpace\mmfplace\dist\config.toml    
2025-04-01T07:43:13.023632Z  INFO place::db: Loading database once path=".\\place.db"
2025-04-01T07:43:13.369426Z  INFO place::process: 🎉 success parse datetime from text text="[Exif IFD0] Date/Time = 2001:04:06 11:51:40" datetime=2001-04-06 00:00:00 UTC
2025-04-01T07:43:13.369753Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Digitized = 2001:04:06 11:51:40" datetime=2001-04-06 00:00:00 UTC
2025-04-01T07:43:13.369907Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Original = 2001:04:06 11:51:40" datetime=2001-04-06 00:00:00 UTC
2025-04-01T07:43:13.370417Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:13.372684Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2001\\04\\nikonmakernotetype1.jpg" earliest=Some(2001-04-06T08:00:00+08:00)
2025-04-01T07:43:13.378306Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:13.378383Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Original = 1996:11:04 14:55:52" datetime=1996-11-04 00:00:00 UTC
2025-04-01T07:43:13.378487Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Original = 2001:01:28 13:59:33" datetime=2001-01-28 00:00:00 UTC
2025-04-01T07:43:13.378627Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:13.378705Z  INFO place::process: 🎉 success parse datetime from text text="[Exif IFD0] Date/Time = 2001:01:28 13:59:33" datetime=2001-01-28 00:00:00 UTC
2025-04-01T07:43:13.378828Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Digitized = 2001:01:28 13:59:33" datetime=2001-01-28 00:00:00 UTC
2025-04-01T07:43:13.378899Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\1996\\11\\withuncompressedycbcrthumbnail3.jpg" earliest=Some(1996-11-04T08:00:00+08:00)
2025-04-01T07:43:13.379060Z  INFO place::process: 🎉 success parse datetime from text text="[Exif Thumbnail] Date/Time = 2001:01:28 13:59:33" datetime=2001-01-28 00:00:00 UTC
2025-04-01T07:43:13.379228Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2001\\01\\withexif.jpg" earliest=Some(2001-01-28T08:00:00+08:00)
2025-04-01T07:43:13.382652Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2001\\04\\nikonmakernotetype1.jpg"
2025-04-01T07:43:13.385636Z  INFO place::process: ✅ [1/53] success place with new parsed finish from=".\\tests\\2001\\04\\nikonmakernotetype1.jpg" to=".\\tests.output\\2001\\04\\nikonmakernotetype1.jpg"
2025-04-01T07:43:13.388927Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Original = 2002:08:29 17:31:40" datetime=2002-08-29 00:00:00 UTC
2025-04-01T07:43:13.389069Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:13.389194Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Digitized = 2002:08:29 17:31:40" datetime=2002-08-29 00:00:00 UTC
2025-04-01T07:43:13.389271Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:13.389347Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Original = 2002:05:08 17:28:03" datetime=2002-05-08 00:00:00 UTC
2025-04-01T07:43:13.389429Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:13.389499Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:13.389574Z  INFO place::process: 🎉 success parse datetime from text text="[IPTC] Date Created = 2000:01:01" datetime=2000-01-01 00:00:00 UTC
2025-04-01T07:43:13.389666Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Digitized = 2002:05:08 17:28:03" datetime=2002-05-08 00:00:00 UTC
2025-04-01T07:43:13.389749Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Original = 1996:11:10 20:59:21" datetime=1996-11-10 00:00:00 UTC
2025-04-01T07:43:13.389815Z  INFO place::process: 🎉 success parse datetime from text text="[Exif IFD0] Date/Time = 2002:08:29 17:31:40" datetime=2002-08-29 00:00:00 UTC
2025-04-01T07:43:13.389879Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\1996\\11\\withuncompressedycbcrthumbnail.jpg" earliest=Some(1996-11-10T08:00:00+08:00)
2025-04-01T07:43:13.389978Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2002\\05\\crash01.jpg" earliest=Some(2002-05-08T08:00:00+08:00)
2025-04-01T07:43:13.390067Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2000\\01\\withiptc.jpg" earliest=Some(2000-01-01T08:00:00+08:00)
2025-04-01T07:43:13.390153Z  INFO place::process: 🎉 success parse datetime from text text="Aufnahmedatum : 2002/08/29 17:31:40" datetime=2002-08-29 00:00:00 UTC
2025-04-01T07:43:13.390382Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2002\\08\\nikonmakernotetype2b.jpg" earliest=Some(2002-08-29T08:00:00+08:00)
2025-04-01T07:43:13.392656Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\1996\\11\\withuncompressedycbcrthumbnail3.jpg"
2025-04-01T07:43:13.393703Z  INFO place::process: ✅ [2/53] success place with new parsed finish from=".\\tests\\1996\\11\\withuncompressedycbcrthumbnail3.jpg" to=".\\tests.output\\1996\\11\\withuncompressedycbcrthumbnail3.jpg"
2025-04-01T07:43:13.400550Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2001\\01\\withexif.jpg"
2025-04-01T07:43:13.401542Z  INFO place::process: ✅ [3/53] success place with new parsed finish from=".\\tests\\2001\\01\\withexif.jpg" to=".\\tests.output\\2001\\01\\withexif.jpg"
2025-04-01T07:43:13.404043Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:13.404169Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:13.404252Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Original = 1996:12:20 18:32:02" datetime=1996-12-20 00:00:00 UTC
2025-04-01T07:43:13.404369Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Original = 2000:10:26 16:46:51" datetime=2000-10-26 00:00:00 UTC
2025-04-01T07:43:13.404541Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\1996\\12\\withuncompressedycbcrthumbnail2.jpg" earliest=Some(1996-12-20T08:00:00+08:00)
2025-04-01T07:43:13.404626Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2000\\10\\withuncompressedrgbthumbnail.jpg" earliest=Some(2000-10-26T08:00:00+08:00)
2025-04-01T07:43:13.408511Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\1996\\11\\withuncompressedycbcrthumbnail.jpg"
2025-04-01T07:43:13.409209Z  INFO place::process: ✅ [4/53] success place with new parsed finish from=".\\tests\\1996\\11\\withuncompressedycbcrthumbnail.jpg" to=".\\tests.output\\1996\\11\\withuncompressedycbcrthumbnail.jpg"
2025-04-01T07:43:13.416414Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2002\\05\\crash01.jpg"
2025-04-01T07:43:13.417497Z  INFO place::process: ✅ [5/53] success place with new parsed finish from=".\\tests\\2002\\05\\crash01.jpg" to=".\\tests.output\\2002\\05\\crash01.jpg"
2025-04-01T07:43:13.425287Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2000\\01\\withiptc.jpg"
2025-04-01T07:43:13.426702Z  INFO place::process: ✅ [6/53] success place with new parsed finish from=".\\tests\\2000\\01\\withiptc.jpg" to=".\\tests.output\\2000\\01\\withiptc.jpg"
2025-04-01T07:43:13.434880Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2002\\08\\nikonmakernotetype2b.jpg"
2025-04-01T07:43:13.436917Z  INFO place::process: ✅ [7/53] success place with new parsed finish from=".\\tests\\2002\\08\\nikonmakernotetype2b.jpg" to=".\\tests.output\\2002\\08\\nikonmakernotetype2b.jpg"
2025-04-01T07:43:13.445207Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\1996\\12\\withuncompressedycbcrthumbnail2.jpg"
2025-04-01T07:43:13.446709Z  INFO place::process: ✅ [8/53] success place with new parsed finish from=".\\tests\\1996\\12\\withuncompressedycbcrthumbnail2.jpg" to=".\\tests.output\\1996\\12\\withuncompressedycbcrthumbnail2.jpg"
2025-04-01T07:43:13.453925Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2000\\10\\withuncompressedrgbthumbnail.jpg"
2025-04-01T07:43:13.454952Z  INFO place::process: ✅ [9/53] success place with new parsed finish from=".\\tests\\2000\\10\\withuncompressedrgbthumbnail.jpg" to=".\\tests.output\\2000\\10\\withuncompressedrgbthumbnail.jpg"
2025-04-01T07:43:13.484650Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Original = 2002:07:13 15:58:28" datetime=2002-07-13 00:00:00 UTC
2025-04-01T07:43:13.484793Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:13.484961Z  INFO place::process: 🎉 success parse datetime from text text="[IPTC] Date Created = 2002:06:20" datetime=2002-06-20 00:00:00 UTC
2025-04-01T07:43:13.485277Z  INFO place::process: 🎉 success parse datetime from text text="[Exif IFD0] Date/Time = 2002:07:19 13:28:10" datetime=2002-07-19 00:00:00 UTC
2025-04-01T07:43:13.485470Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Digitized = 2002:07:13 15:58:28" datetime=2002-07-13 00:00:00 UTC
2025-04-01T07:43:13.485659Z  INFO place::process: 🎉 success parse datetime from text text="[XMP] photoshop:DateCreated = 2002-06-20" datetime=2002-06-20 00:00:00 UTC
2025-04-01T07:43:13.485815Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2002\\06\\withiptcexifgps.jpg" earliest=Some(2002-06-20T08:00:00+08:00)
2025-04-01T07:43:13.495270Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2002\\06\\withiptcexifgps.jpg"
2025-04-01T07:43:13.496512Z  INFO place::process: ✅ [10/53] success place with new parsed finish from=".\\tests\\2002\\06\\withiptcexifgps.jpg" to=".\\tests.output\\2002\\06\\withiptcexifgps.jpg"
2025-04-01T07:43:13.659682Z  INFO place::process: 🎉 success parse datetime from text text="[Exif IFD0] Date/Time = 2002:11:27 18:00:35" datetime=2002-11-27 00:00:00 UTC
2025-04-01T07:43:13.659953Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:13.660336Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2002\\11\\manuallyaddedthumbnail.jpg" earliest=Some(2002-11-27T08:00:00+08:00)
2025-04-01T07:43:13.668676Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2002\\11\\manuallyaddedthumbnail.jpg"
2025-04-01T07:43:13.669981Z  INFO place::process: ✅ [11/53] success place with new parsed finish from=".\\tests\\2002\\11\\manuallyaddedthumbnail.jpg" to=".\\tests.output\\2002\\11\\manuallyaddedthumbnail.jpg"
2025-04-01T07:43:13.670741Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Digitized = 2002:11:16 15:27:01" datetime=2002-11-16 00:00:00 UTC
2025-04-01T07:43:13.671778Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Original = 2002:11:16 15:27:01" datetime=2002-11-16 00:00:00 UTC
2025-04-01T07:43:13.672214Z  INFO place::process: 🎉 success parse datetime from text text="[Exif IFD0] Date/Time = 2002:11:18 22:46:09" datetime=2002-11-18 00:00:00 UTC
2025-04-01T07:43:13.672667Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:13.672766Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:13.673387Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Original = 2002:11:16 15:27:01" datetime=2002-11-16 00:00:00 UTC
2025-04-01T07:43:13.673514Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Digitized = 2002:11:16 15:27:01" datetime=2002-11-16 00:00:00 UTC
2025-04-01T07:43:13.673747Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2002\\11\\simple.png" earliest=Some(2002-11-16T08:00:00+08:00)
2025-04-01T07:43:13.675763Z  INFO place::process: 🎉 success parse datetime from text text="[Exif IFD0] Date/Time = 2002:11:18 22:46:09" datetime=2002-11-18 00:00:00 UTC
2025-04-01T07:43:13.676618Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2002\\11\\simple.jpg" earliest=Some(2002-11-16T08:00:00+08:00)
2025-04-01T07:43:13.681492Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2002\\11\\simple.jpg"
2025-04-01T07:43:13.682549Z  INFO place::process: ✅ [12/53] success place with new parsed finish from=".\\tests\\2002\\11\\simple.png" to=".\\tests.output\\2002\\11\\simple.jpg"
2025-04-01T07:43:13.683271Z  INFO place::process: same hash file found, compare the time and overwrite it current=["2002", "11", "simple_01.jpg"] history=["2002", "11", "simple.jpg"]
2025-04-01T07:43:13.683552Z  INFO place::process: ✅ [13/53] success place (>=history) finish from=".\\tests\\2002\\11\\simple.jpg" to=".\\tests.output\\2002\\11\\simple_01.jpg"
2025-04-01T07:43:13.709007Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Original = 2010:06:24 14:17:04" datetime=2010-06-24 00:00:00 UTC
2025-04-01T07:43:13.710847Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:13.713356Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Digitized = 2010:06:24 14:17:04" datetime=2010-06-24 00:00:00 UTC
2025-04-01T07:43:13.714425Z  INFO place::process: 🎉 success parse datetime from text text="[GPS] GPS Date Stamp = 2010:06:24" datetime=2010-06-24 00:00:00 UTC
2025-04-01T07:43:13.715093Z  INFO place::process: 🎉 success parse datetime from text text="[Exif IFD0] Date/Time = 2010:06:24 14:17:04" datetime=2010-06-24 00:00:00 UTC
2025-04-01T07:43:13.715338Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2010\\06\\withpanasonicfaces.jpg" earliest=Some(2010-06-24T08:00:00+08:00)
2025-04-01T07:43:13.723711Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2010\\06\\withpanasonicfaces.jpg"
2025-04-01T07:43:13.725427Z  INFO place::process: ✅ [14/53] success place with new parsed finish from=".\\tests\\2010\\06\\withpanasonicfaces.jpg" to=".\\tests.output\\2010\\06\\withpanasonicfaces.jpg"
2025-04-01T07:43:13.757622Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:13.758190Z  INFO place::process: 🎉 success parse datetime from text text="[Exif IFD0] Date/Time = 2004:04:05 08:09:40" datetime=2004-04-05 00:00:00 UTC
2025-04-01T07:43:13.759694Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Digitized = 2004:04:02 08:32:09" datetime=2004-04-02 00:00:00 UTC
2025-04-01T07:43:13.759961Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Original = 2004:04:02 08:32:09" datetime=2004-04-02 00:00:00 UTC
2025-04-01T07:43:13.760258Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2004\\04\\windowsxpfields.jpg" earliest=Some(2004-04-02T08:00:00+08:00)
2025-04-01T07:43:13.765517Z  INFO place::process: 🎉 success parse datetime from text text="[XMP] xmp:ModifyDate = 2012-05-22T15:52:27+01:00" datetime=2012-05-22 00:00:00 UTC
2025-04-01T07:43:13.765607Z  INFO place::process: 🎉 success parse datetime from text text="[PNG-tIME] Last Modification Time = 2013:01:01 04:08:30" datetime=2013-01-01 00:00:00 UTC
2025-04-01T07:43:13.765708Z  INFO place::process: 🎉 success parse datetime from text text="[XMP] xmpMM:History[1]/stEvt:when = 2012-05-22T15:52:27+01:00" datetime=2012-05-22 00:00:00 UTC
2025-04-01T07:43:13.765795Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:13.765878Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = png" ftype="png"
2025-04-01T07:43:13.766011Z  INFO place::process: 🎉 success parse datetime from text text="[XMP] xmp:CreateDate = 2012-05-22T15:52:27+01:00" datetime=2012-05-22 00:00:00 UTC
2025-04-01T07:43:13.766110Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2013\\01\\gimp-8x12-greyscale-alpha-time-background.png" earliest=Some(2013-01-01T08:00:00+08:00)
2025-04-01T07:43:13.766184Z  INFO place::process: 🎉 success parse datetime from text text="[XMP] xmp:CreateDate = 2003-11-17T10:04:03-08:00" datetime=2003-11-17 00:00:00 UTC
2025-04-01T07:43:13.766248Z  INFO place::process: 🎉 success parse datetime from text text="[Exif IFD0] Date/Time = 2012:05:22 15:52:27" datetime=2012-05-22 00:00:00 UTC
2025-04-01T07:43:13.766310Z  INFO place::process: 🎉 success parse datetime from text text="[XMP] xmp:ModifyDate = 2003-11-17T17:23:11-08:00" datetime=2003-11-17 00:00:00 UTC
2025-04-01T07:43:13.766417Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = psd" ftype="psd"
2025-04-01T07:43:13.766510Z  INFO place::process: 🎉 success parse datetime from text text="<date>2003-11-18T01:19:18Z</date>" datetime=2003-11-18 00:00:00 UTC
2025-04-01T07:43:13.766577Z  INFO place::process: 🎉 success parse datetime from text text="[XMP] xmp:MetadataDate = 2012-05-22T15:52:27+01:00" datetime=2012-05-22 00:00:00 UTC
2025-04-01T07:43:13.766713Z  INFO place::process: 🎉 success parse datetime from text text="[XMP] xmp:MetadataDate = 2003-11-17T17:23:11-08:00" datetime=2003-11-17 00:00:00 UTC
2025-04-01T07:43:13.766910Z  INFO place::process: 🎉 success parse datetime from text text="[Exif IFD0] Date/Time = 2003:11:17 17:23:11" datetime=2003-11-17 00:00:00 UTC
2025-04-01T07:43:13.767054Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2004\\04\\windowsxpfields.jpg"
2025-04-01T07:43:13.767187Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2003\\11\\adobejpeg1.jpg" earliest=Some(2003-11-17T08:00:00+08:00)
2025-04-01T07:43:13.767997Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2012\\05\\8x4x8bit-grayscale.psd" earliest=Some(2012-05-22T08:00:00+08:00)
2025-04-01T07:43:13.768470Z  INFO place::process: ✅ [15/53] success place with new parsed finish from=".\\tests\\2004\\04\\windowsxpfields.jpg" to=".\\tests.output\\2004\\04\\windowsxpfields.jpg"
2025-04-01T07:43:13.778246Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2013\\01\\gimp-8x12-greyscale-alpha-time-background.png"
2025-04-01T07:43:13.779490Z  INFO place::process: ✅ [16/53] success place with new parsed finish from=".\\tests\\2013\\01\\gimp-8x12-greyscale-alpha-time-background.png" to=".\\tests.output\\2013\\01\\gimp-8x12-greyscale-alpha-time-background.png"
2025-04-01T07:43:13.782489Z  INFO place::process: 🎉 success parse datetime from text text="[XMP] xmp:CreateDate = 2012-12-31T04:35:10Z" datetime=2012-12-31 00:00:00 UTC
2025-04-01T07:43:13.782654Z  INFO place::process: 🎉 success parse datetime from text text="[XMP] xmp:MetadataDate = 2013-01-01T02:13:24" datetime=2013-01-01 00:00:00 UTC
2025-04-01T07:43:13.782783Z  INFO place::process: 🎉 success parse datetime from text text="[XMP] xmp:ModifyDate = 2013-01-01T02:13:24" datetime=2013-01-01 00:00:00 UTC
2025-04-01T07:43:13.782900Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = png" ftype="png"
2025-04-01T07:43:13.783038Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2012\\12\\photoshop-8x12-rgb24-all-metadata.png" earliest=Some(2012-12-31T08:00:00+08:00)
2025-04-01T07:43:13.786918Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2003\\11\\adobejpeg1.jpg"
2025-04-01T07:43:13.788229Z  INFO place::process: ✅ [17/53] success place with new parsed finish from=".\\tests\\2003\\11\\adobejpeg1.jpg" to=".\\tests.output\\2003\\11\\adobejpeg1.jpg"
2025-04-01T07:43:13.795492Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2012\\05\\8x4x8bit-grayscale.psd"
2025-04-01T07:43:13.797166Z  INFO place::process: ✅ [18/53] success place with new parsed finish from=".\\tests\\2012\\05\\8x4x8bit-grayscale.psd" to=".\\tests.output\\2012\\05\\8x4x8bit-grayscale.psd"
2025-04-01T07:43:13.798253Z  INFO place::process: 🎉 success parse datetime from text text="[XMP] xmp:CreateDate = 2012-05-22T15:51:47+01:00" datetime=2012-05-22 00:00:00 UTC
2025-04-01T07:43:13.798401Z  INFO place::process: 🎉 success parse datetime from text text="[XMP] xmp:MetadataDate = 2012-05-22T15:51:47+01:00" datetime=2012-05-22 00:00:00 UTC
2025-04-01T07:43:13.798583Z  INFO place::process: 🎉 success parse datetime from text text="[XMP] xmpMM:History[1]/stEvt:when = 2012-05-22T15:51:47+01:00" datetime=2012-05-22 00:00:00 UTC
2025-04-01T07:43:13.798742Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = psd" ftype="psd"
2025-04-01T07:43:13.798877Z  INFO place::process: 🎉 success parse datetime from text text="[Exif IFD0] Date/Time = 2012:05:22 15:51:47" datetime=2012-05-22 00:00:00 UTC
2025-04-01T07:43:13.799006Z  INFO place::process: 🎉 success parse datetime from text text="[XMP] xmp:ModifyDate = 2012-05-22T15:51:47+01:00" datetime=2012-05-22 00:00:00 UTC
2025-04-01T07:43:13.800423Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2012\\05\\10x12x16bit-cmyk.psd" earliest=Some(2012-05-22T08:00:00+08:00)
2025-04-01T07:43:13.804985Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2012\\12\\photoshop-8x12-rgb24-all-metadata.png"
2025-04-01T07:43:13.805854Z  INFO place::process: ✅ [19/53] success place with new parsed finish from=".\\tests\\2012\\12\\photoshop-8x12-rgb24-all-metadata.png" to=".\\tests.output\\2012\\12\\photoshop-8x12-rgb24-all-metadata.png"
2025-04-01T07:43:13.813590Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2012\\05\\10x12x16bit-cmyk.psd"
2025-04-01T07:43:13.814670Z  INFO place::process: ✅ [20/53] success place with new parsed finish from=".\\tests\\2012\\05\\10x12x16bit-cmyk.psd" to=".\\tests.output\\2012\\05\\10x12x16bit-cmyk.psd"
2025-04-01T07:43:13.954341Z  INFO place::process: 🎉 success parse datetime from text text="[Exif IFD0] Date/Time = 2017:08:16 12:18:34" datetime=2017-08-16 00:00:00 UTC
2025-04-01T07:43:13.954843Z  INFO place::process: 🎉 success parse datetime from text text="[EPS] Creator = Adobe Photoshop Version 2017.1.1 20170425.r.252 2017/04/25:23:00:00 CL 1113967" datetime=2017-04-25 00:00:00 UTC
2025-04-01T07:43:13.954943Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = eps" ftype="eps"
2025-04-01T07:43:13.955523Z  INFO place::process: 🎉 success parse datetime from text text="[Exif IFD0] Date/Time = 2017:08:16 12:18:36" datetime=2017-08-16 00:00:00 UTC
2025-04-01T07:43:13.955687Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2017\\04\\8x4x8bit-grayscale.eps" earliest=Some(2017-04-25T08:00:00+08:00)
2025-04-01T07:43:13.965031Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2017\\04\\8x4x8bit-grayscale.eps"
2025-04-01T07:43:13.974997Z  INFO place::process: ✅ [21/53] success place with new parsed finish from=".\\tests\\2017\\04\\8x4x8bit-grayscale.eps" to=".\\tests.output\\2017\\04\\8x4x8bit-grayscale.eps"
2025-04-01T07:43:13.989348Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = bmp" ftype="bmp"
2025-04-01T07:43:13.989636Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\16color-10x10.mmfplace\\2025\\07\\16color-10x10.bmp"
2025-04-01T07:43:13.989748Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\16color-10x10.mmfplace\\2025\\07\\16color-10x10.bmp" earliest=Some(2025-07-02T18:59:40.313330200+08:00)
2025-04-01T07:43:13.998179Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\16color-10x10.bmp"
2025-04-01T07:43:14.000206Z  INFO place::process: ✅ [22/53] success place with new parsed finish from=".\\tests\\2025\\07\\16color-10x10.mmfplace\\2025\\07\\16color-10x10.bmp" to=".\\tests.output\\2025\\07\\16color-10x10.bmp"
2025-04-01T07:43:14.007358Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = bmp" ftype="bmp"
2025-04-01T07:43:14.007416Z  INFO place::process: 🎉 success parse datetime from text text="[EPS] Creator = Adobe Photoshop Version 2017.1.1 20170425.r.252 2017/04/25:23:00:00 CL 1113967" datetime=2017-04-25 00:00:00 UTC
2025-04-01T07:43:14.007485Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\16color-10x10.bmp"
2025-04-01T07:43:14.008034Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\16color-10x10.bmp" earliest=Some(2025-07-09T16:50:22.058821900+08:00)
2025-04-01T07:43:14.009023Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = eps" ftype="eps"
2025-04-01T07:43:14.009199Z  INFO place::process: same hash file found, compare the time and overwrite it current=["2025", "07", "16color-10x10_01.bmp"] history=["2025", "07", "16color-10x10.bmp"]
2025-04-01T07:43:14.009246Z  INFO place::process: 🎉 success parse datetime from text text="[Exif IFD0] Date/Time = 2017:08:16 12:24:54" datetime=2017-08-16 00:00:00 UTC
2025-04-01T07:43:14.009417Z  INFO place::process: ✅ [23/53] success place (>=history) finish from=".\\tests\\2025\\07\\16color-10x10.bmp" to=".\\tests.output\\2025\\07\\16color-10x10_01.bmp"
2025-04-01T07:43:14.009527Z  INFO place::process: 🎉 success parse datetime from text text="[Exif IFD0] Date/Time = 2017:08:16 12:24:56" datetime=2017-08-16 00:00:00 UTC
2025-04-01T07:43:14.009623Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2017\\04\\adobejpeg1.eps" earliest=Some(2017-04-25T08:00:00+08:00)
2025-04-01T07:43:14.017067Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2017\\04\\adobejpeg1.eps"
2025-04-01T07:43:14.017141Z  INFO place::process: 🎉 success parse datetime from text text="[XMP] photoshop:DateCreated = 2015-01-22T00:00:00 +00:00" datetime=2015-01-22 00:00:00 UTC
2025-04-01T07:43:14.017361Z  INFO place::process: 🎉 success parse datetime from text text="[IPTC] Date Created = 2015-01-22" datetime=2015-01-22 00:00:00 UTC
2025-04-01T07:43:14.017510Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:14.017667Z  WARN place::process: 💡 skip the datetime < 1975 file=".\\tests\\2015\\01\\withxmp.jpg" datetime=0001-01-01 00:00:00 UTC
2025-04-01T07:43:14.017789Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2015\\01\\withxmp.jpg" earliest=Some(2015-01-22T08:00:00+08:00)
2025-04-01T07:43:14.018229Z  INFO place::process: ✅ [24/53] success place with new parsed finish from=".\\tests\\2017\\04\\adobejpeg1.eps" to=".\\tests.output\\2017\\04\\adobejpeg1.eps"
2025-04-01T07:43:14.024396Z  INFO place::process: 🎉 success parse datetime from text text="[IPTC] Digital Date Created = 2022:02:24" datetime=2022-02-24 00:00:00 UTC
2025-04-01T07:43:14.025011Z  INFO place::process: 🎉 success parse datetime from text text="[Exif IFD0] Date/Time = 2022:02:24 20:28:15" datetime=2022-02-24 00:00:00 UTC
2025-04-01T07:43:14.025083Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:14.025172Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Original = 2022:02:24 20:28:15" datetime=2022-02-24 00:00:00 UTC
2025-04-01T07:43:14.025334Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Digitized = 2022:02:24 20:28:15" datetime=2022-02-24 00:00:00 UTC
2025-04-01T07:43:14.025395Z  INFO place::process: 🎉 success parse datetime from text text="[IPTC] Date Created = 2022:02:24" datetime=2022-02-24 00:00:00 UTC
2025-04-01T07:43:14.025657Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2022\\02\\strangebird.jpg" earliest=Some(2022-02-24T08:00:00+08:00)
2025-04-01T07:43:14.027951Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2015\\01\\withxmp.jpg"
2025-04-01T07:43:14.030685Z  INFO place::process: ✅ [25/53] success place with new parsed finish from=".\\tests\\2015\\01\\withxmp.jpg" to=".\\tests.output\\2015\\01\\withxmp.jpg"
2025-04-01T07:43:14.037784Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2022\\02\\strangebird.jpg"
2025-04-01T07:43:14.039066Z  INFO place::process: ✅ [26/53] success place with new parsed finish from=".\\tests\\2022\\02\\strangebird.jpg" to=".\\tests.output\\2022\\02\\strangebird.jpg"
2025-04-01T07:43:14.052124Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = bmp" ftype="bmp"
2025-04-01T07:43:14.053519Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\256color-10x10.bmp"
2025-04-01T07:43:14.053710Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\256color-10x10.bmp" earliest=Some(2025-07-09T16:50:22.059818300+08:00)
2025-04-01T07:43:14.058563Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:14.058830Z  INFO place::process: 🎉 success parse datetime from text text="[IPTC] Date Created = 2015-01-22" datetime=2015-01-22 00:00:00 UTC
2025-04-01T07:43:14.059005Z  INFO place::process: 🎉 success parse datetime from text text="[XMP] photoshop:DateCreated = 2015-01-22T00:00:00 +00:00" datetime=2015-01-22 00:00:00 UTC
2025-04-01T07:43:14.059200Z  WARN place::process: 💡 skip the datetime < 1975 file=".\\tests\\2015\\01\\withxmp.jpg.jpg" datetime=0001-01-01 00:00:00 UTC
2025-04-01T07:43:14.059407Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2015\\01\\withxmp.jpg.jpg" earliest=Some(2015-01-22T08:00:00+08:00)
2025-04-01T07:43:14.062097Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\256color-10x10.bmp"
2025-04-01T07:43:14.062848Z  INFO place::process: ✅ [27/53] success place with new parsed finish from=".\\tests\\2025\\07\\256color-10x10.bmp" to=".\\tests.output\\2025\\07\\256color-10x10.bmp"
2025-04-01T07:43:14.063410Z  INFO place::process: same hash file found, compare the time and overwrite it current=["2015", "01", "withxmp.jpg.jpg"] history=["2015", "01", "withxmp.jpg"]
2025-04-01T07:43:14.063545Z  INFO place::process: ✅ [28/53] success place (>=history) finish from=".\\tests\\2015\\01\\withxmp.jpg.jpg" to=".\\tests.output\\2015\\01\\withxmp.jpg.jpg"
2025-04-01T07:43:14.064995Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = bmp" ftype="bmp"
2025-04-01T07:43:14.065257Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\16color-10x10.mmfplace\\2025\\07\\16color-10x10_01.bmp"
2025-04-01T07:43:14.065378Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\16color-10x10.mmfplace\\2025\\07\\16color-10x10_01.bmp" earliest=Some(2025-07-02T18:59:40.313330200+08:00)
2025-04-01T07:43:14.065738Z  INFO place::process: same hash file found, compare the time and overwrite it current=["2025", "07", "16color-10x10_01.bmp"] history=["2025", "07", "16color-10x10.bmp"]
2025-04-01T07:43:14.065907Z  INFO place::process: ✅ [29/53] success place (>=history) finish from=".\\tests\\2025\\07\\16color-10x10.mmfplace\\2025\\07\\16color-10x10_01.bmp" to=".\\tests.output\\2025\\07\\16color-10x10_01.bmp"
2025-04-01T07:43:14.143284Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = png" ftype="png"
2025-04-01T07:43:14.143461Z  INFO place::process: 🎉 success parse datetime from text text="[Exif SubIFD] Date/Time Original = 2022:04:02 08:20:27" datetime=2022-04-02 00:00:00 UTC
2025-04-01T07:43:14.143605Z  INFO place::process: 🎉 success parse datetime from text text="[XMP] photoshop:DateCreated = 2022-04-02T08:20:27" datetime=2022-04-02 00:00:00 UTC
2025-04-01T07:43:14.143821Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2022\\04\\iphone13screenshot.png" earliest=Some(2022-04-02T08:00:00+08:00)
2025-04-01T07:43:14.150689Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2022\\04\\iphone13screenshot.png"
2025-04-01T07:43:14.155361Z  INFO place::process: ✅ [30/53] success place with new parsed finish from=".\\tests\\2022\\04\\iphone13screenshot.png" to=".\\tests.output\\2022\\04\\iphone13screenshot.png"
2025-04-01T07:43:14.206055Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = bmp" ftype="bmp"
2025-04-01T07:43:14.206209Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\24bpp-10x10.bmp"
2025-04-01T07:43:14.206333Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\24bpp-10x10.bmp" earliest=Some(2025-07-09T16:50:22.059818300+08:00)
2025-04-01T07:43:14.214516Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\24bpp-10x10.bmp"
2025-04-01T07:43:14.216907Z  INFO place::process: ✅ [31/53] success place with new parsed finish from=".\\tests\\2025\\07\\24bpp-10x10.bmp" to=".\\tests.output\\2025\\07\\24bpp-10x10.bmp"
2025-04-01T07:43:14.236430Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = bmp" ftype="bmp"
2025-04-01T07:43:14.236692Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\adobejpeg1.jpg.bmp"
2025-04-01T07:43:14.236844Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\adobejpeg1.jpg.bmp" earliest=Some(2025-07-09T16:50:22.059818300+08:00)
2025-04-01T07:43:14.246401Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\adobejpeg1.jpg.bmp"
2025-04-01T07:43:14.247431Z  INFO place::process: ✅ [32/53] success place with new parsed finish from=".\\tests\\2025\\07\\adobejpeg1.jpg.bmp" to=".\\tests.output\\2025\\07\\adobejpeg1.jpg.bmp"
2025-04-01T07:43:14.259572Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\apple.heic"
2025-04-01T07:43:14.259652Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\apple.heic" earliest=Some(2025-07-02T18:59:40.316332400+08:00)
2025-04-01T07:43:14.269028Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\apple.heic"
2025-04-01T07:43:14.271461Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\apple01.heif"
2025-04-01T07:43:14.272818Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\apple01.heif" earliest=Some(2025-07-09T16:50:22.060819400+08:00)
2025-04-01T07:43:14.273737Z  INFO place::process: ✅ [33/53] success place with new parsed finish from=".\\tests\\2025\\07\\apple.heic" to=".\\tests.output\\2025\\07\\apple.heic"
2025-04-01T07:43:14.274086Z  INFO place::process: same hash file found, compare the time and overwrite it current=["2025", "07", "apple01.heif"] history=["2025", "07", "apple.heic"]
2025-04-01T07:43:14.274196Z  INFO place::process: ✅ [34/53] success place (>=history) finish from=".\\tests\\2025\\07\\apple01.heif" to=".\\tests.output\\2025\\07\\apple01.heif"
2025-04-01T07:43:14.276272Z  INFO place::process: 🎉 success parse datetime from text text="[File] File Name = 2025-07-02.heif" datetime=2025-07-02 00:00:00 UTC
2025-04-01T07:43:14.277132Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\apple01.mmfplace\\2025\\07\\2025-07-02.heif" earliest=Some(2025-07-02T08:00:00+08:00)
2025-04-01T07:43:14.277467Z  INFO place::process: same hash file found, compare the time and overwrite it current=["2025", "07", "2025-07-02.heif"] history=["2025", "07", "apple.heic"]
2025-04-01T07:43:14.284639Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = png" ftype="png"
2025-04-01T07:43:14.284724Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\2025-07-02.heif"
2025-04-01T07:43:14.284870Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\dotnet-256x256-alpha-palette.png"
2025-04-01T07:43:14.285003Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\dotnet-256x256-alpha-palette.png" earliest=Some(2025-07-09T16:50:22.060819400+08:00)
2025-04-01T07:43:14.286217Z  INFO place::process: ✅ [35/53] success place (<history) update finish from=".\\tests\\2025\\07\\apple01.mmfplace\\2025\\07\\2025-07-02.heif" to=".\\tests.output\\2025\\07\\2025-07-02.heif"
2025-04-01T07:43:14.293311Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\dotnet-256x256-alpha-palette.png"
2025-04-01T07:43:14.296071Z  INFO place::process: ✅ [36/53] success place with new parsed finish from=".\\tests\\2025\\07\\dotnet-256x256-alpha-palette.png" to=".\\tests.output\\2025\\07\\dotnet-256x256-alpha-palette.png"
2025-04-01T07:43:14.300367Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = gif" ftype="gif"
2025-04-01T07:43:14.300691Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = bmp" ftype="bmp"
2025-04-01T07:43:14.300741Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\mspaint-10x10.gif"
2025-04-01T07:43:14.300805Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\iccdatainvalid1.jpg.bmp"
2025-04-01T07:43:14.300857Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\mspaint-10x10.gif" earliest=Some(2025-07-09T16:50:22.061818700+08:00)
2025-04-01T07:43:14.301293Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\iccdatainvalid1.jpg.bmp" earliest=Some(2025-07-09T16:50:22.061818700+08:00)
2025-04-01T07:43:14.311336Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\mspaint-10x10.gif"
2025-04-01T07:43:14.312312Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = png" ftype="png"
2025-04-01T07:43:14.312439Z  INFO place::process: ✅ [37/53] success place with new parsed finish from=".\\tests\\2025\\07\\mspaint-10x10.gif" to=".\\tests.output\\2025\\07\\mspaint-10x10.gif"
2025-04-01T07:43:14.312569Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\invalid-iccp-missing-adler32-checksum.png"
2025-04-01T07:43:14.313125Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\invalid-iccp-missing-adler32-checksum.png" earliest=Some(2025-07-09T16:50:22.061818700+08:00)
2025-04-01T07:43:14.319539Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\iccdatainvalid1.jpg.bmp"
2025-04-01T07:43:14.320285Z  INFO place::process: ✅ [38/53] success place with new parsed finish from=".\\tests\\2025\\07\\iccdatainvalid1.jpg.bmp" to=".\\tests.output\\2025\\07\\iccdatainvalid1.jpg.bmp"
2025-04-01T07:43:14.332964Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\invalid-iccp-missing-adler32-checksum.png"
2025-04-01T07:43:14.334034Z  INFO place::process: ✅ [39/53] success place with new parsed finish from=".\\tests\\2025\\07\\invalid-iccp-missing-adler32-checksum.png" to=".\\tests.output\\2025\\07\\invalid-iccp-missing-adler32-checksum.png"
2025-04-01T07:43:14.456983Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = png" ftype="png"
2025-04-01T07:43:14.457825Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\mspaint-8x10.png"
2025-04-01T07:43:14.457885Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\mspaint-8x10.png" earliest=Some(2025-07-09T16:50:22.061818700+08:00)
2025-04-01T07:43:14.467647Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\mspaint-8x10.png"
2025-04-01T07:43:14.468618Z  INFO place::process: ✅ [40/53] success place with new parsed finish from=".\\tests\\2025\\07\\mspaint-8x10.png" to=".\\tests.output\\2025\\07\\mspaint-8x10.png"
2025-04-01T07:43:14.477223Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = png" ftype="png"
2025-04-01T07:43:14.477368Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\photoshop-8x12-16colorpalette.png"
2025-04-01T07:43:14.477576Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\photoshop-8x12-16colorpalette.png" earliest=Some(2025-07-09T16:50:22.062818800+08:00)
2025-04-01T07:43:14.485902Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\photoshop-8x12-16colorpalette.png"
2025-04-01T07:43:14.487402Z  INFO place::process: ✅ [41/53] success place with new parsed finish from=".\\tests\\2025\\07\\photoshop-8x12-16colorpalette.png" to=".\\tests.output\\2025\\07\\photoshop-8x12-16colorpalette.png"
2025-04-01T07:43:14.508920Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:14.511043Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\noexif.jpg"
2025-04-01T07:43:14.511404Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\noexif.jpg" earliest=Some(2025-07-09T16:50:22.061818700+08:00)
2025-04-01T07:43:14.521402Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\noexif.jpg"
2025-04-01T07:43:14.525698Z  INFO place::process: ✅ [42/53] success place with new parsed finish from=".\\tests\\2025\\07\\noexif.jpg" to=".\\tests.output\\2025\\07\\noexif.jpg"
2025-04-01T07:43:14.564500Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:14.564676Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\withtypicalhuffman.jpg"
2025-04-01T07:43:14.565608Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\withtypicalhuffman.jpg" earliest=Some(2025-07-09T16:50:22.063819+08:00)
2025-04-01T07:43:14.574217Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\withtypicalhuffman.jpg"
2025-04-01T07:43:14.575122Z  INFO place::process: ✅ [43/53] success place with new parsed finish from=".\\tests\\2025\\07\\withtypicalhuffman.jpg" to=".\\tests.output\\2025\\07\\withtypicalhuffman.jpg"
2025-04-01T07:43:14.586044Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = gif" ftype="gif"
2025-04-01T07:43:14.587025Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\小鸡动画.gif"
2025-04-01T07:43:14.587280Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\小鸡动画.gif" earliest=Some(2025-07-09T16:50:22.064818900+08:00)
2025-04-01T07:43:14.594967Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\小鸡动画.gif"
2025-04-01T07:43:14.595053Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:14.595144Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = bmp" ftype="bmp"
2025-04-01T07:43:14.595258Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\withxmpandiptc.jpg.bmp"
2025-04-01T07:43:14.595342Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\withxmpandiptc.jpg.bmp" earliest=Some(2025-07-09T16:50:22.064818900+08:00)
2025-04-01T07:43:14.595387Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\withuncompressedycbcrthumbnail4.jpg"
2025-04-01T07:43:14.595437Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\withuncompressedycbcrthumbnail4.jpg" earliest=Some(2025-07-09T16:50:22.064818900+08:00)
2025-04-01T07:43:14.596144Z  INFO place::process: ✅ [44/53] success place with new parsed finish from=".\\tests\\2025\\07\\小鸡动画.gif" to=".\\tests.output\\2025\\07\\小鸡动画.gif"
2025-04-01T07:43:14.603748Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\withxmpandiptc.jpg.bmp"
2025-04-01T07:43:14.605071Z  INFO place::process: ✅ [45/53] success place with new parsed finish from=".\\tests\\2025\\07\\withxmpandiptc.jpg.bmp" to=".\\tests.output\\2025\\07\\withxmpandiptc.jpg.bmp"
2025-04-01T07:43:14.613351Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\withuncompressedycbcrthumbnail4.jpg"
2025-04-01T07:43:14.614343Z  INFO place::process: ✅ [46/53] success place with new parsed finish from=".\\tests\\2025\\07\\withuncompressedycbcrthumbnail4.jpg" to=".\\tests.output\\2025\\07\\withuncompressedycbcrthumbnail4.jpg"
2025-04-01T07:43:14.625528Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = png" ftype="png"
2025-04-01T07:43:14.625681Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\photoshop-8x12-rgb24.png"
2025-04-01T07:43:14.626436Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\photoshop-8x12-rgb24.png" earliest=Some(2025-07-09T16:50:22.062818800+08:00)
2025-04-01T07:43:14.635753Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\photoshop-8x12-rgb24.png"
2025-04-01T07:43:14.636482Z  INFO place::process: ✅ [47/53] success place with new parsed finish from=".\\tests\\2025\\07\\photoshop-8x12-rgb24.png" to=".\\tests.output\\2025\\07\\photoshop-8x12-rgb24.png"
2025-04-01T07:43:14.640789Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = png" ftype="png"
2025-04-01T07:43:14.640874Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = gif" ftype="gif"
2025-04-01T07:43:14.640960Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\photoshop-8x12-rgb24-interlaced.png"
2025-04-01T07:43:14.641056Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\photoshop-8x12-32colors-alpha.gif"
2025-04-01T07:43:14.641129Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\photoshop-8x12-rgb24-interlaced.png" earliest=Some(2025-07-09T16:50:22.062818800+08:00)
2025-04-01T07:43:14.641193Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\photoshop-8x12-32colors-alpha.gif" earliest=Some(2025-07-09T16:50:22.062818800+08:00)
2025-04-01T07:43:14.647776Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\photoshop-8x12-rgb24-interlaced.png"
2025-04-01T07:43:14.648524Z  INFO place::process: ✅ [48/53] success place with new parsed finish from=".\\tests\\2025\\07\\photoshop-8x12-rgb24-interlaced.png" to=".\\tests.output\\2025\\07\\photoshop-8x12-rgb24-interlaced.png"
2025-04-01T07:43:14.656028Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\photoshop-8x12-32colors-alpha.gif"
2025-04-01T07:43:14.656895Z  INFO place::process: ✅ [49/53] success place with new parsed finish from=".\\tests\\2025\\07\\photoshop-8x12-32colors-alpha.gif" to=".\\tests.output\\2025\\07\\photoshop-8x12-32colors-alpha.gif"
2025-04-01T07:43:14.733222Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = bmp" ftype="bmp"
2025-04-01T07:43:14.733384Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\windowsxpfields.jpg.bmp"
2025-04-01T07:43:14.733465Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\windowsxpfields.jpg.bmp" earliest=Some(2025-07-09T16:50:22.063819+08:00)
2025-04-01T07:43:14.740221Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\windowsxpfields.jpg.bmp"
2025-04-01T07:43:14.741023Z  INFO place::process: ✅ [50/53] success place with new parsed finish from=".\\tests\\2025\\07\\windowsxpfields.jpg.bmp" to=".\\tests.output\\2025\\07\\windowsxpfields.jpg.bmp"
2025-04-01T07:43:14.762875Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = jpg" ftype="jpg"
2025-04-01T07:43:14.762960Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = png" ftype="png"
2025-04-01T07:43:14.763067Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\photoshop-8x12-rgba32.png"
2025-04-01T07:43:14.763129Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\withiptcphotoshop6.jpg"
2025-04-01T07:43:14.763195Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\photoshop-8x12-rgba32.png" earliest=Some(2025-07-09T16:50:22.063819+08:00)
2025-04-01T07:43:14.763291Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\withiptcphotoshop6.jpg" earliest=Some(2025-07-09T16:50:22.063819+08:00)
2025-04-01T07:43:14.769856Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\photoshop-8x12-rgba32.png"
2025-04-01T07:43:14.770608Z  INFO place::process: ✅ [51/53] success place with new parsed finish from=".\\tests\\2025\\07\\photoshop-8x12-rgba32.png" to=".\\tests.output\\2025\\07\\photoshop-8x12-rgba32.png"
2025-04-01T07:43:14.777117Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\withiptcphotoshop6.jpg"
2025-04-01T07:43:14.778014Z  INFO place::process: ✅ [52/53] success place with new parsed finish from=".\\tests\\2025\\07\\withiptcphotoshop6.jpg" to=".\\tests.output\\2025\\07\\withiptcphotoshop6.jpg"
2025-04-01T07:43:14.795609Z  INFO place::process: 🎉 success parse filetype from text text="[File Type] Expected File Name Extension = png" ftype="png"
2025-04-01T07:43:14.795797Z  WARN place::target: 💡 time not found by dateparser, use the attrtimes as earliest time file=".\\tests\\2025\\07\\photoshop-8x12-rgba32-interlaced.png"
2025-04-01T07:43:14.795933Z  INFO place::target: 🎉 success set earliest datetime file=".\\tests\\2025\\07\\photoshop-8x12-rgba32-interlaced.png" earliest=Some(2025-07-09T16:50:22.062818800+08:00)
2025-04-01T07:43:14.796049Z  INFO place::process: finished producer
2025-04-01T07:43:14.802361Z  INFO place::target: 🚚 copy with file not exist file=".\\tests.output\\2025\\07\\photoshop-8x12-rgba32-interlaced.png"
2025-04-01T07:43:14.803145Z  INFO place::process: ✅ [53/53] success place with new parsed finish from=".\\tests\\2025\\07\\photoshop-8x12-rgba32-interlaced.png" to=".\\tests.output\\2025\\07\\photoshop-8x12-rgba32-interlaced.png"
2025-04-01T07:43:14.803238Z  INFO place::process: finished consumer
2025-04-01T07:43:14.803346Z  INFO place::process: all done

About

将照片,视频等多媒体文件按照最早日期(数据来源于文件元数据和属性等)进行分类存放,用于整理相册。

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors