1
0
Fork 0
timelinize/datasources/iphone/cameraroll.go
Matt Holt 746e5d6b5c
Refactored import flow, new import UI, thumbnails stored in timeline, etc. (close #3) (#43)
* Schema revisions for new import flow and thumbnails

* WIP settings

* WIP quick schema fix

* gallery: Image search using ML embeddings

Still very rough around the edges, but basically works.

'uv' gets auto-installed, but currently requires restarting Timelinize before it can be used.

Lots of tunings and optimizations are needed. There is much room for improvement.

Still migrating from imports -> jobs, so that part of the code and schema is still a mess.

* Implement search for similar items

* Finish import/planning rewrite; it compiles and tests pass

* Fix some bugs, probably introduce other bugs

* WIP new import planning page

* Fix Google Photos and Twitter recognition

* Finish most of import page UI; start button still WIP

* WIP: Start Import button

* Fixes to jobs, thumbnail job, import job, etc.

* Implement proper checkpointing support; jobs fixes
2024-12-06 11:03:29 -07:00

101 lines
2.8 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 iphone
import (
"context"
"fmt"
"io"
"os"
"path"
"github.com/timelinize/timelinize/datasources/media"
"github.com/timelinize/timelinize/timeline"
"go.uber.org/zap"
)
func (fimp *FileImporter) cameraRoll(ctx context.Context) error {
rows, err := fimp.manifest.QueryContext(ctx, "SELECT fileID, relativePath FROM Files WHERE domain='CameraRollDomain' AND relativePath LIKE 'Media/DCIM/%'")
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
var fileID, relativePath *string
err := rows.Scan(&fileID, &relativePath)
if err != nil {
return fmt.Errorf("scanning row: %w", err)
}
// I haven't seen this, but just to avoid a panic
if fileID == nil || relativePath == nil {
continue
}
fimp.processCameraRollFile(ctx, *fileID, *relativePath)
}
if err = rows.Err(); err != nil {
return fmt.Errorf("scanning rows: %w", err)
}
return nil
}
func (fimp *FileImporter) processCameraRollFile(_ context.Context, fileID, relativePath string) {
// skip unsupported file types
class, supported := media.ItemClassByExtension(relativePath)
if !supported {
return
}
fsys := domainFS{fimp, cameraRollDomain}
// skip sidecar movie files ("live photos") because we'll connect it when
// we process the actual photograph
if media.IsSidecarVideo(fsys, relativePath) {
return
}
item := &timeline.Item{
Classification: class,
OriginalLocation: fmt.Sprintf("%s:%s", cameraRollDomain, relativePath),
IntermediateLocation: fimp.fileIDToRelativePath(fileID),
Content: timeline.ItemData{
Filename: path.Base(relativePath),
Data: func(ctx context.Context) (io.ReadCloser, error) {
return fimp.openFile(ctx, cameraRollDomain, relativePath)
},
},
}
// get as much metadata as possible (ignore any embedded media, I guess?)
_, err := media.ExtractAllMetadata(fimp.opt.Log, os.DirFS(fimp.root), fimp.fileIDToRelativePath(fileID), item, timeline.MetaMergeAppend)
if err != nil {
fimp.opt.Log.Warn("extracting metadata",
zap.String("file", relativePath),
zap.Error(err))
}
ig := &timeline.Graph{Item: item}
media.ConnectMotionPhoto(fimp.opt.Log, fsys, relativePath, ig)
fimp.opt.Pipeline <- ig
}