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

Add Album configuration option to set the Album target in Google Photos #396

Merged
merged 7 commits into from
Oct 23, 2023
Merged
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/) and this
## 4.1.0
### Added
- Flag `--port` to configure the port where the authentication server will listen to when using the `auth` command ([#370][i370])
- **New command to reset the already uploaded file tracker** (`reset file-tracker`), which removes the internal database ([#182][i182])
- New **`Album` option in Job's configuration** which allows to set a fixed album's name to upload objects to. ([#393][i393])

### Deprecated
- The `CreateAlbums` option in Job's configuration is deprecated in favor of a new `Album` option.

[i370]: https://github.com/gphotosuploader/gphotos-uploader-cli/issues/370
[i182]: https://github.com/gphotosuploader/gphotos-uploader-cli/issues/182
[i393]: https://github.com/gphotosuploader/gphotos-uploader-cli/issues/393

## 4.0.0
### Added
Expand Down
14 changes: 13 additions & 1 deletion internal/cli/push/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,18 @@ func (cmd *PushCmd) Run(cobraCmd *cobra.Command, args []string) error {

// launch all folder upload jobs
for _, config := range cli.Config.Jobs {

//nolint:staticcheck // CreateAlbums is maintained for backwards compatibility
if config.CreateAlbums != "" {
cli.Logger.Warn("Deprecated 'CreateAlbums' option is used in configuration. Please, use the 'Album' option instead.")
}

// TODO: Translate the deprecated CreateAlbums into the Albums option for backwards compatibility
//nolint:staticcheck // CreateAlbums is maintained for backwards compatibility
if config.Album == "" && config.CreateAlbums != "" && config.CreateAlbums != "Off" {
config.Album = "auto:" + config.CreateAlbums
}

sourceFolder := config.SourceFolder

filterFiles, err := filter.Compile(config.IncludePatterns, config.ExcludePatterns)
Expand All @@ -71,7 +83,7 @@ func (cmd *PushCmd) Run(cobraCmd *cobra.Command, args []string) error {
FileTracker: cli.FileTracker,

SourceFolder: sourceFolder,
CreateAlbums: config.CreateAlbums,
Album: config.Album,
Filter: filterFiles,
}

Expand Down
38 changes: 34 additions & 4 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"io"
"path/filepath"
"strings"

"github.com/hjson/hjson-go/v4"
"github.com/mitchellh/go-homedir"
Expand Down Expand Up @@ -106,7 +107,7 @@ func readFile(fs afero.Fs, filename string) (*Config, error) {
return nil, err
}

// convert all path to absolute paths.
// convert all paths to absolute paths.
if err := config.ensureSourceFolderAbsolutePaths(); err != nil {
return nil, err
}
Expand Down Expand Up @@ -141,7 +142,11 @@ func (c Config) validateJobs(fs afero.Fs) error {
if !exist {
return fmt.Errorf("folder '%s' does not exist", job.SourceFolder)
}
if !isValidCreateAlbums(job.CreateAlbums) {
if job.Album != "" && !isValidAlbum(job.Album) {
return fmt.Errorf("option Album is invalid, '%s", job.Album)
}
// TODO: Check CreateAlbums for backwards compatibility. It should be removed on version 5.x
if job.Album == "" && !isValidCreateAlbums(job.CreateAlbums) {
return fmt.Errorf("option CreateAlbums is invalid, '%s", job.CreateAlbums)
}
}
Expand All @@ -159,7 +164,7 @@ func (c Config) validateSecretsBackendType() error {

func (c Config) ensureSourceFolderAbsolutePaths() error {
for i := range c.Jobs {
item := &c.Jobs[i] // we do that way to modify original object while iterating.
item := &c.Jobs[i] // we do that way to modify an original object while iterating.
src, err := homedir.Expand(item.SourceFolder)
if err != nil {
return err
Expand All @@ -169,6 +174,31 @@ func (c Config) ensureSourceFolderAbsolutePaths() error {
return nil
}

func isValidAlbumGenerationMethod(method string) bool {
if method != "folderPath" && method != "folderName" {
return false
}
return true
}

// isValidAlbum checks if the value is a valid Album option.
func isValidAlbum(value string) bool {
before, after, found := strings.Cut(value, ":")
if !found {
return false
}
if after == "" {
return false
}
switch before {
case "name":
return true
case "auto":
return isValidAlbumGenerationMethod(after)
}
return false
}

// isValidCreateAlbums checks if the value is a valid CreateAlbums option.
func isValidCreateAlbums(value string) bool {
switch value {
Expand Down Expand Up @@ -218,7 +248,7 @@ func defaultSettings() Config {
Jobs: []FolderUploadJob{
{
SourceFolder: "YOUR_FOLDER_PATH",
CreateAlbums: "folderName",
Album: "",
DeleteAfterUpload: false,
},
},
Expand Down
25 changes: 17 additions & 8 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,22 @@ func TestFromFile(t *testing.T) {
want string
isErrExpected bool
}{
{"Should success", "testdata/valid-config/config.hjson", "[email protected]", false},
{"Should fail if dir does not exist", "testdata/non-existent/config.hjson", "", true},
{"Should fail if Account is invalid", "testdata/invalid-config/Account.hjson", "", true},
{"Should fail if SourceFolder does not exist", "testdata/invalid-config/SourceFolder.hjson", "", true},
{"Should fail if SecretsBackendType is invalid", "testdata/invalid-config/SecretsBackendType.hjson", "", true},
{"Should fail if AppAPICredentials are invalid", "testdata/invalid-config/AppAPICredentials.hjson", "", true},
{"Should fail if CreateAlbums is invalid", "testdata/invalid-config/CreateAlbums.hjson", "", true},
{"Should success with Album's name option", "testdata/valid-config/configWithAlbumNameOption.hjson", "[email protected]", false},
{"Should success with Album's auto folderName option", "testdata/valid-config/configWithAlbumAutoFolderNameOption.hjson", "[email protected]", false},
{"Should success with Album's auto folderPath option", "testdata/valid-config/configWithAlbumAutoFolderPathOption.hjson", "[email protected]", false},
{"Should success with deprecated CreateAlbums option", "testdata/valid-config/configWithDeprecatedCreateAlbumsOption.hjson", "[email protected]", false},

{"Should fail if config dir does not exist", "testdata/non-existent/config.hjson", "", true},
{"Should fail if Account is invalid", "testdata/invalid-config/EmptyAccount.hjson", "", true},
{"Should fail if SourceFolder does not exist", "testdata/invalid-config/NonExistentSourceFolder.hjson", "", true},
{"Should fail if SecretsBackendType is invalid", "testdata/invalid-config/BadSecretsBackendType.hjson", "", true},
{"Should fail if AppAPICredentials are invalid", "testdata/invalid-config/EmptyAppAPICredentials.hjson", "", true},
{"Should fail if Jobs is empty", "testdata/invalid-config/NoJobs.hjson", "", true},
{"Should fail if Album's format is invalid", "testdata/invalid-config/AlbumBadFormat.hjson", "", true},
{"Should fail if Album's key is invalid", "testdata/invalid-config/AlbumBadKey.hjson", "", true},
{"Should fail if Album's name is invalid", "testdata/invalid-config/AlbumEmptyName.hjson", "", true},
{"Should fail if Album's auto value is invalid", "testdata/invalid-config/AlbumBadAutoValue.hjson", "", true},
{"Should fail if deprecated CreateAlbums is invalid", "testdata/invalid-config/DeprecatedCreateAlbums.hjson", "", true},
}

for _, tc := range testCases {
Expand Down Expand Up @@ -100,14 +108,15 @@ func TestConfig_SafePrint(t *testing.T) {
Jobs: []config.FolderUploadJob{
{
SourceFolder: "foo",
Album: "name:albumName",
CreateAlbums: "folderPath",
DeleteAfterUpload: false,
IncludePatterns: []string{},
ExcludePatterns: []string{},
},
},
}
want := `{"APIAppCredentials":{"ClientID":"client-id","ClientSecret":"REMOVED"},"Account":"account","SecretsBackendType":"auto","Jobs":[{"SourceFolder":"foo","CreateAlbums":"folderPath","DeleteAfterUpload":false,"IncludePatterns":[],"ExcludePatterns":[]}]}`
want := `{"APIAppCredentials":{"ClientID":"client-id","ClientSecret":"REMOVED"},"Account":"account","SecretsBackendType":"auto","Jobs":[{"SourceFolder":"foo","Album":"name:albumName","CreateAlbums":"folderPath","DeleteAfterUpload":false,"IncludePatterns":[],"ExcludePatterns":[]}]}`

if want != cfg.SafePrint() {
t.Errorf("want: %s, got: %s", want, cfg.SafePrint())
Expand Down
18 changes: 16 additions & 2 deletions internal/config/schema.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package config

// Config represents the content of configuration file.
// Config represents the content of the configuration file.
// It defines the schema for Marshal and Unmarshal the data of the configuration file.
type Config struct {
// APIAppCredentials represents Google Photos API credentials for OAuth.
Expand Down Expand Up @@ -29,8 +29,22 @@ type FolderUploadJob struct {
// SourceFolder is the folder containing the objects to be uploaded.
SourceFolder string `json:"SourceFolder"`

// Album is the album where objects will be uploaded.
// If the Album option is not set, the objects will not be associated with an album in Google Photos.
//
// These are the valid values: "name:", "id:" and "auto:".
// "name:" : Followed by the album name in Google Photos (album names are not unique, so the first to match
// will be selected)
// "auto:" : Followed either "folderPath" or "folderName" will use an autogenerated album name based on the
// object's folder path or object's folder name.
Album string `json:"Album,omitempty"`

// CreateAlbums is the parameter to create albums on Google Photos.
// Valid options are:
//
// Deprecated: CreateAlbums exists to maintain backwards compatibility with version 4.x. It should not be used in
// favor of the Album option.
//
// Valid options were:
// Off: Disable album creation (default).
// folderPath: Creates album with the name based on full folder path.
// folderName: Creates album with the name based on the folder name.
Expand Down
19 changes: 19 additions & 0 deletions internal/config/testdata/invalid-config/AlbumAuto.hjson
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
APIAppCredentials:
{
ClientID: client-id
ClientSecret: client-secret
}
Account: [email protected]
SecretsBackendType: auto
Jobs:
[
{
SourceFolder: ./testdata
Album: auto:invalid
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
19 changes: 19 additions & 0 deletions internal/config/testdata/invalid-config/AlbumBadFormat.hjson
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
APIAppCredentials:
{
ClientID: client-id
ClientSecret: client-secret
}
Account: [email protected]
SecretsBackendType: auto
Jobs:
[
{
SourceFolder: ./testdata
Album: invalid
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
19 changes: 19 additions & 0 deletions internal/config/testdata/invalid-config/AlbumBadKey.hjson
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
APIAppCredentials:
{
ClientID: client-id
ClientSecret: client-secret
}
Account: [email protected]
SecretsBackendType: auto
Jobs:
[
{
SourceFolder: ./testdata
Album: invalid:fooBar
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
19 changes: 19 additions & 0 deletions internal/config/testdata/invalid-config/AlbumEmptyName.hjson
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
APIAppCredentials:
{
ClientID: client-id
ClientSecret: client-secret
}
Account: [email protected]
SecretsBackendType: auto
Jobs:
[
{
SourceFolder: ./testdata
Album: name:
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@
[
{
SourceFolder: ./testdata
CreateAlbums: folderName
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@
[
{
SourceFolder: ./testdata
CreateAlbums: folderName
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@
[
{
SourceFolder: ./testdata
CreateAlbums: folderName
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@
[
{
SourceFolder: ./testdata/non-existent
CreateAlbums: folderName
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
}
3 changes: 1 addition & 2 deletions internal/config/testdata/valid-config/config.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@
[
{
SourceFolder: ./testdata/valid-config
CreateAlbums: folderName
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
APIAppCredentials:
{
ClientID: client-id
ClientSecret: client-secret
}
Account: [email protected]
SecretsBackendType: auto
Jobs:
[
{
SourceFolder: ./testdata/valid-config
Album: auto:folderName
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
APIAppCredentials:
{
ClientID: client-id
ClientSecret: client-secret
}
Account: [email protected]
SecretsBackendType: auto
Jobs:
[
{
SourceFolder: ./testdata/valid-config
Album: name:albumName
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
Loading
Loading