v1.0.4
This commit is contained in:
163
main.go
163
main.go
@@ -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, "")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user