diff --git a/browser/gp/googlephotos.go b/browser/gp/googlephotos.go
index c3162a29..6457b95a 100644
--- a/browser/gp/googlephotos.go
+++ b/browser/gp/googlephotos.go
@@ -2,7 +2,6 @@ package gp
import (
"context"
- "fmt"
"io/fs"
"path"
"path/filepath"
@@ -20,55 +19,38 @@ import (
)
type Takeout struct {
- fsyss []fs.FS
- catalogs dirCatalog // file catalogs by directory in the set of the all takeout parts
- jsonByYear map[jsonKey]*GoogleMetaData // assets by year of capture and base name
- uploaded map[fileKey]any // track files already uploaded
- albums map[string]browser.LocalAlbum // tack album names by folder
- log *fileevent.Recorder
- sm immich.SupportedMedia
- banned namematcher.List // Banned files
+ fsyss []fs.FS
+ catalogs map[string]directoryCatalog // file catalogs by directory in the set of the all takeout parts
+ albums map[string]browser.LocalAlbum // track album names by folder
+ log *fileevent.Recorder
+ sm immich.SupportedMedia
+
+ banned namematcher.List // Banned files
+ acceptMissingJSON bool
}
-// dirCatalog collects all directory catalogs
-type dirCatalog map[string]directoryCatalog // by directory in the walker
-
// directoryCatalog captures all files in a given directory
type directoryCatalog struct {
- unMatchedFiles map[string]fileInfo // map of fileInfo by base name
- matchedFiles map[string]fileInfo // map of fileInfo by base name
+ jsons map[string]*GoogleMetaData // JSONs in the catalog by base name
+ unMatchedFiles map[string]*assetFile // files to be matched map by base name
+ matchedFiles map[string]*assetFile // files matched by base name
}
-// fileInfo keep information collected during pass one
-type fileInfo struct {
+// assetFile keep information collected during pass one
+type assetFile struct {
fsys fs.FS // Remember in which part of the archive the the file
base string // Remember the original file name
length int // file length in bytes
- count int // Track duplicates
md *GoogleMetaData // will point to the associated metadata
}
-// fileKey is the key of the uploaded files map
-// GP can't have duplicate JSON name in the same year.
-type fileKey struct {
- base string
- length int
- year int
-}
-
-// jsonKey allow to map jsons by base name and year of capture
-type jsonKey struct {
- name string
- year int
-}
-
func NewTakeout(ctx context.Context, l *fileevent.Recorder, sm immich.SupportedMedia, fsyss ...fs.FS) (*Takeout, error) {
to := Takeout{
- fsyss: fsyss,
- jsonByYear: map[jsonKey]*GoogleMetaData{},
- albums: map[string]browser.LocalAlbum{},
- log: l,
- sm: sm,
+ fsyss: fsyss,
+ catalogs: map[string]directoryCatalog{},
+ albums: map[string]browser.LocalAlbum{},
+ log: l,
+ sm: sm,
}
return &to, nil
@@ -79,11 +61,15 @@ func (to *Takeout) SetBannedFiles(banned namematcher.List) *Takeout {
return to
}
+func (to *Takeout) SetAcceptMissingJSON(flag bool) *Takeout {
+ to.acceptMissingJSON = flag
+ return to
+}
+
// Prepare scans all files in all walker to build the file catalog of the archive
// metadata files content is read and kept
func (to *Takeout) Prepare(ctx context.Context) error {
- to.catalogs = dirCatalog{}
for _, w := range to.fsyss {
err := to.passOneFsWalk(ctx, w)
if err != nil {
@@ -113,15 +99,20 @@ func (to *Takeout) passOneFsWalk(ctx context.Context, w fs.FS) error {
dir = strings.TrimSuffix(dir, "/")
ext := strings.ToLower(path.Ext(base))
- dirCatalog := to.catalogs[dir]
- if dirCatalog.unMatchedFiles == nil {
- dirCatalog.unMatchedFiles = map[string]fileInfo{}
+ dirCatalog, ok := to.catalogs[dir]
+ if !ok {
+ dirCatalog.jsons = map[string]*GoogleMetaData{}
+ dirCatalog.unMatchedFiles = map[string]*assetFile{}
+ dirCatalog.matchedFiles = map[string]*assetFile{}
}
- if dirCatalog.matchedFiles == nil {
- dirCatalog.matchedFiles = map[string]fileInfo{}
+ if _, ok := dirCatalog.unMatchedFiles[base]; ok {
+ // to.log.Record(ctx, fileevent.AnalysisLocalDuplicate, nil, name)
+ return nil
}
+
finfo, err := d.Info()
if err != nil {
+ to.log.Record(ctx, fileevent.Error, nil, name, "error", err.Error())
return err
}
switch ext {
@@ -130,7 +121,8 @@ func (to *Takeout) passOneFsWalk(ctx context.Context, w fs.FS) error {
if err == nil {
switch {
case md.isAsset():
- to.addJSON(dir, base, md)
+ md.foundInPaths = append(md.foundInPaths, dir)
+ dirCatalog.jsons[base] = md
to.log.Record(ctx, fileevent.DiscoveredSidecar, nil, name, "type", "asset metadata", "title", md.Title)
case md.isAlbum():
a := to.albums[dir]
@@ -172,15 +164,10 @@ func (to *Takeout) passOneFsWalk(ctx context.Context, w fs.FS) error {
return nil
}
- fi := dirCatalog.unMatchedFiles[base]
- fi.base = base
- fi.fsys = w
- fi.length = int(finfo.Size())
- fi.count++
- dirCatalog.unMatchedFiles[base] = fi
- if fi.count > 1 {
- // #380 not all GP duplicates are detected correctly, counters are wrong
- to.log.Record(ctx, fileevent.AnalysisLocalDuplicate, nil, name)
+ dirCatalog.unMatchedFiles[base] = &assetFile{
+ fsys: w,
+ base: base,
+ length: int(finfo.Size()),
}
}
to.catalogs[dir] = dirCatalog
@@ -190,36 +177,6 @@ func (to *Takeout) passOneFsWalk(ctx context.Context, w fs.FS) error {
return err
}
-// addJSON stores metadata and all paths where the combo base+year has been found
-func (to *Takeout) addJSON(dir, base string, md *GoogleMetaData) {
- k := jsonKey{
- name: base,
- year: md.PhotoTakenTime.Time().Year(),
- }
-
- if mdPresent, ok := to.jsonByYear[k]; ok {
- md = mdPresent
- }
- md.foundInPaths = append(md.foundInPaths, dir)
- to.jsonByYear[k] = md
-}
-
-type matcherFn func(jsonName string, fileName string, sm immich.SupportedMedia) bool
-
-// matchers is a list of matcherFn from the most likely to be used to the least one
-var matchers = []struct {
- name string
- fn matcherFn
-}{
- {name: "normalMatch", fn: normalMatch},
- {name: "livePhotoMatch", fn: livePhotoMatch},
- {name: "matchWithOneCharOmitted", fn: matchWithOneCharOmitted},
- {name: "matchVeryLongNameWithNumber", fn: matchVeryLongNameWithNumber},
- {name: "matchDuplicateInYear", fn: matchDuplicateInYear},
- {name: "matchEditedName", fn: matchEditedName},
- {name: "matchForgottenDuplicates", fn: matchForgottenDuplicates},
-}
-
// solvePuzzle prepares metadata with information collected during pass one for each accepted files
//
// JSON files give important information about the relative photos / movies:
@@ -228,7 +185,9 @@ var matchers = []struct {
// - The GPS coordinates (will be useful in a future release)
//
// Each JSON is checked. JSON is duplicated in albums folder.
-// Associated files with the JSON can be found in the JSON's folder, or in the Year photos.
+// --Associated files with the JSON can be found in the JSON's folder, or in the Year photos.--
+// ++JSON and files are located in the same folder
+///
// Once associated and sent to the main program, files are tagged for not been associated with an other one JSON.
// Association is done with the help of a set of matcher functions. Each one implement a rule
//
@@ -241,62 +200,63 @@ var matchers = []struct {
// - of course those rules are likely to collide. They have to be applied from the most common to the least one.
// - sometimes the file isn't in the same folder than the json... It can be found in Year's photos folder
//
-// The duplicates files (same name, same length in bytes) found in the local source are discarded before been presented to the immich server.
+// --The duplicates files (same name, same length in bytes) found in the local source are discarded before been presented to the immich server.
+// ++ Duplicates are presented to the next layer to allow the album handling
//
+// To solve the puzzle, each directory is checked with all matchers in the order of the most common to the least.
-func (to *Takeout) solvePuzzle(ctx context.Context) error {
- jsonKeys := gen.MapKeys(to.jsonByYear)
- sort.Slice(jsonKeys, func(i, j int) bool {
- yd := jsonKeys[i].year - jsonKeys[j].year
- switch {
- case yd < 0:
- return true
- case yd > 0:
- return false
- }
- return jsonKeys[i].name < jsonKeys[j].name
- })
+type matcherFn func(jsonName string, fileName string, sm immich.SupportedMedia) bool
- // For the most common matcher to the least,
- for _, matcher := range matchers {
- // Check files that match each json files
- for _, k := range jsonKeys {
- md := to.jsonByYear[k]
-
- // list of paths where to search the assets: paths where this json has been found + year path in all of the walkers
- paths := map[string]any{}
- paths[path.Join(path.Dir(md.foundInPaths[0]), fmt.Sprintf("Photos from %d", md.PhotoTakenTime.Time().Year()))] = nil
- for _, d := range md.foundInPaths {
- paths[d] = nil
- }
- for d := range paths {
- l := to.catalogs[d]
- for f := range l.unMatchedFiles {
+// matchers is a list of matcherFn from the most likely to be used to the least one
+var matchers = []struct {
+ name string
+ fn matcherFn
+}{
+ {name: "normalMatch", fn: normalMatch},
+ {name: "livePhotoMatch", fn: livePhotoMatch},
+ {name: "matchWithOneCharOmitted", fn: matchWithOneCharOmitted},
+ {name: "matchVeryLongNameWithNumber", fn: matchVeryLongNameWithNumber},
+ {name: "matchDuplicateInYear", fn: matchDuplicateInYear},
+ {name: "matchEditedName", fn: matchEditedName},
+ {name: "matchForgottenDuplicates", fn: matchForgottenDuplicates},
+}
+
+func (to *Takeout) solvePuzzle(ctx context.Context) error {
+ dirs := gen.MapKeys(to.catalogs)
+ sort.Strings(dirs)
+ for _, dir := range dirs {
+ cat := to.catalogs[dir]
+ jsons := gen.MapKeys(cat.jsons)
+ sort.Strings(jsons)
+ for _, matcher := range matchers {
+ for _, json := range jsons {
+ md := cat.jsons[json]
+ for f := range cat.unMatchedFiles {
select {
case <-ctx.Done():
return ctx.Err()
default:
- if matcher.fn(k.name, f, to.sm) {
- i := l.unMatchedFiles[f]
+ if matcher.fn(json, f, to.sm) {
+ i := cat.unMatchedFiles[f]
i.md = md
- l.matchedFiles[f] = i
- to.log.Record(ctx, fileevent.AnalysisAssociatedMetadata, l.unMatchedFiles[f], filepath.Join(d, f), "json", k.name, "year", k.year, "size", i.length, "matcher", matcher.name)
- delete(l.unMatchedFiles, f)
+ cat.matchedFiles[f] = i
+ to.log.Record(ctx, fileevent.AnalysisAssociatedMetadata, cat.unMatchedFiles[f], filepath.Join(dir, f), "json", json, "size", i.length, "matcher", matcher.name)
+ delete(cat.unMatchedFiles, f)
}
}
}
- to.catalogs[d] = l
}
}
- }
-
- paths := gen.MapKeys(to.catalogs)
- sort.Strings(paths)
- for _, p := range paths {
- files := gen.MapKeys(to.catalogs[p].unMatchedFiles)
+ to.catalogs[dir] = cat
+ files := gen.MapKeys(cat.unMatchedFiles)
sort.Strings(files)
for _, f := range files {
- to.log.Record(ctx, fileevent.AnalysisMissingAssociatedMetadata, to.catalogs[p].unMatchedFiles[f], filepath.Join(p, f))
+ to.log.Record(ctx, fileevent.AnalysisMissingAssociatedMetadata, f, filepath.Join(dir, f))
+ if to.acceptMissingJSON {
+ cat.matchedFiles[f] = cat.unMatchedFiles[f]
+ delete(cat.unMatchedFiles, f)
+ } else {
+ }
}
}
return nil
@@ -335,21 +295,24 @@ func livePhotoMatch(jsonName string, fileName string, sm immich.SupportedMedia)
// PXL_20230809_203449253.LONG_EXPOSURE-02.ORIGIN.json
// PXL_20230809_203449253.LONG_EXPOSURE-02.ORIGINA.jpg
//
-// 05yqt21kruxwwlhhgrwrdyb6chhwszi9bqmzu16w0 2.jp.json
+// 05yqt21kruxwwlhhgrwrdyb6chhwszi9bqmzu16w0 2.jp.json <-- match also with LivePhoto matcher
// 05yqt21kruxwwlhhgrwrdyb6chhwszi9bqmzu16w0 2.jpg
//
// 😀😃😄😁😆😅😂🤣🥲☺️😊😇🙂🙃😉😌😍🥰😘😗😙😚😋.json
// 😀😃😄😁😆😅😂🤣🥲☺️😊😇🙂🙃😉😌😍🥰😘😗😙😚😋😛.jpg
func matchWithOneCharOmitted(jsonName string, fileName string, sm immich.SupportedMedia) bool {
- base := strings.TrimSuffix(jsonName, path.Ext(jsonName))
- if strings.HasPrefix(fileName, base) {
- if t := sm.TypeFromExt(path.Ext(base)); t == immich.TypeImage || t == immich.TypeVideo {
- // Trim only if the EXT is known extension, and not .COVER or .ORIGINAL
- base = strings.TrimSuffix(base, path.Ext(base))
- }
- fileName = strings.TrimSuffix(fileName, path.Ext(fileName))
- a, b := utf8.RuneCountInString(fileName), utf8.RuneCountInString(base)
+ baseJSON := strings.TrimSuffix(jsonName, path.Ext(jsonName))
+ ext := path.Ext(baseJSON)
+ if sm.IsExtensionPrefix(ext) {
+ baseJSON = strings.TrimSuffix(baseJSON, ext)
+ }
+ fileName = strings.TrimSuffix(fileName, path.Ext(fileName))
+ if fileName == baseJSON {
+ return true
+ }
+ if strings.HasPrefix(fileName, baseJSON) {
+ a, b := utf8.RuneCountInString(fileName), utf8.RuneCountInString(baseJSON)
if a-b <= 1 {
return true
}
@@ -387,23 +350,72 @@ func matchVeryLongNameWithNumber(jsonName string, fileName string, sm immich.Sup
//
// IMG_3479.JPG(2).json
// IMG_3479(2).JPG
+//
+
+// Fast implementation, but does't work with live photos
func matchDuplicateInYear(jsonName string, fileName string, sm immich.SupportedMedia) bool {
jsonName = strings.TrimSuffix(jsonName, path.Ext(jsonName))
p1JSON := strings.Index(jsonName, "(")
if p1JSON < 1 {
return false
}
+ p1File := strings.Index(fileName, "(")
+ if p1File < 0 {
+ return false
+ }
+ jsonExt := path.Ext(jsonName[:p1JSON])
+
p2JSON := strings.Index(jsonName, ")")
if p2JSON < 0 || p2JSON != len(jsonName)-1 {
return false
}
- num := jsonName[p1JSON:]
- jsonName = strings.TrimSuffix(jsonName, num)
- ext := path.Ext(jsonName)
- jsonName = strings.TrimSuffix(jsonName, ext) + num + ext
- return jsonName == fileName
+ p2File := strings.Index(fileName, ")")
+ if p2File < 0 || p2File < p1File {
+ return false
+ }
+
+ fileExt := path.Ext(fileName)
+
+ if fileExt != jsonExt {
+ return false
+ }
+
+ jsonBase := strings.TrimSuffix(jsonName[:p1JSON], path.Ext(jsonName[:p1JSON]))
+
+ if jsonBase != fileName[:p1File] {
+ return false
+ }
+
+ if fileName[p1File+1:p2File] != jsonName[p1JSON+1:p2JSON] {
+ return false
+ }
+
+ return true
+}
+
+/*
+// Regexp implementation, work with live photos, 10 times slower
+var (
+ reDupInYearJSON = regexp.MustCompile(`(.*)\.(.{2,4})\((\d+)\)\..{2,4}$`)
+ reDupInYearFile = regexp.MustCompile(`(.*)\((\d+)\)\..{2,4}$`)
+)
+
+func matchDuplicateInYear(jsonName string, fileName string, sm immich.SupportedMedia) bool {
+ mFile := reDupInYearFile.FindStringSubmatch(fileName)
+ if len(mFile) < 3 {
+ return false
+ }
+ mJSON := reDupInYearJSON.FindStringSubmatch(jsonName)
+ if len(mJSON) < 4 {
+ return false
+ }
+ if mFile[1] == mJSON[1] && mFile[2] == mJSON[3] {
+ return true
+ }
+ return false
}
+*/
// matchEditedName
// PXL_20220405_090123740.PORTRAIT.jpg.json
@@ -426,7 +438,7 @@ func matchEditedName(jsonName string, fileName string, sm immich.SupportedMedia)
// TODO: This one interferes with matchVeryLongNameWithNumber
// matchForgottenDuplicates
-// original_1d4caa6f-16c6-4c3d-901b-9387de10e528_.json
+// "original_1d4caa6f-16c6-4c3d-901b-9387de10e528_.json"
// original_1d4caa6f-16c6-4c3d-901b-9387de10e528_P.jpg
// original_1d4caa6f-16c6-4c3d-901b-9387de10e528_P(1).jpg
@@ -448,12 +460,13 @@ func matchForgottenDuplicates(jsonName string, fileName string, sm immich.Suppor
// each file net yet sent to immich is sent with associated metadata
func (to *Takeout) Browse(ctx context.Context) chan *browser.LocalAssetFile {
- to.uploaded = map[fileKey]any{}
assetChan := make(chan *browser.LocalAssetFile)
go func() {
defer close(assetChan)
- for dir := range to.catalogs {
+ dirs := gen.MapKeys(to.catalogs)
+ sort.Strings(dirs)
+ for _, dir := range dirs {
if len(to.catalogs[dir].matchedFiles) > 0 {
err := to.passTwo(ctx, dir, assetChan)
if err != nil {
@@ -465,31 +478,57 @@ func (to *Takeout) Browse(ctx context.Context) chan *browser.LocalAssetFile {
return assetChan
}
+// detect livephotos and motion pictures
+// 1. get all pictures
+// 2. scan vidoes, if a picture matches, this is a live photo
func (to *Takeout) passTwo(ctx context.Context, dir string, assetChan chan *browser.LocalAssetFile) error {
catalog := to.catalogs[dir]
linkedFiles := map[string]struct {
- video fileInfo
- image fileInfo
+ video *assetFile
+ image *assetFile
}{}
- // detects couples image + video, likely been a motion picture
+ // Scan pictures
for _, f := range gen.MapKeys(catalog.matchedFiles) {
ext := path.Ext(f)
- base := strings.TrimSuffix(f, ext)
- ext2 := path.Ext(base)
- if to.sm.IsMedia(ext2) {
- base = strings.TrimSuffix(base, ext2)
+ if to.sm.TypeFromExt(ext) == immich.TypeImage {
+ linked := linkedFiles[f]
+ linked.image = catalog.matchedFiles[f]
+ linkedFiles[f] = linked
}
+ }
- linked := linkedFiles[base]
- switch to.sm.TypeFromExt(ext) {
- case immich.TypeVideo:
+ // Scan videos
+nextVideo:
+ for _, f := range gen.MapKeys(catalog.matchedFiles) {
+ ext := path.Ext(f)
+ if to.sm.TypeFromExt(ext) == immich.TypeVideo {
+ name := strings.TrimSuffix(f, ext)
+ for i, linked := range linkedFiles {
+ if linked.image == nil {
+ continue
+ }
+ if linked.image != nil && linked.video != nil {
+ continue
+ }
+ p := linked.image.base
+ ext := path.Ext(p)
+ p = strings.TrimSuffix(p, ext)
+ ext = path.Ext(p)
+ if strings.ToUpper(ext) == ".MP" {
+ p = strings.TrimSuffix(p, ext)
+ }
+ if p == name {
+ linked.video = catalog.matchedFiles[f]
+ linkedFiles[i] = linked
+ continue nextVideo
+ }
+ }
+ linked := linkedFiles[f]
linked.video = catalog.matchedFiles[f]
- case immich.TypeImage:
- linked.image = catalog.matchedFiles[f]
+ linkedFiles[f] = linked
}
- linkedFiles[base] = linked
}
for _, base := range gen.MapKeys(linkedFiles) {
@@ -498,14 +537,14 @@ func (to *Takeout) passTwo(ctx context.Context, dir string, assetChan chan *brow
linked := linkedFiles[base]
- if linked.image.md != nil {
- a, err = to.googleMDToAsset(linked.image.md, linked.image.fsys, path.Join(dir, linked.image.base))
+ if linked.image != nil {
+ a, err = to.makeAsset(linked.image.md, linked.image.fsys, path.Join(dir, linked.image.base))
if err != nil {
to.log.Record(ctx, fileevent.Error, nil, path.Join(dir, linked.image.base), "error", err.Error())
continue
}
- if linked.video.md != nil {
- i, err := to.googleMDToAsset(linked.video.md, linked.video.fsys, path.Join(dir, linked.video.base))
+ if linked.video != nil {
+ i, err := to.makeAsset(linked.video.md, linked.video.fsys, path.Join(dir, linked.video.base))
if err != nil {
to.log.Record(ctx, fileevent.Error, nil, path.Join(dir, linked.video.base), "error", err.Error())
} else {
@@ -513,90 +552,85 @@ func (to *Takeout) passTwo(ctx context.Context, dir string, assetChan chan *brow
}
}
} else {
- a, err = to.googleMDToAsset(linked.video.md, linked.video.fsys, path.Join(dir, linked.video.base))
+ a, err = to.makeAsset(linked.video.md, linked.video.fsys, path.Join(dir, linked.video.base))
if err != nil {
to.log.Record(ctx, fileevent.Error, nil, path.Join(dir, linked.video.base), "error", err.Error())
continue
}
}
-
select {
case <-ctx.Done():
return ctx.Err()
default:
- fk := fileKey{
- base: filepath.Base(a.FileName),
- length: a.FileSize,
- year: a.Metadata.DateTaken.Year(),
- }
- if _, found := to.uploaded[fk]; !found {
- assetChan <- a
- to.uploaded[fk] = nil
- } else {
- to.log.Record(ctx, fileevent.AnalysisLocalDuplicate, nil, a.FileName, "title", a.Title)
- if a.LivePhoto != nil {
- to.log.Record(ctx, fileevent.AnalysisLocalDuplicate, nil, a.LivePhoto.FileName, "title", a.LivePhoto.Title)
- }
- }
+ assetChan <- a
}
}
return nil
}
-// googleMDToAsset makes a localAssetFile based on the google metadata
-func (to *Takeout) googleMDToAsset(md *GoogleMetaData, fsys fs.FS, name string) (*browser.LocalAssetFile, error) {
- // Change file's title with the asset's title and the actual file's extension
- title := md.Title
- titleExt := path.Ext(title)
- fileExt := path.Ext(name)
- if titleExt != fileExt {
- title = strings.TrimSuffix(title, titleExt)
- titleExt = path.Ext(title)
- if titleExt != fileExt {
- title = strings.TrimSuffix(title, titleExt) + fileExt
- }
- }
-
+// makeAsset makes a localAssetFile based on the google metadata
+func (to *Takeout) makeAsset(md *GoogleMetaData, fsys fs.FS, name string) (*browser.LocalAssetFile, error) {
i, err := fs.Stat(fsys, name)
if err != nil {
return nil, err
}
+
a := browser.LocalAssetFile{
- FileName: name,
- FileSize: int(i.Size()),
- Title: title,
- Archived: md.Archived,
- FromPartner: md.isPartner(),
- Trashed: md.Trashed,
- Favorite: md.Favorited,
-
- FSys: fsys,
+ FileName: name,
+ FileSize: int(i.Size()),
+ Title: path.Base(name),
+ FSys: fsys,
}
- // Prepare sidecar data to force Immich with Google metadata
- sidecar := metadata.Metadata{
- Description: md.Description,
- DateTaken: md.PhotoTakenTime.Time(),
- }
- if md.GeoDataExif.Latitude != 0 || md.GeoDataExif.Longitude != 0 {
- sidecar.Latitude = md.GeoDataExif.Latitude
- sidecar.Longitude = md.GeoDataExif.Longitude
- }
- if md.GeoData.Latitude != 0 || md.GeoData.Longitude != 0 {
- sidecar.Latitude = md.GeoData.Latitude
- sidecar.Longitude = md.GeoData.Longitude
+ if album, ok := to.albums[path.Dir(name)]; ok {
+ a.Albums = append(a.Albums, album)
}
- for _, p := range md.foundInPaths {
- if album, exists := to.albums[p]; exists {
- if (album.Latitude != 0 || album.Longitude != 0) && (sidecar.Latitude == 0 && sidecar.Longitude == 0) {
- sidecar.Latitude = album.Latitude
- sidecar.Longitude = album.Longitude
+ if md != nil {
+ // Change file's title with the asset's title and the actual file's extension
+ title := md.Title
+ titleExt := path.Ext(title)
+ fileExt := path.Ext(name)
+
+ if titleExt != fileExt {
+ title = strings.TrimSuffix(title, titleExt)
+ titleExt = path.Ext(title)
+ if titleExt != fileExt {
+ title = strings.TrimSuffix(title, titleExt) + fileExt
+ }
+ }
+ a.Title = title
+ a.Archived = md.Archived
+ a.FromPartner = md.isPartner()
+ a.Trashed = md.Trashed
+ a.Favorite = md.Favorited
+
+ // Prepare sidecar data to force Immich with Google metadata
+
+ sidecar := metadata.Metadata{
+ Description: md.Description,
+ DateTaken: md.PhotoTakenTime.Time(),
+ }
+
+ if md.GeoDataExif.Latitude != 0 || md.GeoDataExif.Longitude != 0 {
+ sidecar.Latitude = md.GeoDataExif.Latitude
+ sidecar.Longitude = md.GeoDataExif.Longitude
+ }
+
+ if md.GeoData.Latitude != 0 || md.GeoData.Longitude != 0 {
+ sidecar.Latitude = md.GeoData.Latitude
+ sidecar.Longitude = md.GeoData.Longitude
+ }
+ for _, p := range md.foundInPaths {
+ if album, exists := to.albums[p]; exists {
+ if (album.Latitude != 0 || album.Longitude != 0) && (sidecar.Latitude == 0 && sidecar.Longitude == 0) {
+ sidecar.Latitude = album.Latitude
+ sidecar.Longitude = album.Longitude
+ }
}
- a.Albums = append(a.Albums, album)
}
+ a.Metadata = sidecar
}
- a.Metadata = sidecar
return &a, nil
}
diff --git a/browser/gp/googlephotos_test.go b/browser/gp/googlephotos_test.go
index ef883290..3c467a82 100644
--- a/browser/gp/googlephotos_test.go
+++ b/browser/gp/googlephotos_test.go
@@ -6,37 +6,94 @@ import (
"github.com/simulot/immich-go/immich"
)
-func Test_matchEditedName(t *testing.T) {
+func Test_matchers(t *testing.T) {
tests := []struct {
jsonName string
fileName string
- want bool
+ want string
}{
+ {
+ jsonName: "PXL_20211013_220651983.jpg.json",
+ fileName: "PXL_20211013_220651983.jpg",
+ want: "normalMatch",
+ },
{
jsonName: "PXL_20220405_090123740.PORTRAIT.jpg.json",
fileName: "PXL_20220405_090123740.PORTRAIT-modifié.jpg",
- want: true,
+ want: "matchEditedName",
},
{
jsonName: "PXL_20220405_090123740.PORTRAIT.jpg.json",
fileName: "PXL_20220405_100123740.PORTRAIT-modifié.jpg",
- want: false,
+ want: "",
},
{
jsonName: "DSC_0238.JPG.json",
fileName: "DSC_0238.JPG",
- want: true,
+ want: "normalMatch",
+ },
+ {
+ jsonName: "DSC_0238.JPG(1).json",
+ fileName: "DSC_0238(1).JPG",
+ want: "matchDuplicateInYear",
+ },
+ {
+ jsonName: "IMG_2710.HEIC(1).json",
+ fileName: "IMG_2710(1).HEIC",
+ want: "matchDuplicateInYear",
+ },
+ {
+ jsonName: "PXL_20231118_035751175.MP.jpg.json",
+ fileName: "PXL_20231118_035751175.MP.jpg",
+ want: "normalMatch",
+ },
+ {
+ jsonName: "PXL_20231118_035751175.MP.jpg.json",
+ fileName: "PXL_20231118_035751175.MP",
+ want: "livePhotoMatch",
+ },
+ {
+ jsonName: "PXL_20230809_203449253.LONG_EXPOSURE-02.ORIGIN.json",
+ fileName: "PXL_20230809_203449253.LONG_EXPOSURE-02.ORIGINA.jpg",
+ want: "matchWithOneCharOmitted",
+ },
+ {
+ jsonName: "05yqt21kruxwwlhhgrwrdyb6chhwszi9bqmzu16w0 2.jp.json",
+ fileName: "05yqt21kruxwwlhhgrwrdyb6chhwszi9bqmzu16w0 2.jpg",
+ want: "livePhotoMatch",
+ },
+ {
+ jsonName: "😀😃😄😁😆😅😂🤣🥲☺️😊😇🙂🙃😉😌😍🥰😘😗😙😚😋.json",
+ fileName: "😀😃😄😁😆😅😂🤣🥲☺️😊😇🙂🙃😉😌😍🥰😘😗😙😚😋😛.jpg",
+ want: "matchWithOneCharOmitted",
+ },
+ {
+ jsonName: "Backyard_ceremony_wedding_photography_xxxxxxx_(494).json",
+ fileName: "Backyard_ceremony_wedding_photography_xxxxxxx_m(494).jpg",
+ want: "matchVeryLongNameWithNumber",
+ },
+ {
+ jsonName: "original_1d4caa6f-16c6-4c3d-901b-9387de10e528_.json",
+ fileName: "original_1d4caa6f-16c6-4c3d-901b-9387de10e528_P.jpg",
+ want: "matchWithOneCharOmitted",
+ },
+ {
+ jsonName: "original_1d4caa6f-16c6-4c3d-901b-9387de10e528_.json",
+ fileName: "original_1d4caa6f-16c6-4c3d-901b-9387de10e528_P(1).jpg",
+ want: "matchForgottenDuplicates",
},
- // {
- // jsonName: "DSC_0238.JPG.json",
- // fileName: "DSC_0238(1).JPG",
- // want: false,
- // },
}
for _, tt := range tests {
t.Run(tt.fileName, func(t *testing.T) {
- if got := matchEditedName(tt.jsonName, tt.fileName, immich.DefaultSupportedMedia); got != tt.want {
- t.Errorf("matchEditedName() = %v, want %v", got, tt.want)
+ matcher := ""
+ for _, m := range matchers {
+ if m.fn(tt.jsonName, tt.fileName, immich.DefaultSupportedMedia) {
+ matcher = m.name
+ break
+ }
+ }
+ if matcher != tt.want {
+ t.Errorf("matcher is '%s', want %v", matcher, tt.want)
}
})
}
@@ -125,3 +182,37 @@ func Test_matchForgottenDuplicates(t *testing.T) {
})
}
}
+
+/* indexes, but false
+goos: linux
+goarch: amd64
+pkg: github.com/simulot/immich-go/browser/gp
+cpu: Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz
+Benchmark_matchDuplicateInYear-12 27067428 52.06 ns/op 0 B/op 0 allocs/op
+PASS
+ok github.com/simulot/immich-go/browser/gp 1.458s
+
+goos: linux
+goarch: amd64
+pkg: github.com/simulot/immich-go/browser/gp
+cpu: Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz
+Benchmark_matchDuplicateInYear-12 881652 1491 ns/op 240 B/op 4 allocs/op
+PASS
+ok github.com/simulot/immich-go/browser/gp 1.332s
+
+
+goos: linux
+goarch: amd64
+pkg: github.com/simulot/immich-go/browser/gp
+cpu: Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz
+Benchmark_matchDuplicateInYear-12 25737067 43.88 ns/op 0 B/op 0 allocs/op
+PASS
+ok github.com/simulot/immich-go/browser/gp 1.180s
+
+*/
+
+func Benchmark_matchDuplicateInYear(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ matchDuplicateInYear("IMG_3479.JPG(2).json", "IMG_3479(2).JPG", nil)
+ }
+}
diff --git a/browser/gp/testgp_bigread_test.go b/browser/gp/testgp_bigread_test.go
index a0d3304e..d3283a6e 100644
--- a/browser/gp/testgp_bigread_test.go
+++ b/browser/gp/testgp_bigread_test.go
@@ -31,7 +31,7 @@ func TestReadBigTakeout(t *testing.T) {
return
}
cnt := 0
- fsyss, err := fshelper.ParsePath(m, true)
+ fsyss, err := fshelper.ParsePath(m)
to, err := NewTakeout(context.Background(), j, immich.DefaultSupportedMedia, fsyss...)
if err != nil {
t.Error(err)
diff --git a/browser/gp/testgp_samples_test.go b/browser/gp/testgp_samples_test.go
index 5853d950..a14a9b14 100644
--- a/browser/gp/testgp_samples_test.go
+++ b/browser/gp/testgp_samples_test.go
@@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"errors"
+ "io/fs"
"path"
"sort"
"strconv"
@@ -12,6 +13,7 @@ import (
"github.com/psanford/memfs"
"github.com/simulot/immich-go/immich/metadata"
+ "github.com/simulot/immich-go/internal/fakefs"
)
type inMemFS struct {
@@ -25,6 +27,10 @@ func newInMemFS() *inMemFS {
}
}
+func (mfs *inMemFS) FSs() []fs.FS {
+ return []fs.FS{mfs}
+}
+
func (mfs *inMemFS) addFile(name string, content []byte) *inMemFS {
if mfs.err != nil {
return mfs
@@ -113,110 +119,239 @@ func sortFileResult(s []fileResult) []fileResult {
return s
}
-func simpleYear() *inMemFS {
+func simpleYear() []fs.FS {
+ return newInMemFS().
+ addJSONImage("Photos from 2023/PXL_20230922_144936660.jpg.json", "PXL_20230922_144936660.jpg").
+ addImage("Photos from 2023/PXL_20230922_144936660.jpg", 10).
+ addJSONImage("Photos from 2023/PXL_20230922_144956000.jpg.json", "PXL_20230922_144956000.jpg").
+ addImage("Photos from 2023/PXL_20230922_144956000.jpg", 20).FSs()
+}
+
+func simpleAlbum() []fs.FS {
+ return newInMemFS().
+ addJSONImage("Photos from 2020/IMG_8172.jpg.json", "IMG_8172.jpg", takenTime("20200101103000")).
+ addImage("Photos from 2020/IMG_8172.jpg", 25).
+ addJSONImage("Photos from 2023/PXL_20230922_144936660.jpg.json", "PXL_20230922_144936660.jpg", takenTime("PXL_20230922_144936660")).
+ addJSONImage("Photos from 2023/PXL_20230922_144934440.jpg.json", "PXL_20230922_144934440.jpg", takenTime("PXL_20230922_144934440")).
+ addJSONImage("Photos from 2023/IMG_8172.jpg.json", "IMG_8172.jpg", takenTime("20230922102100")).
+ addImage("Photos from 2023/PXL_20230922_144936660.jpg", 10).
+ addImage("Photos from 2023/PXL_20230922_144934440.jpg", 15).
+ addImage("Photos from 2023/IMG_8172.jpg", 52).
+ addJSONAlbum("Album/anyname.json", "Album").
+ addJSONImage("Album/IMG_8172.jpg.json", "IMG_8172.jpg", takenTime("20230922102100")).
+ addJSONImage("Album/PXL_20230922_144936660.jpg.json", "PXL_20230922_144936660.jpg", takenTime("PXL_20230922_144936660")).
+ addImage("Album/IMG_8172.jpg", 52).
+ addImage("Album/PXL_20230922_144936660.jpg", 10).FSs()
+}
+
+func albumWithoutImage() []fs.FS {
return newInMemFS().
- addJSONImage("Takeout/Google Photos/Photos from 2023/PXL_20230922_144936660.jpg.json", "PXL_20230922_144936660.jpg").
- addImage("Takeout/Google Photos/Photos from 2023/PXL_20230922_144936660.jpg", 10).
- addJSONImage("Takeout/Google Photos/Photos from 2023/PXL_20230922_144956000.jpg.json", "PXL_20230922_144956000.jpg").
- addImage("Takeout/Google Photos/Photos from 2023/PXL_20230922_144956000.jpg", 20)
+ addJSONAlbum("Album/anyname.json", "Album").
+ addJSONImage("Album/PXL_20230922_144936660.jpg.json", "PXL_20230922_144936660.jpg").
+ addJSONImage("Album/PXL_20230922_144934440.jpg.json", "PXL_20230922_144934440.jpg").
+ addImage("Album/PXL_20230922_144936660.jpg", 10).
+ addJSONImage("Photos from 2023/PXL_20230922_144934440.jpg.json", "PXL_20230922_144934440.jpg").
+ addJSONImage("Photos from 2023/PXL_20230922_144936660.jpg.json", "PXL_20230922_144936660.jpg").
+ addImage("Photos from 2023/PXL_20230922_144934440.jpg", 15).
+ addImage("Photos from 2023/PXL_20230922_144936660.jpg", 10).FSs()
}
-func simpleAlbum() *inMemFS {
+func namesWithNumbers() []fs.FS {
return newInMemFS().
- addJSONImage("Takeout/Google Photos/Photos from 2023/PXL_20230922_144936660.jpg.json", "PXL_20230922_144936660.jpg", takenTime("PXL_20230922_144936660")).
- addImage("Takeout/Google Photos/Photos from 2023/PXL_20230922_144936660.jpg", 10).
- addJSONImage("Takeout/Google Photos/Photos from 2023/PXL_20230922_144934440.jpg.json", "PXL_20230922_144934440.jpg", takenTime("PXL_20230922_144934440")).
- addImage("Takeout/Google Photos/Photos from 2023/PXL_20230922_144934440.jpg", 15).
- addJSONAlbum("Takeout/Google Photos/Album/anyname.json", "Album").
- addJSONImage("Takeout/Google Photos/Album/PXL_20230922_144936660.jpg.json", "PXL_20230922_144936660.jpg", takenTime("PXL_20230922_144936660")).
- addImage("Takeout/Google Photos/Album/PXL_20230922_144936660.jpg", 10).
- addJSONImage("Takeout/Google Photos/Photos from 2023/IMG_8172.jpg.json", "IMG_8172.jpg", takenTime("20230922102100")).
- addImage("Takeout/Google Photos/Photos from 2023/IMG_8172.jpg", 52).
- addJSONImage("Takeout/Google Photos/Album/IMG_8172.jpg.json", "IMG_8172.jpg", takenTime("20230922102100")).
- addImage("Takeout/Google Photos/Album/IMG_8172.jpg", 52).
- addJSONImage("Takeout/Google Photos/Photos from 2020/IMG_8172.jpg.json", "IMG_8172.jpg", takenTime("20200101103000")).
- addImage("Takeout/Google Photos/Photos from 2020/IMG_8172.jpg", 25)
-}
-
-func albumWithoutImage() *inMemFS {
+ addJSONImage("Photos from 2009/IMG_3479.JPG.json", "IMG_3479.JPG").
+ addImage("Photos from 2009/IMG_3479.JPG", 10).
+ addJSONImage("Photos from 2009/IMG_3479.JPG(1).json", "IMG_3479.JPG").
+ addImage("Photos from 2009/IMG_3479(1).JPG", 12).
+ addJSONImage("Photos from 2009/IMG_3479.JPG(2).json", "IMG_3479.JPG").
+ addImage("Photos from 2009/IMG_3479(2).JPG", 15).FSs()
+}
+
+func namesTruncated() []fs.FS {
return newInMemFS().
- addJSONImage("Takeout/Google Photos/Photos from 2023/PXL_20230922_144936660.jpg.json", "PXL_20230922_144936660.jpg").
- addImage("Takeout/Google Photos/Photos from 2023/PXL_20230922_144936660.jpg", 10).
- addJSONImage("Takeout/Google Photos/Photos from 2023/PXL_20230922_144934440.jpg.json", "PXL_20230922_144934440.jpg").
- addImage("Takeout/Google Photos/Photos from 2023/PXL_20230922_144934440.jpg", 15).
- addJSONAlbum("Takeout/Google Photos/Album/anyname.json", "Album").
- addJSONImage("Takeout/Google Photos/Album/PXL_20230922_144936660.jpg.json", "PXL_20230922_144936660.jpg").
- addImage("Takeout/Google Photos/Album/PXL_20230922_144936660.jpg", 10).
- addJSONImage("Takeout/Google Photos/Album/PXL_20230922_144934440.jpg.json", "PXL_20230922_144934440.jpg")
+ addJSONImage("Photos from 2023/😀😃😄😁😆😅😂🤣🥲☺️😊😇🙂🙃😉😌😍🥰😘😗😙😚😋.json", "😀😃😄😁😆😅😂🤣🥲☺️😊😇🙂🙃😉😌😍🥰😘😗😙😚😋😛😝😜🤪🤨🧐🤓😎🥸🤩🥳😏😒😞😔😟😕🙁☹️😣😖😫😩🥺😢😭😤😠😡🤬🤯😳🥵🥶.jpg").
+ addImage("Photos from 2023/😀😃😄😁😆😅😂🤣🥲☺️😊😇🙂🙃😉😌😍🥰😘😗😙😚😋😛.jpg", 10).
+ addJSONImage("Photos from 2023/PXL_20230809_203449253.LONG_EXPOSURE-02.ORIGIN.json", "PXL_20230809_203449253.LONG_EXPOSURE-02.ORIGINAL.jpg").
+ addImage("Photos from 2023/PXL_20230809_203449253.LONG_EXPOSURE-02.ORIGINA.jpg", 40).
+ addJSONImage("Photos from 2023/05yqt21kruxwwlhhgrwrdyb6chhwszi9bqmzu16w0 2.jp.json", "05yqt21kruxwwlhhgrwrdyb6chhwszi9bqmzu16w0 2.jpg").
+ addImage("Photos from 2023/05yqt21kruxwwlhhgrwrdyb6chhwszi9bqmzu16w0 2.jpg", 25).FSs()
}
-func namesWithNumbers() *inMemFS {
+func imagesEditedJSON() []fs.FS {
return newInMemFS().
- addJSONImage("Takeout/Google Photos/Photos from 2009/IMG_3479.JPG.json", "IMG_3479.JPG").
- addImage("Takeout/Google Photos/Photos from 2009/IMG_3479.JPG", 10).
- addJSONImage("Takeout/Google Photos/Photos from 2009/IMG_3479.JPG(1).json", "IMG_3479.JPG").
- addImage("Takeout/Google Photos/Photos from 2009/IMG_3479(1).JPG", 12).
- addJSONImage("Takeout/Google Photos/Photos from 2009/IMG_3479.JPG(2).json", "IMG_3479.JPG").
- addImage("Takeout/Google Photos/Photos from 2009/IMG_3479(2).JPG", 15)
+ addJSONImage("Photos from 2023/PXL_20220405_090123740.PORTRAIT.jpg.json", "PXL_20220405_090123740.PORTRAIT.jpg").
+ addImage("Photos from 2023/PXL_20220405_090123740.PORTRAIT.jpg", 41).
+ addImage("Photos from 2023/PXL_20220405_090123740.PORTRAIT-modifié.jpg", 21).
+ addImage("Photos from 2023/PXL_20220405_090200110.PORTRAIT-modifié.jpg", 12).FSs()
}
-func namesTruncated() *inMemFS {
+func titlesWithForbiddenChars() []fs.FS {
return newInMemFS().
- addJSONImage("Takeout/Google Photos/Photos from 2023/😀😃😄😁😆😅😂🤣🥲☺️😊😇🙂🙃😉😌😍🥰😘😗😙😚😋.json", "😀😃😄😁😆😅😂🤣🥲☺️😊😇🙂🙃😉😌😍🥰😘😗😙😚😋😛😝😜🤪🤨🧐🤓😎🥸🤩🥳😏😒😞😔😟😕🙁☹️😣😖😫😩🥺😢😭😤😠😡🤬🤯😳🥵🥶.jpg").
- addImage("Takeout/Google Photos/Photos from 2023/😀😃😄😁😆😅😂🤣🥲☺️😊😇🙂🙃😉😌😍🥰😘😗😙😚😋😛.jpg", 10).
- addJSONImage("Takeout/Google Photos/Photos from 2023/PXL_20230809_203449253.LONG_EXPOSURE-02.ORIGIN.json", "PXL_20230809_203449253.LONG_EXPOSURE-02.ORIGINAL.jpg").
- addImage("Takeout/Google Photos/Photos from 2023/PXL_20230809_203449253.LONG_EXPOSURE-02.ORIGINA.jpg", 40).
- addJSONImage("Takeout/Google Photos/Photos from 2023/05yqt21kruxwwlhhgrwrdyb6chhwszi9bqmzu16w0 2.jp.json", "05yqt21kruxwwlhhgrwrdyb6chhwszi9bqmzu16w0 2.jpg").
- addImage("Takeout/Google Photos/Photos from 2023/05yqt21kruxwwlhhgrwrdyb6chhwszi9bqmzu16w0 2.jpg", 25)
+ addJSONImage("Photos from 2012/27_06_12 - 1.mov.json", "27/06/12 - 1", takenTime("20120627")).
+ addImage("Photos from 2012/27_06_12 - 1.mov", 52).
+ addJSONImage("Photos from 2012/27_06_12 - 2.json", "27/06/12 - 2", takenTime("20120627")).
+ addImage("Photos from 2012/27_06_12 - 2.jpg", 24).FSs()
}
-func imagesEditedJSON() *inMemFS {
+func namesIssue39() []fs.FS {
return newInMemFS().
- addJSONImage("Takeout/Google Photos/Photos from 2023/PXL_20220405_090123740.PORTRAIT.jpg.json", "PXL_20220405_090123740.PORTRAIT.jpg").
- addImage("Takeout/Google Photos/Photos from 2023/PXL_20220405_090123740.PORTRAIT.jpg", 41).
- addImage("Takeout/Google Photos/Photos from 2023/PXL_20220405_090123740.PORTRAIT-modifié.jpg", 21).
- addImage("Takeout/Google Photos/Photos from 2023/PXL_20220405_090200110.PORTRAIT-modifié.jpg", 12)
+ addJSONAlbum("Album/anyname.json", "Album").
+ addJSONImage("Album/Backyard_ceremony_wedding_photography_xxxxxxx_.json", "Backyard_ceremony_wedding_photography_xxxxxxx_magnoliastudios-371.jpg", takenTime("20200101")).
+ addJSONImage("Album/Backyard_ceremony_wedding_photography_xxxxxxx_(1).json", "Backyard_ceremony_wedding_photography_xxxxxxx_magnoliastudios-181.jpg", takenTime("20200101")).
+ addJSONImage("Album/Backyard_ceremony_wedding_photography_xxxxxxx_(494).json", "Backyard_ceremony_wedding_photography_markham_magnoliastudios-19.jpg", takenTime("20200101")).
+ addImage("Album/Backyard_ceremony_wedding_photography_xxxxxxx_m.jpg", 1).
+ addImage("Album/Backyard_ceremony_wedding_photography_xxxxxxx_m(1).jpg", 181).
+ addImage("Album/Backyard_ceremony_wedding_photography_xxxxxxx_m(494).jpg", 494).
+ addJSONImage("Photos from 2020/Backyard_ceremony_wedding_photography_xxxxxxx_.json", "Backyard_ceremony_wedding_photography_xxxxxxx_magnoliastudios-371.jpg", takenTime("20200101")).
+ addJSONImage("Photos from 2020/Backyard_ceremony_wedding_photography_xxxxxxx_(1).json", "Backyard_ceremony_wedding_photography_xxxxxxx_magnoliastudios-181.jpg", takenTime("20200101")).
+ addJSONImage("Photos from 2020/Backyard_ceremony_wedding_photography_xxxxxxx_(494).json", "Backyard_ceremony_wedding_photography_markham_magnoliastudios-19.jpg", takenTime("20200101")).
+ addImage("Photos from 2020/Backyard_ceremony_wedding_photography_xxxxxxx_m(1).jpg", 181).
+ addImage("Photos from 2020/Backyard_ceremony_wedding_photography_xxxxxxx_m(494).jpg", 494).
+ addImage("Photos from 2020/Backyard_ceremony_wedding_photography_xxxxxxx_m.jpg", 1).FSs()
}
-func titlesWithForbiddenChars() *inMemFS {
+func issue68MPFiles() []fs.FS {
return newInMemFS().
- addJSONImage("Takeout/Google Photos/Photos from 2012/27_06_12 - 1.mov.json", "27/06/12 - 1", takenTime("20120627")).
- addImage("Takeout/Google Photos/Photos from 2012/27_06_12 - 1.mov", 52).
- addJSONImage("Takeout/Google Photos/Photos from 2012/27_06_12 - 2.json", "27/06/12 - 2", takenTime("20120627")).
- addImage("Takeout/Google Photos/Photos from 2012/27_06_12 - 2.jpg", 24)
+ addJSONImage("Photos from 2022/PXL_20221228_185930354.MP.jpg.json", "PXL_20221228_185930354.MP.jpg", takenTime("20220101")).
+ addImage("Photos from 2022/PXL_20221228_185930354.MP", 1).
+ addImage("Photos from 2022/PXL_20221228_185930354.MP.jpg", 2).FSs()
}
-func namesIssue39() *inMemFS {
+func issue68LongExposure() []fs.FS {
return newInMemFS().
- addJSONAlbum("Takeout/Google Photos/Album/anyname.json", "Album").
- addJSONImage("Takeout/Google Photos/Album/Backyard_ceremony_wedding_photography_xxxxxxx_.json", "Backyard_ceremony_wedding_photography_xxxxxxx_magnoliastudios-371.jpg", takenTime("20200101")).
- addImage("Takeout/Google Photos/Album/Backyard_ceremony_wedding_photography_xxxxxxx_m.jpg", 1).
- addImage("Takeout/Google Photos/Photos from 2020/Backyard_ceremony_wedding_photography_xxxxxxx_m.jpg", 1).
- addJSONImage("Takeout/Google Photos/Album/Backyard_ceremony_wedding_photography_xxxxxxx_(1).json", "Backyard_ceremony_wedding_photography_xxxxxxx_magnoliastudios-181.jpg", takenTime("20200101")).
- addImage("Takeout/Google Photos/Album/Backyard_ceremony_wedding_photography_xxxxxxx_m(1).jpg", 181).
- addImage("Takeout/Google Photos/Photos from 2020/Backyard_ceremony_wedding_photography_xxxxxxx_m(1).jpg", 181).
- addJSONImage("Takeout/Google Photos/Album/Backyard_ceremony_wedding_photography_xxxxxxx_(494).json", "Backyard_ceremony_wedding_photography_markham_magnoliastudios-19.jpg", takenTime("20200101")).
- addImage("Takeout/Google Photos/Photos from 2020/Backyard_ceremony_wedding_photography_xxxxxxx_m(494).jpg", 494)
-}
-
-func issue68MPFiles() *inMemFS {
+ addJSONImage("Photos from 2023/PXL_20230814_201154491.LONG_EXPOSURE-01.COVER..json", "PXL_20230814_201154491.LONG_EXPOSURE-01.COVER.jpg", takenTime("20230101")).
+ addImage("Photos from 2023/PXL_20230814_201154491.LONG_EXPOSURE-01.COVER.jpg", 1).
+ addJSONImage("Photos from 2023/PXL_20230814_201154491.LONG_EXPOSURE-02.ORIGIN.json", "PXL_20230814_201154491.LONG_EXPOSURE-02.ORIGINAL.jpg", takenTime("20230101")).
+ addImage("Photos from 2023/PXL_20230814_201154491.LONG_EXPOSURE-02.ORIGINA.jpg", 2).FSs()
+}
+
+func issue68ForgottenDuplicates() []fs.FS {
return newInMemFS().
- addJSONImage("Takeout/Google Photos/Photos from 2022/PXL_20221228_185930354.MP.jpg.json", "PXL_20221228_185930354.MP.jpg", takenTime("20220101")).
- addImage("Takeout/Google Photos/Photos from 2022/PXL_20221228_185930354.MP", 1).
- addImage("Takeout/Google Photos/Photos from 2022/PXL_20221228_185930354.MP.jpg", 2)
+ addJSONImage("Photos from 2022/original_1d4caa6f-16c6-4c3d-901b-9387de10e528_.json", "original_1d4caa6f-16c6-4c3d-901b-9387de10e528_PXL_20220516_164814158.jpg", takenTime("20220101")).
+ addImage("Photos from 2022/original_1d4caa6f-16c6-4c3d-901b-9387de10e528_P.jpg", 1).
+ addImage("Photos from 2022/original_1d4caa6f-16c6-4c3d-901b-9387de10e528_P(1).jpg", 2).FSs()
}
-func issue68LongExposure() *inMemFS {
+// #390 Question: report shows way less images uploaded than scanned
+func issue390WrongCount() []fs.FS {
return newInMemFS().
- addJSONImage("Takeout/Google Photos/Photos from 2023/PXL_20230814_201154491.LONG_EXPOSURE-01.COVER..json", "PXL_20230814_201154491.LONG_EXPOSURE-01.COVER.jpg", takenTime("20230101")).
- addImage("Takeout/Google Photos/Photos from 2023/PXL_20230814_201154491.LONG_EXPOSURE-01.COVER.jpg", 1).
- addJSONImage("Takeout/Google Photos/Photos from 2023/PXL_20230814_201154491.LONG_EXPOSURE-02.ORIGIN.json", "PXL_20230814_201154491.LONG_EXPOSURE-02.ORIGINAL.jpg", takenTime("20230101")).
- addImage("Takeout/Google Photos/Photos from 2023/PXL_20230814_201154491.LONG_EXPOSURE-02.ORIGINA.jpg", 2)
+ addJSONImage("Takeout/Google Photos/Photos from 2021/image000000.jpg.json", "image000000.jpg").
+ addJSONImage("Takeout/Google Photos/Photos from 2021/image000000.jpg(1).json", "image000000.jpg").
+ addJSONImage("Takeout/Google Photos/Photos from 2021/image000000.gif.json", "image000000.gif.json").
+ addImage("Takeout/Google Photos/Photos from 2021/image000000.gif", 10).
+ addImage("Takeout/Google Photos/Photos from 2021/image000000.jpg", 20).FSs()
}
-func issue68ForgottenDuplicates() *inMemFS {
+func issue390WrongCount2() []fs.FS {
return newInMemFS().
- addJSONImage("Takeout/Google Photos/Photos from 2022/original_1d4caa6f-16c6-4c3d-901b-9387de10e528_.json", "original_1d4caa6f-16c6-4c3d-901b-9387de10e528_PXL_20220516_164814158.jpg", takenTime("20220101")).
- addImage("Takeout/Google Photos/Photos from 2022/original_1d4caa6f-16c6-4c3d-901b-9387de10e528_P.jpg", 1).
- addImage("Takeout/Google Photos/Photos from 2022/original_1d4caa6f-16c6-4c3d-901b-9387de10e528_P(1).jpg", 2)
+ addJSONImage("Takeout/Google Photos/2017 - Croatia/IMG_0170.jpg.json", "IMG_0170.jpg").
+ addJSONImage("Takeout/Google Photos/Photos from 2018/IMG_0170.JPG.json", "IMG_0170.JPG").
+ addJSONImage("Takeout/Google Photos/Photos from 2018/IMG_0170.HEIC.json", "IMG_0170.HEIC").
+ addJSONImage("Takeout/Google Photos/Photos from 2023/IMG_0170.HEIC.json", "IMG_0170.HEIC").
+ addJSONImage("Takeout/Google Photos/2018 - Cambodia/IMG_0170.JPG.json", "IMG_0170.JPG").
+ addJSONImage("Takeout/Google Photos/2023 - Belize/IMG_0170.HEIC.json", "IMG_0170.HEIC").
+ addJSONImage("Takeout/Google Photos/Photos from 2017/IMG_0170.jpg.json", "IMG_0170.jpg").
+ addImage("Takeout/Google Photos/2017 - Croatia/IMG_0170.jpg", 514963).
+ addImage("Takeout/Google Photos/Photos from 2018/IMG_0170.HEIC", 1332980).
+ addImage("Takeout/Google Photos/Photos from 2018/IMG_0170.JPG", 4570661).
+ addImage("Takeout/Google Photos/Photos from 2023/IMG_0170.MP4", 6024972).
+ addImage("Takeout/Google Photos/Photos from 2023/IMG_0170.HEIC", 4443973).
+ addImage("Takeout/Google Photos/Photos from 2018/IMG_0170.MP4", 2288647).
+ addImage("Takeout/Google Photos/2018 - Cambodia/IMG_0170.JPG", 4570661).
+ addImage("Takeout/Google Photos/2023 - Belize/IMG_0170.MP4", 6024972).
+ addImage("Takeout/Google Photos/2023 - Belize/IMG_0170.HEIC", 4443973).
+ addImage("Takeout/Google Photos/Photos from 2017/IMG_0170.jpg", 514963).FSs()
+}
+
+func checkLivePhoto() []fs.FS {
+ return newInMemFS().
+ addJSONImage("Motion test/20231227_152817.jpg.json", "20231227_152817.jpg").
+ addImage("Motion test/20231227_152817.jpg", 7426453).
+ addImage("Motion test/20231227_152817.MP4", 5192477).
+ addJSONImage("Motion Test/PXL_20231118_035751175.MP.jpg.json", "20231118_035751175.MP.jpg").
+ addImage("Motion Test/PXL_20231118_035751175.MP", 3478685).
+ addImage("Motion Test/PXL_20231118_035751175.MP.jpg", 8025699).FSs()
+}
+
+func loadFromString(dateFormat string, s string) []fs.FS {
+ fss, err := fakefs.ScanStringList(dateFormat, s)
+ if err != nil {
+ panic(err.Error())
+ }
+ return fss
+}
+
+func checkLivePhotoPixil() []fs.FS {
+ return loadFromString("01-02-2006 15:04", `Archive: takeout-20230720T065335Z-001.zip
+ Length Date Time Name
+--------- ---------- ----- ----
+ 309 03-05-2023 10:10 Takeout/Google Photos/2022 - Germany/metadata.json
+ 801 07-19-2023 23:59 Takeout/Google Photos/2022 - Germany/IMG_4573.HEIC.json
+ 2232086 07-19-2023 23:59 Takeout/Google Photos/2022 - Germany/IMG_4573.MP4
+ 3530351 07-20-2023 00:00 Takeout/Google Photos/2022 - Germany/IMG_4573.HEIC
+ 319 03-05-2023 10:10 Takeout/Google Photos/2022 - Germany - Private/metadata.json
+ 802 07-20-2023 00:03 Takeout/Google Photos/2022 - Germany - Private/IMG_4573.HEIC.json
+ 3530351 07-19-2023 23:56 Takeout/Google Photos/2022 - Germany - Private/IMG_4573.HEIC
+ 2232086 07-19-2023 23:56 Takeout/Google Photos/2022 - Germany - Private/IMG_4573.MP4
+ 803 07-19-2023 23:58 Takeout/Google Photos/Photos from 2022/IMG_4573.HEIC.json
+ 3530351 07-19-2023 23:59 Takeout/Google Photos/Photos from 2022/IMG_4573.HEIC
+ 2232086 07-19-2023 23:59 Takeout/Google Photos/Photos from 2022/IMG_4573.MP4
+`)
+}
+
+func checkMissingJSON() []fs.FS {
+ return loadFromString("01-02-2006 15:04", `Archive: takeout-20230720T065335Z-001.zip
+ Length Date Time Name
+--------- ---------- ----- ----
+ 803 07-19-2023 23:58 Takeout/Google Photos/Photos from 2022/IMG_4573.HEIC.json
+ 3530351 07-19-2023 23:59 Takeout/Google Photos/Photos from 2022/IMG_4573.HEIC
+ 1352455 07-19-2023 15:18 Takeout/Google Foto/Photos from 2016/IMG-20161201-WA0035.jpeg
+ 3530351 07-19-2023 23:56 Takeout/Google Photos/2022 - Germany - Private/IMG_4553.HEIC
+ 309 03-05-2023 10:10 Takeout/Google Photos/2022 - Germany/metadata.json
+ 2232086 07-19-2023 23:59 Takeout/Google Photos/2022 - Germany/IMG_1234.MP4
+ 3530351 07-20-2023 00:00 Takeout/Google Photos/2022 - Germany/IMG_1234.HEIC
+`)
+}
+
+func checkDuplicates() []fs.FS {
+ return loadFromString("01-02-2006 15:04", `Archive: takeout-20230720T065335Z-001.zip
+-rw-r--r-- 0/0 365022 2024-07-19 01:19 Takeout/Google Foto/[E&S] 2016-01-05 - Castello De Albertis e Mostra d/20160105_121621_LLS.jpg
+-rw-r--r-- 0/0 708 2024-07-19 01:19 Takeout/Google Foto/[E&S] 2016-01-05 - Castello De Albertis e Mostra d/20160105_121621_LLS.jpg.json
+-rw-r--r-- 0/0 364041 2024-07-19 01:51 Takeout/Google Foto/Photos from 2016/20160105_121621_LLS.jpg
+-rw-r--r-- 0/0 709 2024-07-19 01:51 Takeout/Google Foto/Photos from 2016/20160105_121621_LLS.jpg.json
+-rw-r--r-- 0/0 708 2024-07-19 02:13 Takeout/Google Foto/2016-01-05 - _3/20160105_121621_LLS.jpg.json
+-rw-r--r-- 0/0 364041 2024-07-19 02:20 Takeout/Google Foto/2016-01-05 - _3/20160105_121621_LLS.jpg
+Archive: takeout-20230720T065335Z-002.zip
+-rw-r--r-- 0/0 364041 2024-07-19 06:14 Takeout/Google Foto/2016-01-05 - _3/20160105_121621_LLS.jpg
+-rw-r--r-- 0/0 708 2024-07-19 02:13 Takeout/Google Foto/2016-01-05 - _3/20160105_121621_LLS.jpg.json
+`)
+}
+
+/*
+func checkMP_405() []fs.FS {
+ return loadFromString("2006-01-02 15:04", `Archive: takeout-20230720T065335Z-001.zip
+ 893 2024-01-21 16:33 Takeout/Google Photos/Family _ friends/PXL_20210102_221126856.MP.jpg.json
+ 896 2024-01-21 16:33 Takeout/Google Photos/Family _ friends/PXL_20210102_221126856.MP~2.jpg.json
+ 895 2024-01-21 16:52 Takeout/Google Photos/Untitled(1)/PXL_20210102_221126856.MP~2.jpg.json
+ 893 2024-01-21 16:52 Takeout/Google Photos/Untitled(1)/PXL_20210102_221126856.MP.jpg.json
+ 893 2024-01-21 17:46 Takeout/Google Photos/Photos from 2021/PXL_20210102_221126856.MP.jpg.json
+ 895 2024-01-21 17:46 Takeout/Google Photos/Photos from 2021/PXL_20210102_221126856.MP~2.jpg.json
+ 3242290 2024-01-21 16:58 Takeout/Google Photos/Untitled(1)/PXL_20210102_221126856.MP
+ 1214365 2024-01-21 16:58 Takeout/Google Photos/Untitled(1)/PXL_20210102_221126856.MP~2
+ 4028710 2024-01-21 16:59 Takeout/Google Photos/Untitled(1)/PXL_20210102_221126856.MP~2.jpg
+ 6486725 2024-01-21 16:59 Takeout/Google Photos/Untitled(1)/PXL_20210102_221126856.MP.jpg
+ 1214365 2024-01-21 15:40 Takeout/Google Photos/Family _ friends/PXL_20210102_221126856.MP~2
+ 3242290 2024-01-21 15:40 Takeout/Google Photos/Family _ friends/PXL_20210102_221126856.MP
+ 6486725 2024-01-21 15:41 Takeout/Google Photos/Family _ friends/PXL_20210102_221126856.MP.jpg
+ 4028710 2024-01-21 15:41 Takeout/Google Photos/Family _ friends/PXL_20210102_221126856.MP~2.jpg
+ 3242290 2024-01-21 17:53 Takeout/Google Photos/Photos from 2021/PXL_20210102_221126856.MP
+ 1214365 2024-01-21 17:53 Takeout/Google Photos/Photos from 2021/PXL_20210102_221126856.MP~2
+ 6486725 2024-01-21 17:53 Takeout/Google Photos/Photos from 2021/PXL_20210102_221126856.MP.jpg
+ 4028710 2024-01-21 17:53 Takeout/Google Photos/Photos from 2021/PXL_20210102_221126856.MP~2.jpg
+ 1214365 2024-01-21 16:14 Takeout/Google Photos/pi_info/PXL_20210102_221126856.MP~2
+ 3242290 2024-01-21 16:14 Takeout/Google Photos/pi_info/PXL_20210102_221126856.MP
+ 892 2024-01-21 16:14 Takeout/Google Photos/pi_info/PXL_20210102_221126856.MP.jpg.json
+ 895 2024-01-21 16:14 Takeout/Google Photos/pi_info/PXL_20210102_221126856.MP~2.jpg.json
+ 4028710 2024-01-21 16:14 Takeout/Google Photos/pi_info/PXL_20210102_221126856.MP~2.jpg
+ 6486725 2024-01-21 16:14 Takeout/Google Photos/pi_info/PXL_20210102_221126856.MP.jpg`)
}
+*/
diff --git a/browser/gp/testgp_test.go b/browser/gp/testgp_test.go
index 9d194e43..7fa93e76 100644
--- a/browser/gp/testgp_test.go
+++ b/browser/gp/testgp_test.go
@@ -3,6 +3,7 @@ package gp
import (
"context"
"io"
+ "io/fs"
"log/slog"
"path"
"reflect"
@@ -15,9 +16,9 @@ import (
func TestBrowse(t *testing.T) {
tc := []struct {
- name string
- gen func() *inMemFS
- results []fileResult // file name / title
+ name string
+ gen func() []fs.FS
+ want []fileResult // file name / title
}{
{
"simpleYear", simpleYear,
@@ -30,16 +31,19 @@ func TestBrowse(t *testing.T) {
{
"simpleAlbum", simpleAlbum,
sortFileResult([]fileResult{
+ {name: "PXL_20230922_144936660.jpg", size: 10, title: "PXL_20230922_144936660.jpg"},
{name: "PXL_20230922_144936660.jpg", size: 10, title: "PXL_20230922_144936660.jpg"},
{name: "PXL_20230922_144934440.jpg", size: 15, title: "PXL_20230922_144934440.jpg"},
- {name: "IMG_8172.jpg", size: 52, title: "IMG_8172.jpg"},
{name: "IMG_8172.jpg", size: 25, title: "IMG_8172.jpg"},
+ {name: "IMG_8172.jpg", size: 52, title: "IMG_8172.jpg"},
+ {name: "IMG_8172.jpg", size: 52, title: "IMG_8172.jpg"},
}),
},
{
"albumWithoutImage", albumWithoutImage,
sortFileResult([]fileResult{
+ {name: "PXL_20230922_144936660.jpg", size: 10, title: "PXL_20230922_144936660.jpg"},
{name: "PXL_20230922_144936660.jpg", size: 10, title: "PXL_20230922_144936660.jpg"},
{name: "PXL_20230922_144934440.jpg", size: 15, title: "PXL_20230922_144934440.jpg"},
}),
@@ -79,14 +83,18 @@ func TestBrowse(t *testing.T) {
{
"namesIssue39", namesIssue39,
sortFileResult([]fileResult{
+ {name: "Backyard_ceremony_wedding_photography_xxxxxxx_m.jpg", size: 1, title: "Backyard_ceremony_wedding_photography_xxxxxxx_magnoliastudios-371.jpg"},
{name: "Backyard_ceremony_wedding_photography_xxxxxxx_m.jpg", size: 1, title: "Backyard_ceremony_wedding_photography_xxxxxxx_magnoliastudios-371.jpg"},
{name: "Backyard_ceremony_wedding_photography_xxxxxxx_m(1).jpg", size: 181, title: "Backyard_ceremony_wedding_photography_xxxxxxx_magnoliastudios-181.jpg"},
+ {name: "Backyard_ceremony_wedding_photography_xxxxxxx_m(1).jpg", size: 181, title: "Backyard_ceremony_wedding_photography_xxxxxxx_magnoliastudios-181.jpg"},
+ {name: "Backyard_ceremony_wedding_photography_xxxxxxx_m(494).jpg", size: 494, title: "Backyard_ceremony_wedding_photography_markham_magnoliastudios-19.jpg"},
{name: "Backyard_ceremony_wedding_photography_xxxxxxx_m(494).jpg", size: 494, title: "Backyard_ceremony_wedding_photography_markham_magnoliastudios-19.jpg"},
}),
},
{
"issue68MPFiles", issue68MPFiles,
sortFileResult([]fileResult{
+ {name: "PXL_20221228_185930354.MP", size: 1, title: "PXL_20221228_185930354.MP"},
{name: "PXL_20221228_185930354.MP.jpg", size: 2, title: "PXL_20221228_185930354.MP.jpg"},
}),
},
@@ -105,19 +113,38 @@ func TestBrowse(t *testing.T) {
{name: "original_1d4caa6f-16c6-4c3d-901b-9387de10e528_P(1).jpg", size: 2, title: "original_1d4caa6f-16c6-4c3d-901b-9387de10e528_PXL_20220516_164814158.jpg"},
}),
},
+ {
+ "issue390WrongCount", issue390WrongCount,
+ sortFileResult([]fileResult{
+ {name: "image000000.gif", size: 10, title: "image000000.gif"},
+ {name: "image000000.jpg", size: 20, title: "image000000.jpg"},
+ }),
+ },
+ {
+ "issue390WrongCount2", issue390WrongCount2,
+ sortFileResult([]fileResult{
+ {name: "IMG_0170.jpg", size: 514963, title: "IMG_0170.jpg"},
+ {name: "IMG_0170.HEIC", size: 1332980, title: "IMG_0170.HEIC"},
+ {name: "IMG_0170.JPG", size: 4570661, title: "IMG_0170.JPG"},
+ {name: "IMG_0170.MP4", size: 6024972, title: "IMG_0170.MP4"},
+ {name: "IMG_0170.HEIC", size: 4443973, title: "IMG_0170.HEIC"},
+ {name: "IMG_0170.MP4", size: 2288647, title: "IMG_0170.MP4"},
+ {name: "IMG_0170.JPG", size: 4570661, title: "IMG_0170.JPG"},
+ {name: "IMG_0170.MP4", size: 6024972, title: "IMG_0170.MP4"},
+ {name: "IMG_0170.HEIC", size: 4443973, title: "IMG_0170.HEIC"},
+ {name: "IMG_0170.jpg", size: 514963, title: "IMG_0170.jpg"},
+ }),
+ },
}
for _, c := range tc {
t.Run(c.name, func(t *testing.T) {
fsys := c.gen()
- if fsys.err != nil {
- t.Error(fsys.err)
- return
- }
+
ctx := context.Background()
log := slog.New(slog.NewTextHandler(io.Discard, nil))
- b, err := NewTakeout(ctx, fileevent.NewRecorder(log, false), immich.DefaultSupportedMedia, fsys)
+ b, err := NewTakeout(ctx, fileevent.NewRecorder(log, false), immich.DefaultSupportedMedia, fsys...)
if err != nil {
t.Error(err)
}
@@ -130,12 +157,15 @@ func TestBrowse(t *testing.T) {
results := []fileResult{}
for a := range b.Browse(ctx) {
results = append(results, fileResult{name: path.Base(a.FileName), size: a.FileSize, title: a.Title})
+ if a.LivePhoto != nil {
+ results = append(results, fileResult{name: path.Base(a.LivePhoto.FileName), size: a.LivePhoto.FileSize, title: a.LivePhoto.Title})
+ }
}
results = sortFileResult(results)
- if !reflect.DeepEqual(results, c.results) {
+ if !reflect.DeepEqual(results, c.want) {
t.Errorf("difference\n")
- pretty.Ldiff(t, c.results, results)
+ pretty.Ldiff(t, c.want, results)
}
})
}
@@ -144,19 +174,19 @@ func TestBrowse(t *testing.T) {
func TestAlbums(t *testing.T) {
type album map[string][]fileResult
tc := []struct {
- name string
- gen func() *inMemFS
- albums album
+ name string
+ gen func() []fs.FS
+ want album
}{
{
- name: "simpleYear",
- gen: simpleYear,
- albums: album{},
+ name: "simpleYear",
+ gen: simpleYear,
+ want: album{},
},
{
name: "simpleAlbum",
gen: simpleAlbum,
- albums: album{
+ want: album{
"Album": sortFileResult([]fileResult{
{name: "IMG_8172.jpg", size: 52, title: "IMG_8172.jpg"},
{name: "PXL_20230922_144936660.jpg", size: 10, title: "PXL_20230922_144936660.jpg"},
@@ -166,9 +196,8 @@ func TestAlbums(t *testing.T) {
{
name: "albumWithoutImage",
gen: albumWithoutImage,
- albums: album{
+ want: album{
"Album": sortFileResult([]fileResult{
- {name: "PXL_20230922_144934440.jpg", size: 15, title: "PXL_20230922_144934440.jpg"},
{name: "PXL_20230922_144936660.jpg", size: 10, title: "PXL_20230922_144936660.jpg"},
}),
},
@@ -176,7 +205,7 @@ func TestAlbums(t *testing.T) {
{
name: "namesIssue39",
gen: namesIssue39,
- albums: album{
+ want: album{
"Album": sortFileResult([]fileResult{
{name: "Backyard_ceremony_wedding_photography_xxxxxxx_m.jpg", size: 1, title: "Backyard_ceremony_wedding_photography_xxxxxxx_magnoliastudios-371.jpg"},
{name: "Backyard_ceremony_wedding_photography_xxxxxxx_m(1).jpg", size: 181, title: "Backyard_ceremony_wedding_photography_xxxxxxx_magnoliastudios-181.jpg"},
@@ -190,11 +219,8 @@ func TestAlbums(t *testing.T) {
t.Run(c.name, func(t *testing.T) {
ctx := context.Background()
fsys := c.gen()
- if fsys.err != nil {
- t.Error(fsys.err)
- return
- }
- b, err := NewTakeout(ctx, fileevent.NewRecorder(nil, false), immich.DefaultSupportedMedia, fsys)
+
+ b, err := NewTakeout(ctx, fileevent.NewRecorder(nil, false), immich.DefaultSupportedMedia, fsys...)
if err != nil {
t.Error(err)
}
@@ -218,10 +244,137 @@ func TestAlbums(t *testing.T) {
albums[k] = sortFileResult(al)
}
- if !reflect.DeepEqual(albums, c.albums) {
+ if !reflect.DeepEqual(albums, c.want) {
t.Errorf("difference\n")
- pretty.Ldiff(t, c.albums, albums)
+ pretty.Ldiff(t, c.want, albums)
}
})
}
}
+
+func TestArchives(t *testing.T) {
+ type photo map[string]string
+ type album map[string][]string
+ tc := []struct {
+ name string
+ gen func() []fs.FS
+ acceptMissingJSON bool
+ wantLivePhotos photo
+ wantAlbum album
+ wantAsset photo
+ }{
+ {
+ name: "checkLivePhoto",
+ gen: checkLivePhoto,
+ wantAsset: photo{},
+ wantLivePhotos: photo{
+ "Motion Test/PXL_20231118_035751175.MP.jpg": "Motion Test/PXL_20231118_035751175.MP",
+ "Motion test/20231227_152817.jpg": "Motion test/20231227_152817.MP4",
+ },
+ wantAlbum: album{},
+ },
+ {
+ name: "checkLivePhotoPixil",
+ gen: checkLivePhotoPixil,
+ wantAsset: photo{},
+ wantLivePhotos: photo{
+ "Takeout/Google Photos/2022 - Germany - Private/IMG_4573.HEIC": "Takeout/Google Photos/2022 - Germany - Private/IMG_4573.MP4",
+ "Takeout/Google Photos/Photos from 2022/IMG_4573.HEIC": "Takeout/Google Photos/Photos from 2022/IMG_4573.MP4",
+ "Takeout/Google Photos/2022 - Germany/IMG_4573.HEIC": "Takeout/Google Photos/2022 - Germany/IMG_4573.MP4",
+ },
+ wantAlbum: album{
+ "2022 - Germany - Private": []string{"IMG_4573.HEIC"},
+ "2022 - Germany": []string{"IMG_4573.HEIC"},
+ },
+ },
+ {
+ name: "checkMissingJSON-No",
+ gen: checkMissingJSON,
+ wantAsset: photo{
+ "Takeout/Google Photos/Photos from 2022/IMG_4573.HEIC": "",
+ },
+ wantLivePhotos: photo{},
+ wantAlbum: album{},
+ },
+ {
+ name: "checkMissingJSON-Yes",
+ gen: checkMissingJSON,
+ acceptMissingJSON: true,
+ wantAsset: photo{
+ "Takeout/Google Photos/Photos from 2022/IMG_4573.HEIC": "",
+ "Takeout/Google Foto/Photos from 2016/IMG-20161201-WA0035.jpeg": "",
+ "Takeout/Google Photos/2022 - Germany - Private/IMG_4553.HEIC": "",
+ },
+ wantLivePhotos: photo{
+ "Takeout/Google Photos/2022 - Germany/IMG_1234.HEIC": "Takeout/Google Photos/2022 - Germany/IMG_1234.MP4",
+ },
+ wantAlbum: album{
+ "2022 - Germany": []string{"IMG_1234.HEIC"},
+ },
+ },
+ {
+ name: "checkDuplicates",
+ gen: checkDuplicates,
+ wantAsset: photo{
+ "Takeout/Google Foto/[E&S] 2016-01-05 - Castello De Albertis e Mostra d/20160105_121621_LLS.jpg": "",
+ "Takeout/Google Foto/Photos from 2016/20160105_121621_LLS.jpg": "",
+ "Takeout/Google Foto/2016-01-05 - _3/20160105_121621_LLS.jpg": "",
+ },
+ wantLivePhotos: photo{},
+ wantAlbum: album{},
+ },
+ // // { // #405
+ // // name: "checkMP_405",
+ // // gen: checkMP_405,
+ // // },
+ }
+ for _, c := range tc {
+ t.Run(
+ c.name,
+ func(t *testing.T) {
+ ctx := context.Background()
+ fsys := c.gen()
+
+ b, err := NewTakeout(ctx, fileevent.NewRecorder(nil, false), immich.DefaultSupportedMedia, fsys...)
+ if err != nil {
+ t.Error(err)
+ }
+ b.SetAcceptMissingJSON(c.acceptMissingJSON)
+ err = b.Prepare(ctx)
+ if err != nil {
+ t.Error(err)
+ }
+
+ livePhotos := photo{}
+ assets := photo{}
+ albums := album{}
+ for a := range b.Browse(ctx) {
+ if a.LivePhoto != nil {
+ photo := a.FileName
+ video := a.LivePhoto.FileName
+ livePhotos[photo] = video
+ } else {
+ assets[a.FileName] = ""
+ }
+ for _, al := range a.Albums {
+ l := albums[al.Title]
+ l = append(l, path.Base(a.FileName))
+ albums[al.Title] = l
+ }
+ }
+ if !reflect.DeepEqual(assets, c.wantAsset) {
+ t.Errorf("difference assets\n")
+ pretty.Ldiff(t, c.wantAsset, assets)
+ }
+ if !reflect.DeepEqual(livePhotos, c.wantLivePhotos) {
+ t.Errorf("difference LivePhotos\n")
+ pretty.Ldiff(t, c.wantLivePhotos, livePhotos)
+ }
+ if !reflect.DeepEqual(albums, c.wantAlbum) {
+ t.Errorf("difference Album\n")
+ pretty.Ldiff(t, c.wantAlbum, albums)
+ }
+ },
+ )
+ }
+}
diff --git a/cmd/shared.go b/cmd/shared.go
index 390ebbac..46c862e1 100644
--- a/cmd/shared.go
+++ b/cmd/shared.go
@@ -17,6 +17,7 @@ import (
"github.com/simulot/immich-go/helpers/myflag"
"github.com/simulot/immich-go/helpers/tzone"
"github.com/simulot/immich-go/immich"
+ fakeimmich "github.com/simulot/immich-go/internal/fakeImmich"
"github.com/simulot/immich-go/ui"
"github.com/telemachus/humane"
)
@@ -38,6 +39,7 @@ type SharedFlags struct {
NoUI bool // Disable user interface
JSONLog bool // Enable JSON structured log
DebugCounters bool // Enable CSV action counters per file
+ DebugFileList bool // When true, the file argument is a file wile the list of Takeout files
Immich immich.ImmichInterface // Immich client
Log *slog.Logger // Logger
@@ -94,6 +96,11 @@ func (app *SharedFlags) Start(ctx context.Context) error {
app.Jnl = fileevent.NewRecorder(nil, app.DebugCounters)
}
+ if app.DebugFileList {
+ app.Immich = &fakeimmich.MockedCLient{}
+ _ = os.Remove(app.LogFile)
+ }
+
if app.LogFile != "" {
if app.LogWriterCloser == nil {
err := configuration.MakeDirForFile(app.LogFile)
diff --git a/cmd/upload/e2e_takeout_test.go b/cmd/upload/e2e_takeout_test.go
new file mode 100644
index 00000000..3c570f97
--- /dev/null
+++ b/cmd/upload/e2e_takeout_test.go
@@ -0,0 +1,85 @@
+//go:build e2e
+// +build e2e
+
+package upload
+
+import (
+ "context"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/simulot/immich-go/cmd"
+ "github.com/simulot/immich-go/internal/fakefs"
+)
+
+// Simulate a takeout archive with the list of zipped files
+func simulate_upload(t *testing.T, zipList string, dateFormat string, forceMissingJSON bool) {
+ ic := &icCatchUploadsAssets{
+ albums: map[string][]string{},
+ }
+ ctx := context.Background()
+
+ // log := slog.New(slog.NewTextHandler(io.Discard, nil))
+ serv := cmd.SharedFlags{
+ Immich: ic,
+ LogLevel: "INFO",
+ // Jnl: fileevent.NewRecorder(log, false),
+ // Log: log,
+ }
+
+ fsOpener := func() ([]fs.FS, error) {
+ return fakefs.ScanFileList(zipList, dateFormat)
+ }
+ os.Remove(filepath.Dir(zipList) + "/debug.log")
+ args := []string{"-google-photos", "-no-ui", "-debug-counters", "-log-file=" + filepath.Dir(zipList) + "/debug.log"}
+
+ app, err := newCommand(ctx, &serv, args, fsOpener)
+ if err != nil {
+ t.Errorf("can't instantiate the UploadCmd: %s", err)
+ return
+ }
+ app.ForceUploadWhenNoJSON = forceMissingJSON
+ err = app.run(ctx)
+ if err != nil {
+ t.Errorf("can't run the UploadCmd: %s", err)
+ return
+ }
+}
+
+func TestPixilTakeOut(t *testing.T) {
+ initMyEnv(t)
+
+ simulate_upload(t, myEnv["IMMICH_TESTFILES"]+"/User Files/pixil/list.lst", "01-02-2006 15:04", false)
+}
+
+func TestPhyl404TakeOut(t *testing.T) {
+ initMyEnv(t)
+
+ simulate_upload(t, myEnv["IMMICH_TESTFILES"]+"/User Files/Phyl404/list.lst", "2006-01-02 15:04", false)
+}
+
+func TestPhyl404_2TakeOut(t *testing.T) {
+ initMyEnv(t)
+
+ simulate_upload(t, myEnv["IMMICH_TESTFILES"]+"/User Files/Phy404#2/list.lst", "2006-01-02 15:04", false)
+}
+
+func TestSteve81TakeOut(t *testing.T) {
+ initMyEnv(t)
+
+ simulate_upload(t, myEnv["IMMICH_TESTFILES"]+"/User Files/Steve81/list.list", "2006-01-02 15:04", false)
+}
+
+func TestMuetyTakeOut(t *testing.T) {
+ initMyEnv(t)
+
+ simulate_upload(t, myEnv["IMMICH_TESTFILES"]+"/User Files/muety/list.lst", "01-02-2006 15:04", false)
+}
+
+func TestMissingJSONTakeOut(t *testing.T) {
+ initMyEnv(t)
+
+ simulate_upload(t, myEnv["IMMICH_TESTFILES"]+"/User Files/MissingJSON/list.lst", "01-02-2006 15:04", true)
+}
diff --git a/cmd/upload/noui.go b/cmd/upload/noui.go
index 4344cdf2..7fb81075 100644
--- a/cmd/upload/noui.go
+++ b/cmd/upload/noui.go
@@ -2,7 +2,10 @@ package upload
import (
"context"
+ "errors"
"fmt"
+ "strings"
+ "sync/atomic"
"time"
"github.com/simulot/immich-go/helpers/fileevent"
@@ -13,6 +16,8 @@ func (app *UpCmd) runNoUI(ctx context.Context) error {
ctx, cancel := context.WithCancelCause(ctx)
defer cancel(nil)
+ var preparationDone atomic.Bool
+
stopProgress := make(chan any)
var maxImmich, currImmich int
spinner := []rune{' ', ' ', '.', ' ', ' '}
@@ -23,41 +28,37 @@ func (app *UpCmd) runNoUI(ctx context.Context) error {
}
progressString := func() string {
- var s string
counts := app.Jnl.GetCounts()
+ defer func() {
+ spinIdx++
+ if spinIdx == len(spinner) {
+ spinIdx = 0
+ }
+ }()
immichPct := 0
if maxImmich > 0 {
immichPct = 100 * currImmich / maxImmich
+ } else {
+ immichPct = 100
}
- ScannedAssets := counts[fileevent.DiscoveredImage] + counts[fileevent.DiscoveredVideo] - counts[fileevent.DiscoveredDiscarded]
- ProcessedAssets := counts[fileevent.Uploaded] +
- counts[fileevent.UploadServerError] +
- counts[fileevent.UploadNotSelected] +
- counts[fileevent.UploadUpgraded] +
- counts[fileevent.UploadServerDuplicate] +
- counts[fileevent.UploadServerBetter] +
- counts[fileevent.DiscoveredDiscarded] +
- counts[fileevent.AnalysisLocalDuplicate]
if app.GooglePhotos {
- gpPct := 0
- upPct := 0
- if ScannedAssets > 0 {
- gpPct = int(100 * counts[fileevent.AnalysisAssociatedMetadata] / ScannedAssets)
- }
- if counts[fileevent.AnalysisAssociatedMetadata] > 0 {
- upPct = int(100 * ProcessedAssets / counts[fileevent.AnalysisAssociatedMetadata])
+ gpTotal := app.Jnl.TotalAssets()
+ gpProcessed := app.Jnl.TotalProcessedGP()
+
+ gpPercent := int(100 * gpProcessed / gpTotal)
+ upProcessed := int64(0)
+ if preparationDone.Load() {
+ upProcessed = app.Jnl.TotalProcessed(app.ForceUploadWhenNoJSON)
}
+ upTotal := app.Jnl.TotalAssets()
+ upPercent := 100 * upProcessed / upTotal
- s = fmt.Sprintf("\rImmich read %d%%, Google Photos Analysis: %d%%, Upload errors: %d, Uploaded %d%% %s", immichPct, gpPct, counts[fileevent.UploadServerError], upPct, string(spinner[spinIdx]))
- } else {
- s = fmt.Sprintf("\rImmich read %d%%, Processed %d, Upload errors: %d, Uploaded %d %s", immichPct, ProcessedAssets, counts[fileevent.UploadServerError], counts[fileevent.Uploaded], string(spinner[spinIdx]))
+ return fmt.Sprintf("\rImmich read %d%%, Assets found: %d, Google Photos Analysis: %d%%, Upload errors: %d, Uploaded %d%% %s",
+ immichPct, app.Jnl.TotalAssets(), gpPercent, counts[fileevent.UploadServerError], upPercent, string(spinner[spinIdx]))
}
- spinIdx++
- if spinIdx == len(spinner) {
- spinIdx = 0
- }
- return s
+
+ return fmt.Sprintf("\rImmich read %d%%, Assets found: %d, Upload errors: %d, Uploaded %d %s", immichPct, app.Jnl.TotalAssets(), counts[fileevent.UploadServerError], counts[fileevent.Uploaded], string(spinner[spinIdx]))
}
uiGrp := errgroup.Group{}
@@ -111,10 +112,25 @@ func (app *UpCmd) runNoUI(ctx context.Context) error {
return err
}
}
+ preparationDone.Store(true)
err = app.uploadLoop(ctx)
if err != nil {
cancel(err)
}
+
+ counts := app.Jnl.GetCounts()
+ messages := strings.Builder{}
+ if counts[fileevent.Error]+counts[fileevent.UploadServerError] > 0 {
+ messages.WriteString("Some errors have occurred. Look at the log file for details\n")
+ }
+ if app.GooglePhotos && counts[fileevent.AnalysisMissingAssociatedMetadata] > 0 && !app.ForceUploadWhenNoJSON {
+ messages.WriteString(fmt.Sprintf("\n%d JSON files are missing.\n", counts[fileevent.AnalysisMissingAssociatedMetadata]))
+ messages.WriteString("- Verify if all takeout parts have been included in the processing.\n")
+ messages.WriteString("- Request another takeout, either for one year at a time or in smaller increments.\n")
+ }
+ if messages.Len() > 0 {
+ cancel(errors.New(messages.String()))
+ }
close(stopProgress)
return err
})
diff --git a/cmd/upload/ui.go b/cmd/upload/ui.go
index ee312060..06320454 100644
--- a/cmd/upload/ui.go
+++ b/cmd/upload/ui.go
@@ -6,6 +6,7 @@ import (
"fmt"
"io"
"log/slog"
+ "strings"
"sync/atomic"
"time"
@@ -51,6 +52,7 @@ func (app *UpCmd) runUI(ctx context.Context) error {
var preparationDone atomic.Bool
var uploadDone atomic.Bool
var uiGroup errgroup.Group
+ var messages strings.Builder
uiApp.SetRoot(pages, true)
@@ -61,9 +63,7 @@ func (app *UpCmd) runUI(ctx context.Context) error {
}
}
- modal := newModal()
pages.AddPage("ui", ui.screen, true, true)
- pages.AddPage("modal", modal, true, false)
// handle Ctrl+C and Ctrl+Q
uiApp.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
@@ -128,15 +128,13 @@ func (app *UpCmd) runUI(ctx context.Context) error {
ui.getCountView(c, counts[c])
}
if app.GooglePhotos {
- ui.immichPrepare.SetMaxValue(int(counts[fileevent.DiscoveredImage] + counts[fileevent.DiscoveredVideo]))
- ui.immichPrepare.SetValue(int(counts[fileevent.AnalysisAssociatedMetadata]))
-
- ui.immichUpload.SetMaxValue(int(counts[fileevent.DiscoveredImage] + counts[fileevent.DiscoveredVideo]))
- ui.immichUpload.SetValue(int(counts[fileevent.UploadNotSelected] +
- counts[fileevent.UploadUpgraded] +
- counts[fileevent.UploadServerDuplicate] +
- counts[fileevent.UploadServerBetter] +
- counts[fileevent.Uploaded]))
+ ui.immichPrepare.SetMaxValue(int(app.Jnl.TotalAssets()))
+ ui.immichPrepare.SetValue(int(app.Jnl.TotalProcessedGP()))
+
+ if preparationDone.Load() {
+ ui.immichUpload.SetMaxValue(int(app.Jnl.TotalAssets()))
+ }
+ ui.immichUpload.SetValue(int(app.Jnl.TotalProcessed(app.ForceUploadWhenNoJSON)))
}
})
}
@@ -195,9 +193,21 @@ func (app *UpCmd) runUI(ctx context.Context) error {
return context.Cause(ctx)
}
uploadDone.Store(true)
+ counts := app.Jnl.GetCounts()
+ if counts[fileevent.Error]+counts[fileevent.UploadServerError] > 0 {
+ messages.WriteString("Some errors have occurred. Look at the log file for details\n")
+ }
+ if app.GooglePhotos && counts[fileevent.AnalysisMissingAssociatedMetadata] > 0 && !app.ForceUploadWhenNoJSON {
+ messages.WriteString(fmt.Sprintf("\n%d JSON files are missing.\n", counts[fileevent.AnalysisMissingAssociatedMetadata]))
+ messages.WriteString("- Verify if all takeout parts have been included in the processing.\n")
+ messages.WriteString("- Request another takeout, either for one year at a time or in smaller increments.\n")
+ }
+ modal := newModal(messages.String())
+ pages.AddPage("modal", modal, true, false)
// upload is done!
pages.ShowPage("modal")
+
return err
})
@@ -209,10 +219,15 @@ func (app *UpCmd) runUI(ctx context.Context) error {
// Time to leave
app.Jnl.Report()
+ if messages.Len() > 0 {
+ return (errors.New(messages.String()))
+ }
return err
}
-func newModal() tview.Primitive {
+func newModal(message string) tview.Primitive {
+ message += "\nYou can quit the program safely.\n\nPress the [enter] key to exit."
+ lines := strings.Count(message, "\n")
// Returns a new primitive which puts the provided primitive in the center and
// sets its size to the given width and height.
modal := func(p tview.Primitive, width, height int) tview.Primitive {
@@ -224,12 +239,12 @@ func newModal() tview.Primitive {
AddItem(nil, 0, 1, false), width, 1, true).
AddItem(nil, 0, 1, false)
}
- text := tview.NewTextView().SetText("\nYou can quit the program safely.\n\nPress the [enter] key to exit.").SetTextAlign(tview.AlignCenter)
+ text := tview.NewTextView().SetText(message)
box := tview.NewBox().
SetBorder(true).
SetTitle("Upload completed")
text.Box = box
- return modal(text, 40, 7)
+ return modal(text, 80, 2+lines)
}
func newUI(ctx context.Context, app *UpCmd) *uiPage {
diff --git a/cmd/upload/upload.go b/cmd/upload/upload.go
index ed182ffc..ccf4bc36 100644
--- a/cmd/upload/upload.go
+++ b/cmd/upload/upload.go
@@ -27,6 +27,7 @@ import (
"github.com/simulot/immich-go/helpers/namematcher"
"github.com/simulot/immich-go/helpers/stacking"
"github.com/simulot/immich-go/immich"
+ "github.com/simulot/immich-go/internal/fakefs"
)
type UpCmd struct {
@@ -55,6 +56,7 @@ type UpCmd struct {
StackBurst bool // Stack burst (Default: TRUE)
DiscardArchived bool // Don't import archived assets (Default: FALSE)
WhenNoDate string // When the date can't be determined use the FILE's date or NOW (default: FILE)
+ ForceUploadWhenNoJSON bool // Some takeout don't supplies all JSON. When true, files are uploaded without any additional metadata
BannedFiles namematcher.List // List of banned file name patterns
BrowserConfig Configuration
@@ -70,7 +72,7 @@ type UpCmd struct {
}
func UploadCommand(ctx context.Context, common *cmd.SharedFlags, args []string) error {
- app, err := newCommand(ctx, common, args)
+ app, err := newCommand(ctx, common, args, nil)
if err != nil {
return err
}
@@ -80,7 +82,9 @@ func UploadCommand(ctx context.Context, common *cmd.SharedFlags, args []string)
return app.run(ctx)
}
-func newCommand(ctx context.Context, common *cmd.SharedFlags, args []string) (*UpCmd, error) {
+type fsOpener func() ([]fs.FS, error)
+
+func newCommand(ctx context.Context, common *cmd.SharedFlags, args []string, fsOpener fsOpener) (*UpCmd, error) {
var err error
cmd := flag.NewFlagSet("upload", flag.ExitOnError)
@@ -149,14 +153,14 @@ func newCommand(ctx context.Context, common *cmd.SharedFlags, args []string) (*U
cmd.BoolFunc(
"create-stacks",
- "Stack jpg/raw or bursts (default TRUE)", myflag.BoolFlagFn(&app.CreateStacks, true))
+ "Stack jpg/raw or bursts (default FALSE)", myflag.BoolFlagFn(&app.CreateStacks, false))
cmd.BoolFunc(
"stack-jpg-raw",
- "Control the stacking of jpg/raw photos (default TRUE)", myflag.BoolFlagFn(&app.StackJpgRaws, true))
+ "Control the stacking of jpg/raw photos (default TRUE)", myflag.BoolFlagFn(&app.StackJpgRaws, false))
cmd.BoolFunc(
"stack-burst",
- "Control the stacking bursts (default TRUE)", myflag.BoolFlagFn(&app.StackBurst, true))
+ "Control the stacking bursts (default TRUE)", myflag.BoolFlagFn(&app.StackBurst, false))
// cmd.BoolVar(&app.Delete, "delete", false, "Delete local assets after upload")
@@ -170,11 +174,27 @@ func newCommand(ctx context.Context, common *cmd.SharedFlags, args []string) (*U
cmd.Var(&app.BannedFiles, "exclude-files", "Ignore files based on a pattern. Case insensitive. Add one option for each pattern do you need.")
+ cmd.BoolVar(&app.ForceUploadWhenNoJSON, "upload-when-missing-JSON", app.ForceUploadWhenNoJSON, "when true, photos are upload even without associated JSON file.")
+ cmd.BoolVar(&app.DebugFileList, "debug-file-list", app.DebugFileList, "Check how the your file list would be processed")
+
err = cmd.Parse(args)
if err != nil {
return nil, err
}
+ if app.DebugFileList {
+ if len(cmd.Args()) < 2 {
+ return nil, fmt.Errorf("the option -debug-file-list requires a file name and a date format")
+ }
+ app.LogFile = strings.TrimSuffix(cmd.Arg(0), filepath.Ext(cmd.Arg(0))) + ".log"
+ _ = os.Remove(app.LogFile)
+
+ fsOpener = func() ([]fs.FS, error) {
+ return fakefs.ScanFileList(cmd.Arg(0), cmd.Arg(1))
+ }
+ } else {
+ }
+
app.WhenNoDate = strings.ToUpper(app.WhenNoDate)
switch app.WhenNoDate {
case "FILE", "NOW":
@@ -188,7 +208,12 @@ func newCommand(ctx context.Context, common *cmd.SharedFlags, args []string) (*U
return nil, err
}
- app.fsyss, err = fshelper.ParsePath(cmd.Args(), app.GooglePhotos)
+ if fsOpener == nil {
+ fsOpener = func() ([]fs.FS, error) {
+ return fshelper.ParsePath(cmd.Args())
+ }
+ }
+ app.fsyss, err = fsOpener()
if err != nil {
return nil, err
}
@@ -459,6 +484,9 @@ func (app *UpCmd) handleAsset(ctx context.Context, a *browser.LocalAssetFile) er
app.Jnl.Record(ctx, fileevent.UploadServerDuplicate, a, a.FileName)
} else {
app.Jnl.Record(ctx, fileevent.AnalysisLocalDuplicate, a, a.FileName)
+ if a.LivePhoto != nil {
+ app.Jnl.Record(ctx, fileevent.AnalysisLocalDuplicate, a, a.LivePhoto.FileName)
+ }
}
app.manageAssetAlbum(ctx, advice.ServerAsset.ID, a, advice)
@@ -565,6 +593,7 @@ func (app *UpCmd) ReadGoogleTakeOut(ctx context.Context, fsyss []fs.FS) (browser
return nil, err
}
b.SetBannedFiles(app.BannedFiles)
+ b.SetAcceptMissingJSON(app.ForceUploadWhenNoJSON)
return b, err
}
diff --git a/cpu.prof b/cpu.prof
new file mode 100644
index 00000000..829e26ee
Binary files /dev/null and b/cpu.prof differ
diff --git a/docs/google-takeout.md b/docs/google-takeout.md
index d1ff5554..d662b7e8 100644
--- a/docs/google-takeout.md
+++ b/docs/google-takeout.md
@@ -72,17 +72,18 @@ takeout-20240712T112341Z-010.zip:
```
-## Some key names are spelled in the user language
+## Some key file names are spelled in the user language
-| Language | Google Photos folder name | Album's metadata |
-| ---------- | ------------------------- | ---------------- |
-| US English | Google Photos | metadata.json |
-| French | Google Photos | métadonnées.json |
-| Italian | Google Foto | |
-| Slovak | Fotky Google | metadáta.json |
+| Language | Google Photos folder | Album's metadata | Edited photo name |
+| ---------- | -------------------- | ---------------- | ----------------- |
+| US English | Google Photos | metadata.json | |
+| French | Google Photos | métadonnées.json | \*-modifié.\* |
+| Italian | Google Foto | metadati.json | \*-modificato.\* |
+| Slovak | Fotky Google | metadáta.json | |
+| German | Google Fotos | Metadaten.json | \*-bearbeitet.\* |
# What if you have problems with a takeout archive?
-Please open an issue with details. You cna share your files using Discord DM @`simulot`.
+Please open an issue with details. You cna share your files using Discord DM `@simulot`.
I'll check if I can improve the program.
Sometime a manual import is the best option.
diff --git a/docs/how-to-send-debug-data.md b/docs/how-to-send-debug-data.md
new file mode 100644
index 00000000..992a2e33
--- /dev/null
+++ b/docs/how-to-send-debug-data.md
@@ -0,0 +1,87 @@
+# How to share data with the developer?
+
+The structure of the takeout archive can be weird enough to get `Immich-go` confused.
+
+
+In most of the cases, the list of files is sufficient for trouble-shooting the problem.
+This size of the list is much smaller than the full archive and contains enough information for simulating the import process.
+
+This list reveals only the files's name and size, and the albums' name.
+
+If you agree, you can share it with me via a DM on discord @simulot.
+
+
+
+## Get the file list from a zip takeout
+
+```sh
+for f in *.zip; do echo "$f:"; unzip -l $f; done >list.lst
+```
+
+This produces a file like this:
+```
+takeout-20240523T170453Z-001.zip:
+Archive: takeout-20240523T170453Z-001.zip
+ Length Date Time Name
+--------- ---------- ----- ----
+ 800432 2024-05-23 19:31 Takeout/Google Photos/Photos from 2018/IMG_8135.JPG
+ 1166223 2024-05-23 19:31 Takeout/Google Photos/Photos from 2018/IMG_8133.JPG
+ 17132148 2024-05-23 19:31 Takeout/Google Photos/Photos from 2018/VID_20180819_191954.mp4
+ 604784 2024-05-23 19:31 Takeout/Google Photos/Photos from 2018/IMG_8131.JPG
+ 645224 2024-05-23 19:31 Takeout/Google Photos/Photos from 2018/IMG_8130.JPG
+ 188804 2024-05-23 19:31 Takeout/Google Photos/Photos from 2018/IMG_8132.JPG
+ 375981 2024-05-23 19:31 Takeout/Google Photos/Photos from 2018/IMG_8129.JPG
+ 478073 2024-05-23 19:31 Takeout/Google Photos/Photos from 2018/IMG_8128.JPG
+ 2047609 2024-05-23 19:31 Takeout/Google Photos/Photos from 2018/IMG_8125.JPG
+ 2250833 2024-05-23 19:31 Takeout/Google Photos/Photos from 2018/IMG_8124.JPG
+ 429040 2024-05-23 19:31 Takeout/Google Photos/Photos from 2018/IMG_8120.JPG
+ 908856 2024-05-23 19:31 Takeout/Google Photos/Photos from 2018/IMG_8117.JPG
+ 699546 2024-05-23 19:31 Takeout/Google Photos/Photos from 2018/IMG_8118.JPG
+ 625635 2024-05-23 19:31 Takeout/Google Photos/Photos from 2018/IMG_8115.JPG
+ 1006873 2024-05-23 19:31 Takeout/Google Photos/Photos from 2018/IMG_8116.JPG
+ 499507 2024-05-23 19:31 Takeout/Google Photos/Photos from 2018/IMG_8114.JPG
+ 43189565 2024-05-23 19:31 Takeout/Google Photos/Photos from 2018/VID_20180819_192245.mp4
+ 541875 2024-05-23 19:32 Takeout/Google Photos/Photos from 2018/IMG_8112.JPG
+ 503405 2024-05-23 19:32 Takeout/Google Photos/Photos from 2018/IMG_8113.JPG
+ 1070437 2024-05-23 19:32 Takeout/Google Photos/Photos from 2018/IMG_8111.JPG
+ 583809 2024-05-23 19:32 Takeout/Google Photos/Photos from 2018/IMG_8110.JPG
+ 808994 2024-05-23 19:32 Takeout/Google Photos/Photos from 2018/IMG_20180718_163816.jpg
+ 798787 2024-05-23 19:32 Takeout/Google Photos/Photos from 2018/IMG_20180718_163817.jpg
+...
+```
+
+
+
+## Get the file list from a tgz takeout
+
+```sh
+for f in *.tgz; do echo "$f:"; tar -tzvf $f; done >list.lst
+```
+
+This produces a file like this:
+```
+takeout-20231209T153001Z-001.tgz:
+-rw-r--r-- 0/0 3987330 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231207_192201288.PORTRAIT.jpg
+-rw-r--r-- 0/0 3825143 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231207_192200378.PORTRAIT.jpg
+-rw-r--r-- 0/0 838 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231207_202525504.jpg.json
+-rw-r--r-- 0/0 4136113 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231207_192157945.PORTRAIT.jpg
+-rw-r--r-- 0/0 2817334 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231207_192146687.PORTRAIT.jpg
+-rw-r--r-- 0/0 838 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231207_202513366.jpg.json
+-rw-r--r-- 0/0 827 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231209_074450784.LS.mp4.json
+-rw-r--r-- 0/0 1453060 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231207_202525504.jpg
+-rw-r--r-- 0/0 819 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231207_192200378.PORTRAIT.jpg.json
+-rw-r--r-- 0/0 849 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231207_192157945.PORTRAIT.jpg.json
+-rw-r--r-- 0/0 2852580 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231207_192125032.PORTRAIT.jpg
+-rw-r--r-- 0/0 827 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231209_073951854.LS.mp4.json
+-rw-r--r-- 0/0 3046592 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231207_192127213.PORTRAIT.jpg
+-rw-r--r-- 0/0 684979 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231209_074450784.LS.mp4
+-rw-r--r-- 0/0 2638469 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231207_192128472.PORTRAIT.jpg
+-rw-r--r-- 0/0 819 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231207_192201288.PORTRAIT.jpg.json
+-rw-r--r-- 0/0 1046367 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231207_202513366.jpg
+-rw-r--r-- 0/0 867 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231207_192128472.PORTRAIT.jpg.json
+-rw-r--r-- 0/0 602708 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231207_191742033.jpg
+-rw-r--r-- 0/0 867 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231207_192127213.PORTRAIT.jpg.json
+-rw-r--r-- 0/0 867 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231207_192146687.PORTRAIT.jpg.json
+-rw-r--r-- 0/0 867 2023-12-09 16:30 Takeout/Google Photos/Photos from 2023/PXL_20231207_192125032.PORTRAIT.jpg.json
+...
+```
\ No newline at end of file
diff --git a/docs/releases.md b/docs/releases.md
index a052512b..af3f761e 100644
--- a/docs/releases.md
+++ b/docs/releases.md
@@ -5,6 +5,41 @@
- [Github Sponsor page](https://github.com/sponsors/simulot)
- [paypal donor page](https://www.paypal.com/donate/?hosted_button_id=VGU2SQE88T2T4)
+## Release 0.20.1
+
+### Refactoring the Google Photos import another time
+Lot of users have reported inconsistencies in upload counters. Each user case a different, and the takeout structure varies a bit.
+In order to debug those cases, I have developed a way to simulate the takeout import using only the the file list. Read [how to send debug data](/docs/how-to-send-debug-data.md) without sharing photos.
+
+
+### Option to force the upload of images despite the lack of JSON
+Each image in a takeout is supposed to come with A JSON file giving the date of capture and the GPS coordinate. There a few reason for this:
+1. The original file is copied, modified... and sometime there ins't a JSON for all versions
+2. JSON aren't in the same ZIP file than the image, and only one part of the takeout is processed
+3. The takeout misses a bunch of JSON
+
+When asking another takeout isn't an option, it's possible to force the upload of photos with no JSON. Use the option `-upload-when-missing-JSON`
+
+### The stack function is disabled
+The stack function need to be improved [#399](https://github.com/simulot/immich-go/issues/399), [#345](https://github.com/simulot/immich-go/issues/345), [#235](https://github.com/simulot/immich-go/issues/235)
+Meanwhile, it is disabled by default. You can enable it using the option `-create-stacks=TRUE`.
+
+
+
+
+### fixes:
+- [#376](https://github.com/simulot/immich-go/issues/376) errors when uploading are disturbing the the % of the progression
+- files with same path and name, but in different part of the takeout file set was forgotten in duplicate counters
+- iPhone's Live photos recognition when the name is duplicated: ex IMG_2710(1).MP4 and IMG_2710(1).HEIC
+- Missing a file when a directory contain several files with the same name, but of a different type. Ex: IMG_0170.HEIC, IMG_0170.JPG
+- Live videos attached to duplicated photos are now counted as duplicate as well, making the final report more relevant
+- [#402](https://github.com/simulot/immich-go/issues/402) Wrong album assignment for images with the same name
+- [#390](https://github.com/simulot/immich-go/issues/390) Question: report shows way less images uploaded than scanned
+- [#376](https://github.com/simulot/immich-go/issues/376) errors when uploading are disturbing the the % of the progression
+- [#401](https://github.com/simulot/immich-go/issues/401) Add an option to import images/movies even if there is no JSON file in the takeout
+
+
+## Release 0.20.1
### changes
- add git action to build and release
diff --git a/docs/todo.md b/docs/todo.md
new file mode 100644
index 00000000..0980c27f
--- /dev/null
+++ b/docs/todo.md
@@ -0,0 +1,11 @@
+# TO DO list
+- Motion pictures
+ - [ ] [#405](https://github.com/simulot/immich-go/issues/405) MP~2 files
+- Report connection errors
+ - [ ] [#395](https://github.com/simulot/immich-go/issues/395)
+ - [ ] [#396](https://github.com/simulot/immich-go/issues/396)
+ - [ ] [#393](https://github.com/simulot/immich-go/issues/393)
+- Creation date incorrect
+ - [ ] [#397](https://github.com/simulot/immich-go/issues/397)
+
+
diff --git a/go.mod b/go.mod
index 7fbf35e0..d8491f9b 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,8 @@
module github.com/simulot/immich-go
-go 1.21
+go 1.22
+
+toolchain go1.22.5
require (
github.com/gdamore/tcell/v2 v2.7.4
@@ -8,11 +10,11 @@ require (
github.com/joho/godotenv v1.5.1
github.com/kr/pretty v0.3.1
github.com/melbahja/goph v1.4.0
- github.com/navidys/tvxwidgets v0.6.0
+ github.com/navidys/tvxwidgets v0.7.0
github.com/psanford/memfs v0.0.0-20230130182539-4dbf7e3e865e
- github.com/rivo/tview v0.0.0-20240501114654-1f4d5e8f881d
+ github.com/rivo/tview v0.0.0-20240616192244-23476fa0bab2
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
- github.com/telemachus/humane v0.5.1
+ github.com/telemachus/humane v0.6.0
github.com/thlib/go-timezone-local v0.0.3
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31
golang.org/x/sync v0.7.0
@@ -29,7 +31,7 @@ require (
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
golang.org/x/crypto v0.17.0 // indirect
- golang.org/x/sys v0.17.0 // indirect
- golang.org/x/term v0.17.0 // indirect
- golang.org/x/text v0.14.0 // indirect
+ golang.org/x/sys v0.20.0 // indirect
+ golang.org/x/term v0.20.0 // indirect
+ golang.org/x/text v0.15.0 // indirect
)
diff --git a/go.sum b/go.sum
index 2db40562..58531bee 100644
--- a/go.sum
+++ b/go.sum
@@ -8,13 +8,13 @@ github.com/gdamore/tcell/v2 v2.7.4 h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAY
github.com/gdamore/tcell/v2 v2.7.4/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
-github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
-github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
+github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
-github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
+github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
@@ -31,12 +31,12 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/melbahja/goph v1.4.0 h1:z0PgDbBFe66lRYl3v5dGb9aFgPy0kotuQ37QOwSQFqs=
github.com/melbahja/goph v1.4.0/go.mod h1:uG+VfK2Dlhk+O32zFrRlc3kYKTlV6+BtvPWd/kK7U68=
-github.com/navidys/tvxwidgets v0.6.0 h1:ARIXGfx4aURHMhq+LW5vIoCCDx1X/PdTF8AcUq+nWZ0=
-github.com/navidys/tvxwidgets v0.6.0/go.mod h1:wd6aS2OzjZczFbg8GCaVuwkFcY1eixlT/y7Lc/YIwlg=
-github.com/onsi/ginkgo/v2 v2.16.0 h1:7q1w9frJDzninhXxjZd+Y/x54XNjG/UlRLIYPZafsPM=
-github.com/onsi/ginkgo/v2 v2.16.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
-github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
-github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0=
+github.com/navidys/tvxwidgets v0.7.0 h1:ls5tikzqXnsHwAAV/8zwnRwx/DvSybepUih9txkwjwE=
+github.com/navidys/tvxwidgets v0.7.0/go.mod h1:hzFnllDl4o2Ten/67T0F8ZgC1NiLrZYqWxLVjxWu+zo=
+github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
+github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
+github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
+github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -47,8 +47,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/psanford/memfs v0.0.0-20230130182539-4dbf7e3e865e h1:51xcRlSMBU5rhM9KahnJGfEsBPVPz3182TgFRowA8yY=
github.com/psanford/memfs v0.0.0-20230130182539-4dbf7e3e865e/go.mod h1:tcaRap0jS3eifrEEllL6ZMd9dg8IlDpi2S1oARrQ+NI=
-github.com/rivo/tview v0.0.0-20240501114654-1f4d5e8f881d h1:NuIJVkAVUqxc34PBJx3itBUHxhcK/WJPCq7hYboc1hs=
-github.com/rivo/tview v0.0.0-20240501114654-1f4d5e8f881d/go.mod h1:02iFIz7K/A9jGCvrizLPvoqr4cEIx7q54RH5Qudkrss=
+github.com/rivo/tview v0.0.0-20240616192244-23476fa0bab2 h1:LXMiBMxtuXw8e2paN61dI2LMp8JZYyH4UXDwssRI3ys=
+github.com/rivo/tview v0.0.0-20240616192244-23476fa0bab2/go.mod h1:02iFIz7K/A9jGCvrizLPvoqr4cEIx7q54RH5Qudkrss=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
@@ -63,8 +63,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/telemachus/humane v0.5.1 h1:GgYzcpJKPzuwDcCd1YjPU8vzj/AU4gqbt46pUX+mVis=
-github.com/telemachus/humane v0.5.1/go.mod h1:lToNtEtpBOvw6DSyq7mzBhVbbEm6756mzl4KwXkcckY=
+github.com/telemachus/humane v0.6.0 h1:JNT5SWeg8pOHTRo3STy24E247LpQYBy2vxD2HwYwyvU=
+github.com/telemachus/humane v0.6.0/go.mod h1:T2XzA97m+JPk/WDe9VHamk/JOArXlOy4jlIGDKte3ic=
github.com/thlib/go-timezone-local v0.0.3 h1:ie5XtZWG5lQ4+1MtC5KZ/FeWlOKzW2nPoUnXYUbV/1s=
github.com/thlib/go-timezone-local v0.0.3/go.mod h1:/Tnicc6m/lsJE0irFMA0LfIwTBo4QP7A8IfyIv4zZKI=
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 h1:OXcKh35JaYsGMRzpvFkLv/MEyPuL49CThT1pZ8aSml4=
@@ -85,8 +85,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
-golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
+golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -102,28 +102,31 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
+golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
+golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
+golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
+golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
-golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
+golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
+golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/helpers/fileevent/fileevents.go b/helpers/fileevent/fileevents.go
index 2026a31e..3482e996 100644
--- a/helpers/fileevent/fileevents.go
+++ b/helpers/fileevent/fileevents.go
@@ -221,3 +221,28 @@ func (r *Recorder) WriteFileCounts(w io.Writer) error {
}
return nil
}
+
+func (r *Recorder) TotalAssets() int64 {
+ return atomic.LoadInt64(&r.counts[DiscoveredImage]) + atomic.LoadInt64(&r.counts[DiscoveredVideo])
+}
+
+func (r *Recorder) TotalProcessedGP() int64 {
+ return atomic.LoadInt64(&r.counts[AnalysisAssociatedMetadata]) +
+ atomic.LoadInt64(&r.counts[AnalysisMissingAssociatedMetadata]) +
+ atomic.LoadInt64(&r.counts[DiscoveredDiscarded])
+}
+
+func (r *Recorder) TotalProcessed(forcedMissingJSON bool) int64 {
+ v := atomic.LoadInt64(&r.counts[Uploaded]) +
+ atomic.LoadInt64(&r.counts[UploadServerError]) +
+ atomic.LoadInt64(&r.counts[UploadNotSelected]) +
+ atomic.LoadInt64(&r.counts[UploadUpgraded]) +
+ atomic.LoadInt64(&r.counts[UploadServerDuplicate]) +
+ atomic.LoadInt64(&r.counts[UploadServerBetter]) +
+ atomic.LoadInt64(&r.counts[DiscoveredDiscarded]) +
+ atomic.LoadInt64(&r.counts[AnalysisLocalDuplicate])
+ if !forcedMissingJSON {
+ v += atomic.LoadInt64(&r.counts[AnalysisMissingAssociatedMetadata])
+ }
+ return v
+}
diff --git a/helpers/fshelper/globwalkfs.go b/helpers/fshelper/globwalkfs.go
index e595995d..1a841fde 100644
--- a/helpers/fshelper/globwalkfs.go
+++ b/helpers/fshelper/globwalkfs.go
@@ -1,6 +1,7 @@
package fshelper
import (
+ "fmt"
"io/fs"
"os"
"path"
@@ -108,7 +109,7 @@ func (gw GlobWalkFS) ReadDir(name string) ([]fs.DirEntry, error) {
}
entries, err := fs.ReadDir(gw.rootFS, name)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("ReadDir %s: %w", name, err)
}
returned := []fs.DirEntry{}
diff --git a/helpers/fshelper/parseArgs.go b/helpers/fshelper/parseArgs.go
index fff24059..8af89c70 100644
--- a/helpers/fshelper/parseArgs.go
+++ b/helpers/fshelper/parseArgs.go
@@ -16,7 +16,7 @@ import (
//
// TODO: Implement a tgz reader for non google-photos archives
-func ParsePath(args []string, googlePhoto bool) ([]fs.FS, error) {
+func ParsePath(args []string) ([]fs.FS, error) {
var errs error
fsyss := []fs.FS{}
diff --git a/immich/client.go b/immich/client.go
index aef111b5..97b107ac 100644
--- a/immich/client.go
+++ b/immich/client.go
@@ -7,7 +7,10 @@ import (
"io"
"net/http"
"os"
+ "slices"
+ "sort"
"strings"
+ "sync"
"time"
)
@@ -223,16 +226,24 @@ func (sm SupportedMedia) IsMedia(ext string) bool {
return t == TypeVideo || t == TypeImage
}
+var (
+ _supportedExtension []string
+ initSupportedExtion sync.Once
+)
+
func (sm SupportedMedia) IsExtensionPrefix(ext string) bool {
- ext = strings.ToLower(ext)
- for e, t := range sm {
- if t == TypeVideo || t == TypeImage {
- if ext == e[:len(e)-1] {
- return true
- }
+ initSupportedExtion.Do(func() {
+ _supportedExtension = make([]string, len(sm))
+ i := 0
+ for k := range sm {
+ _supportedExtension[i] = k[:len(k)-2]
+ i++
}
- }
- return false
+ sort.Strings(_supportedExtension)
+ })
+ ext = strings.ToLower(ext)
+ _, b := slices.BinarySearch(_supportedExtension, ext)
+ return b
}
func (sm SupportedMedia) IsIgnoredExt(ext string) bool {
diff --git a/immich/client_test.go b/immich/client_test.go
new file mode 100644
index 00000000..0e20511f
--- /dev/null
+++ b/immich/client_test.go
@@ -0,0 +1,30 @@
+package immich
+
+import "testing"
+
+/*
+baseline
+
+goos: linux
+goarch: amd64
+pkg: github.com/simulot/immich-go/immich
+cpu: Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz
+Benchmark_IsExtensionPrefix-12 4096238 297.3 ns/op 3 B/op 1 allocs/op
+PASS
+ok github.com/simulot/immich-go/immich 1.518s
+
+goos: linux
+goarch: amd64
+pkg: github.com/simulot/immich-go/immich
+cpu: Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz
+Benchmark_IsExtensionPrefix-12 16536936 72.85 ns/op 3 B/op 1 allocs/op
+PASS
+ok github.com/simulot/immich-go/immich 1.283s
+*/
+func Benchmark_IsExtensionPrefix(b *testing.B) {
+ sm := DefaultSupportedMedia
+ sm.IsExtensionPrefix(".JP")
+ for i := 0; i < b.N; i++ {
+ sm.IsExtensionPrefix(".JP")
+ }
+}
diff --git a/internal/fakeImmich/immich.go b/internal/fakeImmich/immich.go
new file mode 100644
index 00000000..dd06a72f
--- /dev/null
+++ b/internal/fakeImmich/immich.go
@@ -0,0 +1,97 @@
+package fakeimmich
+
+import (
+ "context"
+ "io"
+
+ "github.com/simulot/immich-go/browser"
+ "github.com/simulot/immich-go/immich"
+)
+
+type MockedCLient struct{}
+
+func (c *MockedCLient) GetAllAssetsWithFilter(context.Context, func(*immich.Asset) error) error {
+ return nil
+}
+
+func (c *MockedCLient) AssetUpload(context.Context, *browser.LocalAssetFile) (immich.AssetResponse, error) {
+ return immich.AssetResponse{}, nil
+}
+
+func (c *MockedCLient) DeleteAssets(context.Context, []string, bool) error {
+ return nil
+}
+
+func (c *MockedCLient) GetAllAlbums(context.Context) ([]immich.AlbumSimplified, error) {
+ return nil, nil
+}
+
+func (c *MockedCLient) AddAssetToAlbum(context.Context, string, []string) ([]immich.UpdateAlbumResult, error) {
+ return nil, nil
+}
+
+func (c *MockedCLient) CreateAlbum(context.Context, string, string, []string) (immich.AlbumSimplified, error) {
+ return immich.AlbumSimplified{}, nil
+}
+
+func (c *MockedCLient) UpdateAssets(ctx context.Context, ids []string, isArchived bool, isFavorite bool, latitude float64, longitude float64, removeParent bool, stackParentID string) error {
+ return nil
+}
+
+func (c *MockedCLient) StackAssets(ctx context.Context, cover string, ids []string) error {
+ return nil
+}
+
+func (c *MockedCLient) UpdateAsset(ctx context.Context, id string, a *browser.LocalAssetFile) (*immich.Asset, error) {
+ return nil, nil
+}
+
+func (c *MockedCLient) EnableAppTrace(w io.Writer) {}
+
+func (c *MockedCLient) GetServerStatistics(ctx context.Context) (immich.ServerStatistics, error) {
+ return immich.ServerStatistics{}, nil
+}
+
+func (c *MockedCLient) PingServer(ctx context.Context) error {
+ return nil
+}
+
+func (c *MockedCLient) SetDeviceUUID(string) {}
+
+func (c *MockedCLient) SetEndPoint(string) {}
+
+func (c *MockedCLient) ValidateConnection(ctx context.Context) (immich.User, error) {
+ return immich.User{}, nil
+}
+
+func (c *MockedCLient) GetAssetAlbums(ctx context.Context, id string) ([]immich.AlbumSimplified, error) {
+ return nil, nil
+}
+
+func (c *MockedCLient) GetAllAssets(ctx context.Context) ([]*immich.Asset, error) {
+ return nil, nil
+}
+
+func (c *MockedCLient) DeleteAlbum(ctx context.Context, id string) error {
+ return nil
+}
+
+func (c *MockedCLient) SupportedMedia() immich.SupportedMedia {
+ return immich.DefaultSupportedMedia
+}
+
+func (c *MockedCLient) GetAssetStatistics(ctx context.Context) (immich.UserStatistics, error) {
+ return immich.UserStatistics{
+ Images: 1,
+ Videos: 1,
+ Total: 1,
+ }, nil
+}
+
+func (c *MockedCLient) GetJobs(ctx context.Context) (map[string]immich.Job, error) {
+ return nil, nil
+}
+
+func (c *MockedCLient) GetAlbumInfo(context.Context, string, bool) (immich.AlbumContent, error) {
+ return immich.AlbumContent{}, nil
+}
diff --git a/internal/fakefs/TESTDATA/one.lst b/internal/fakefs/TESTDATA/one.lst
new file mode 100644
index 00000000..bbeb319c
--- /dev/null
+++ b/internal/fakefs/TESTDATA/one.lst
@@ -0,0 +1,9 @@
+takeout-20240525T201456Z-003.zip:
+Archive: takeout-20240525T201456Z-003.zip
+ Length Date Time Name
+--------- ---------- ----- ----
+245068101 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719203057+0200.mp4
+221047335 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719185840+0200.mp4
+229274490 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719183450+0200.mp4
+--------- -------
+695389926 3 files
diff --git a/internal/fakefs/TESTDATA/small.lst b/internal/fakefs/TESTDATA/small.lst
new file mode 100644
index 00000000..50ae0612
--- /dev/null
+++ b/internal/fakefs/TESTDATA/small.lst
@@ -0,0 +1,986 @@
+takeout-20240525T201456Z-001.zip:
+Archive: takeout-20240525T201456Z-001.zip
+ Length Date Time Name
+--------- ---------- ----- ----
+ 425704 2024-05-25 22:15 Takeout/Google Photos/Guillaumes 2018/IMG_8213.JPG
+ 307 2018-07-28 10:24 Takeout/Google Photos/Guillaumes 2018/métadonnées.json
+ 2754273 2024-05-25 22:15 Takeout/Google Photos/Guillaumes 2018/IMG_20180725_102133.jpg
+ 164272 2024-05-25 22:15 Takeout/Google Photos/Guillaumes 2018/IMG_8416.JPG
+ 748 2024-05-25 22:15 Takeout/Google Photos/Guillaumes 2018/Bebop2_20180723180057+0200.m4v.json
+ 730 2024-05-25 22:15 Takeout/Google Photos/Guillaumes 2018/IMG_8416.JPG.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Guillaumes 2018/IMG_8213.JPG.json
+ 775 2024-05-25 22:15 Takeout/Google Photos/Guillaumes 2018/IMG_20180725_102133.jpg.json
+ 139865 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/✈️ DSC04119.jpg
+ 736 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/✈️ DSC04119.jpg.json
+ 954 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/😀😃😄😁😆😅😂🤣🥲☺️😊😇🙂🙃😉😌😍🥰😘😗😙😚😋.json
+ 353 2023-08-21 19:24 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/métadonnées.json
+ 2021088 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1763.HEIC
+ 145804 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/😀😃😄😁😆😅😂🤣🥲☺️😊😇🙂🙃😉😌😍🥰😘😗😙😚😋😛.jpg
+ 845 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_045312704.jpg.json
+ 845 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_045318279.jpg.json
+ 2548657 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1762.HEIC
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_052150006.jpg.json
+ 866 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_045311490.jpg.json
+ 707 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1762.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1763.HEIC.json
+ 2734800 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1764.HEIC
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_053046647.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1764.HEIC.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_053056618.jpg.json
+ 165685 2024-05-25 22:15 Takeout/Google Photos/Guillaumes 2018/IMG_8416-modifié.JPG
+ 2797125 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_045318279.jpg
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_053450706.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_053252586.jpg.json
+ 2246976 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_052150006.jpg
+ 1829557 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_053252586.jpg
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_053257151.jpg.json
+ 1819407 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_053046647.jpg
+ 2629906 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_045312704.jpg
+ 3161101 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_045311490.jpg
+ 1811270 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_053257151.jpg
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_054704630.jpg.json
+ 880 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_054809080.jpg.json
+ 2238741 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_053056618.jpg
+ 850 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_060922712.jpg.json
+ 882 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_061449612.jpg.json
+ 1706679 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_054704630.jpg
+ 868 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_061617240.jpg.json
+ 1099162 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_061617240.jpg
+ 866 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_065633104.jpg.json
+ 864 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_065541326.jpg.json
+ 2018556 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_053450706.jpg
+ 1758371 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_060922712.jpg
+ 1506192 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_054809080.jpg
+ 866 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_065651473.jpg.json
+ 1605479 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_061449612.jpg
+ 854 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_065801190.LS.mp4.json
+ 864 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_070314539.jpg.json
+ 864 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_071125792.jpg.json
+ 2513620 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_065633104.jpg
+ 2689434 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_065541326.jpg
+ 848 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_072708786.jpg.json
+ 1680663 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_071125792.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1772.MOV.json
+ 868 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_070539132.jpg.json
+ 705 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1767.MOV.json
+ 1839801 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_070539132.jpg
+ 830 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_074101341.LS.mp4.json
+ 2364569 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_065651473.jpg
+ 2174283 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_070314539.jpg
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_074326135.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_074330974.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_075720250.jpg.json
+ 826044 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_065801190.LS.mp4
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_080614621.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_075722087.jpg.json
+ 1475614 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_074330974.jpg
+ 1402475 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_074326135.jpg
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_080729129.jpg.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_080832073.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_080615786.jpg.json
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_074318267.jpg.json
+ 2357148 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_080614621.jpg
+ 2258518 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_072708786.jpg
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_085230735.jpg.json
+ 1960901 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_075722087.jpg
+ 2428606 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_080832073.jpg
+ 2009846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_075720250.jpg
+ 1067912 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_074318267.jpg
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_085317917.jpg.json
+ 866 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_065344256.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_085545918.jpg.json
+ 866 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_065426987.jpg.json
+ 2877589 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_080729129.jpg
+ 20458330 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1772.MOV
+ 2764415 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_085230735.jpg
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_085707338.jpg.json
+ 2754160 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_080615786.jpg
+ 43921624 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1767.MOV
+ 1664294 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_065344256.jpg
+ 837 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_093742072.PANO.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_085824598.jpg.json
+ 1947640 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_065426987.jpg
+ 837 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_095051535.PANO.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_094133465.jpg.json
+ 834 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_095014093.jpg.json
+ 2951881 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_085220659.jpg
+ 2928838 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_093742072.PANO.jpg
+ 3054790 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_085317917.jpg
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_100426120.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_095241390.jpg.json
+ 874 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_101130729.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_101025932.jpg.json
+ 3249369 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_095051535.PANO.jpg
+ 853 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_104957115.LS.mp4.json
+ 3079050 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1773.HEIC
+ 3136587 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_085545918.jpg
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_114039025.jpg.json
+ 3100522 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_085707338.jpg
+ 1396573 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1775.HEIC
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_143108095.jpg.json
+ 3434665 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_085824598.jpg
+ 2852147 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_094133465.jpg
+ 2030183 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1776.HEIC
+ 2442085 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1774.HEIC
+ 1245550 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_100426120.jpg
+ 2682608 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1778.HEIC
+ 1905682 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1780.HEIC
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1774.HEIC.json
+ 1183599 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1782.HEIC
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1775.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1776.HEIC.json
+ 1768315 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1777.HEIC
+ 3461019 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_095241390.jpg
+ 1546245 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_074101341.LS.mp4
+ 2596682 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_143108095.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1779.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1780.HEIC.json
+ 2577241 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1779.HEIC
+ 3180012 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_101130729.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1778.HEIC.json
+ 699580 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1787.HEIC
+ 4030321 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_095014093.jpg
+ 3402509 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1781.HEIC
+ 2281196 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1786.HEIC
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1777.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1783.HEIC.json
+ 3181783 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1783.HEIC
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1784.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1781.HEIC.json
+ 2332185 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1791.HEIC
+ 2296877 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1785.HEIC
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1782.HEIC.json
+ 3649659 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1784.HEIC
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1785.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1786.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1791.HEIC.json
+ 3173006 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_101025932.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1788.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1787.HEIC.json
+ 2778043 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1788.HEIC
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_175934570.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_173903662.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_172315843.jpg.json
+ 5621814 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_114039025.jpg
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_175940633.jpg.json
+ 27203585 2024-05-25 22:15 Takeout/Google Photos/Guillaumes 2018/Bebop2_20180723180057+0200.m4v
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_172540663.jpg.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_181336232.jpg.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_181337872.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_180819064.jpg.json
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_180515752.jpg.json
+ 2894559 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_173903662.jpg
+ 1875846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_180819064.jpg
+ 4769063 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_104957115.LS.mp4
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_181918957.jpg.json
+ 852 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_202853503.NIGHT.jpg.json
+ 852 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_202938567.NIGHT.jpg.json
+ 3611141 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_172315843.jpg
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_181952207.jpg.json
+ 953011 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1795.HEIC
+ 2005029 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1794.HEIC
+ 3031838 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_180515752.jpg
+ 2052762 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_181336232.jpg
+ 2239188 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_181337872.jpg
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_203007881.jpg.json
+ 868 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_203016989.NIGHT.jpg.json
+ 848 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_203051232.NIGHT.jpg.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_203004692.jpg.json
+ 2249199 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_175940633.jpg
+ 2971596 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_181952207.jpg
+ 2149199 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_175934570.jpg
+ 1988569 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1796.HEIC
+ 4191014 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_172540663.jpg
+ 1785180 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1798.HEIC
+ 1685183 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_203004692.jpg
+ 3673018 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_181918957.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1795.HEIC.json
+ 2372396 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_202853503.NIGHT.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1794.HEIC.json
+ 1688525 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_203007881.jpg
+ 1474426 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_203051232.NIGHT.jpg
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_071217029.jpg.json
+ 954052 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1803.HEIC
+ 1357647 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1802.HEIC
+ 2217158 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1801.HEIC
+ 2045173 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1797.HEIC
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1796.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1798.HEIC.json
+ 2291074 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1799.HEIC
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1797.HEIC.json
+ 2518704 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_202938567.NIGHT.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1799.HEIC.json
+ 1752476 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_203016989.NIGHT.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1800.HEIC.json
+ 2350286 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1800.HEIC
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1801.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1802.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1803.HEIC.json
+ 840 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_083758386.jpg.json
+ 2750364 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_071217029.jpg
+ 878 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_085041151.jpg.json
+ 853 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_085134035.LS.mp4.json
+ 848 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_085348060.mp4.json
+ 828 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_085241731.mp4.json
+ 828 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_090912639.mp4.json
+ 853 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_085142027.TS.mp4.json
+ 848 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_085641282.mp4.json
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_091023980.jpg.json
+ 848 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_085417927.mp4.json
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_091029409.jpg.json
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_091031920.jpg.json
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_091035587.jpg.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_091328950.jpg.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_091609310.jpg.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_091608239.jpg.json
+ 154044 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_085417927.mp4
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_091959433.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_092000946.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_092304596.jpg.json
+ 2473525 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_091959433.jpg
+ 4594317 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_085041151.jpg
+ 1894384 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_092000946.jpg
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_092309141.jpg.json
+ 850572 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1804.HEIC
+ 828 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_093331250.mp4.json
+ 2356936 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_091031920.jpg
+ 514406 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_085641282.mp4
+ 3200270 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_085134035.LS.mp4
+ 3232353 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_091035587.jpg
+ 4151500 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_083758386.jpg
+ 850 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_093746681.mp4.json
+ 3846315 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_091608239.jpg
+ 3839435 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_091609310.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1804.HEIC.json
+ 2439866 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_091029409.jpg
+ 880 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_094846004.PORTRAIT.ORIGINAL.jpg.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_094907387.jpg.json
+ 4431123 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_091328950.jpg
+ 4982318 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_091023980.jpg
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_095433860.jpg.json
+ 838 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_100609571.jpg.json
+ 8171047 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_090912639.mp4
+ 1751462 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_095433860.jpg
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_100754362.jpg.json
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_100934441.jpg.json
+ 2590405 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_094907387.jpg
+ 4204935 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_092304596.jpg
+ 1913930 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_094846004.PORTRAIT.ORIGINAL.jpg
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_101954705.jpg.json
+ 4938850 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_085348060.mp4
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_101958829.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_101956501.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_103647627.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_102604015.jpg.json
+ 2616162 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_100754362.jpg
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_102135803.jpg.json
+ 1676634 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_101956501.jpg
+ 2122990 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_100934441.jpg
+ 1704142 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_101954705.jpg
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_103652839.jpg.json
+ 838 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_104311200.jpg.json
+ 4648621 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_085241731.mp4
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_103704197.jpg.json
+ 838 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_104355371.jpg.json
+ 9350036 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_093331250.mp4
+ 1894488 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_101958829.jpg
+ 3945359 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_100609571.jpg
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_104439655.jpg.json
+ 2883623 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_092309141.jpg
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_104535388.jpg.json
+ 2177026 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_102604015.jpg
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_104550263.jpg.json
+ 4094755 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_102135803.jpg
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_104646669.jpg.json
+ 3365027 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_103652839.jpg
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_105115882.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_105118064.jpg.json
+ 2824711 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_103647627.jpg
+ 874 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_111430712.jpg.json
+ 2523883 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_103704197.jpg
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_111310171.jpg.json
+ 845 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_112118374.PANO.jpg.json
+ 2306670 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_105118064.jpg
+ 1516138 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_112118374.PANO.jpg
+ 872 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_112941866.jpg.json
+ 3807836 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_104550263.jpg
+ 872 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_112926703.jpg.json
+ 323256 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_093746681.mp4
+ 872 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_112944670.jpg.json
+ 1387263 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1805.HEIC
+ 2711896 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_104355371.jpg
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_113132593.jpg.json
+ 2940811 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_112926703.jpg
+ 876 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_113936027.jpg.json
+ 876 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_114626362.jpg.json
+ 3446024 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_104439655.jpg
+ 2281660 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_105115882.jpg
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_120327153.jpg.json
+ 3784479 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_111430712.jpg
+ 2502925 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_113132593.jpg
+ 2924684 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_104311200.jpg
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_121239554.jpg.json
+ 1998148 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_113936027.jpg
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_120803085.jpg.json
+ 2280645 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_120327153.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1805.HEIC.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_122023413.jpg.json
+ 2833588 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1809.HEIC
+ 4079835 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_104535388.jpg
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_122032538.jpg.json
+ 3783408 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1810.HEIC
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_121248049.jpg.json
+ 2905349 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_111310171.jpg
+ 1664304 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_114626362.jpg
+ 4787027 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_104646669.jpg
+ 850 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_134329900.mp4.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_134755219.jpg.json
+ 3464559 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1811.HEIC
+ 2879828 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1808.HEIC
+ 1094337 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1812.HEIC
+ 3286583 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_121248049.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1808.HEIC.json
+ 10006046 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_085142027.TS.mp4
+ 2540915 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_120803085.jpg
+ 2850159 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_121239554.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1810.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1809.HEIC.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_185012667.jpg.json
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_191603424.jpg.json
+ 2829587 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_122023413.jpg
+ 2819007 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_122032538.jpg
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_185027454.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1811.HEIC.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_191303726.jpg.json
+ 4694514 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_112941866.jpg
+ 2706891 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1814.HEIC
+ 1474839 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1813.HEIC
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_191605842.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1812.HEIC.json
+ 2264631 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_185012667.jpg
+ 4497231 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_112944670.jpg
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_192412458.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1813.HEIC.json
+ 878 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_193423423.jpg.json
+ 878 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_193422379.jpg.json
+ 2385333 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_191603424.jpg
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_192636406.jpg.json
+ 2885377 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1817.HEIC
+ 1671841 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1815.HEIC
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1814.HEIC.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_194519579.jpg.json
+ 2712576 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_191605842.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1816.HEIC.json
+ 2917329 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1816.HEIC
+ 4388501 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_134755219.jpg
+ 3456118 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_191303726.jpg
+ 864 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_195900654.NIGHT.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_195917841.jpg.json
+ 705 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1818.MOV.json
+ 68756797 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1818.MOV
+ 1804560 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_192636406.jpg
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_195924456.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1815.HEIC.json
+ 705 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1819.MOV.json
+ 2552809 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_193423423.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1817.HEIC.json
+ 27203198 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1819.MOV
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_200210541.jpg.json
+ 2240140 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_185027454.jpg
+ 2500593 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_192412458.jpg
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_200156149.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_200308368.jpg.json
+ 831 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_200310052.TS.mp4.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_200529292.jpg.json
+ 2522639 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_193422379.jpg
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_200532278.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_201038861.jpg.json
+ 3251996 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_195924456.jpg
+ 3499095 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_194519579.jpg
+ 3147675 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_195917841.jpg
+ 804 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_201154491.LONG_EXPOSURE-01.COVER..json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_201231937.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_201300341.jpg.json
+ 1749506 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_201205358.ACTION_PAN-01.COVER.jpg
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_203801941.jpg.json
+ 801 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_201205358.ACTION_PAN-01.COVER.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_203823133.jpg.json
+ 3247533 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_195900654.NIGHT.jpg
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_203824642.jpg.json
+ 3727860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_200308368.jpg
+ 3264214 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_200210541.jpg
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_203825515.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_204729094.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_204736848.jpg.json
+ 3260444 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_200156149.jpg
+ 3721976 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_201154491.LONG_EXPOSURE-01.COVER.jpg
+ 2583993 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_200529292.jpg
+ 12783566 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_200310052.TS.mp4
+ 843 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_203826359.jpg.json
+ 3227076 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_201300341.jpg
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_213833167.jpg.json
+ 3538166 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_201038861.jpg
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_214029337.jpg.json
+ 3006962 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_203823133.jpg
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_214049584.jpg.json
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_220414717.jpg.json
+ 3144573 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_203825515.jpg
+ 2859457 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_204736848.jpg
+ 852 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_221244885.NIGHT.jpg.json
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_220430652.jpg.json
+ 16335360 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1820.MOV
+ 3205962 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_203801941.jpg
+ 3638799 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_201231937.jpg
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_073609646.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_092115663.jpg.json
+ 705 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1820.MOV.json
+ 2950346 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_203826359.jpg
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_092244364.jpg.json
+ 3706201 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_213833167.jpg
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_092314014.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_092420813.jpg.json
+ 3166324 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_203824642.jpg
+ 2076957 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_073609646.jpg
+ 2450989 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_200532278.jpg
+ 2061705 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_220414717.jpg
+ 2711975 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_204729094.jpg
+ 2365786 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_220430652.jpg
+ 1356103 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_221244885.NIGHT.jpg
+ 2929617 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_214049584.jpg
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_092454769.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_092549245.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_092559642.jpg.json
+ 15697268 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_134329900.mp4
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_092609468.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_092742640.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_095719059.jpg.json
+ 4392195 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230814_214029337.jpg
+ 3267005 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_092314014.jpg
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_095911791.jpg.json
+ 3479639 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_092115663.jpg
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_110610989.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_110716350.jpg.json
+ 856 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_110637215.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_110627557.jpg.json
+ 838 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_110956602.jpg.json
+ 3795394 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_092549245.jpg
+ 856 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_110909980.jpg.json
+ 838 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_110958888.jpg.json
+ 3332616 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_092420813.jpg
+ 2555347 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_092559642.jpg
+ 1104969 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_110958888.jpg
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_111355138.jpg.json
+ 2822809 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1821.HEIC
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_111416343.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_111400308.jpg.json
+ 1477767 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1822.HEIC
+ 4891737 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_110610989.jpg
+ 3257767 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_095719059.jpg
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_111624099.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_111805566.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_111809021.jpg.json
+ 5538289 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_092454769.jpg
+ 2801493 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_110956602.jpg
+ 4236065 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_110637215.jpg
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_112215507.jpg.json
+ 3825053 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_110909980.jpg
+ 3882670 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_095911791.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1821.HEIC.json
+ 3682575 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_111400308.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1822.HEIC.json
+ 4421482 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_092609468.jpg
+ 3807495 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_110627557.jpg
+ 1406035 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1826.HEIC
+ 5056540 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_092742640.jpg
+ 876 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_112252009.jpg.json
+ 840 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_112349398.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_112447598.jpg.json
+ 3655827 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_111355138.jpg
+ 2021117 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1824.HEIC
+ 2928784 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_111809021.jpg
+ 874 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_112517602.jpg.json
+ 3280542 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_111416343.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1823.HEIC.json
+ 2752273 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_111805566.jpg
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_113519297.jpg.json
+ 3392793 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1823.HEIC
+ 3234964 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_112215507.jpg
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_114315562.jpg.json
+ 2672206 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_112252009.jpg
+ 1902366 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1829.HEIC
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1826.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1825.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1824.HEIC.json
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_114438496.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1827.HEIC.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_114334732.jpg.json
+ 4857728 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_111624099.jpg
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_114451718.jpg.json
+ 1590243 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1828.HEIC
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1828.HEIC.json
+ 3276096 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_112517602.jpg
+ 2207069 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_092244364.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1829.HEIC.json
+ 2880413 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1827.HEIC
+ 4622459 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_110716350.jpg
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_114603834.jpg.json
+ 4107927 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_112349398.jpg
+ 874 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_114655917.jpg.json
+ 1858441 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1825.HEIC
+ 1230898 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_114941122.jpg
+ 2329548 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_114334732.jpg
+ 3027958 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_113519297.jpg
+ 852 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_114708377.jpg.json
+ 4163914 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_112447598.jpg
+ 1288661 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1831.HEIC
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_114849873.jpg.json
+ 1755648 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_114438496.jpg
+ 3186118 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1832.HEIC
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_114750722.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_115247743.jpg.json
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_114941122.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_115339983.jpg.json
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_115324982.jpg.json
+ 2281324 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1830.HEIC
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_115341662.jpg.json
+ 2817633 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_114603834.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1831.HEIC.json
+ 3084847 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_114849873.jpg
+ 2159034 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1835.HEIC
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1832.HEIC.json
+ 3083608 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1833.HEIC
+ 830653 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_115247743.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1833.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1830.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1834.HEIC.json
+ 1571401 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1834.HEIC
+ 2711792 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1836.HEIC
+ 1995263 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_114708377.jpg
+ 4297880 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_114451718.jpg
+ 2672283 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_114655917.jpg
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_121631092.jpg.json
+ 2200132 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_115341662.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1835.HEIC.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_121633187.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_122137232.jpg.json
+ 2085527 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_115339983.jpg
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_122826011.jpg.json
+ 2189884 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_122137232.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1836.HEIC.json
+ 2463125 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1837.HEIC
+ 1882389 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_121631092.jpg
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_122926905.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_123051917.jpg.json
+ 2471414 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1838.HEIC
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_123055666.jpg.json
+ 1721502 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_114750722.jpg
+ 2282623 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_115324982.jpg
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_123109118.jpg.json
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_123314870.jpg.json
+ 1865198 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1839.HEIC
+ 853 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_124031987.TS.mp4.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_123624823.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1837.HEIC.json
+ 2772827 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_123109118.jpg
+ 2347070 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_122926905.jpg
+ 876 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_124552627.PORTRAIT.ORIGINAL.jpg.json
+ 1953026 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_121633187.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1838.HEIC.json
+ 1738977 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_114315562.jpg
+ 867 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_124817964.PORTRAIT.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_124911036.jpg.json
+ 5106360 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_124817964.PORTRAIT.jpg
+ 1700453 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1842.HEIC
+ 3246872 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_122826011.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1839.HEIC.json
+ 1281223 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1840.HEIC
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1840.HEIC.json
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_125508511.jpg.json
+ 2744968 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_123051917.jpg
+ 1731604 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_123314870.jpg
+ 2988456 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_123055666.jpg
+ 1801212 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1841.HEIC
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_125516847.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1841.HEIC.json
+ 1325046 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_124031987.TS.mp4
+ 2189746 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1843.HEIC
+ 2503191 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1844.HEIC
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1842.HEIC.json
+ 2452627 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_123624823.jpg
+ 831 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_125540275.TS.mp4.json
+ 2722453 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_124552627.PORTRAIT.ORIGINAL.jpg
+ 836 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_125637640.jpg.json
+ 2530601 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1845.HEIC
+ 836 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_125646665.jpg.json
+ 2806033 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1848.HEIC
+ 874 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_125905261.PORTRAIT.ORIGINAL.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1844.HEIC.json
+ 2739332 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_124911036.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1843.HEIC.json
+ 2576854 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1846.HEIC
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1845.HEIC.json
+ 3078212 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_125516847.jpg
+ 2309189 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1847.HEIC
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_130155836.PORTRAIT.ORIGINAL.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1846.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1848.HEIC.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_130631866.jpg.json
+ 2747390 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_125646665.jpg
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1847.HEIC.json
+ 874 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_131013972.jpg.json
+ 874 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_131100760.jpg.json
+ 874 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_131020934.jpg.json
+ 2787837 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_125508511.jpg
+ 2213164 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_125905261.PORTRAIT.ORIGINAL.jpg
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_132140303.jpg.json
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_132154767.jpg.json
+ 856 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_132231336.jpg.json
+ 845 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_150752249.PANO.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_152254524.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_151238032.jpg.json
+ 876 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_152303067.jpg.json
+ 2479158 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_130631866.jpg
+ 3184937 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_132140303.jpg
+ 3051320 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_130155836.PORTRAIT.ORIGINAL.jpg
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_152405874.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_152841555.jpg.json
+ 5022885 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_131013972.jpg
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_152846037.jpg.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_152846037~2.jpg.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_153003181~3.jpg.json
+ 4805476 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_131020934.jpg
+ 899 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_153003181~2.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_153110793.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_153308948.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_153003181.jpg.json
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_153603660.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_153359013.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_155435571.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_155505084.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_160052888.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1849.HEIC.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_160105481.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_160831688.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_160934535.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_161357571.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_160934535~2.jpg.json
+ 854 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_170918725.jpg.json
+ 854 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_170921221.jpg.json
+ 840 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_172103511.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_184541352.jpg.json
+ 854 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_171225430.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1850.HEIC.json
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_191055522.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_184608476.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_191300435.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1851.HEIC.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_193522767.jpg.json
+ 850 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_201809279.NIGHT.jpg.json
+ 876 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_202246258.jpg.json
+ 882 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_202327379.NIGHT.jpg.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_201655580.NIGHT.jpg.json
+ 884 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_202818387.NIGHT.jpg.json
+ 884 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_202820142.NIGHT.jpg.json
+ 850 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_065539076.NIGHT.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_060414573.jpg.json
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_071349105.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1852.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1853.HEIC.json
+ 1186801 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1856.HEIC
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1854.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1855.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1856.HEIC.json
+ 853 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_113900491.LS.mp4.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1857.HEIC.json
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_112210934.jpg.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_115318027.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1859.HEIC.json
+ 123247 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1862.HEIC
+ 868 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_132648337.NIGHT.jpg.json
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_143203982.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1860.HEIC.json
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_143208443.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1861.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1862.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1864.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1865.HEIC.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_191312144.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1863.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1866.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1869.HEIC.json
+ 878 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_211707485.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1870.HEIC.json
+ 876 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_202044782.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1871.HEIC.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_071450157.jpg.json
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_072539487.jpg.json
+ 733 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/FFE79A68-B0E6-472E-836D-EC49F351F588.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_080105375.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_081251352.jpg.json
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_121601920.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_075312564.jpg.json
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_131815116.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1875.HEIC.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1876.HEIC.json
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_170752393.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_181209268.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1877.HEIC.json
+ 845 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_175514506.PANO.jpg.json
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_181502346.jpg.json
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_181456746.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_183021857.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_183618613.jpg.json
+ 831 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_183716695.LS.mp4.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_183751525.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_183811061.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_183801046.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_183826357.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1773.HEIC.json
+ 733 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/509C843D-D4B9-4F02-81B0-05BE938F12DF.jpg.json
+ 706 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1878.HEIC.json
+ 856 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_193237877.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_194439146.jpg.json
+ 853 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_195943901.LS.mp4.json
+ 876 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_200858587.jpg.json
+ 876 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_200900799.jpg.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_201212577.jpg.json
+ 848 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_202545906.NIGHT.jpg.json
+ 848 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_202555916.NIGHT.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230813_085220659.jpg.json
+ 829 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_202746758.LS.mp4.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_062827230.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_072713034.jpg.json
+ 842 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_072728336.jpg.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_073034374.jpg.json
+ 845 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_073228970.PANO.jpg.json
+ 831 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_073402211.TS.mp4.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_073704846.jpg.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_073846874.jpg.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_074307497.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_080921955.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_080922936.jpg.json
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_080937939.jpg.json
+ 858 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_081231702.jpg.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_082348579.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_082351912.jpg.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_130000489.jpg.json
+ 844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_132150855.jpg.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_144557167.jpg.json
+ 295 2018-07-21 09:08 Takeout/Google Photos/Garros 19 Juillet 2018/métadonnées.json
+ 860 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_134521846.jpg.json
+ 878 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_144231600.jpg.json
+ 862 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_144716103.jpg.json
+ 846 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_150420324.jpg.json
+ 720710 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_20180719_191506.jpg
+ 789 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719182934+0200.mp4.json
+ 812 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_161304257.jpg.json
+ 407330 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8151.JPG
+ 785 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719183450+0200.mp4.json
+ 763 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719185209+0200.mp4.json
+ 369458 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8153.JPG
+ 763 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719185840+0200.mp4.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8152.JPG.json
+ 810 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_20180719_191506.jpg.json
+ 854 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_20180719_191638.jpg.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8151.JPG.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8153.JPG.json
+ 763 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719194940+0200.mp4.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8155.JPG.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8156.JPG.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8157.JPG.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8158.JPG.json
+ 810 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_20180719_200157.jpg.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8162.JPG.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8163.JPG.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8164.JPG.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8165.JPG.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8166.JPG.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8167.JPG.json
+ 763 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719201922+0200.mp4.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8168.JPG.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8169.JPG.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8172.JPG.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8170.JPG.json
+ 763 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719203057+0200.mp4.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8177.JPG.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8178.JPG.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8179.JPG.json
+ 731 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8181.JPG.json
+ 763 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719211425+0200.mp4.json
+ 806 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719211742+0200.jpg.json
+ 336456 2024-05-25 22:15 Takeout/archive_browser.html
+--------- -------
+1070219299 807 files
+takeout-20240525T201456Z-002.zip:
+Archive: takeout-20240525T201456Z-002.zip
+ Length Date Time Name
+--------- ---------- ----- ----
+ 2730889 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_152303067.jpg
+ 5363355 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_132231336.jpg
+ 2422683 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_153003181~3.jpg
+ 4243153 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_132154767.jpg
+ 2531099 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_152846037.jpg
+ 2085622 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_131100760.jpg
+ 2553353 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_152841555.jpg
+ 2919006 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_153003181.jpg
+ 2148816 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_152846037~2.jpg
+ 2936280 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_152254524.jpg
+ 2422348 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_153003181~2.jpg
+ 3301585 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_153110793.jpg
+ 1781697 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_125637640.jpg
+ 2917045 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_152405874.jpg
+ 2865401 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1849.HEIC
+ 7552407 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_150752249.PANO.jpg
+ 4061761 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_153603660.jpg
+ 3637703 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_153308948.jpg
+ 3337879 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_155435571.jpg
+ 4970836 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_151238032.jpg
+ 3267844 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_153359013.jpg
+ 2905051 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_160052888.jpg
+ 3833491 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1850.HEIC
+ 2315157 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_155505084.jpg
+ 1810781 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_171225430.jpg
+ 3089972 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_160105481.jpg
+ 2991863 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_160934535~2.jpg
+ 2795499 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_160831688.jpg
+ 1797635 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_170921221.jpg
+ 3035389 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1851.HEIC
+ 2110158 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_170918725.jpg
+ 4455300 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_125540275.TS.mp4
+ 3174153 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_161357571.jpg
+ 3529803 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_160934535.jpg
+ 3058540 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_172103511.jpg
+ 2205991 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_191300435.jpg
+ 1808446 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1854.HEIC
+ 2152786 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1852.HEIC
+ 2078603 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_201655580.NIGHT.jpg
+ 2460454 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_191055522.jpg
+ 1695246 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1855.HEIC
+ 2452610 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_184608476.jpg
+ 1514652 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_065539076.NIGHT.jpg
+ 2019308 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_202818387.NIGHT.jpg
+ 4238096 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_184541352.jpg
+ 2467619 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1857.HEIC
+ 1836534 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1853.HEIC
+ 1971312 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_202820142.NIGHT.jpg
+ 2553153 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_202327379.NIGHT.jpg
+ 2015307 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_060414573.jpg
+ 3543300 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_193522767.jpg
+ 2084436 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_201809279.NIGHT.jpg
+ 2875776 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230815_202246258.jpg
+ 1593944 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1859.HEIC
+ 1587732 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_112210934.jpg
+ 1669594 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1860.HEIC
+ 2611926 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1864.HEIC
+ 3620068 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1865.HEIC
+ 2891650 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1861.HEIC
+ 1763765 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_143203982.jpg
+ 1172303 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1870.HEIC
+ 1703266 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1863.HEIC
+ 1993207 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_115318027.jpg
+ 2813596 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_071349105.jpg
+ 1023438 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_113900491.LS.mp4
+ 504153 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1871.HEIC
+ 2609777 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1866.HEIC
+ 1928320 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_132648337.NIGHT.jpg
+ 1679572 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_143208443.jpg
+ 2154644 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1869.HEIC
+ 1382021 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/FFE79A68-B0E6-472E-836D-EC49F351F588.jpg
+ 942104 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1875.HEIC
+ 1097934 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1876.HEIC
+ 3537613 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_191312144.jpg
+ 2723602 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_080105375.jpg
+ 3517863 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1877.HEIC
+ 2905752 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_202044782.jpg
+ 3391315 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_075312564.jpg
+ 2220923 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_131815116.jpg
+ 2520832 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230816_211707485.jpg
+ 2378485 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_071450157.jpg
+ 3485518 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_121601920.jpg
+ 3695668 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_072539487.jpg
+ 3414098 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_175514506.PANO.jpg
+ 3322147 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_081251352.jpg
+ 2424218 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/IMG_1878.HEIC
+ 2410890 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_181502346.jpg
+ 2330097 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_183801046.jpg
+ 3377082 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_183618613.jpg
+ 2557888 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_181209268.jpg
+ 2630534 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_183021857.jpg
+ 2496726 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_183826357.jpg
+ 2413675 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_181456746.jpg
+ 2214172 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/509C843D-D4B9-4F02-81B0-05BE938F12DF.jpg
+ 1985323 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_183811061.jpg
+ 2071081 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_194439146.jpg
+ 1875898 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_200900799.jpg
+ 5670281 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_170752393.jpg
+ 2963787 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_183751525.jpg
+ 2108272 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_183716695.LS.mp4
+ 2131124 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_202545906.NIGHT.jpg
+ 3579884 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_193237877.jpg
+ 2963124 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_200858587.jpg
+ 1964148 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_072713034.jpg
+ 1902861 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_202555916.NIGHT.jpg
+ 1468513 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_201212577.jpg
+ 1959607 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_062827230.jpg
+ 2666859 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_195943901.LS.mp4
+ 1757662 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_080922936.jpg
+ 1661260 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_080937939.jpg
+ 3945089 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_073228970.PANO.jpg
+ 2307315 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230817_202746758.LS.mp4
+ 2702757 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_072728336.jpg
+ 2015016 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_082351912.jpg
+ 1969639 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_082348579.jpg
+ 1789401 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_080921955.jpg
+ 3342821 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_073846874.jpg
+ 3243628 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_073034374.jpg
+ 4464270 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_073704846.jpg
+ 2910174 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_074307497.jpg
+ 2336173 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_130000489.jpg
+ 1929416 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_132150855.jpg
+ 5934269 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_081231702.jpg
+ 3169095 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_144716103.jpg
+ 3046438 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_134521846.jpg
+ 2138959 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_161304257.jpg
+ 1531803 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_20180719_191638.jpg
+ 2974227 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_144557167.jpg
+ 393806 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8155.JPG
+ 485394 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8152.JPG
+ 474952 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8156.JPG
+ 2538601 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_144231600.jpg
+ 496970 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8157.JPG
+ 371649 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8158.JPG
+ 3422738 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_150420324.jpg
+ 723610 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_20180719_200157.jpg
+ 635305 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8162.JPG
+ 708902 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8163.JPG
+ 560297 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8164.JPG
+ 381046 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8167.JPG
+ 483752 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8165.JPG
+ 456804 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8166.JPG
+ 343792 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8168.JPG
+ 367294 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8169.JPG
+ 489839 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8178.JPG
+ 669605 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8170.JPG
+ 474541 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8177.JPG
+ 1627237 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8181.JPG
+ 548717 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8179.JPG
+ 1951988 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/IMG_8172.JPG
+ 1419461 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719211742+0200.jpg
+ 13156833 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/PXL_20230818_073402211.TS.mp4
+ 1729388 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719211742+0200-modifié.jpg
+ 98595828 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719211425+0200.mp4
+145104161 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719185209+0200.mp4
+ 89765082 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719182934+0200.mp4
+ 98016626 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719194940+0200.mp4
+258577282 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719201922+0200.mp4
+--------- -------
+1065090969 158 files
+takeout-20240525T201456Z-003.zip:
+Archive: takeout-20240525T201456Z-003.zip
+ Length Date Time Name
+--------- ---------- ----- ----
+245068101 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719203057+0200.mp4
+221047335 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719185840+0200.mp4
+229274490 2024-05-25 22:15 Takeout/Google Photos/Garros 19 Juillet 2018/Bebop2_20180719183450+0200.mp4
+--------- -------
+695389926 3 files
diff --git a/internal/fakefs/fakefs.go b/internal/fakefs/fakefs.go
new file mode 100644
index 00000000..186b7a03
--- /dev/null
+++ b/internal/fakefs/fakefs.go
@@ -0,0 +1,212 @@
+package fakefs
+
+import (
+ "crypto/rand"
+ "fmt"
+ "io"
+ "io/fs"
+ "path"
+ "path/filepath"
+ "sort"
+ "strings"
+ "time"
+
+ "github.com/simulot/immich-go/helpers/gen"
+ "github.com/simulot/immich-go/immich/metadata"
+)
+
+/*
+ simulate a file system based on the list of files contained into a set of archive.
+
+*/
+
+type FakeDirEntry struct {
+ name string // name of the file
+ size int64 // length in bytes for regular files; system-dependent for others
+ mode fs.FileMode // file mode bits
+ modTime time.Time // modification time
+}
+
+func (fi FakeDirEntry) Name() string { return path.Base(fi.name) }
+func (fi FakeDirEntry) Size() int64 { return fi.size }
+func (fi FakeDirEntry) Mode() fs.FileMode { return fi.mode }
+func (fi FakeDirEntry) ModTime() time.Time { return fi.modTime }
+func (fi FakeDirEntry) IsDir() bool { return fi.mode.IsDir() }
+func (fi FakeDirEntry) Sys() any { return nil }
+func (fi FakeDirEntry) Type() fs.FileMode { return fi.mode }
+func (fi FakeDirEntry) Info() (fs.FileInfo, error) { return fi, nil }
+
+type FakeFile struct {
+ fi FakeDirEntry
+ r io.Reader
+ pos int64
+}
+
+func (f FakeFile) Stat() (fs.FileInfo, error) {
+ return f.fi, nil
+}
+
+func (f *FakeFile) Read(b []byte) (int, error) {
+ if f.pos < f.fi.size {
+ n, err := f.r.Read(b)
+ f.pos += int64(n)
+ return n, err
+ }
+ return 0, io.EOF
+}
+
+func (f *FakeFile) Close() error {
+ f.pos = 0
+ return nil
+}
+
+type FakeFS struct {
+ name string
+ files map[string]map[string]FakeDirEntry
+}
+
+func (fsys FakeFS) Name() string {
+ return fsys.name
+}
+
+func normalizeName(name string) string {
+ if name != "." && !strings.HasPrefix(name, "./") {
+ return "./" + name
+ }
+ return name
+}
+
+func (fsys FakeFS) Stat(name string) (fs.FileInfo, error) {
+ name = normalizeName(name)
+ name = filepath.ToSlash(name)
+ dir, base := path.Split(name)
+ dir = strings.TrimSuffix(dir, "/")
+ var l map[string]FakeDirEntry
+ if dir == "" {
+ dir = "."
+ }
+ l = fsys.files[dir]
+ if len(l) == 0 {
+ return nil, fmt.Errorf("%s:%s: %w", fsys.name, name, fs.ErrNotExist)
+ }
+ if e, ok := l[base]; ok {
+ return e, nil
+ }
+ return nil, fs.ErrNotExist
+}
+
+func (fsys FakeFS) Open(name string) (fs.File, error) {
+ name = normalizeName(name)
+ info, err := fsys.Stat(name)
+ if err != nil {
+ return nil, err
+ }
+
+ fakeInfo := info.(FakeDirEntry)
+ var r io.Reader
+
+ ext := path.Ext(name)
+ if strings.ToLower(ext) == ".json" {
+ base := path.Base(name)
+ switch base {
+ case "métadonnées.json", "metadata.json", "metadati.json", "metadáta.json", "Metadaten.json":
+ album := path.Base(path.Dir(name))
+ r, fakeInfo.size = fakeAlbumData(album)
+ case "print-subscriptions.json", "shared_album_comments.json", "user-generated-memory-titles.json":
+ r, fakeInfo.size = fakeJSON()
+ default:
+ d := info.ModTime()
+ if d2 := metadata.TakeTimeFromName(name); !d2.IsZero() {
+ d = d2
+ }
+ title := strings.TrimSuffix(path.Base(name), path.Ext(base))
+ r, fakeInfo.size = fakePhotoData(title, d)
+ }
+ } else {
+ r = rand.Reader
+ }
+ return &FakeFile{fi: fakeInfo, r: r}, nil
+}
+
+func (fsys FakeFS) ReadDir(name string) ([]fs.DirEntry, error) {
+ name = normalizeName(name)
+ info, err := fsys.Stat(name)
+ if err != nil {
+ return nil, err
+ }
+ if !info.IsDir() {
+ return nil, fs.ErrNotExist
+ }
+
+ entries := fsys.files[name]
+ if len(entries) == 0 {
+ return nil, fs.ErrNotExist
+ }
+
+ keys := gen.MapKeys(entries)
+ sort.Strings(keys)
+ out := []fs.DirEntry{}
+ for _, k := range keys {
+ if k != "." {
+ out = append(out, entries[k])
+ }
+ }
+ return out, nil
+}
+
+func (fsys FakeFS) addFile(name string, size int64, modDate time.Time) {
+ name = normalizeName(name)
+ dir, base := path.Split(name)
+ dir = strings.TrimSuffix(dir, "/")
+ parts := strings.Split(dir, "/")
+
+ for i, p := range parts {
+ // create the entry in the parent
+ if i == 0 {
+ sub := "."
+ if _, ok := fsys.files[sub]; !ok {
+ //
+ e := FakeDirEntry{
+ name: ".",
+ modTime: time.Now(),
+ size: 0,
+ mode: 0o777 | fs.ModeDir,
+ }
+
+ fsys.files[sub] = map[string]FakeDirEntry{
+ ".": e,
+ }
+ }
+ } else {
+ // add entry in the parent
+ parent := strings.Join(parts[:i], "/")
+ dir := parent + "/" + p
+ if _, ok := fsys.files[parent][p]; !ok {
+ fsys.files[parent][p] = FakeDirEntry{
+ name: dir,
+ modTime: time.Now(),
+ size: 0,
+ mode: 0o777 | fs.ModeDir,
+ }
+ }
+ // create the dir entry
+ if _, ok := fsys.files[dir]; !ok {
+ fsys.files[dir] = map[string]FakeDirEntry{
+ ".": {
+ name: dir + "/.",
+ modTime: time.Now(),
+ size: 0,
+ mode: 0o777 | fs.ModeDir,
+ },
+ }
+ }
+ }
+ }
+ l := fsys.files[dir]
+ l[base] = FakeDirEntry{
+ name: name,
+ modTime: modDate,
+ size: size,
+ mode: 0o777,
+ }
+}
diff --git a/internal/fakefs/metadata.go b/internal/fakefs/metadata.go
new file mode 100644
index 00000000..1b7376fa
--- /dev/null
+++ b/internal/fakefs/metadata.go
@@ -0,0 +1,77 @@
+package fakefs
+
+import (
+ "fmt"
+ "io"
+ "strings"
+ "time"
+)
+
+const albumTemplate = `{
+ "title": "%s",
+ "description": "",
+ "access": "",
+ "date": {
+ "timestamp": "0",
+ "formatted": "1 janv. 1970, 00:00:00 UTC"
+ },
+ "geoData": {
+ "latitude": 0.0,
+ "longitude": 0.0,
+ "altitude": 0.0,
+ "latitudeSpan": 0.0,
+ "longitudeSpan": 0.0
+ }
+}`
+
+func fakeAlbumData(name string) (io.Reader, int64) {
+ t := fmt.Sprintf(albumTemplate, name)
+ return strings.NewReader(t), int64(len(t))
+}
+
+const pictureTemplate = `{
+ "title": "%[1]s",
+ "description": "",
+ "imageViews": "50",
+ "creationTime": {
+ "timestamp": "%[2]d"
+ },
+ "photoTakenTime": {
+ "timestamp": "%[2]d"
+ },
+ "geoData": {
+ "latitude": 48.0,
+ "longitude": 1.0,
+ "altitude": 102.86,
+ "latitudeSpan": 0.0,
+ "longitudeSpan": 0.0
+ },
+ "geoDataExif": {
+ "latitude": 48.0,
+ "longitude": 1.0,
+ "altitude": 102.86,
+ "latitudeSpan": 0.0,
+ "longitudeSpan": 0.0
+ },
+ "url": "https://photos.google.com/photo/AF1QipMZVTuUYj4K1jaN5vy6mkflX6yiWLQO2GDXSNKl",
+ "googlePhotosOrigin": {
+ "webUpload": {
+ "computerUpload": {
+ }
+ }
+ }
+}`
+
+func fakePhotoData(name string, captureDate time.Time) (io.Reader, int64) {
+ t := fmt.Sprintf(pictureTemplate, name, captureDate.Unix())
+ return strings.NewReader(t), int64(len(t))
+}
+
+const fakeJSONTemplate = `{
+ "Nothing": ""
+}`
+
+func fakeJSON() (io.Reader, int64) {
+ t := fakeJSONTemplate
+ return strings.NewReader(t), int64(len(t))
+}
diff --git a/internal/fakefs/ziplist.go b/internal/fakefs/ziplist.go
new file mode 100644
index 00000000..40094ff2
--- /dev/null
+++ b/internal/fakefs/ziplist.go
@@ -0,0 +1,92 @@
+package fakefs
+
+/*
+ for f in *.zip; do echo "$f: "; unzip -l $f; done >list.lst
+*/
+import (
+ "bufio"
+ "io"
+ "io/fs"
+ "os"
+ "regexp"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/simulot/immich-go/helpers/gen"
+)
+
+// ` 2104348 07-20-2023 00:00 Takeout/Google Photos/2020 - Costa Rica/IMG_3235.MP4`
+
+var reZipList = regexp.MustCompile(`(-rw-r--r-- 0/0\s+)?(\d+)\s+(.{16})\s+(.*)$`)
+
+func readFileLine(l string, dateFormat string) (string, int64, time.Time) {
+ if len(l) < 30 {
+ return "", 0, time.Time{}
+ }
+ m := reZipList.FindStringSubmatch(l)
+ if len(m) < 5 {
+ return "", 0, time.Time{}
+ }
+ size, _ := strconv.ParseInt(m[2], 10, 64)
+ modTime, _ := time.ParseInLocation(dateFormat, m[3], time.Local)
+ return m[4], size, modTime
+}
+
+func ScanStringList(dateFormat string, s string) ([]fs.FS, error) {
+ r := strings.NewReader(s)
+
+ return ScanFileListReader(r, dateFormat)
+}
+
+func ScanFileList(name string, dateFormat string) ([]fs.FS, error) {
+ f, err := os.Open(name)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ return ScanFileListReader(f, dateFormat)
+}
+
+func ScanFileListReader(f io.Reader, dateFormat string) ([]fs.FS, error) {
+ fsyss := map[string]*FakeFS{}
+ var fsys *FakeFS
+ currentZip := ""
+ ok := false
+
+ scanner := bufio.NewScanner(f)
+ for scanner.Scan() {
+ l := scanner.Text()
+ if strings.HasPrefix(l, "Archive:") {
+ currentZip = strings.TrimSpace(strings.TrimPrefix(l, "Archive:"))
+ fsys, ok = fsyss[currentZip]
+ if !ok {
+ fsys = &FakeFS{
+ name: currentZip,
+ files: map[string]map[string]FakeDirEntry{},
+ }
+
+ fsyss[currentZip] = fsys
+ }
+ continue
+ }
+ if name, size, modTime := readFileLine(l, dateFormat); name != "" {
+ fsys.addFile(name, size, modTime)
+ }
+ }
+
+ if err := scanner.Err(); err != nil {
+ return nil, err
+ }
+
+ names := gen.MapKeys(fsyss)
+ sort.Strings(names)
+ output := make([]fs.FS, len(fsyss))
+ i := 0
+ for _, name := range names {
+ output[i] = fsyss[name]
+ i++
+ }
+ return output, nil
+}
diff --git a/internal/fakefs/ziplist_test.go b/internal/fakefs/ziplist_test.go
new file mode 100644
index 00000000..91964f45
--- /dev/null
+++ b/internal/fakefs/ziplist_test.go
@@ -0,0 +1,118 @@
+package fakefs
+
+import (
+ "fmt"
+ "io/fs"
+ "testing"
+ "time"
+)
+
+func Test_readFileLine(t *testing.T) {
+ type args struct {
+ l string
+ dateFormat string
+ }
+ tests := []struct {
+ name string
+ args args
+ wantName string
+ wantModTime time.Time
+ wantSize int64
+ }{
+ {
+ name: "simulot",
+ args: args{
+ l: " 145804 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/😀😃😄😁😆😅😂🤣🥲☺️😊😇🙂🙃😉😌😍🥰😘😗😙😚😋😛.jpg",
+ dateFormat: "2006-01-02 15:04",
+ },
+ wantName: "Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/😀😃😄😁😆😅😂🤣🥲☺️😊😇🙂🙃😉😌😍🥰😘😗😙😚😋😛.jpg",
+ wantSize: 145804,
+ wantModTime: time.Date(2024, 5, 25, 22, 15, 0, 0, time.Local),
+ },
+ {
+ name: "pixil",
+ args: args{
+ l: " 197486 07-19-2023 23:53 Takeout/Google Photos/2011 - Omaha Zoo/IMG_20110702_153447.jpg",
+ dateFormat: "01-02-2006 15:04",
+ },
+ wantName: "Takeout/Google Photos/2011 - Omaha Zoo/IMG_20110702_153447.jpg",
+ wantSize: 197486,
+ wantModTime: time.Date(2023, 7, 19, 23, 53, 0, 0, time.Local),
+ },
+ {
+ name: "Phyl404",
+ args: args{
+ l: "2555584471 2024-07-12 13:41 Takeout/Google Foto/Vultures 1/IMG_2002_580.MOV",
+ dateFormat: "2006-01-02 15:04",
+ },
+ wantName: "Takeout/Google Foto/Vultures 1/IMG_2002_580.MOV",
+ wantSize: 2555584471,
+ wantModTime: time.Date(2024, 7, 12, 13, 41, 0, 0, time.Local),
+ },
+ {
+ name: "tgz",
+ args: args{
+ l: "-rw-r--r-- 0/0 717454980 2023-12-09 17:23 Takeout/Google Photos/Photos from 2019/VID_20190621_193014.mp4",
+ dateFormat: "2006-01-02 15:04",
+ },
+ wantName: "Takeout/Google Photos/Photos from 2019/VID_20190621_193014.mp4",
+ wantModTime: time.Date(2023, 12, 9, 17, 23, 0, 0, time.Local),
+ wantSize: 717454980,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ gotName, gotSize, gotModTime := readFileLine(tt.args.l, tt.args.dateFormat)
+
+ if gotName != tt.wantName {
+ t.Errorf("readFileLine() got = %v, want %v", gotName, tt.wantName)
+ }
+ if gotSize != tt.wantSize {
+ t.Errorf("readFileLine() got = %v, want %v", gotSize, tt.wantSize)
+ }
+ if !gotModTime.Equal(tt.wantModTime) {
+ t.Errorf("readFileLine() got = %v, want %v", gotModTime, tt.wantModTime)
+ }
+ })
+ }
+}
+
+func BenchmarkReadFileLine(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ _, _, _ = readFileLine(" 145804 2024-05-25 22:15 Takeout/Google Photos/🇵🇹 Lisbonne ❤️ en famille 👨👩👦👦/😀😃😄😁😆😅😂🤣🥲☺️😊😇🙂🙃😉😌😍🥰😘😗😙😚😋😛.jpg", "2006-01-02 15:04")
+ }
+}
+
+type NameFS interface {
+ Name() string
+}
+
+func TestFakeFS(t *testing.T) {
+ fsyss, err := ScanFileList("TESTDATA/small.lst", "2006-01-02 15:04")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ for _, fsys := range fsyss {
+ if fsys, ok := fsys.(NameFS); ok {
+ fmt.Println(fsys.Name())
+ }
+ err := fs.WalkDir(fsys, ".",
+ func(name string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ f, err := d.Info()
+ if f != nil {
+ fmt.Printf("%9d %s %s\n", f.Size(), f.ModTime().Format("2006-01-02 15:04"), name)
+ }
+ return err
+ })
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ }
+ fmt.Println()
+}
diff --git a/readme.md b/readme.md
index 6953d780..46fc3938 100644
--- a/readme.md
+++ b/readme.md
@@ -11,7 +11,6 @@
- [paypal donor page](https://www.paypal.com/donate/?hosted_button_id=VGU2SQE88T2T4)
-
## Key Features:
* **Effortlessly Upload Large Google Photos Takeouts:** Immich-Go excels at handling the massive archives you download from Google Photos using Google Takeout. It efficiently processes these archives while preserving valuable metadata like GPS location, date taken, and album information.
@@ -33,9 +32,13 @@
* It's important to import all the parts of the takeout together, since some data might be spread across multiple files.
Use `/path/to/your/files/takeout-*.zip` as file name.
* For **.tgz** files (compressed tar archives), you'll need to decompress all the files into a single folder before importing. When using the import tool, don't forget the `-google-photos` option.
- * You can remove any unwanted files or folders from your takeout before importing. Immich-go might warn you about missing JSON files, but it should still import your photos successfully.
+ * You can remove any unwanted files or folders from your takeout before importing.
* Restarting an interrupted import won't cause any problems and it will resume the work where it was left.
+* **Lot of files are not imported**: What are the options?
+ * Verify if all takeout parts have been included in the processing.
+ * Request another takeout, either for an entire year or in smaller increments.
+ * Force the import of files despite the missing JSON. Use the option `-upload-when-missing-JSON`
For insights into the reasoning behind this alternative to `immich-cli`, please read the motivation [here](docs/motivation.md).
@@ -75,7 +78,7 @@ Example: Immich-go check the server's SSL certificate. you can disable this beha
| `-server=URL` | URL of the Immich service, example http://:2283 or https://your-domain | |
| `-api=URL` | URL of the Immich api endpoint (http://container_ip:3301) | |
| `-device-uuid=VALUE` | Force the device identification | `$HOSTNAME` |
-| `-client-timeout=duration` | Set the timeout for server calls. The duration is a decimal number with a unit suffix, such as "300ms", "1.5m" or "45m". Valid time units are "ms", "s", "m", "h". | `5m` |
+| `-client-timeout=duration` | Set the timeout for server calls. The duration is a decimal number with a unit suffix, such as "300ms", "1.5m" or "45m". Valid time units are "ms", "s", "m", "h". | `5m` |
| `-skip-verify-ssl` | Skip SSL verification for use with self-signed certificates | `false` |
| `-key=KEY` | A key generated by the user. Uploaded photos will belong to the key's owner. | |
| `-log-level=LEVEL` | Adjust the log verbosity as follows:
- `ERROR`: Display only errors
- `WARNING`: Same as previous one plus non-blocking error
- `INFO`: Information messages | `INFO` |
@@ -83,8 +86,8 @@ Example: Immich-go check the server's SSL certificate. you can disable this beha
| `-log-json` | Output the log as line-delimited JSON file | `false` |
| `-time-zone=time_zone_name` | Set the time zone for dates without time zone information | the system's time zone |
| `-no-ui` | Disable the user interface | `false` |
-| `-debug-counters` | Enable the generation a CSV beside the log file | `false` |
-| `-api-trace` | Enable trace of API calls | `false` |
+| `-debug-counters` | Enable the generation a CSV beside the log file | `false` |
+| `-api-trace` | Enable trace of API calls | `false` |
## Command `upload`
@@ -92,18 +95,18 @@ Use this command for uploading photos and videos from a local directory, a zippe
### Switches and options:
-| **Parameter** | **Description** | **Default value** |
-| ------------------------------------ | --------------------------------------------------------------------------------------- | ----------------- |
-| `-album="ALBUM NAME"` | Import assets into the Immich album `ALBUM NAME`. | |
-| `-dry-run` | Preview all actions as they would be done. | `FALSE` |
-| `-create-album-folder` | Generate immich albums after folder names. | `FALSE` |
-| `-create-stacks` | Stack jpg/raw or bursts. | `TRUE` |
-| `-stack-jpg-raw` | Control the stacking of jpg/raw photos. | `TRUE` |
-| `-stack-burst` | Control the stacking bursts. | `TRUE` |
-| `-select-types=".ext,.ext,.ext..."` | List of accepted extensions. | |
-| `-exclude-types=".ext,.ext,.ext..."` | List of excluded extensions. | |
-| `-when-no-date=FILE\|NOW` | When the date of take can't be determined, use the FILE's date or the current time NOW. | `FILE` |
-| `-exclude-files=pattern` | Ignore files based on a pattern. Case insensitive. Repeat the option for each pattern do you need. | `@eaDir/`
`@__thumb/`
`SYNOFILE_THUMB_*.*`
`Lightroom Catalog/`
`thumbnails/`|
+| **Parameter** | **Description** | **Default value** |
+| ------------------------------------ | -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
+| `-album="ALBUM NAME"` | Import assets into the Immich album `ALBUM NAME`. | |
+| `-dry-run` | Preview all actions as they would be done. | `FALSE` |
+| `-create-album-folder` | Generate immich albums after folder names. | `FALSE` |
+| `-create-stacks` | Stack jpg/raw or bursts. | `FALSE` |
+| `-stack-jpg-raw` | Control the stacking of jpg/raw photos. | `FALSE` |
+| `-stack-burst` | Control the stacking bursts. | `FALS` |
+| `-select-types=".ext,.ext,.ext..."` | List of accepted extensions. | |
+| `-exclude-types=".ext,.ext,.ext..."` | List of excluded extensions. | |
+| `-when-no-date=FILE\|NOW` | When the date of take can't be determined, use the FILE's date or the current time NOW. | `FILE` |
+| `-exclude-files=pattern` | Ignore files based on a pattern. Case insensitive. Repeat the option for each pattern do you need. | `@eaDir/`
`@__thumb/`
`SYNOFILE_THUMB_*.*`
`Lightroom Catalog/`
`thumbnails/` |
### Date selection:
Fine-tune import based on specific dates:
@@ -143,6 +146,7 @@ Specialized options for Google Photos management:
| `-keep-partner` | Specifies inclusion or exclusion of partner-taken photos. | `TRUE` |
| `-partner-album="partner's album"` | import assets from partner into given album. | |
| `-discard-archived` | don't import archived assets. | `FALSE` |
+| `-upload-when-missing-JSON` | Upload photos not associated with a JSON metadata file | `FALSE` |
Read [here](docs/google-takeout.md) to understand how Google Photos takeout isn't easy to handle.
@@ -325,11 +329,13 @@ Or you can add `immich-go` to your `configuration.nix` in the `environment.syste
Kudos to the Immich team for their stunning project! 🤩
This program use following 3rd party libraries:
-- github.com/rwcarlsen/goexif to get date of capture from JPEG files
-- github.com/ttacon/chalk for having logs nicely colored
-- github.com/thlib/go-timezone-local for its windows timezone management
+- [https://github.com/rivo/tview](https://github.com/rivo/tview) the terminal user interface
+- [github.com/rwcarlsen/goexif](github.com/rwcarlsen/goexif) to get date of capture from JPEG files
+- [github.com/thlib/go-timezone-local](github.com/thlib/go-timezone-local) for its windows timezone management
A big thank you to the project contributors:
- [rodneyosodo](https://github.com/rodneyosodo) gitub CI, go linter, and advice
- [sigmahour](https://github.com/sigmahour) SSL management
- [mrwulf](https://github.com/mrwulf) Partner sharing album
+- [erkexzcx](https://github.com/erkexzcx) Date determination based on file path and file name
+