diff --git a/.gitignore b/.gitignore index 959b2c8..4abbde2 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,6 @@ data/ venv/ .venv/ +logs/ + site/ \ No newline at end of file diff --git a/cmd/splashscreen-changer/args.go b/cmd/splashscreen-changer/args.go index 49b3636..d370708 100644 --- a/cmd/splashscreen-changer/args.go +++ b/cmd/splashscreen-changer/args.go @@ -37,6 +37,7 @@ func printHelp() { fmt.Println() fmt.Println("GitHub: https://github.com/tomacheese/splashscreen-changer") + fmt.Println("Booth: https://tomachi.booth.pm/items/6284870") } func getSourcePath(config *Config) (string, error) { diff --git a/cmd/splashscreen-changer/config.go b/cmd/splashscreen-changer/config.go index 7ac47b8..94ee262 100644 --- a/cmd/splashscreen-changer/config.go +++ b/cmd/splashscreen-changer/config.go @@ -20,6 +20,9 @@ type Config struct { Width int `yaml:"width" help:"Width of the destination image" default:"800"` Height int `yaml:"height" help:"Height of the destination image" default:"450"` } `yaml:"destination" required:"true"` + Log struct { + Path string `yaml:"path" help:"Path to the log file" default:""` + } `yaml:"log"` } // 設定ファイルを読み込む diff --git a/cmd/splashscreen-changer/log.go b/cmd/splashscreen-changer/log.go new file mode 100644 index 0000000..78670f0 --- /dev/null +++ b/cmd/splashscreen-changer/log.go @@ -0,0 +1,30 @@ +package main + +import ( + "os" + "path/filepath" + "time" +) + +func getLogFilePath(logParamPath *string) string { + // ログフォルダパスは環境変数 LOG_PATH または引数 -log で指定し、指定されていない場合は "logs/" とする。 + // "logs/" の場所は、実行ファイルと同じディレクトリにあるものとする。go runで実行する場合は、カレントディレクトリにあるものとする。 + // ログファイルのファイル名は、yyyy-mm-dd.log とする。 + if *logParamPath != "" { + return *logParamPath + } + + date := time.Now().Format("2006-01-02") + + exePath, err := os.Executable() + if err != nil { + return filepath.Join("logs", date+".log") + } + + if isGoRun() { + return filepath.Join("logs", date+".log") + } + + exeDir := filepath.Dir(exePath) + return filepath.Join(exeDir, "logs", date+".log") +} diff --git a/cmd/splashscreen-changer/main.go b/cmd/splashscreen-changer/main.go index d3f6ac2..146ecf3 100644 --- a/cmd/splashscreen-changer/main.go +++ b/cmd/splashscreen-changer/main.go @@ -5,6 +5,8 @@ import ( "fmt" "image" "image/png" + "io" + "log" "os" "path/filepath" "strings" @@ -184,81 +186,96 @@ func main() { // バージョン情報を表示する if *versionFlag { - fmt.Println("splashscreen-changer") - fmt.Println("|- Version", GetAppVersion()) - fmt.Println("|- Build date:", GetAppDate()) + log.Println("splashscreen-changer") + log.Println("|- Version", GetAppVersion()) + log.Println("|- Build date:", GetAppDate()) return } // 設定ファイルを読み込む。 configPath := getConfigPath(configParamPath) - fmt.Println("Loading config file:", configPath) + log.Println("Loading config file:", configPath) config, err := LoadConfig(configPath) if err != nil { - fmt.Println("Failed to load configuration file:", err) + log.Println("Failed to load configuration file:", err) return } + // ログファイルを開く + path := getLogFilePath(&config.Log.Path) + // ログファイルの親ディレクトリが存在しない場合は作成する + if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil { + log.Fatal(err) + } + file, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644) + if err != nil { + log.Fatal(err) + } + + defer file.Close() + mw := io.MultiWriter(os.Stdout, file) + log.SetOutput(mw) + sourcePath, err := getSourcePath(config) if err != nil { - fmt.Println("Failed to obtain source path") - fmt.Println() - fmt.Println("The following steps are used to obtain the source paths. This error occurs because the following steps could not be taken to obtain the source path.") - fmt.Println("1. Environment variable SOURCE_PATH. If this is not set, the following steps are taken.") - fmt.Println("2. source.path in Configuration file. If this is not set, the following steps are taken.") - fmt.Println("3. Check if the VRChat folder exists in the Pictures folder in the user folder.") - fmt.Println("If the VRChat folder exists, the path to the VRChat folder is used as the source path.") + log.Println("Failed to obtain source path") + log.Println() + log.Println("The following steps are used to obtain the source paths. This error occurs because the following steps could not be taken to obtain the source path.") + log.Println("1. Environment variable SOURCE_PATH. If this is not set, the following steps are taken.") + log.Println("2. source.path in Configuration file. If this is not set, the following steps are taken.") + log.Println("3. Check if the VRChat folder exists in the Pictures folder in the user folder.") + log.Println("If the VRChat folder exists, the path to the VRChat folder is used as the source path.") return } destinationPath, err := getDestinationPath(config) if err != nil { - fmt.Println("Failed to obtain destination path") - fmt.Println() - fmt.Println("The following steps are used to obtain the destination paths. This error occurs because the following steps could not be taken to obtain the destination path.") - fmt.Println("1. Environment variable DESTINATION_PATH. If this is not set, the following steps are taken.") - fmt.Println("2. destination.path in Configuration file. If this is not set, the following steps are taken.") - fmt.Println("3. Get the installation destination folder of VRChat from the Steam library folder.") - fmt.Println("If the EasyAntiCheat folder exists in the VRChat folder, the path to the VRChat folder is used as the destination path.") + log.Println("Failed to obtain destination path") + log.Println() + log.Println("The following steps are used to obtain the destination paths. This error occurs because the following steps could not be taken to obtain the destination path.") + log.Println("1. Environment variable DESTINATION_PATH. If this is not set, the following steps are taken.") + log.Println("2. destination.path in Configuration file. If this is not set, the following steps are taken.") + log.Println("3. Get the installation destination folder of VRChat from the Steam library folder.") + log.Println("If the EasyAntiCheat folder exists in the VRChat folder, the path to the VRChat folder is used as the destination path.") return } // 設定値を表示する - fmt.Printf("Source Path: %s\n", sourcePath) - fmt.Printf("Source Recursive: %t\n", config.Source.Recursive) - fmt.Printf("Destination Path: %s\n", destinationPath) - fmt.Printf("Destination Width: %d\n", config.Destination.Width) - fmt.Printf("Destination Height: %d\n", config.Destination.Height) + log.Printf("Source Path: %s\n", sourcePath) + log.Printf("Source Recursive: %t\n", config.Source.Recursive) + log.Printf("Destination Path: %s\n", destinationPath) + log.Printf("Destination Width: %d\n", config.Destination.Width) + log.Printf("Destination Height: %d\n", config.Destination.Height) // ソースディレクトリ以下のPNGファイルをリストする files, err := listPNGFiles(sourcePath, config.Source.Recursive) if err != nil { - fmt.Println("Error:", err) + log.Println("Error:", err) return } // ランダムで1つのファイルを選択する if len(files) == 0 { - fmt.Println("No PNG files found") + log.Println("No PNG files found") return } pickedFile, err := pickRandomFile(files) if err != nil { - fmt.Println("Error:", err) + log.Println("Error:", err) return } - fmt.Println("Picked file:", pickedFile) + log.Println("Picked file:", pickedFile) // ファイルをリサイズして EasyAntiCheat ディレクトリに保存する destFile := filepath.Join(destinationPath, "EasyAntiCheat", "SplashScreen.png") err = resizePNGFile(pickedFile, destFile, config.Destination.Width, config.Destination.Height) if err != nil { - fmt.Println("Error:", err) + log.Println("Error:", err) return } - fmt.Println("Resized file saved to:", destFile) + log.Println("Resized file saved to:", destFile) } diff --git a/docs/settings/file.md b/docs/settings/file.md index d0f40c2..be99db4 100644 --- a/docs/settings/file.md +++ b/docs/settings/file.md @@ -102,6 +102,16 @@ この設定項目の値と、`destination.width` の値から、選択された画像を自動的にクロップ・リサイズします。具体的な挙動については、後述する「クロップ・リサイズの仕様」をご覧ください。 +### log.path + +| 必須か | デフォルト値 | 環境変数 | +| :- | :- | :- | +| いいえ | `logs/` | `LOG_PATH` | + +ログファイルの出力先フォルダパスを指定します。 + +指定しない場合、実行ファイルと同じ階層の `logs` フォルダに `yyyy-MM-dd.log` 形式で出力します。 + ## クロップ・リサイズの仕様 選択された画像は、設定ファイルで指定された `destination.width` と `destination.height` の値に基づき、自動的にクロップおよびリサイズされます。