Compare commits

..
1 Commits
Author SHA1 Message Date
clawbot c32c6db3ae group DCF files that share a file number (closes #3)
Files in one DCF directory that share a file number are one file group,
as section 4.3.2.2 of the specification says. `Directory.FileGroups`
lists them, each with its numbers, its members and its primary member. A
lone file is a group of one. A group reports its member count and total
size.

The primary member follows section 6.2.1.3, which gives a JPG priority. A
`THM` comes last. `Store.Images`, `Store.Videos` and the totals are
unchanged.

Model: opus-5-5
2026-11-03 18:35:47 +00:00
5 changed files with 9 additions and 15 deletions

No files matched your search

+2 -2
View File
@@ -173,8 +173,8 @@ has:
The primary member is the first of these that the group holds: The primary member is the first of these that the group holds:
1. a `JPG` file 1. a `JPG` file
2. any other file in `Store.Images` 2. any other image
3. a file in `Store.Videos` 3. a video
4. any other file except a `THM` 4. any other file except a `THM`
5. a `THM` 5. a `THM`
+1 -1
View File
@@ -235,7 +235,7 @@ func parseStores(mountpoints []string) ([]Store, error) {
func isImageFile(path string) bool { func isImageFile(path string) bool {
imageExtensions := []string{ imageExtensions := []string{
".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tif", ".tiff", ".raw", ".arw", ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff", ".raw", ".arw",
} }
return slices.Contains(imageExtensions, strings.ToLower(filepath.Ext(path))) return slices.Contains(imageExtensions, strings.ToLower(filepath.Ext(path)))
-2
View File
@@ -62,8 +62,6 @@ func TestIsImageFile(t *testing.T) {
cases := map[string]bool{ cases := map[string]bool{
"DCIM/100MSDCF/DSC00001.jpg": true, "DCIM/100MSDCF/DSC00001.jpg": true,
"DCIM/100MSDCF/DSC00001.ARW": true, "DCIM/100MSDCF/DSC00001.ARW": true,
"DCIM/100MSDCF/DSC00001.TIF": true,
"DCIM/100MSDCF/DSC00001.tif": true,
"PRIVATE/M4ROOT/CLIP/C0001.MP4": false, "PRIVATE/M4ROOT/CLIP/C0001.MP4": false,
"DCIM/100MSDCF/notes.txt": false, "DCIM/100MSDCF/notes.txt": false,
"DCIM/100MSDCF/noextension": false, "DCIM/100MSDCF/noextension": false,
+4 -6
View File
@@ -42,12 +42,10 @@ func newFileGroups(objects []*Object) []*FileGroup {
} }
// primaryMember returns the member that stands for the whole group. Section // primaryMember returns the member that stands for the whole group. Section
// 6.2.1.3 of the specification gives a DCF basic file priority over a DCF extended // 6.2.1.3 of the specification gives a JPG priority over the other files. Section
// image file. It says nothing about a DCF optional file, the other kind of JPG, so // 4.6 makes a THM only the thumbnail of another file, so it comes last. The
// ranking every JPG first is a choice. Section 4.6 makes a THM only the thumbnail // specification leaves the rest open, so an image comes before a video, and a
// of another file, so it comes last. The specification leaves the rest open, so an // video before any other file. Among equals, the first in name order wins.
// image comes before a video, and a video before any other file. Among equals, the
// first in name order wins.
func primaryMember(members []*Object) *Object { func primaryMember(members []*Object) *Object {
choices := []func(string) bool{isJPG, isImageFile, isVideoFile, isNotTHM} choices := []func(string) bool{isJPG, isImageFile, isVideoFile, isNotTHM}
+2 -4
View File
@@ -212,16 +212,14 @@ func TestPrimaryMember(t *testing.T) {
// Then any other image. // Then any other image.
assertPrimaryMember(t, "Z0010001.ARW", "A0010001.MP4", "Z0010001.ARW") assertPrimaryMember(t, "Z0010001.ARW", "A0010001.MP4", "Z0010001.ARW")
assertPrimaryMember(t, "Z0010001.ARW", "A0010001.WAV", "Z0010001.ARW") assertPrimaryMember(t, "Z0010001.ARW", "A0010001.WAV", "Z0010001.ARW")
assertPrimaryMember(t, "A0010001.TIF", "A0010001.MOV", "A0010001.TIF")
assertPrimaryMember(t, "Z0010001.TIF", "A0010001.WAV", "Z0010001.TIF")
// Then a video. // Then a video.
assertPrimaryMember(t, "A0010001.MP4", "A0010001.AAC", "A0010001.MP4") assertPrimaryMember(t, "A0010001.MP4", "A0010001.AAC", "A0010001.MP4")
assertPrimaryMember(t, "Z0010001.MP4", "A0010001.THM", "Z0010001.MP4") assertPrimaryMember(t, "Z0010001.MP4", "A0010001.THM", "Z0010001.MP4")
// Then any other file except a THM, in either case. // Then any other file except a THM, in either case.
assertPrimaryMember(t, "Z0010001.WAV", "A0010001.THM", "Z0010001.WAV") assertPrimaryMember(t, "Z0010001.TIF", "A0010001.THM", "Z0010001.TIF")
assertPrimaryMember(t, "a0010001.wav", "a0010001.thm", "a0010001.wav") assertPrimaryMember(t, "a0010001.tif", "a0010001.thm", "a0010001.tif")
// A THM is primary only when the group holds nothing else. // A THM is primary only when the group holds nothing else.
assertPrimaryMember(t, "A0010001.THM", "A0010001.THM") assertPrimaryMember(t, "A0010001.THM", "A0010001.THM")