diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..603f653 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..cb9ec05 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,53 @@ +name: CI Pipeline + +on: + pull_request: + branches: + - main + push: + branches: + - main + +jobs: + ci: + name: Continuous Integration + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: 1.21.x + cache-dependency-path: "go.sum" + + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + version: latest + + - name: Build binary + run: | + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o immich-linux-amd64.exe -ldflags="-s -w -extldflags=-static" main.go + CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o immich-linux-arm64.exe -ldflags="-s -w -extldflags=-static" main.go + CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -o immich-linux-arm6.exe -ldflags="-s -w -extldflags=-static" main.go + CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -o immich-linux-arm7.exe -ldflags="-s -w -extldflags=-static" main.go + + CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o immich-windows-amd64.exe -ldflags="-s -w -extldflags=-static" main.go + CGO_ENABLED=0 GOOS=windows GOARCH=arm64 go build -o immich-windows-arm64.exe -ldflags="-s -w -extldflags=-static" main.go + CGO_ENABLED=0 GOOS=windows GOARCH=arm GOARM=6 go build -o immich-windows-arm6.exe -ldflags="-s -w -extldflags=-static" main.go + CGO_ENABLED=0 GOOS=windows GOARCH=arm GOARM=7 go build -o immich-windows-arm7.exe -ldflags="-s -w -extldflags=-static" main.go + + CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o immich-darwin-amd64.exe -ldflags="-s -w -extldflags=-static" main.go + CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o immich-darwin-arm64.exe -ldflags="-s -w -extldflags=-static" main.go + + CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -o immich-freebsd-amd64.exe -ldflags="-s -w -extldflags=-static" main.go + CGO_ENABLED=0 GOOS=freebsd GOARCH=arm64 go build -o immich-freebsd-arm64.exe -ldflags="-s -w -extldflags=-static" main.go + CGO_ENABLED=0 GOOS=freebsd GOARCH=arm GOARM=6 go build -o immich-freebsd-arm6.exe -ldflags="-s -w -extldflags=-static" main.go + CGO_ENABLED=0 GOOS=freebsd GOARCH=arm GOARM=7 go build -o immich-freebsd-arm7.exe -ldflags="-s -w -extldflags=-static" main.go + + - name: Run tests + run: | + go test --race -v -count=1 -coverprofile=coverage.out ./... diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..3b5d9c0 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,49 @@ +run: + timeout: 3m + +issues: + max-issues-per-linter: 100 + max-same-issues: 100 + +linters-settings: + gocritic: + enabled-checks: + - captLocal + - singleCaseSwitch + - switchTrue + - httpNoBody + - emptyStringTest + - builtinShadow + - exposedSyncMutex + enabled-tags: + - diagnostic + disabled-tags: + - performance + - style + - experimental + - opinionated + +linters: + disable-all: true + enable: + - gocritic + - gosimple + - govet + - ineffassign + - misspell + - whitespace + - gci + - gofmt + - goimports + - loggercheck + - asasalint + - contextcheck + - decorder + - dogsled + - errchkjson + - exportloopref + - ginkgolinter + - gocheckcompilerdirectives + - goprintffuncname + - mirror + - nakedret diff --git a/browser/files/localassets.go b/browser/files/localassets.go index 3d8ce5a..09cdd13 100644 --- a/browser/files/localassets.go +++ b/browser/files/localassets.go @@ -54,7 +54,6 @@ func (la *LocalAssetBrowser) Browse(ctx context.Context) chan *browser.LocalAsse } } return nil - }) if err != nil { // Check if the context has been cancelled before sending the error @@ -68,7 +67,6 @@ func (la *LocalAssetBrowser) Browse(ctx context.Context) chan *browser.LocalAsse } } } - }(ctx) return fileChan @@ -151,7 +149,6 @@ func (la *LocalAssetBrowser) handleFolder(ctx context.Context, fsys fs.FS, fileC default: fileChan <- &f } - } return nil } @@ -174,7 +171,6 @@ func (la *LocalAssetBrowser) checkSidecar(fsys fs.FS, f *browser.LocalAssetFile, la.log.AddEntry(name, logger.ASSOCIATED_META, "") return true } - } } return false @@ -193,8 +189,7 @@ func baseNames(n string) []string { return names } n = strings.TrimSuffix(n, ext) - names = append(names, n) - names = append(names, n+".*") + names = append(names, n, n+".*") ext = path.Ext(n) } } diff --git a/browser/files/localassets_test.go b/browser/files/localassets_test.go index 8f1d4a5..622409f 100644 --- a/browser/files/localassets_test.go +++ b/browser/files/localassets_test.go @@ -8,11 +8,10 @@ import ( "sort" "testing" - "github.com/simulot/immich-go/browser/files" - "github.com/simulot/immich-go/logger" - "github.com/kr/pretty" "github.com/psanford/memfs" + "github.com/simulot/immich-go/browser/files" + "github.com/simulot/immich-go/logger" ) type inMemFS struct { @@ -91,8 +90,6 @@ func TestLocalAssets(t *testing.T) { t.Errorf("difference\n") pretty.Ldiff(t, c.expected, results) } - }) - } } diff --git a/browser/gp/googlephotos.go b/browser/gp/googlephotos.go index b133046..740870f 100644 --- a/browser/gp/googlephotos.go +++ b/browser/gp/googlephotos.go @@ -11,7 +11,6 @@ import ( "unicode/utf8" "github.com/simulot/immich-go/browser" - "github.com/simulot/immich-go/helpers/fshelper" "github.com/simulot/immich-go/helpers/gen" "github.com/simulot/immich-go/logger" @@ -94,7 +93,6 @@ func (to *Takeout) passOne(ctx context.Context) error { func (to *Takeout) passOneFsWalk(ctx context.Context, w fs.FS) error { err := fs.WalkDir(w, ".", func(name string, d fs.DirEntry, err error) error { - if err != nil { return err } @@ -474,7 +472,6 @@ func (to *Takeout) passTwoWalk(ctx context.Context, w fs.FS, assetChan chan *bro } return nil }) - } // googleMDToAsset makes a localAssetFile based on the google metadata diff --git a/browser/gp/json_test.go b/browser/gp/json_test.go index c8f18ce..c5f10eb 100644 --- a/browser/gp/json_test.go +++ b/browser/gp/json_test.go @@ -7,7 +7,6 @@ import ( ) func TestPresentFields(t *testing.T) { - tcs := []struct { name string json string @@ -132,5 +131,4 @@ func TestPresentFields(t *testing.T) { } }) } - } diff --git a/browser/gp/testgp_samples_test.go b/browser/gp/testgp_samples_test.go index 119e982..0c57ac9 100644 --- a/browser/gp/testgp_samples_test.go +++ b/browser/gp/testgp_samples_test.go @@ -10,9 +10,8 @@ import ( "strings" "time" - "github.com/simulot/immich-go/immich/metadata" - "github.com/psanford/memfs" + "github.com/simulot/immich-go/immich/metadata" ) type inMemFS struct { @@ -36,9 +35,9 @@ func (mfs *inMemFS) addFile(name string, content []byte) *inMemFS { return mfs } -func (mfs *inMemFS) addImage(name string, len int) *inMemFS { - b := make([]byte, len) - for i := 0; i < len; i++ { +func (mfs *inMemFS) addImage(name string, length int) *inMemFS { + b := make([]byte, length) + for i := 0; i < length; i++ { b[i] = byte(i % 256) } mfs.addFile(name, b) @@ -124,7 +123,6 @@ func simpleYear() *inMemFS { 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) - } func simpleAlbum() *inMemFS { @@ -190,7 +188,6 @@ func titlesWithForbiddenChars() *inMemFS { addImage("Takeout/Google Photos/Photos from 2012/27_06_12 - 1.mov", 52). addJSONImage("Takeout/Google Photos/Photos from 2012/27_06_12 - 1.json", "27/06/12 - 1"). addImage("Takeout/Google Photos/Photos from 2012/27_06_12 - 1.jpg", 24) - } func namesIssue39() *inMemFS { diff --git a/browser/gp/testgp_test.go b/browser/gp/testgp_test.go index a1ca321..f051d9f 100644 --- a/browser/gp/testgp_test.go +++ b/browser/gp/testgp_test.go @@ -6,9 +6,8 @@ import ( "reflect" "testing" - "github.com/simulot/immich-go/logger" - "github.com/kr/pretty" + "github.com/simulot/immich-go/logger" ) func TestBrowse(t *testing.T) { @@ -97,7 +96,6 @@ func TestBrowse(t *testing.T) { } for _, c := range tc { t.Run(c.name, func(t *testing.T) { - fsys := c.gen() if fsys.err != nil { t.Error(fsys.err) @@ -122,11 +120,9 @@ func TestBrowse(t *testing.T) { } }) } - } func TestAlbums(t *testing.T) { - type album map[string][]fileResult tc := []struct { name string @@ -173,7 +169,6 @@ func TestAlbums(t *testing.T) { for _, c := range tc { t.Run(c.name, func(t *testing.T) { - ctx := context.Background() fsys := c.gen() if fsys.err != nil { @@ -203,7 +198,6 @@ func TestAlbums(t *testing.T) { t.Errorf("difference\n") pretty.Ldiff(t, c.albums, albums) } - }) } } diff --git a/browser/readersearch.go b/browser/readersearch.go index c984b05..0a2b820 100644 --- a/browser/readersearch.go +++ b/browser/readersearch.go @@ -47,7 +47,6 @@ func searchPattern(r io.Reader, pattern []byte, maxDataLen int) ([]byte, error) } func seekReaderAtPattern(r io.Reader, pattern []byte) (io.Reader, error) { - var err error pos := 0 // Create a buffer to hold the chunk of dataZ diff --git a/cmdduplicate/duplicate.go b/cmdduplicate/duplicate.go index 3910d37..2cad238 100644 --- a/cmdduplicate/duplicate.go +++ b/cmdduplicate/duplicate.go @@ -103,11 +103,8 @@ func DuplicateCommand(ctx context.Context, ic *immich.ImmichClient, log *logger. return false } c = strings.Compare(keys[i].Name, keys[j].Name) - switch c { - case -1: - return true - } - return false + + return c == -1 }) for _, k := range keys { @@ -118,12 +115,12 @@ func DuplicateCommand(ctx context.Context, ic *immich.ImmichClient, log *logger. l := app.assetsByBaseAndDate[k] app.logger.OK("There are %d copies of the asset %s, taken on %s ", len(l), k.Name, l[0].ExifInfo.DateTimeOriginal.Format(time.RFC3339)) albums := []immich.AlbumSimplified{} - delete := []string{} + assetsToDelete := []string{} sort.Slice(l, func(i, j int) bool { return l[i].ExifInfo.FileSizeInByte < l[j].ExifInfo.FileSizeInByte }) for p, a := range l { if p < len(l)-1 { log.OK(" delete %s %dx%d, %s, %s", a.OriginalFileName, a.ExifInfo.ExifImageWidth, a.ExifInfo.ExifImageHeight, ui.FormatBytes(a.ExifInfo.FileSizeInByte), a.OriginalPath) - delete = append(delete, a.ID) + assetsToDelete = append(assetsToDelete, a.ID) r, err := app.Immich.GetAssetAlbums(ctx, a.ID) if err != nil { log.Error("Can't get asset's albums: %s", err.Error()) @@ -143,7 +140,7 @@ func DuplicateCommand(ctx context.Context, ic *immich.ImmichClient, log *logger. } } if yes { - err = app.Immich.DeleteAssets(ctx, delete, false) + err = app.Immich.DeleteAssets(ctx, assetsToDelete, false) if err != nil { log.Error("Can't delete asset: %s", err.Error()) } else { diff --git a/cmdtool/cmdalbum/cmdalbum.go b/cmdtool/cmdalbum/cmdalbum.go index 0ba6e9a..5be2a87 100644 --- a/cmdtool/cmdalbum/cmdalbum.go +++ b/cmdtool/cmdalbum/cmdalbum.go @@ -18,8 +18,7 @@ func AlbumCommand(ctx context.Context, ic *immich.ImmichClient, log *logger.Log, cmd := args[0] args = args[1:] - switch cmd { - case "delete": + if cmd == "delete" { return deleteAlbum(ctx, ic, log, args) } } diff --git a/cmdtool/cmdtool.go b/cmdtool/cmdtool.go index 0edffe4..34a6e0e 100644 --- a/cmdtool/cmdtool.go +++ b/cmdtool/cmdtool.go @@ -14,8 +14,7 @@ func CommandTool(ctx context.Context, ic *immich.ImmichClient, logger *logger.Lo cmd := args[0] args = args[1:] - switch cmd { - case "album": + if cmd == "album" { return cmdalbum.AlbumCommand(ctx, ic, logger, args) } } diff --git a/cmdupload/assets.go b/cmdupload/assets.go index 61a4f28..a407336 100644 --- a/cmdupload/assets.go +++ b/cmdupload/assets.go @@ -41,9 +41,9 @@ func (ai *AssetIndex) Len() int { return len(ai.assets) } -func (ai *AssetIndex) AddLocalAsset(la *browser.LocalAssetFile, ImmichID string) { +func (ai *AssetIndex) AddLocalAsset(la *browser.LocalAssetFile, immichID string) { sa := &immich.Asset{ - ID: ImmichID, + ID: immichID, DeviceAssetID: la.DeviceAssetID(), OriginalFileName: strings.TrimSuffix(path.Base(la.Title), path.Ext(la.Title)), ExifInfo: immich.ExifInfo{ diff --git a/cmdupload/configuration.go b/cmdupload/configuration.go index fca95d2..958a7b6 100644 --- a/cmdupload/configuration.go +++ b/cmdupload/configuration.go @@ -28,7 +28,6 @@ func (c *Configuration) IsValid() error { c.ExcludeExtensions, _ = checkExtensions(c.ExcludeExtensions) return jerr - } func checkExtensions(l StringList) (StringList, error) { diff --git a/cmdupload/stringlist_test.go b/cmdupload/stringlist_test.go index 210aae7..d6a7c6e 100644 --- a/cmdupload/stringlist_test.go +++ b/cmdupload/stringlist_test.go @@ -3,7 +3,6 @@ package cmdupload import "testing" func TestStringList_Include(t *testing.T) { - tests := []struct { name string sl StringList @@ -51,7 +50,6 @@ func TestStringList_Include(t *testing.T) { } func TestStringList_Exclude(t *testing.T) { - tests := []struct { name string sl StringList diff --git a/cmdupload/upload.go b/cmdupload/upload.go index f68f412..b89967e 100644 --- a/cmdupload/upload.go +++ b/cmdupload/upload.go @@ -23,7 +23,6 @@ import ( "github.com/simulot/immich-go/helpers/stacking" "github.com/simulot/immich-go/immich" "github.com/simulot/immich-go/immich/metadata" - "github.com/simulot/immich-go/logger" ) @@ -196,7 +195,6 @@ func NewUpCmd(ctx context.Context, ic iClient, log logger.Logger, args []string) app.AssetIndex.ReIndex() return &app, err - } func UploadCommand(ctx context.Context, ic iClient, log logger.Logger, args []string) error { @@ -205,7 +203,6 @@ func UploadCommand(ctx context.Context, ic iClient, log logger.Logger, args []st return err } return app.Run(ctx, app.fsys) - } func (app *UpCmd) journalAsset(a *browser.LocalAssetFile, action logger.Action, comment ...string) { @@ -213,7 +210,6 @@ func (app *UpCmd) journalAsset(a *browser.LocalAssetFile, action logger.Action, } func (app *UpCmd) Run(ctx context.Context, fsyss []fs.FS) error { - var browser browser.Browser var err error @@ -498,7 +494,6 @@ func (app *UpCmd) handleAsset(ctx context.Context, a *browser.LocalAssetFile) er } return nil - } func (app *UpCmd) isInAlbum(a *browser.LocalAssetFile, album string) bool { @@ -527,7 +522,6 @@ func (app *UpCmd) UploadAsset(ctx context.Context, a *browser.LocalAssetFile) (s var resp immich.AssetResponse var err error if !app.DryRun { - if app.ForceSidecar { sc := metadata.SideCar{} sc.DateTaken = a.DateTaken @@ -553,7 +547,6 @@ func (app *UpCmd) UploadAsset(ctx context.Context, a *browser.LocalAssetFile) (s if app.CreateStacks { app.stacks.ProcessAsset(resp.ID, a.FileName, a.DateTaken) } - } else { app.journalAsset(a, logger.SERVER_DUPLICATE, "already on the server") } @@ -574,12 +567,12 @@ func (app *UpCmd) albumName(al browser.LocalAlbum) string { return Name } -func (app *UpCmd) AddToAlbum(ID string, album string) { +func (app *UpCmd) AddToAlbum(id string, album string) { l := app.updateAlbums[album] if l == nil { l = map[string]any{} } - l[ID] = nil + l[id] = nil app.updateAlbums[album] = l } @@ -596,7 +589,6 @@ func (app *UpCmd) DeleteLocalAssets() error { } else { app.Journal.Warning("file %q not deleted, dry run mode", a.Title) } - } return nil } @@ -619,7 +611,6 @@ func (app *UpCmd) ManageAlbums(ctx context.Context) error { return fmt.Errorf("can't get the album list from the server: %w", err) } for album, list := range app.updateAlbums { - found := false for _, sal := range serverAlbums { if sal.AlbumName == album { @@ -728,7 +719,6 @@ func (ai *AssetIndex) adviceIDontKnow(la *browser.LocalAssetFile) *Advice { } func (ai *AssetIndex) adviceSameOnServer(sa *immich.Asset) *Advice { - return &Advice{ Advice: SameOnServer, Message: fmt.Sprintf("An asset with the same name:%q, date:%q and size:%s exists on the server. No need to upload.", sa.OriginalFileName, sa.ExifInfo.DateTimeOriginal.Format(time.DateTime), formatBytes(sa.ExifInfo.FileSizeInByte)), @@ -791,7 +781,6 @@ func (ai *AssetIndex) ShouldUpload(la *browser.LocalAssetFile) (*Advice, error) size := int(la.Size()) if err != nil { return ai.adviceIDontKnow(la), nil - } for _, sa = range l { compareDate := compareDate(dateTaken, sa.ExifInfo.DateTimeOriginal.Time) diff --git a/cmdupload/upload_test.go b/cmdupload/upload_test.go index 8982d09..2291257 100644 --- a/cmdupload/upload_test.go +++ b/cmdupload/upload_test.go @@ -9,12 +9,11 @@ import ( "slices" "testing" + "github.com/kr/pretty" "github.com/simulot/immich-go/browser" "github.com/simulot/immich-go/helpers/gen" "github.com/simulot/immich-go/immich" "github.com/simulot/immich-go/logger" - - "github.com/kr/pretty" ) type stubIC struct { @@ -38,15 +37,15 @@ func (c *stubIC) AddAssetToAlbum(context.Context, string, []string) ([]immich.Up func (c *stubIC) CreateAlbum(context.Context, string, []string) (immich.AlbumSimplified, error) { return immich.AlbumSimplified{}, nil } -func (c *stubIC) UpdateAssets(ctx context.Context, IDs []string, isArchived bool, isFavorite bool, latitude float64, longitude float64, removeParent bool, stackParentId string) error { +func (c *stubIC) UpdateAssets(ctx context.Context, ids []string, isArchived bool, isFavorite bool, latitude float64, longitude float64, removeParent bool, stackParentId string) error { return nil } -func (c *stubIC) StackAssets(ctx context.Context, cover string, IDs []string) error { +func (c *stubIC) StackAssets(ctx context.Context, cover string, ids []string) error { return nil } -func (c *stubIC) UpdateAsset(ctx context.Context, ID string, a *browser.LocalAssetFile) (*immich.Asset, error) { +func (c *stubIC) UpdateAsset(ctx context.Context, id string, a *browser.LocalAssetFile) (*immich.Asset, error) { return nil, nil } diff --git a/helpers/docker/docker.go b/helpers/docker/docker.go index eb96acc..5b8f798 100644 --- a/helpers/docker/docker.go +++ b/helpers/docker/docker.go @@ -96,14 +96,12 @@ func (d *DockerConnect) connect(ctx context.Context, host string, container stri if l[:len(l)-1] == d.Container { return nil } - } return fmt.Errorf("container 'immich_server' not found: %w", err) } // Download a file from the docker container func (d *DockerConnect) Download(ctx context.Context, hostFile string) (io.Reader, error) { - cmd, err := d.proxy.docker(ctx, "cp", d.Container+":"+hostFile, "-") if err != nil { return nil, err @@ -173,6 +171,9 @@ func (d *DockerConnect) Upload(ctx context.Context, file string, size int64, r i return } _, err = io.Copy(tw, r) + if err != nil { + return + } }() err = cmd.Start() @@ -194,6 +195,9 @@ func (d *DockerConnect) BatchUpload(ctx context.Context, dir string) (*batchUplo } out, err := cmd.StdoutPipe() + if err != nil { + return nil, err + } go func() { io.Copy(os.Stdout, out) }() @@ -212,13 +216,12 @@ func (d *DockerConnect) BatchUpload(ctx context.Context, dir string) (*batchUplo var err error tw := tar.NewWriter(mw) defer func() { - //f.Close() + // f.Close() tw.Close() in.Close() cmd.Wait() }() for { - select { case <-ctx.Done(): return @@ -286,6 +289,9 @@ func (d *DockerConnect) Command(ctx context.Context, args ...string) (string, er buffOut := bytes.NewBuffer(nil) out, err := cmd.StdoutPipe() + if err != nil { + return "", err + } go func() { io.Copy(buffOut, out) }() diff --git a/helpers/fshelper/parseArgs.go b/helpers/fshelper/parseArgs.go index c42fc47..22374af 100644 --- a/helpers/fshelper/parseArgs.go +++ b/helpers/fshelper/parseArgs.go @@ -57,8 +57,7 @@ func ParsePath(args []string, googlePhoto bool) ([]fs.FS, error) { for _, f := range p.files { d, b := filepath.Split(f) d = filepath.Clean(d) - l := append(p.paths[d], b) - p.paths[d] = l + p.paths[d] = append(p.paths[d], b) } for pa, l := range p.paths { diff --git a/helpers/fshelper/readjson.go b/helpers/fshelper/readjson.go index 59d75c2..52c1397 100644 --- a/helpers/fshelper/readjson.go +++ b/helpers/fshelper/readjson.go @@ -8,9 +8,9 @@ import ( // readJSON reads a JSON file from the provided file system (fs.FS) // with the given name and unmarshals it into the provided type T. -func ReadJSON[T any](FSys fs.FS, name string) (*T, error) { +func ReadJSON[T any](fsys fs.FS, name string) (*T, error) { var object T - b, err := fs.ReadFile(FSys, name) + b, err := fs.ReadFile(fsys, name) if err != nil { return nil, err } diff --git a/helpers/fshelper/removefs.go b/helpers/fshelper/removefs.go index db53ac0..e230844 100644 --- a/helpers/fshelper/removefs.go +++ b/helpers/fshelper/removefs.go @@ -28,7 +28,6 @@ type dirRemoveFS struct { } func DirRemoveFS(name string) fs.FS { - fsys := &dirRemoveFS{ FS: os.DirFS(name), dir: name, diff --git a/helpers/gen/slices.go b/helpers/gen/slices.go index f221b47..c9aa466 100644 --- a/helpers/gen/slices.go +++ b/helpers/gen/slices.go @@ -1,9 +1,9 @@ package gen -func DeleteItem[T comparable](s []T, delete T) []T { +func DeleteItem[T comparable](s []T, item T) []T { r := make([]T, 0, len(s)) for i := range s { - if s[i] != delete { + if s[i] != item { r = append(r, s[i]) } } diff --git a/helpers/myflag/boolfn.go b/helpers/myflag/boolfn.go index 5dc0322..1bdf47c 100644 --- a/helpers/myflag/boolfn.go +++ b/helpers/myflag/boolfn.go @@ -12,7 +12,6 @@ import ( func BoolFlagFn(b *bool, defaultValue bool) func(string) error { *b = defaultValue return func(v string) error { - switch strings.ToLower(v) { case "": *b = true @@ -26,5 +25,4 @@ func BoolFlagFn(b *bool, defaultValue bool) func(string) error { return err } } - } diff --git a/helpers/stacking/stack.go b/helpers/stacking/stack.go index 87fcfb5..6f0e506 100644 --- a/helpers/stacking/stack.go +++ b/helpers/stacking/stack.go @@ -45,10 +45,9 @@ func NewStackBuilder() *StackBuilder { sb.dateRange.Set("1850-01-04,2030-01-01") return &sb - } -func (sb *StackBuilder) ProcessAsset(ID string, fileName string, captureDate time.Time) { +func (sb *StackBuilder) ProcessAsset(id string, fileName string, captureDate time.Time) { if !sb.dateRange.InRange(captureDate) { return } @@ -82,18 +81,18 @@ func (sb *StackBuilder) ProcessAsset(ID string, fileName string, captureDate tim } s, ok := sb.stacks[k] if !ok { - s.CoverID = ID + s.CoverID = id s.Date = captureDate } - s.IDs = append(s.IDs, ID) + s.IDs = append(s.IDs, id) s.Names = append(s.Names, path.Base(fileName)) if burst { s.StackType = StackBurst } if cover { - s.CoverID = ID + s.CoverID = id } else if !burst && slices.Contains([]string{".jpeg", ".jpg", ".jpe"}, ext) { - s.CoverID = ID + s.CoverID = id } sb.stacks[k] = s } @@ -183,7 +182,6 @@ func (sb *StackBuilder) Stacks() []Stack { }) s.IDs = ids stacks = append(stacks, s) - } sort.Slice(stacks, func(i, j int) bool { c := stacks[i].Date.Compare(stacks[j].Date) @@ -194,11 +192,8 @@ func (sb *StackBuilder) Stacks() []Stack { return false } c = strings.Compare(stacks[i].Names[0], stacks[j].Names[0]) - switch c { - case -1: - return true - } - return false + + return c == -1 }) return stacks } diff --git a/helpers/stacking/statck_test.go b/helpers/stacking/statck_test.go index 0603b3b..e28a0ce 100644 --- a/helpers/stacking/statck_test.go +++ b/helpers/stacking/statck_test.go @@ -216,6 +216,5 @@ func Test_Stack(t *testing.T) { pretty.Ldiff(t, tt.want, got) } }) - } } diff --git a/helpers/tzone/timezone.go b/helpers/tzone/timezone.go index 8fdff76..b94adb1 100644 --- a/helpers/tzone/timezone.go +++ b/helpers/tzone/timezone.go @@ -22,7 +22,7 @@ var ( func SetLocal(tz string) (*time.Location, error) { onceSetLocal.Do(func() { - if len(tz) == 0 { + if tz == "" { tz, _err = tzlocal.RuntimeTZ() if _err != nil { return @@ -31,7 +31,6 @@ func SetLocal(tz string) (*time.Location, error) { _local, _err = time.LoadLocation(strings.TrimSuffix(tz, "\n")) }) return _local, _err - } func Local() (*time.Location, error) { diff --git a/immich/albums.go b/immich/albums.go index 5fbbd0c..ce7fa0a 100644 --- a/immich/albums.go +++ b/immich/albums.go @@ -27,7 +27,6 @@ func (ic *ImmichClient) GetAllAlbums(ctx context.Context) ([]AlbumSimplified, er return nil, err } return albums, nil - } type AlbumContent struct { @@ -82,7 +81,6 @@ func (ic *ImmichClient) GetAssetsAlbums(ctx context.Context, id string) ([]Album return nil, err } return albums, nil - } type UpdateAlbum struct { @@ -96,7 +94,6 @@ type UpdateAlbumResult struct { } func (ic *ImmichClient) AddAssetToAlbum(ctx context.Context, albumID string, assets []string) ([]UpdateAlbumResult, error) { - var r []UpdateAlbumResult body := UpdateAlbum{ IDS: assets, diff --git a/immich/asset.go b/immich/asset.go index 2f8519c..d702dc7 100644 --- a/immich/asset.go +++ b/immich/asset.go @@ -132,14 +132,12 @@ func (ic *ImmichClient) AssetUpload(ctx context.Context, la *browser.LocalAssetF return } } - }() err = ic.newServerCall(ctx, "AssetUpload"). do(post("/asset/upload", m.FormDataContentType(), setAcceptJSON(), setBody(body)), responseJSON(&ar)) return ar, err - } var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"") @@ -200,7 +198,6 @@ func (ic *ImmichClient) GetAllAssets(ctx context.Context, opt *GetAssetOptions) // // It calls the server for IMAGE, VIDEO, normal item, trashed Items func (ic *ImmichClient) GetAllAssetsWithFilter(ctx context.Context, opt *GetAssetOptions, filter func(*Asset)) error { - for _, t := range []string{"IMAGE", "VIDEO", "AUDIO", "OTHER"} { values := opt.Values() values.Set("type", t) @@ -239,7 +236,7 @@ func (ic *ImmichClient) GetAssetByID(ctx context.Context, id string) (*Asset, er return &r, err } -func (ic *ImmichClient) UpdateAssets(ctx context.Context, IDs []string, +func (ic *ImmichClient) UpdateAssets(ctx context.Context, ids []string, isArchived bool, isFavorite bool, latitude float64, longitude float64, removeParent bool, stackParentId string) error { @@ -254,7 +251,7 @@ func (ic *ImmichClient) UpdateAssets(ctx context.Context, IDs []string, } param := updAssets{ - IDs: IDs, + IDs: ids, IsArchived: isArchived, IsFavorite: isFavorite, Latitude: latitude, @@ -265,8 +262,7 @@ func (ic *ImmichClient) UpdateAssets(ctx context.Context, IDs []string, return ic.newServerCall(ctx, "updateAssets").do(put("/asset", setJSONBody(param))) } -func (ic *ImmichClient) UpdateAsset(ctx context.Context, ID string, a *browser.LocalAssetFile) (*Asset, error) { - +func (ic *ImmichClient) UpdateAsset(ctx context.Context, id string, a *browser.LocalAssetFile) (*Asset, error) { type updAsset struct { IsArchived bool `json:"isArchived"` IsFavorite bool `json:"isFavorite"` @@ -282,15 +278,15 @@ func (ic *ImmichClient) UpdateAsset(ctx context.Context, ID string, a *browser.L Longitude: a.Longitude, } r := Asset{} - err := ic.newServerCall(ctx, "updateAsset").do(put("/asset/"+ID, setJSONBody(param)), responseJSON(&r)) + err := ic.newServerCall(ctx, "updateAsset").do(put("/asset/"+id, setJSONBody(param)), responseJSON(&r)) return &r, err } -func (ic *ImmichClient) StackAssets(ctx context.Context, coverID string, IDs []string) error { +func (ic *ImmichClient) StackAssets(ctx context.Context, coverID string, ids []string) error { cover, err := ic.GetAssetByID(ctx, coverID) if err != nil { return err } - return ic.UpdateAssets(ctx, IDs, cover.IsArchived, cover.IsFavorite, cover.ExifInfo.Latitude, cover.ExifInfo.Longitude, false, coverID) + return ic.UpdateAssets(ctx, ids, cover.IsArchived, cover.IsFavorite, cover.ExifInfo.Latitude, cover.ExifInfo.Longitude, false, coverID) } diff --git a/immich/call.go b/immich/call.go index 29b8cb3..9942844 100644 --- a/immich/call.go +++ b/immich/call.go @@ -148,8 +148,7 @@ func setPaginator(pageParameter string, startPage int) serverCallOption { type requestFunction func(sc *serverCall) *http.Request func (sc *serverCall) request(method string, url string, opts ...serverRequestOption) *http.Request { - - req, err := http.NewRequestWithContext(sc.ctx, method, url, nil) + req, err := http.NewRequestWithContext(sc.ctx, method, url, http.NoBody) if sc.joinError(err) != nil { return nil } diff --git a/immich/call_test.go b/immich/call_test.go index 323774c..1dd1b4a 100644 --- a/immich/call_test.go +++ b/immich/call_test.go @@ -88,5 +88,4 @@ func TestCall(t *testing.T) { t.Logf("response received: %#v", r) }) } - } diff --git a/immich/daterange.go b/immich/daterange.go index d81147c..004f67d 100644 --- a/immich/daterange.go +++ b/immich/daterange.go @@ -12,14 +12,16 @@ type DateRange struct { } func (dr DateRange) String() string { - if dr.day { + switch { + case dr.day: return dr.After.Format("2006-01-02") - } else if dr.month { + case dr.month: return dr.After.Format("2006-01") - } else if dr.year { + case dr.year: return dr.After.Format("2006") + default: + return dr.After.Format("2006-01-02") + "," + dr.Before.AddDate(0, 0, -1).Format("2006-01-02") } - return dr.After.Format("2006-01-02") + "," + dr.Before.AddDate(0, 0, -1).Format("2006-01-02") } func (dr *DateRange) Set(s string) (err error) { @@ -32,21 +34,21 @@ func (dr *DateRange) Set(s string) (err error) { dr.After, err = time.ParseInLocation("2006", s, time.UTC) if err == nil { dr.Before = dr.After.AddDate(1, 0, 0) - return + return nil } case 7: dr.month = true dr.After, err = time.ParseInLocation("2006-01", s, time.UTC) if err == nil { dr.Before = dr.After.AddDate(0, 1, 0) - return + return nil } case 10: dr.day = true dr.After, err = time.ParseInLocation("2006-01-02", s, time.UTC) if err == nil { dr.Before = dr.After.AddDate(0, 0, 1) - return + return nil } case 21: dr.After, err = time.ParseInLocation("2006-01-02", s[:10], time.UTC) @@ -54,7 +56,7 @@ func (dr *DateRange) Set(s string) (err error) { dr.Before, err = time.ParseInLocation("2006-01-02", s[11:], time.UTC) if err == nil { dr.Before = dr.Before.AddDate(0, 0, 1) - return + return nil } } } diff --git a/immich/daterange_test.go b/immich/daterange_test.go index a659445..c82cbfd 100644 --- a/immich/daterange_test.go +++ b/immich/daterange_test.go @@ -6,7 +6,6 @@ import ( ) func TestDateRange_InRange(t *testing.T) { - tests := []struct { name string check []struct { diff --git a/immich/metadata/direct.go b/immich/metadata/direct.go index 9d4a30a..220a7cf 100644 --- a/immich/metadata/direct.go +++ b/immich/metadata/direct.go @@ -54,7 +54,6 @@ func GetFromReader(rd io.Reader, ext string) (MetaData, error) { // readExifDateTaken pase the file for Exif DateTaken func readExifDateTaken(r io.Reader) (time.Time, error) { - md, err := getExifFromReader(r) return md.DateTaken, err } @@ -104,5 +103,4 @@ func readCR3DateTaken(r *sliceReader) (time.Time, error) { md, err := getExifFromReader(r) return md.DateTaken, err - } diff --git a/immich/metadata/exif.go b/immich/metadata/exif.go index 1c6bf2a..08db722 100644 --- a/immich/metadata/exif.go +++ b/immich/metadata/exif.go @@ -7,9 +7,8 @@ import ( "strings" "time" - "github.com/simulot/immich-go/helpers/tzone" - "github.com/rwcarlsen/goexif/exif" + "github.com/simulot/immich-go/helpers/tzone" ) func getExifFromReader(r io.Reader) (MetaData, error) { diff --git a/immich/metadata/namesdate.go b/immich/metadata/namesdate.go index f5be48a..034a34d 100644 --- a/immich/metadata/namesdate.go +++ b/immich/metadata/namesdate.go @@ -37,7 +37,6 @@ func TakeTimeFromName(name string) time.Time { if i > 0 { m[i-1], _ = strconv.Atoi(mm[i]) } - } t := time.Date(m[0], time.Month(m[1]), m[2], m[3], m[4], m[5], 0, time.UTC) if t.Year() != m[0] || t.Month() != time.Month(m[1]) || t.Day() != m[2] || diff --git a/immich/metadata/quicktime.go b/immich/metadata/quicktime.go index 2eb9cff..8bd040f 100644 --- a/immich/metadata/quicktime.go +++ b/immich/metadata/quicktime.go @@ -33,7 +33,7 @@ If any of the optional fields are present, the size of the atom would increase a */ type MvhdAtom struct { - Marker []byte //4 bytes + Marker []byte // 4 bytes Version uint8 Flags []byte // 3 bytes CreationTime time.Time @@ -48,7 +48,6 @@ type MvhdAtom struct { } func decodeMvhdAtom(r *sliceReader) (*MvhdAtom, error) { - a := &MvhdAtom{} // Read the mvhd marker (4 bytes) @@ -66,7 +65,6 @@ func decodeMvhdAtom(r *sliceReader) (*MvhdAtom, error) { a.ModificationTime = convertTime32(binary.BigEndian.Uint32(b)) b, _ = r.ReadSlice(4) a.CreationTime = convertTime32(binary.BigEndian.Uint32(b)) - } else { // Read the creation time (4 bytes) b, _ := r.ReadSlice(8) diff --git a/immich/metadata/search.go b/immich/metadata/search.go index 31df76c..ab049d9 100644 --- a/immich/metadata/search.go +++ b/immich/metadata/search.go @@ -23,7 +23,6 @@ func (r *sliceReader) ReadSlice(l int) ([]byte, error) { } func searchPattern(r io.Reader, pattern []byte, buffer []byte) (*sliceReader, error) { - var err error pos := 0 ofs := 0 diff --git a/immich/metadata/sidecar.go b/immich/metadata/sidecar.go index f1091ef..495c94b 100644 --- a/immich/metadata/sidecar.go +++ b/immich/metadata/sidecar.go @@ -44,11 +44,9 @@ func (sc *SideCar) Open(fsys fs.FS, name string) (io.ReadCloser, error) { } return io.NopCloser(b), nil - } func (sc *SideCar) Bytes() ([]byte, error) { - b := bytes.NewBuffer(nil) err := sidecarTemplate.Execute(b, sc) if err != nil { diff --git a/immich/trace.go b/immich/trace.go index bcf6b42..ac41bc7 100644 --- a/immich/trace.go +++ b/immich/trace.go @@ -70,5 +70,4 @@ func traceRequest(req *http.Request) { tr := io.TeeReader(req.Body, os.Stdout) req.Body = &smartBodyCloser{body: req.Body, r: tr} } - } diff --git a/logger/journal.go b/logger/journal.go index 4ea14fc..60b4432 100644 --- a/logger/journal.go +++ b/logger/journal.go @@ -62,14 +62,14 @@ func (j *Journal) AddEntry(file string, action Action, comment ...string) { } } j.mut.Lock() - j.counts[action] = j.counts[action] + 1 + j.counts[action]++ if action == UPGRADED { j.counts[UPLOADED]-- } j.mut.Unlock() } -func (j *Journal) Report() { +func (j *Journal) Report() { checkFiles := j.counts[SCANNED_IMAGE] + j.counts[SCANNED_VIDEO] + j.counts[METADATA] + j.counts[UNSUPPORTED] + j.counts[FAILED_VIDEO] + j.counts[DISCARDED] handledFiles := j.counts[NOT_SELECTED] + j.counts[LOCAL_DUPLICATE] + j.counts[SERVER_DUPLICATE] + j.counts[SERVER_BETTER] + j.counts[UPLOADED] + j.counts[UPGRADED] + j.counts[SERVER_ERROR] j.Logger.OK("Scan of the sources:") @@ -95,5 +95,4 @@ func (j *Journal) Report() { j.Logger.OK("%6d errors when uploading", j.counts[SERVER_ERROR]) j.Logger.OK("%6d handled total (difference %d)", handledFiles, j.counts[SCANNED_IMAGE]+j.counts[SCANNED_VIDEO]-handledFiles) - } diff --git a/logger/log.go b/logger/log.go index 6d13a4a..1e6e80c 100644 --- a/logger/log.go +++ b/logger/log.go @@ -70,9 +70,9 @@ type Log struct { out io.WriteCloser } -func NewLogger(DisplayLevel Level, noColors bool, debug bool) *Log { +func NewLogger(displayLevel Level, noColors bool, debug bool) *Log { l := Log{ - displayLevel: DisplayLevel, + displayLevel: displayLevel, noColors: noColors, colorStrings: map[Level]string{}, debug: debug, diff --git a/main.go b/main.go index d37acef..3fba387 100644 --- a/main.go +++ b/main.go @@ -54,7 +54,8 @@ func main() { } if err != nil { log.Error(err.Error()) - os.Exit(1) + log.Close() + os.Exit(1) //nolint:gocritic } log.OK("Done.") } @@ -73,12 +74,11 @@ type Application struct { Immich *immich.ImmichClient // Immich client Logger *logger.Log // Program's logger - LogFile string //Log file + LogFile string // Log file } func Run(ctx context.Context, log *logger.Log) (*logger.Log, error) { - var err error app := Application{} @@ -112,12 +112,12 @@ func Run(ctx context.Context, log *logger.Log) (*logger.Log, error) { } switch { - case len(app.Server) == 0 && len(app.API) == 0: + case app.Server == "" && app.API == "": err = errors.Join(err, errors.New("missing -server, Immich server address (http://:2283 or https://)")) case len(app.Server) > 0 && len(app.API) > 0: err = errors.Join(err, errors.New("give either the -server or the -api option")) } - if len(app.Key) == 0 { + if app.Key == "" { err = errors.Join(err, errors.New("missing -key")) } @@ -179,7 +179,7 @@ func Run(ctx context.Context, log *logger.Log) (*logger.Log, error) { case "tool": err = cmdtool.CommandTool(ctx, app.Immich, app.Logger, flag.Args()[1:]) default: - err = fmt.Errorf("unknwon command: %q", cmd) + err = fmt.Errorf("unknown command: %q", cmd) } return app.Logger, err }