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 the possibility to customize Album names by introducing template as new option for Album name. #431

Merged
merged 10 commits into from
Jan 25, 2024
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# IDE configuration
/.idea/
.history
.vscode/
.devcontainer/

# Binary releases
/dist/
Expand Down
36 changes: 25 additions & 11 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"path/filepath"
"strings"

"github.com/gphotosuploader/gphotos-uploader-cli/internal/upload"
"github.com/hjson/hjson-go/v4"
"github.com/mitchellh/go-homedir"
"github.com/spf13/afero"
Expand Down Expand Up @@ -142,8 +143,10 @@ func (c Config) validateJobs(fs afero.Fs) error {
if !exist {
return fmt.Errorf("folder '%s' does not exist", job.SourceFolder)
}
if job.Album != "" && !isValidAlbum(job.Album) {
return fmt.Errorf("option Album is invalid, '%s", job.Album)

albumErr := ValidateAlbumOption(job.Album)
if job.Album != "" && albumErr != nil {
return albumErr
}
// TODO: Check CreateAlbums for backwards compatibility. It should be removed on version 5.x
if job.Album == "" && !isValidCreateAlbums(job.CreateAlbums) {
Expand Down Expand Up @@ -182,21 +185,32 @@ func isValidAlbumGenerationMethod(method string) bool {
}

// 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
func ValidateAlbumOption(value string) error {
if value == "" {
return fmt.Errorf("option Album could not be empty")
}
if after == "" {
return false

before, after, found := strings.Cut(value, ":")
if !found || after == "" {
return fmt.Errorf("option Album is invalid, '%s.", value)
}

switch before {
case "name":
return true
return nil
case "auto":
return isValidAlbumGenerationMethod(after)
if !isValidAlbumGenerationMethod(after) {
return fmt.Errorf("option Album is invalid: unknown album generation method '%s'", after)
}
return nil
case "template":
err := upload.ValidateAlbumNameTemplate(after)
if err != nil {
return fmt.Errorf("invalid template format: %s", err)
}
return nil
}
return false
return fmt.Errorf("option Album is invalid, '%s", value)
}

// isValidCreateAlbums checks if the value is a valid CreateAlbums option.
Expand Down
4 changes: 4 additions & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func TestFromFile(t *testing.T) {
{"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 Album's template containing token", "testdata/valid-config/configWithAlbumTemplateToken.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},
Expand All @@ -75,6 +76,9 @@ func TestFromFile(t *testing.T) {
{"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 format is invalid", "testdata/invalid-config/AlbumBadFormat.hjson", "", true},
{"Should fail if Album's name auto method is invalid", "testdata/invalid-config/AlbumBadAutoMethod.hjson", "", true},
{"Should fail if Album's name template is invalid", "testdata/invalid-config/AlbumBadNameTemplate.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},
Expand Down
23 changes: 22 additions & 1 deletion internal/config/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,32 @@ type FolderUploadJob struct {
// 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:" and "auto:".
// These are the valid values: "name:", "auto:", "template".
// "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.
// "template": Followed by a template string that can contain the following predefine tokens and functions:
// Tokens:
// %_folderpath% - full path of the folder containing the file.
// %_directory% - name of the folder containing the file.
// %_parent_directory% - Replaced with the name of the parent folder of the file.
// %_day% - day of the month the file was created (in "DD" format).
// %_month% -month the file was created (in "MM" format).
// %_year% - year the file was created (in "YYYY" format).
// %_time% - time the file was created (in "HH:MM:SS" 24-hour format).
// %_time_en% - time the file was created (in "HH:MM:SS AM/PM" 12-hour format).
// Functions:
// $lower(string) - converts the string to lowercase.
// $upper(string) - converts the string to uppercase.
// $sentence(string) - converts the string to sentence case.
// $title(string) - converts the string to title case.
// $regex(string, regex, replacement) - replaces the string with the regex replacement.
// $cutLeft(string, length) - cuts the string from the left.
// $cutRight(string, length) - cuts the string from the right.
//
// Example: "template:%_directory% - %_month%.%_day%.$cutLeft(%_year%,2)"

Album string `json:"Album,omitempty"`

// CreateAlbums is the parameter to create albums on Google Photos.
Expand Down
19 changes: 19 additions & 0 deletions internal/config/testdata/invalid-config/AlbumBadNameTemplate.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: "template:$ssd("
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
Album: "template:%_year%"
DeleteAfterUpload: false
IncludePatterns: []
ExcludePatterns: []
}
]
}
Loading
Loading