Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Give a better user interface to immich-go #222

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions browser/browser.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ import (
)

type Browser interface {
Prepare(cxt context.Context) error
Browse(cxt context.Context) chan *LocalAssetFile
}
29 changes: 18 additions & 11 deletions browser/files/localassets.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,23 @@ import (
"strings"
"time"

"github.com/charmbracelet/log"
"github.com/simulot/immich-go/browser"
"github.com/simulot/immich-go/immich"
"github.com/simulot/immich-go/immich/metadata"
"github.com/simulot/immich-go/logger"
)

type LocalAssetBrowser struct {
fsyss []fs.FS
albums map[string]string
log *logger.Journal
fsyss []fs.FS
albums map[string]string

log *logger.LogAndCount[logger.UpLdAction]
sm immich.SupportedMedia
whenNoDate string
}

func NewLocalFiles(ctx context.Context, log *logger.Journal, fsyss ...fs.FS) (*LocalAssetBrowser, error) {
func NewLocalFiles(ctx context.Context, log *logger.LogAndCount[logger.UpLdAction], fsyss ...fs.FS) (*LocalAssetBrowser, error) {
return &LocalAssetBrowser{
fsyss: fsyss,
albums: map[string]string{},
Expand All @@ -32,6 +34,10 @@ func NewLocalFiles(ctx context.Context, log *logger.Journal, fsyss ...fs.FS) (*L
}, nil
}

func (la *LocalAssetBrowser) Prepare(ctx context.Context) error {
return nil
}

func (la *LocalAssetBrowser) SetSupportedMedia(sm immich.SupportedMedia) *LocalAssetBrowser {
la.sm = sm
return la
Expand All @@ -45,6 +51,7 @@ func (la *LocalAssetBrowser) SetWhenNoDate(opt string) *LocalAssetBrowser {
var toOldDate = time.Date(1980, 1, 1, 0, 0, 0, 0, time.UTC)

func (la *LocalAssetBrowser) Browse(ctx context.Context) chan *browser.LocalAssetFile {
la.log.Stage("Parsing source")
fileChan := make(chan *browser.LocalAssetFile)
// Browse all given FS to collect the list of files
go func(ctx context.Context) {
Expand Down Expand Up @@ -98,23 +105,23 @@ func (la *LocalAssetBrowser) handleFolder(ctx context.Context, fsys fs.FS, fileC
name := e.Name()
fileName := path.Join(folder, name)
ext := strings.ToLower(path.Ext(name))
la.log.AddEntry(fileName, logger.DiscoveredFile, "")
la.log.AddEntry(log.InfoLevel, logger.UpldDiscoveredFile, fileName)

t := la.sm.TypeFromExt(ext)
switch t {
default:
la.log.AddEntry(fileName, logger.Unsupported, "")
la.log.AddEntry(log.InfoLevel, logger.UpldDiscarded, fileName, "reason", "unknown extension")
continue
case immich.TypeIgnored:
la.log.AddEntry(name, logger.Discarded, "File ignored")
la.log.AddEntry(log.InfoLevel, logger.UpldDiscarded, fileName, "reason", "useless file type")
continue
case immich.TypeSidecar:
la.log.AddEntry(name, logger.Metadata, "")
la.log.AddEntry(log.InfoLevel, logger.UpldMetadata, fileName)
continue
case immich.TypeImage:
la.log.AddEntry(name, logger.ScannedImage, "")
la.log.AddEntry(log.InfoLevel, logger.UpldScannedImage, fileName)
case immich.TypeVideo:
la.log.AddEntry(name, logger.ScannedVideo, "")
la.log.AddEntry(log.InfoLevel, logger.UpldScannedVideo, fileName)
}

f := browser.LocalAssetFile{
Expand Down Expand Up @@ -172,7 +179,7 @@ func (la *LocalAssetBrowser) checkSidecar(f *browser.LocalAssetFile, entries []f
FileName: path.Join(dir, e.Name()),
OnFSsys: true,
}
la.log.AddEntry(name, logger.AssociatedMetadata, "")
la.log.AddEntry(log.InfoLevel, logger.UpldAssociatedMetadata, "with", f.FileName)
return true
}
}
Expand Down
8 changes: 6 additions & 2 deletions browser/files/localassets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package files_test
import (
"context"
"errors"
"io"
"path"
"reflect"
"sort"
"testing"

"github.com/charmbracelet/log"
"github.com/kr/pretty"
"github.com/psanford/memfs"
"github.com/simulot/immich-go/browser/files"
Expand Down Expand Up @@ -74,8 +76,10 @@ func TestLocalAssets(t *testing.T) {
return
}
ctx := context.Background()

b, err := files.NewLocalFiles(ctx, logger.NewJournal(logger.NoLog{}), fsys)
l := log.New(io.Discard)
cnt := logger.NewCounters[logger.UpLdAction]()
lc := logger.NewLogAndCount[logger.UpLdAction](l, logger.SendNop, cnt)
b, err := files.NewLocalFiles(ctx, lc, fsys)
if err != nil {
t.Error(err)
}
Expand Down
57 changes: 28 additions & 29 deletions browser/gp/googlephotos.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strings"
"unicode/utf8"

"github.com/charmbracelet/log"
"github.com/simulot/immich-go/browser"
"github.com/simulot/immich-go/helpers/fshelper"
"github.com/simulot/immich-go/helpers/gen"
Expand All @@ -23,7 +24,7 @@ type Takeout struct {
jsonByYear map[jsonKey]*GoogleMetaData // assets by year of capture and base name
uploaded map[fileKey]any // track files already uploaded
albums map[string]string // tack album names by folder
jnl *logger.Journal
log *logger.LogAndCount[logger.UpLdAction]
sm immich.SupportedMedia
}

Expand Down Expand Up @@ -54,27 +55,22 @@ type jsonKey struct {
year int
}

func NewTakeout(ctx context.Context, jnl *logger.Journal, sm immich.SupportedMedia, fsyss ...fs.FS) (*Takeout, error) {
func NewTakeout(ctx context.Context, log *logger.LogAndCount[logger.UpLdAction], sm immich.SupportedMedia, fsyss ...fs.FS) (*Takeout, error) {
to := Takeout{
fsyss: fsyss,
jsonByYear: map[jsonKey]*GoogleMetaData{},
albums: map[string]string{},
jnl: jnl,
log: log,
sm: sm,
}
err := to.passOne(ctx)
if err != nil {
return nil, err
}

to.solvePuzzle()
return &to, err
return &to, nil
}

// passOne scans all files in all walker to build the file catalog of the archive
// metadata files content is read and kept

func (to *Takeout) passOne(ctx context.Context) error {
func (to *Takeout) Prepare(ctx context.Context) error {
to.log.Stage("Parsing takeout files")
to.catalogs = map[fs.FS]walkerCatalog{}
for _, w := range to.fsyss {
to.catalogs[w] = walkerCatalog{}
Expand All @@ -83,6 +79,8 @@ func (to *Takeout) passOne(ctx context.Context) error {
return err
}
}
to.log.Stage("Associating metadata with assets")
to.solvePuzzle()
return nil
}

Expand All @@ -100,14 +98,13 @@ func (to *Takeout) passOneFsWalk(ctx context.Context, w fs.FS) error {
if d.IsDir() {
return nil
}

to.jnl.AddEntry(name, logger.DiscoveredFile, "")
to.log.AddEntry(log.InfoLevel, logger.UpldDiscoveredFile, name)
dir, base := path.Split(name)
dir = strings.TrimSuffix(dir, "/")
ext := strings.ToLower(path.Ext(base))

if slices.Contains(uselessFiles, base) {
to.jnl.AddEntry(name, logger.Discarded, "Useless file")
to.log.AddEntry(log.InfoLevel, logger.UpldDiscarded, "reason", "useless file")
return nil
}

Expand All @@ -126,35 +123,37 @@ func (to *Takeout) passOneFsWalk(ctx context.Context, w fs.FS) error {
switch {
case md.isAsset():
to.addJSON(dir, base, md)
to.jnl.AddEntry(name, logger.Metadata, "Asset Title: "+md.Title)
to.log.AddEntry(log.InfoLevel, logger.UpldMetadata, name, "type", "Google sidecar file", "asset_original_name", md.Title)
case md.isAlbum():
to.albums[dir] = md.Title
to.jnl.AddEntry(name, logger.Metadata, "Album title: "+md.Title)
to.log.AddEntry(log.InfoLevel, logger.UpldMetadata, name, "type", "Google album file", "asset_original_name", md.Title)
default:
to.jnl.AddEntry(name, logger.Discarded, "Unknown json file")
// TODO add support for old takeouts #212
to.log.AddEntry(log.InfoLevel, logger.UpldDiscarded, name, "reason", "unknown JSON file")
return nil
}
} else {
to.jnl.AddEntry(name, logger.Discarded, "Unknown json file")
// TODO add support for old takeouts #212
to.log.AddEntry(log.InfoLevel, logger.UpldDiscarded, name, "reason", "unknown JSON file")
return nil
}
default:
t := to.sm.TypeFromExt(ext)
switch t {
case immich.TypeUnknown:
to.jnl.AddEntry(name, logger.Unsupported, "")
to.log.AddEntry(log.InfoLevel, logger.UpldDiscarded, name, "reason", "unknown extension")
return nil
case immich.TypeIgnored:
to.jnl.AddEntry(name, logger.Discarded, "File ignored")
to.log.AddEntry(log.InfoLevel, logger.UpldDiscarded, name, "reason", "useless file")
return nil
case immich.TypeVideo:
if strings.Contains(name, "Failed Videos") {
to.jnl.AddEntry(name, logger.FailedVideo, "")
to.log.AddEntry(log.InfoLevel, logger.UpldDiscarded, name, "reason", "can't upload failed video")
return nil
}
to.jnl.AddEntry(name, logger.ScannedVideo, "")
to.log.AddEntry(log.InfoLevel, logger.UpldScannedVideo, name)
case immich.TypeImage:
to.jnl.AddEntry(name, logger.ScannedImage, "")
to.log.AddEntry(log.InfoLevel, logger.UpldScannedImage, name)
}
dirCatalog.files[base] = fileInfo{
length: int(finfo.Size()),
Expand Down Expand Up @@ -218,7 +217,7 @@ var matchers = []matcherFn{
//

func (to *Takeout) solvePuzzle() {
to.jnl.Log.OK("Associating JSON and assets...")
to.log.Print("Associating JSON and assets...")
jsonKeys := gen.MapKeys(to.jsonByYear)
sort.Slice(jsonKeys, func(i, j int) bool {
yd := jsonKeys[i].year - jsonKeys[j].year
Expand Down Expand Up @@ -249,7 +248,7 @@ func (to *Takeout) solvePuzzle() {
for f := range l.files {
if l.files[f].md == nil {
if matcher(k.name, f, to.sm) {
to.jnl.AddEntry(path.Join(d, f), logger.AssociatedMetadata, fmt.Sprintf("%s (%d)", k.name, k.year))
to.log.AddEntry(log.InfoLevel, logger.UpldAssociatedMetadata, path.Join(d, f), "with", k.name)
// if not already matched
i := l.files[f]
i.md = md
Expand Down Expand Up @@ -407,7 +406,7 @@ func (to *Takeout) Browse(ctx context.Context) chan *browser.LocalAssetFile {
}

func (to *Takeout) passTwoWalk(ctx context.Context, w fs.FS, assetChan chan *browser.LocalAssetFile) error {
to.jnl.Log.OK("Ready to upload files")
to.log.Stage("Importing takeout assets")
return fs.WalkDir(w, ".", func(name string, d fs.DirEntry, err error) error {
if err != nil {
return nil
Expand All @@ -434,12 +433,12 @@ func (to *Takeout) passTwoWalk(ctx context.Context, w fs.FS, assetChan chan *bro
}

if f.md == nil {
to.jnl.AddEntry(name, logger.ERROR, "JSON File not found for this file")
to.log.AddEntry(log.ErrorLevel, logger.UpldERROR, name, "error", "can't find a JSON file for this file", "hint", "process all takeout files together")
return nil
}
finfo, err := d.Info()
if err != nil {
to.jnl.Log.Error("can't browse: %s", err)
to.log.AddEntry(log.ErrorLevel, logger.UpldERROR, name, "error", err)
return nil
}

Expand All @@ -449,7 +448,7 @@ func (to *Takeout) passTwoWalk(ctx context.Context, w fs.FS, assetChan chan *bro
year: f.md.PhotoTakenTime.Time().Year(),
}
if _, exists := to.uploaded[key]; exists {
to.jnl.AddEntry(name, logger.LocalDuplicate, "")
to.log.AddEntry(log.InfoLevel, logger.UpldLocalDuplicate, name)
return nil
}
a := to.googleMDToAsset(f.md, key, w, name)
Expand Down
11 changes: 6 additions & 5 deletions browser/gp/testgp_bigread_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"path/filepath"
"testing"

"github.com/charmbracelet/log"
"github.com/simulot/immich-go/helpers/fshelper"
"github.com/simulot/immich-go/immich"
"github.com/simulot/immich-go/logger"
Expand All @@ -19,18 +20,18 @@ func TestReadBigTakeout(t *testing.T) {
if err != nil {
panic(err)
}
l := log.New(f)

l := logger.NewLogger(logger.Info, true, false)
l.SetWriter(f)
j := logger.NewJournal(l)
c := logger.NewCounters[logger.UpLdAction]()
lc := logger.NewLogAndCount[logger.UpLdAction](l, logger.SendNop, c)
m, err := filepath.Glob("../../../test-data/full_takeout/*.zip")
if err != nil {
t.Error(err)
return
}
cnt := 0
fsyss, err := fshelper.ParsePath(m, true)
to, err := NewTakeout(context.Background(), j, immich.DefaultSupportedMedia, fsyss...)
to, err := NewTakeout(context.Background(), lc, immich.DefaultSupportedMedia, fsyss...)
if err != nil {
t.Error(err)
return
Expand All @@ -39,6 +40,6 @@ func TestReadBigTakeout(t *testing.T) {
for range to.Browse(context.Background()) {
cnt++
}
to.jnl.Report()
t.Log(to.log.String())
t.Logf("seen %d files", cnt)
}
Loading
Loading