1
0
Fork 0
timelinize/datasources/googlephotos/googlephotos.go
Matthew Holt 3e311d99c3
Sort data sources in import planner; rename some DS
The sorting can help imports go faster if we put DB-heavy sources first, when the database is still small.

The data source names were also standardized to use snake_case like most other word-IDs in the app.
2025-05-16 11:10:23 -06:00

112 lines
3.1 KiB
Go

/*
Timelinize
Copyright (c) 2013 Matthew Holt
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// Package googlephotos implements a data source for Google Photos albums and
// exports, and also via its API documented at https://developers.google.com/photos/.
package googlephotos
import (
"context"
"path/filepath"
"strings"
"github.com/timelinize/timelinize/timeline"
"go.uber.org/zap"
)
const dataSourceName = "google_photos"
func init() {
err := timeline.RegisterDataSource(timeline.DataSource{
Name: dataSourceName,
Title: "Google Photos",
Icon: "google_photos.svg",
NewOptions: func() any { return Options{} },
NewFileImporter: func() timeline.FileImporter { return new(FileImporter) },
})
if err != nil {
timeline.Log.Fatal("registering data source", zap.Error(err))
}
}
// FileImporter imports data from a file or folder.
type FileImporter struct {
filename string
truncatedNames map[string]int
}
// Recognize returns whether this file or folder is supported.
func (FileImporter) Recognize(_ context.Context, dirEntry timeline.DirEntry, _ timeline.RecognizeParams) (timeline.Recognition, error) {
// prefer a Google Takeout archive with Google Photos data inside it
if dirEntry.IsDir() && strings.HasSuffix(filepath.ToSlash(dirEntry.FullPath()), googlePhotosPath) {
return timeline.Recognition{Confidence: .9}, nil
}
return timeline.Recognition{}, nil
}
// FileImport imports data from a file/folder.
func (fimp *FileImporter) FileImport(ctx context.Context, dirEntry timeline.DirEntry, params timeline.ImportParams) error {
fimp.filename = dirEntry.FullPath()
if strings.HasSuffix(filepath.ToSlash(dirEntry.FullPath()), googlePhotosPath) {
return fimp.listFromTakeoutArchive(ctx, params, dirEntry)
}
if err := fimp.listFromAlbumFolder(ctx, params, dirEntry); err != nil {
return err
}
return nil
}
// Options configures the data source.
type Options struct {
// Get albums. This can add significant time to an import.
Albums bool `json:"albums"`
IncludeArchivedMedia bool `json:"include_archived_media"`
}
// recognizedExts is only used for file recognition, as a fallback.
var recognizedExts = map[string]struct{}{
".jpg": {},
".jpe": {},
".jpeg": {},
".mp4": {},
".mov": {},
".mpeg": {},
".avi": {},
".wmv": {},
".png": {},
".raw": {},
".dng": {},
".nef": {},
".tiff": {},
".gif": {},
".heic": {},
".webp": {},
".divx": {},
".m4v": {},
".3gp": {},
".3g2": {},
".m2t": {},
".m2ts": {},
".mts": {},
".mkv": {},
".asf": {},
}