This commit is contained in:
2025-05-24 23:56:41 -05:00
parent 9b2dbdcb5a
commit 6c95387141
2 changed files with 131 additions and 36 deletions

163
main.go
View File

@@ -5,11 +5,14 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io"
"log" "log"
"os" "os"
"path" "path"
"regexp"
"strings" "strings"
"text/template" "text/template"
"time"
"slices" "slices"
@@ -24,14 +27,13 @@ type DownloadDirectoryComplete struct {
Username string `json:"username"` Username string `json:"username"`
} }
var printLogs = true
func (d *DownloadDirectoryComplete) WarnVersion() { func (d *DownloadDirectoryComplete) WarnVersion() {
supportedVersions := []int{ 0 } supportedVersions := []int{ 0 }
if !slices.Contains(supportedVersions, d.Version) { if !slices.Contains(supportedVersions, d.Version) {
supportedVersionsStr := strings.Trim(strings.Join(strings.Fields(fmt.Sprint(supportedVersions)), ","), "[]") supportedVersionsStr := strings.Trim(strings.Join(strings.Fields(fmt.Sprint(supportedVersions)), ","), "[]")
log.Println("WARNING: Given data version not supported.") log.Printf("WARNING: Given data version not supported. Supported versions: %s. Given version: %d. Continuing, but there may be demons\n", supportedVersionsStr, d.Version)
log.Printf( "WARNING: Supported versions: %s\n", supportedVersionsStr)
log.Printf( "WARNING: Given version: %d\n", d.Version)
log.Println("WARNING: Continuing, but there may be demons")
} }
} }
@@ -43,10 +45,6 @@ func main() {
} }
func mainErr() error { func mainErr() error {
if len(os.Args) < 2 {
return errors.New("missing data argument")
}
if _, err := os.Stat(".env"); err == nil { if _, err := os.Stat(".env"); err == nil {
if err := godotenv.Load(".env"); err != nil { if err := godotenv.Load(".env"); err != nil {
return err return err
@@ -60,22 +58,41 @@ func mainErr() error {
return err return err
} }
} }
directory, err := getDir()
if err != nil {
return err
}
return mainPath(directory)
}
func getDir() (string, error) {
if len(os.Args) > 1 {
return os.Args[1], nil
} else {
dataArg := os.Getenv("SLSKD_SCRIPT_DATA")
var data DownloadDirectoryComplete
if err := json.Unmarshal([]byte(dataArg), &data); err != nil {
return "", err
}
data.WarnVersion()
printLogs = false
return data.LocalDirectoryName, nil
}
}
func mainPath(directory string) error {
if err := testEnv(); err != nil { if err := testEnv(); err != nil {
return err return err
} }
// parse $DATA // parse $DATA
log.Println("Parsing slskd data")
dataArg := os.Args[1]
var data DownloadDirectoryComplete
if err := json.Unmarshal([]byte(dataArg), &data); err != nil {
return err
}
data.WarnVersion() files, err := os.ReadDir(directory)
files, err := os.ReadDir(data.LocalDirectoryName)
if err != nil { if err != nil {
return err return err
} }
@@ -84,18 +101,22 @@ func mainErr() error {
} }
// read tags // read tags
tags, _ := getTagsInDir(data.LocalDirectoryName) tags, _ := getTagsInDir(directory)
// move files // move files
if tags != nil { if tags != nil {
log.Println("found tags, moving based on tags") if printLogs {
err := doMoveTags(data.LocalDirectoryName, tags) log.Println("found tags, moving based on tags")
}
err := doMoveTags(directory, tags)
if err != nil { if err != nil {
return err return err
} }
} else { } else {
log.Println("no tags found, moving to notags folder") if printLogs {
err := doMoveNotags(data.LocalDirectoryName) log.Println("no tags found, moving to notags folder")
}
err := doMoveNotags(directory)
if err != nil { if err != nil {
return err return err
} }
@@ -134,27 +155,21 @@ func getTagsInDir(directory string) (tag.Metadata, error) {
return nil, err return nil, err
} }
log.Printf("DIRECTORY: %s\n", directory)
for _, file := range files { for _, file := range files {
fname := path.Join(directory, file.Name()) fname := path.Join(directory, file.Name())
log.Printf(" FILE: %s\n", fname)
if file.IsDir() { if file.IsDir() {
log.Print(" IS DIRECTORY\n")
continue continue
} }
file, err := os.Open(fname) file, err := os.Open(fname)
if err != nil { if err != nil {
log.Print(" CANT OPEN\n")
continue continue
} }
defer file.Close() defer file.Close()
tags, err := tag.ReadFrom(file) tags, err := tag.ReadFrom(file)
if err != nil { if err != nil {
log.Print(" NO TAGS\n")
continue continue
} }
@@ -171,7 +186,7 @@ func doMoveNotags(directory string) error {
_, folderName := path.Split(directory) _, folderName := path.Split(directory)
toDirectory := path.Join(newRoot, folderName) toDirectory := path.Join(newRoot, folderName)
return rename(directory, toDirectory) return move(directory, toDirectory)
} }
func doMoveTags(directory string, albumTags tag.Metadata) error { func doMoveTags(directory string, albumTags tag.Metadata) error {
@@ -186,6 +201,10 @@ func doMoveTags(directory string, albumTags tag.Metadata) error {
continue continue
} }
if file.Name()[0] == '.' {
continue
}
fname := path.Join(directory, file.Name()) fname := path.Join(directory, file.Name())
file, err := os.Open(fname) file, err := os.Open(fname)
if err != nil { if err != nil {
@@ -211,6 +230,10 @@ func doMoveTags(directory string, albumTags tag.Metadata) error {
} }
for _, file := range files { for _, file := range files {
if file.Name()[0] == '.' {
continue
}
fname := path.Join(directory, file.Name()) fname := path.Join(directory, file.Name())
err = doMoveTaggedPathonly(fname, albumTags) err = doMoveTaggedPathonly(fname, albumTags)
@@ -220,7 +243,27 @@ func doMoveTags(directory string, albumTags tag.Metadata) error {
} }
// delete folder // delete folder
return os.Remove(directory) files, err = os.ReadDir(directory)
if err != nil {
return err
}
if len(files) == 0 {
return os.Remove(directory)
}
// some files remain, wait a second to see if they go away
time.Sleep(1 * time.Second)
files, err = os.ReadDir(directory)
if err != nil {
return err
}
if len(files) == 0 {
return os.Remove(directory)
}
return nil
} }
type LibTemplatePath struct { type LibTemplatePath struct {
@@ -291,7 +334,7 @@ func doMoveTagged(filePath string, fileTags tag.Metadata) error {
libFile := path.Join(libRoot, bufPath.String(), bufFile.String() + fileExt) libFile := path.Join(libRoot, bufPath.String(), bufFile.String() + fileExt)
return rename(filePath, libFile) return move(filePath, libFile)
} }
func doMoveTaggedPathonly(filePath string, albumTags tag.Metadata) error { func doMoveTaggedPathonly(filePath string, albumTags tag.Metadata) error {
@@ -316,7 +359,7 @@ func doMoveTaggedPathonly(filePath string, albumTags tag.Metadata) error {
libFile := path.Join(libRoot, bufPath.String(), fileName) libFile := path.Join(libRoot, bufPath.String(), fileName)
return rename(filePath, libFile) return move(filePath, libFile)
} }
func ensureDirectoryPath(filePath string) error { func ensureDirectoryPath(filePath string) error {
@@ -324,7 +367,8 @@ func ensureDirectoryPath(filePath string) error {
return os.MkdirAll(directoryPath, 0777) return os.MkdirAll(directoryPath, 0777)
} }
func rename(from, to string) error { func move(from, to string) error {
to = cleanPath(to)
if _, err := os.Stat(to); err == nil { if _, err := os.Stat(to); err == nil {
return fmt.Errorf("file already exists: %s", to) return fmt.Errorf("file already exists: %s", to)
} }
@@ -333,6 +377,55 @@ func rename(from, to string) error {
return err return err
} }
fmt.Printf("renaming \"%s\" => \"%s\"\n", from, to) if printLogs {
return os.Rename(from, to) log.Printf("moving \"%s\" => \"%s\"\n", from, to)
}
// os.Rename gives error "invalid cross-device link"
if err := os.Rename(from, to); err != nil {
return moveFile(from, to)
}
return nil
}
func moveFile(source, destination string) (err error) {
src, err := os.Open(source)
if err != nil {
return err
}
defer src.Close()
fi, err := src.Stat()
if err != nil {
return err
}
flag := os.O_WRONLY | os.O_CREATE | os.O_TRUNC
perm := fi.Mode() & os.ModePerm
dst, err := os.OpenFile(destination, flag, perm)
if err != nil {
return err
}
defer dst.Close()
_, err = io.Copy(dst, src)
if err != nil {
dst.Close()
os.Remove(destination)
return err
}
err = dst.Close()
if err != nil {
return err
}
err = src.Close()
if err != nil {
return err
}
err = os.Remove(source)
if err != nil {
return err
}
return nil
}
func cleanPath(str string) string {
re := regexp.MustCompile(`[<>:"|?*]`)
return re.ReplaceAllString(str, "")
} }

View File

@@ -2,4 +2,6 @@
move music files in a directory to an organized folder based on tags move music files in a directory to an organized folder based on tags
for now, this only supports slskd's $DATA arg as input for now, this only supports slskd's ~~$DATA arg as input~~ SLSKD_SCRIPT_DATA environment variable as input
(why is this shit not documented well)