Verbose
This commit is contained in:
parent
b1eed73dec
commit
c160a0a24a
97
main.go
97
main.go
@ -12,6 +12,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -26,20 +27,31 @@ type FileQuality struct {
|
|||||||
ReleaseInfo string // Human-readable release info for logging
|
ReleaseInfo string // Human-readable release info for logging
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ProcessResult struct {
|
||||||
|
BaseFile string
|
||||||
|
SpaceRecovered int64
|
||||||
|
Processed bool
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
logFile *os.File
|
logFile *os.File
|
||||||
armed bool
|
armed bool
|
||||||
dryRun bool
|
dryRun bool
|
||||||
forceMode bool // Force processing even if releases appear different
|
forceMode bool // Force processing even if releases appear different
|
||||||
logWriter *bufio.Writer
|
verbose bool // Control console output
|
||||||
version = "1.1.0"
|
concurrency int // Number of concurrent goroutines
|
||||||
dateFormat = "2006-01-02 15:04:05"
|
logWriter *bufio.Writer
|
||||||
|
version = "1.2.0"
|
||||||
|
dateFormat = "2006-01-02 15:04:05"
|
||||||
|
logMutex sync.Mutex
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
flag.BoolVar(&armed, "armed", false, "Enable actual file operations (rename/delete)")
|
flag.BoolVar(&armed, "armed", false, "Enable actual file operations (rename/delete)")
|
||||||
flag.BoolVar(&dryRun, "dry-run", false, "Simulate operations without making changes")
|
flag.BoolVar(&dryRun, "dry-run", false, "Simulate operations without making changes")
|
||||||
flag.BoolVar(&forceMode, "force", false, "Force processing even if releases appear different")
|
flag.BoolVar(&forceMode, "force", false, "Force processing even if releases appear different")
|
||||||
|
flag.BoolVar(&verbose, "verbose", false, "Show detailed logs on console")
|
||||||
|
flag.IntVar(&concurrency, "concurrency", 4, "Number of concurrent file groups to process")
|
||||||
flag.Usage = func() {
|
flag.Usage = func() {
|
||||||
fmt.Fprintf(flag.CommandLine.Output(), "FLAC Duplicate Cleaner v%s\n", version)
|
fmt.Fprintf(flag.CommandLine.Output(), "FLAC Duplicate Cleaner v%s\n", version)
|
||||||
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options] [directory]\n", os.Args[0])
|
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options] [directory]\n", os.Args[0])
|
||||||
@ -47,6 +59,7 @@ func init() {
|
|||||||
fmt.Println("\nBy default, runs in dry-run mode showing what would be done")
|
fmt.Println("\nBy default, runs in dry-run mode showing what would be done")
|
||||||
fmt.Println("Specify -armed to actually perform operations")
|
fmt.Println("Specify -armed to actually perform operations")
|
||||||
fmt.Println("Use -force to process files even if they appear to be from different releases")
|
fmt.Println("Use -force to process files even if they appear to be from different releases")
|
||||||
|
fmt.Println("Add -verbose to show detailed logs on console")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,16 +70,33 @@ func initLogging(logFilename string) error {
|
|||||||
return fmt.Errorf("failed to open log file: %w", err)
|
return fmt.Errorf("failed to open log file: %w", err)
|
||||||
}
|
}
|
||||||
logWriter = bufio.NewWriter(logFile)
|
logWriter = bufio.NewWriter(logFile)
|
||||||
log.SetOutput(io.MultiWriter(os.Stdout, logWriter))
|
|
||||||
|
// Set log output based on verbose flag
|
||||||
|
if verbose {
|
||||||
|
log.SetOutput(io.MultiWriter(os.Stdout, logWriter))
|
||||||
|
} else {
|
||||||
|
log.SetOutput(logWriter)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func closeLogging() {
|
func closeLogging() {
|
||||||
|
logMutex.Lock()
|
||||||
|
defer logMutex.Unlock()
|
||||||
logWriter.Flush()
|
logWriter.Flush()
|
||||||
logFile.Close()
|
logFile.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeLog(message string) {
|
func writeLog(message string) {
|
||||||
|
logMutex.Lock()
|
||||||
|
defer logMutex.Unlock()
|
||||||
|
log.Println(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func printFinalStats(message string) {
|
||||||
|
// Always print final stats to console regardless of verbose setting
|
||||||
|
fmt.Println(message)
|
||||||
log.Println(message)
|
log.Println(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,6 +178,7 @@ func getReleaseInfo(filePath string) string {
|
|||||||
func findDuplicateFiles(rootDir string) (map[string][]FileQuality, error) {
|
func findDuplicateFiles(rootDir string) (map[string][]FileQuality, error) {
|
||||||
dupePattern := regexp.MustCompile(`(?i)(.+)( \(\d+\))\.flac$`)
|
dupePattern := regexp.MustCompile(`(?i)(.+)( \(\d+\))\.flac$`)
|
||||||
fileGroups := make(map[string][]FileQuality)
|
fileGroups := make(map[string][]FileQuality)
|
||||||
|
var filesMutex sync.Mutex
|
||||||
|
|
||||||
err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
|
err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -165,6 +196,9 @@ func findDuplicateFiles(rootDir string) (map[string][]FileQuality, error) {
|
|||||||
|
|
||||||
// Check if file matches the duplicate pattern
|
// Check if file matches the duplicate pattern
|
||||||
matches := dupePattern.FindStringSubmatch(path)
|
matches := dupePattern.FindStringSubmatch(path)
|
||||||
|
filesMutex.Lock()
|
||||||
|
defer filesMutex.Unlock()
|
||||||
|
|
||||||
if len(matches) > 1 {
|
if len(matches) > 1 {
|
||||||
baseFile := matches[1] + ".flac"
|
baseFile := matches[1] + ".flac"
|
||||||
fileInfo, err := os.Stat(path)
|
fileInfo, err := os.Stat(path)
|
||||||
@ -384,6 +418,7 @@ func main() {
|
|||||||
if forceMode {
|
if forceMode {
|
||||||
writeLog("FORCE MODE: Will process even if releases appear different!")
|
writeLog("FORCE MODE: Will process even if releases appear different!")
|
||||||
}
|
}
|
||||||
|
writeLog(fmt.Sprintf("CONCURRENCY: Processing %d file groups simultaneously", concurrency))
|
||||||
writeLog("===============================")
|
writeLog("===============================")
|
||||||
|
|
||||||
// Check mediainfo is available
|
// Check mediainfo is available
|
||||||
@ -406,20 +441,48 @@ func main() {
|
|||||||
writeLog("")
|
writeLog("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process file groups concurrently with limited goroutines
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
results := make(chan ProcessResult, len(fileGroups))
|
||||||
|
semaphore := make(chan struct{}, concurrency)
|
||||||
|
|
||||||
|
for baseFile, group := range fileGroups {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(bf string, g []FileQuality) {
|
||||||
|
defer wg.Done()
|
||||||
|
semaphore <- struct{}{} // Acquire semaphore
|
||||||
|
defer func() { <-semaphore }() // Release semaphore
|
||||||
|
|
||||||
|
spaceRecovered, processed := processFileGroup(bf, g)
|
||||||
|
results <- ProcessResult{
|
||||||
|
BaseFile: bf,
|
||||||
|
SpaceRecovered: spaceRecovered,
|
||||||
|
Processed: processed,
|
||||||
|
}
|
||||||
|
}(baseFile, group)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close results channel once all goroutines complete
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(results)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Process results
|
||||||
var totalRecoverableSpace int64
|
var totalRecoverableSpace int64
|
||||||
processedGroups := 0
|
processedGroups := 0
|
||||||
|
|
||||||
for baseFile, group := range fileGroups {
|
for result := range results {
|
||||||
spaceRecovered, processed := processFileGroup(baseFile, group)
|
if result.Processed {
|
||||||
if processed {
|
|
||||||
processedGroups++
|
processedGroups++
|
||||||
totalRecoverableSpace += spaceRecovered
|
totalRecoverableSpace += result.SpaceRecovered
|
||||||
}
|
}
|
||||||
writeLog("")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
writeLog(fmt.Sprintf("Processed %d of %d file groups", processedGroups, len(fileGroups)))
|
// Final stats - these always print to console regardless of verbose setting
|
||||||
writeLog(fmt.Sprintf("Total recoverable space: %s", bytesToHumanReadable(totalRecoverableSpace)))
|
finalTime := getCurrentTime()
|
||||||
writeLog(fmt.Sprintf("Cleanup completed at %s", getCurrentTime()))
|
printFinalStats(fmt.Sprintf("Processed %d of %d file groups", processedGroups, len(fileGroups)))
|
||||||
writeLog("===============================")
|
printFinalStats(fmt.Sprintf("Total recoverable space: %s", bytesToHumanReadable(totalRecoverableSpace)))
|
||||||
|
printFinalStats(fmt.Sprintf("Cleanup completed at %s", finalTime))
|
||||||
|
printFinalStats("===============================")
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user