diff --git a/docs/releases.md b/docs/releases.md index b275631..db8571d 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -1,5 +1,14 @@ # Release notes +## Release 0.9.0 + +### fix: stack: Samsung #99 +Now,Samsung bursts are detected + +### fix:stack: Huawei Nexus 6P #100 +Now, Huawei bursts are detected + + ## Release 0.9.0 ### feat:transfer google-photo favorite to immich diff --git a/helpers/stacking/stack.go b/helpers/stacking/stack.go index f127382..87fcfb5 100644 --- a/helpers/stacking/stack.go +++ b/helpers/stacking/stack.go @@ -104,7 +104,7 @@ func (sb *StackBuilder) ProcessAsset(ID string, fileName string, captureDate tim // bool -> is this is the cover if the burst type stackMatcher func(name string) (bool, string, bool) -var stackMatchers = []stackMatcher{huaweiBurst, pixelBurst, samsungBurst} +var stackMatchers = []stackMatcher{nexusBurst, huaweiBurst, pixelBurst, samsungBurst} var huaweiBurstRE = regexp.MustCompile(`^(.*)(_BURST\d+)(_COVER)?(\..*)$`) @@ -136,6 +136,16 @@ func samsungBurst(name string) (bool, string, bool) { return true, parts[1], parts[2] == "001" } +var nexusBurstRE = regexp.MustCompile(`^\d{5}IMG_\d{5}_(BURST\d{14})(_COVER)?\..{3}$`) + +func nexusBurst(name string) (bool, string, bool) { + parts := nexusBurstRE.FindStringSubmatch(name) + if len(parts) == 0 { + return false, "", false + } + return true, parts[1], parts[2] == "001" +} + func (sb *StackBuilder) Stacks() []Stack { keys := gen.MapFilterKeys(sb.stacks, func(i Stack) bool { return len(i.IDs) > 1 diff --git a/helpers/stacking/statck_test.go b/helpers/stacking/statck_test.go index ac75d2b..0603b3b 100644 --- a/helpers/stacking/statck_test.go +++ b/helpers/stacking/statck_test.go @@ -181,6 +181,23 @@ func Test_Stack(t *testing.T) { }, }, }, + { + name: " stack: Huawei Nexus 6P #100 ", + input: []asset{ + {ID: "1", FileName: "00001IMG_00001_BURST20171111030039.jpg", DateTaken: metadata.TakeTimeFromName("00001IMG_00001_BURST20171111030039.jpg")}, + {ID: "2", FileName: "00002IMG_00002_BURST20171111030039.jpg", DateTaken: metadata.TakeTimeFromName("00002IMG_00002_BURST20171111030039.jpg")}, + {ID: "3", FileName: "00003IMG_00003_BURST20171111030039_COVER.jpg", DateTaken: metadata.TakeTimeFromName("00003IMG_00003_BURST20171111030039_COVER.jpg")}, + }, + want: []Stack{ + { + CoverID: "1", + IDs: []string{"2", "3"}, + Date: metadata.TakeTimeFromName("00001IMG_00001_BURST20171111030039.jpg"), + Names: []string{"00001IMG_00001_BURST20171111030039.jpg", "00002IMG_00002_BURST20171111030039.jpg", "00003IMG_00003_BURST20171111030039_COVER.jpg"}, + StackType: StackBurst, + }, + }, + }, } for _, tt := range tc { diff --git a/immich/metadata/namesdate.go b/immich/metadata/namesdate.go index 9fc7b12..f5be48a 100644 --- a/immich/metadata/namesdate.go +++ b/immich/metadata/namesdate.go @@ -17,13 +17,20 @@ import ( // Return the value time.Time{} when there isn't any date in the name, or if the date is invalid like 2023-02-30 20:65:00 var guessTimePattern = regexp.MustCompile(`(\d{4})\D?(\d\d)\D?(\d\d)\D?(\d\d)?\D?(\d\d)?\D?(\d\d)?`) +var nexusBurstRE = regexp.MustCompile(`^\d{5}IMG_\d{5}_BURST(\d{14})(_COVER)?\..{3}$`) func TakeTimeFromName(name string) time.Time { local, err := tzone.Local() if err != nil { panic(err) } - mm := guessTimePattern.FindStringSubmatch(name) + + // check for known exceptions... + mm := nexusBurstRE.FindStringSubmatch(name) + if len(mm) > 2 { + name = mm[1] + } + mm = guessTimePattern.FindStringSubmatch(name) m := [7]int{} if len(mm) >= 4 { for i := range mm { diff --git a/immich/metadata/namesdate_test.go b/immich/metadata/namesdate_test.go index 75471fb..a4cdec2 100644 --- a/immich/metadata/namesdate_test.go +++ b/immich/metadata/namesdate_test.go @@ -60,6 +60,10 @@ func TestTakeTimeFromName(t *testing.T) { name: "20223112-125200", expected: time.Time{}, }, + { + name: "00015IMG_00015_BURST20171111030039_COVER.jpg", + expected: time.Date(2017, 11, 11, 4, 0, 39, 0, local), + }, } for _, tt := range tests { diff --git a/readme.md b/readme.md index 178df6b..44635d8 100644 --- a/readme.md +++ b/readme.md @@ -97,6 +97,8 @@ Currently the bursts following this schema are detected: - xxxxx_BURSTnnn_COVER.* - xxxxx.RAW-01.COVER.jpg and xxxxx.RAW-02.ORIGINAL.dng - xxxxx.RAW-01.MP.COVER.jpg and xxxxx.RAW-02.ORIGINAL.dng +- xxxxxIMG_xxxxx_BURSTyyyymmddhhmmss.jpg and xxxxxIMG_xxxxx_BURSTyyyymmddhhmmss_COVER.jpg (Huawei Nexus 6P) +- yyyymmdd_hhmmss_xxx.jpg (Samsung) All images must be taken during the same minute. The COVER image will be the parent image of the stack