Review-first photo backup merge tool with exact and visual comparison
git clone https://github.com/ppyj663/moon-photo-merge.git
cd moon-photo-mergemoon update
moon run cmd/main -- scan "D:\Photos" "E:\PhoneBackup" "D:\PhotoReview"moon run cmd/main -- check "D:\Photos" "E:\PhoneBackup" "C:\Users\你的用户名\Downloads\approved.json"
moon run cmd/main -- apply "D:\Photos" "E:\PhoneBackup" "C:\Users\你的用户名\Downloads\approved.json"moon run cmd/main -- scan "D:\Photos" "E:\PhoneBackup" "D:\PhotoReview" --similarity-limit 8
moon run cmd/main -- check "D:\Photos" "E:\PhoneBackup" "C:\Users\你的用户名\Downloads\approved.json" --similarity-limit 8
moon run cmd/main -- apply "D:\Photos" "E:\PhoneBackup" "C:\Users\你的用户名\Downloads\approved.json" --similarity-limit 8| 分类 | 含义 | 默认选择 |
|---|---|---|
| 完全重复 | 文件字节 SHA-256 相同 | 不可选择 |
| 疑似相似 | 可解码,亮度和颜色的简易视觉指纹接近 | 不选择,人工核对 |
| 同名冲突 | 文件名相同但内容不同 | 不选择,人工核对 |
| 可新增 | 未命中上述情况 | 选择 |
| 图片无法解码 | 扩展名符合,但 JPEG/PNG 解码失败 | 不可选择 |
node scripts/demo.mjsmoon test --target js
node scripts/integration-test.mjsnode scripts/benchmark.mjs "D:\Photos" "E:\PhoneBackup" --runs 5
node scripts/benchmark.mjs "D:\Photos" "E:\PhoneBackup" --runs 5 --output "D:\PhotoReview\benchmark-summary.json"pub(all) struct ExtensionStats {
extension : String
count : Int
readable : Int
unreadable : Int
bytes : Int64
}pub(all) struct ImageInfo {
width : Int
height : Int
luminance : Int
red : Int
green : Int
blue : Int
}pub(all) struct InventorySummary {
total : Int
readable : Int
unreadable : Int
bytes : Int64
minimum_size : Int
maximum_size : Int
average_size : Int64
landscape : Int
portrait : Int
square : Int
unknown_dimensions : Int
pixels : Int64
duplicate_file_groups : Int
duplicate_files : Int
}pub(all) struct Photo {
path : String
name : String
digest : String
fingerprint : String?
size : Int
image_info : ImageInfo?
}pub(all) struct PlanSummary {
total : Int
exact : Int
similar : Int
conflict : Int
new_count : Int
unreadable : Int
copyable : Int
needs_review : Int
selected_by_default : Int
total_bytes : Int64
copyable_bytes : Int64
review_bytes : Int64
}pub(all) struct ReviewQuery {
text : String
kind : Kind?
minimum_size : Int?
maximum_size : Int?
sort : ReviewSort
offset : Int
page_size : Int
}pub(all) enum ReviewSort {
PathAscending
NameAscending
SizeDescending
SimilarityAscending
CategoryThenPath
} derive(Eq)pub(all) struct SimilarityGroup {
paths : Array[String]
pair_count : Int
minimum_distance : Int
maximum_distance : Int
}pub(all) struct SimilarityGroupsSummary {
groups : Int
members : Int
pairs : Int
largest_group : Int
}pub(all) struct SimilarityHit {
path : String
name : String
digest : String
distance : Int
}fn SimilarityIndex::nearest(self : SimilarityIndex, signature : String, limit : Int) -> SimilarityHit?fn SimilarityIndex::search(self : SimilarityIndex, signature : String, limit : Int) -> Array[SimilarityHit]pub(all) enum SizeBand {
Under256KiB
From256KiBTo1MiB
From1MiBTo8MiB
From8MiBTo32MiB
AtLeast32MiB
} derive(Eq)fn csv_field(value : String) -> Stringfn fingerprint(data : Bytes, extension : String) -> String?fn format_bytes(bytes : Int64) -> Stringfn inventory_json(summary : InventorySummary, extensions : Array[ExtensionStats], size_bands : Array[SizeBandStats]) -> Stringfn json_string(value : String) -> Stringfn normalized_extension(name : String) -> Stringfn review_page_number(offset : Int, page_size : Int) -> Intfn review_page_start(page_number : Int, page_size : Int) -> IntInstall
Download zipReview-first photo backup merge tool with exact and visual comparison