multi release
This commit is contained in:
		
							
								
								
									
										227
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										227
									
								
								main.go
									
									
									
									
									
								
							@@ -16,30 +16,37 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type FileQuality struct {
 | 
					type FileQuality struct {
 | 
				
			||||||
	Path     string
 | 
						Path         string
 | 
				
			||||||
	Bitrate  int
 | 
						Bitrate      int
 | 
				
			||||||
	Bitdepth int
 | 
						Bitdepth     int
 | 
				
			||||||
	Size     int64
 | 
						Size         int64
 | 
				
			||||||
 | 
						ReleaseID    string // MusicBrainz release ID or other unique identifier
 | 
				
			||||||
 | 
						ReleaseYear  string // Release year from metadata
 | 
				
			||||||
 | 
						CatalogNum   string // Catalog number
 | 
				
			||||||
 | 
						ReleaseInfo  string // Human-readable release info for logging
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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
 | 
				
			||||||
	logWriter  *bufio.Writer
 | 
						logWriter  *bufio.Writer
 | 
				
			||||||
	version    = "1.0.0"
 | 
						version    = "1.1.0"
 | 
				
			||||||
	dateFormat = "2006-01-02 15:04:05"
 | 
						dateFormat = "2006-01-02 15:04:05"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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.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])
 | 
				
			||||||
		flag.PrintDefaults()
 | 
							flag.PrintDefaults()
 | 
				
			||||||
		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")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -67,12 +74,12 @@ func getCurrentTime() string {
 | 
				
			|||||||
	return time.Now().Format(dateFormat)
 | 
						return time.Now().Format(dateFormat)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getMediaInfo(filePath string) (int, int, error) {
 | 
					func getMediaInfo(filePath string) (int, int, string, string, string, error) {
 | 
				
			||||||
	// Get bitrate
 | 
						// Get bitrate
 | 
				
			||||||
	bitrateCmd := exec.Command("mediainfo", "--Output=Audio;%BitRate%", filePath)
 | 
						bitrateCmd := exec.Command("mediainfo", "--Output=Audio;%BitRate%", filePath)
 | 
				
			||||||
	bitrateOut, err := bitrateCmd.Output()
 | 
						bitrateOut, err := bitrateCmd.Output()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return 0, 0, fmt.Errorf("mediainfo bitrate failed: %w", err)
 | 
							return 0, 0, "", "", "", fmt.Errorf("mediainfo bitrate failed: %w", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	bitrateStr := strings.TrimSpace(string(bitrateOut))
 | 
						bitrateStr := strings.TrimSpace(string(bitrateOut))
 | 
				
			||||||
	bitrate, err := strconv.Atoi(bitrateStr)
 | 
						bitrate, err := strconv.Atoi(bitrateStr)
 | 
				
			||||||
@@ -84,7 +91,7 @@ func getMediaInfo(filePath string) (int, int, error) {
 | 
				
			|||||||
	bitdepthCmd := exec.Command("mediainfo", "--Output=Audio;%BitDepth%", filePath)
 | 
						bitdepthCmd := exec.Command("mediainfo", "--Output=Audio;%BitDepth%", filePath)
 | 
				
			||||||
	bitdepthOut, err := bitdepthCmd.Output()
 | 
						bitdepthOut, err := bitdepthCmd.Output()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return 0, 0, fmt.Errorf("mediainfo bitdepth failed: %w", err)
 | 
							return 0, 0, "", "", "", fmt.Errorf("mediainfo bitdepth failed: %w", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	bitdepthStr := strings.TrimSpace(string(bitdepthOut))
 | 
						bitdepthStr := strings.TrimSpace(string(bitdepthOut))
 | 
				
			||||||
	bitdepth, err := strconv.Atoi(bitdepthStr)
 | 
						bitdepth, err := strconv.Atoi(bitdepthStr)
 | 
				
			||||||
@@ -92,7 +99,50 @@ func getMediaInfo(filePath string) (int, int, error) {
 | 
				
			|||||||
		bitdepth = 0
 | 
							bitdepth = 0
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return bitrate, bitdepth, nil
 | 
						// Get MusicBrainz release ID
 | 
				
			||||||
 | 
						releaseIDCmd := exec.Command("mediainfo", "--Output=General;%MUSICBRAINZ_RELEASETRACKID%", filePath)
 | 
				
			||||||
 | 
						releaseIDOut, err := releaseIDCmd.Output()
 | 
				
			||||||
 | 
						var releaseID string
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							releaseID = strings.TrimSpace(string(releaseIDOut))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get release year
 | 
				
			||||||
 | 
						yearCmd := exec.Command("mediainfo", "--Output=General;%Released_Date%", filePath)
 | 
				
			||||||
 | 
						yearOut, err := yearCmd.Output()
 | 
				
			||||||
 | 
						var year string
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							year = strings.TrimSpace(string(yearOut))
 | 
				
			||||||
 | 
							// If no released date, try recorded date
 | 
				
			||||||
 | 
							if year == "" {
 | 
				
			||||||
 | 
								yearCmd = exec.Command("mediainfo", "--Output=General;%Recorded_Date%", filePath)
 | 
				
			||||||
 | 
								yearOut, err := yearCmd.Output()
 | 
				
			||||||
 | 
								if err == nil {
 | 
				
			||||||
 | 
									year = strings.TrimSpace(string(yearOut))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Extract just the year if it's a full date
 | 
				
			||||||
 | 
							if len(year) >= 4 {
 | 
				
			||||||
 | 
								year = year[0:4]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get catalog number
 | 
				
			||||||
 | 
						catalogCmd := exec.Command("mediainfo", "--Output=General;%CATALOGNUMBER%", filePath)
 | 
				
			||||||
 | 
						catalogOut, err := catalogCmd.Output()
 | 
				
			||||||
 | 
						var catalogNum string
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							catalogNum = strings.TrimSpace(string(catalogOut))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return bitrate, bitdepth, releaseID, year, catalogNum, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getReleaseInfo(filePath string) string {
 | 
				
			||||||
 | 
						// Get a summary of release info for logging
 | 
				
			||||||
 | 
						infoCmd := exec.Command("mediainfo", "--Output=General;Album: %Album%, Released: %Released_Date%, Recorded: %Recorded_Date%, Label: %Label%, CatalogNum: %CATALOGNUMBER%", filePath)
 | 
				
			||||||
 | 
						infoOut, _ := infoCmd.Output()
 | 
				
			||||||
 | 
						return strings.TrimSpace(string(infoOut))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func findDuplicateFiles(rootDir string) (map[string][]FileQuality, error) {
 | 
					func findDuplicateFiles(rootDir string) (map[string][]FileQuality, error) {
 | 
				
			||||||
@@ -162,74 +212,143 @@ func bytesToHumanReadable(bytes int64) string {
 | 
				
			|||||||
	return fmt.Sprintf("%.1f %ciB", float64(bytes)/float64(div), "KMGTPE"[exp])
 | 
						return fmt.Sprintf("%.1f %ciB", float64(bytes)/float64(div), "KMGTPE"[exp])
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func processFileGroup(baseFile string, files []FileQuality) {
 | 
					func processFileGroup(baseFile string, files []FileQuality) (int64, bool) {
 | 
				
			||||||
	writeLog(fmt.Sprintf("Processing group for: %s", baseFile))
 | 
						writeLog(fmt.Sprintf("Processing group for: %s", baseFile))
 | 
				
			||||||
 | 
						processed := false
 | 
				
			||||||
 | 
						var spaceRecovered int64 = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get quality info for all files
 | 
						// Get quality info for all files
 | 
				
			||||||
	var bestFile FileQuality
 | 
						var bestFile FileQuality
 | 
				
			||||||
	var totalSize int64
 | 
						var totalSize int64
 | 
				
			||||||
 | 
						releaseMap := make(map[string][]FileQuality)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i := range files {
 | 
						for i := range files {
 | 
				
			||||||
		bitrate, bitdepth, err := getMediaInfo(files[i].Path)
 | 
							bitrate, bitdepth, releaseID, year, catalogNum, err := getMediaInfo(files[i].Path)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			writeLog(fmt.Sprintf("  - Error getting media info for %s: %v", files[i].Path, err))
 | 
								writeLog(fmt.Sprintf("  - Error getting media info for %s: %v", files[i].Path, err))
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		files[i].Bitrate = bitrate
 | 
							files[i].Bitrate = bitrate
 | 
				
			||||||
		files[i].Bitdepth = bitdepth
 | 
							files[i].Bitdepth = bitdepth
 | 
				
			||||||
 | 
							files[i].ReleaseID = releaseID
 | 
				
			||||||
 | 
							files[i].ReleaseYear = year
 | 
				
			||||||
 | 
							files[i].CatalogNum = catalogNum
 | 
				
			||||||
 | 
							files[i].ReleaseInfo = getReleaseInfo(files[i].Path)
 | 
				
			||||||
		totalSize += files[i].Size
 | 
							totalSize += files[i].Size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		writeLog(fmt.Sprintf("  - Found: %s (Bitrate: %d, Bitdepth: %d, Size: %d bytes)",
 | 
							// Create a unique key for grouping by release
 | 
				
			||||||
			files[i].Path, files[i].Bitrate, files[i].Bitdepth, files[i].Size))
 | 
							releaseKey := releaseID
 | 
				
			||||||
 | 
							if releaseKey == "" {
 | 
				
			||||||
		// Determine the best file using quality then size as tiebreaker
 | 
								// Fallback if no MusicBrainz ID
 | 
				
			||||||
		if files[i].Bitrate > bestFile.Bitrate ||
 | 
								releaseKey = fmt.Sprintf("%s-%s", year, catalogNum)
 | 
				
			||||||
			(files[i].Bitrate == bestFile.Bitrate && files[i].Bitdepth > bestFile.Bitdepth) ||
 | 
					 | 
				
			||||||
			(files[i].Bitrate == bestFile.Bitrate && files[i].Bitdepth == bestFile.Bitdepth && files[i].Size > bestFile.Size) {
 | 
					 | 
				
			||||||
			bestFile = files[i]
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							releaseMap[releaseKey] = append(releaseMap[releaseKey], files[i])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							writeLog(fmt.Sprintf("  - Found: %s (Bitrate: %d, Bitdepth: %d, Size: %s, Year: %s, CatalogNum: %s)",
 | 
				
			||||||
 | 
								files[i].Path, files[i].Bitrate, files[i].Bitdepth, 
 | 
				
			||||||
 | 
								bytesToHumanReadable(files[i].Size), files[i].ReleaseYear, files[i].CatalogNum))
 | 
				
			||||||
 | 
							writeLog(fmt.Sprintf("    Release info: %s", files[i].ReleaseInfo))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if bestFile.Path == "" {
 | 
						// Check if files are from different releases
 | 
				
			||||||
		writeLog("  - No valid files found in group")
 | 
						if len(releaseMap) > 1 && !forceMode {
 | 
				
			||||||
		return
 | 
							writeLog("  ! Files appear to be from different releases. Skipping group (use -force to override).")
 | 
				
			||||||
	}
 | 
							for releaseKey, releaseFiles := range releaseMap {
 | 
				
			||||||
 | 
								writeLog(fmt.Sprintf("    Release group: %s", releaseKey))
 | 
				
			||||||
	writeLog(fmt.Sprintf("  -> Keeping: %s (Bitrate: %d, Bitdepth: %d, Size: %d bytes)",
 | 
								for _, file := range releaseFiles {
 | 
				
			||||||
		bestFile.Path, bestFile.Bitrate, bestFile.Bitdepth, bestFile.Size))
 | 
									writeLog(fmt.Sprintf("      - %s", file.Path))
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Rename best file to original name if it's not already
 | 
					 | 
				
			||||||
	if bestFile.Path != baseFile {
 | 
					 | 
				
			||||||
		action := "Would rename"
 | 
					 | 
				
			||||||
		if armed && !dryRun {
 | 
					 | 
				
			||||||
			action = "Renaming"
 | 
					 | 
				
			||||||
			err := os.Rename(bestFile.Path, baseFile)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				writeLog(fmt.Sprintf("    ! Rename failed: %v", err))
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		writeLog(fmt.Sprintf("  * %s: %s -> %s", action, bestFile.Path, baseFile))
 | 
							return 0, false
 | 
				
			||||||
		bestFile.Path = baseFile
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Delete other files
 | 
						// Process each release group
 | 
				
			||||||
	for _, file := range files {
 | 
						for releaseKey, releaseFiles := range releaseMap {
 | 
				
			||||||
		if file.Path != bestFile.Path {
 | 
							if len(releaseFiles) <= 1 {
 | 
				
			||||||
			action := "Would delete"
 | 
								// Skip if only one file for this release
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							writeLog(fmt.Sprintf("  Processing release group: %s", releaseKey))
 | 
				
			||||||
 | 
							bestFile = FileQuality{}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							// Find best file for this release
 | 
				
			||||||
 | 
							for _, file := range releaseFiles {
 | 
				
			||||||
 | 
								// Determine the best file using quality then size as tiebreaker
 | 
				
			||||||
 | 
								if file.Bitrate > bestFile.Bitrate ||
 | 
				
			||||||
 | 
									(file.Bitrate == bestFile.Bitrate && file.Bitdepth > bestFile.Bitdepth) ||
 | 
				
			||||||
 | 
									(file.Bitrate == bestFile.Bitrate && file.Bitdepth == bestFile.Bitdepth && file.Size > bestFile.Size) {
 | 
				
			||||||
 | 
									bestFile = file
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if bestFile.Path == "" {
 | 
				
			||||||
 | 
								writeLog("    - No valid files found in this release group")
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							writeLog(fmt.Sprintf("  -> Keeping: %s (Bitrate: %d, Bitdepth: %d, Size: %s)",
 | 
				
			||||||
 | 
								bestFile.Path, bestFile.Bitrate, bestFile.Bitdepth, bytesToHumanReadable(bestFile.Size)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// If this is the primary file (without (n) in name), make sure the best quality version is it
 | 
				
			||||||
 | 
							isBaseFile := (bestFile.Path == baseFile || 
 | 
				
			||||||
 | 
							               strings.TrimSuffix(bestFile.Path, ".flac") == strings.TrimSuffix(baseFile, ".flac"))
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							// Generate target filename
 | 
				
			||||||
 | 
							targetFilename := baseFile
 | 
				
			||||||
 | 
							if !isBaseFile && len(releaseMap) > 1 {
 | 
				
			||||||
 | 
								// For multiple releases, append year/identifier to filename to avoid conflicts
 | 
				
			||||||
 | 
								ext := filepath.Ext(baseFile)
 | 
				
			||||||
 | 
								baseName := strings.TrimSuffix(baseFile, ext)
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								// Use year or catalog number as identifier
 | 
				
			||||||
 | 
								identifier := bestFile.ReleaseYear
 | 
				
			||||||
 | 
								if identifier == "" && bestFile.CatalogNum != "" {
 | 
				
			||||||
 | 
									identifier = bestFile.CatalogNum
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								if identifier != "" {
 | 
				
			||||||
 | 
									targetFilename = fmt.Sprintf("%s [%s]%s", baseName, identifier, ext)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Rename best file to target name if needed
 | 
				
			||||||
 | 
							if bestFile.Path != targetFilename {
 | 
				
			||||||
 | 
								action := "Would rename"
 | 
				
			||||||
			if armed && !dryRun {
 | 
								if armed && !dryRun {
 | 
				
			||||||
				action = "Deleting"
 | 
									action = "Renaming"
 | 
				
			||||||
				err := os.Remove(file.Path)
 | 
									err := os.Rename(bestFile.Path, targetFilename)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					writeLog(fmt.Sprintf("    ! Delete failed for %s: %v", file.Path, err))
 | 
										writeLog(fmt.Sprintf("    ! Rename failed: %v", err))
 | 
				
			||||||
					continue
 | 
										continue
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			writeLog(fmt.Sprintf("  * %s: %s (Recovering %s)", action, file.Path, bytesToHumanReadable(file.Size)))
 | 
								writeLog(fmt.Sprintf("  * %s: %s -> %s", action, bestFile.Path, targetFilename))
 | 
				
			||||||
 | 
								bestFile.Path = targetFilename
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Delete other files in this release group
 | 
				
			||||||
 | 
							for _, file := range releaseFiles {
 | 
				
			||||||
 | 
								if file.Path != bestFile.Path {
 | 
				
			||||||
 | 
									action := "Would delete"
 | 
				
			||||||
 | 
									if armed && !dryRun {
 | 
				
			||||||
 | 
										action = "Deleting"
 | 
				
			||||||
 | 
										err := os.Remove(file.Path)
 | 
				
			||||||
 | 
										if err != nil {
 | 
				
			||||||
 | 
											writeLog(fmt.Sprintf("    ! Delete failed for %s: %v", file.Path, err))
 | 
				
			||||||
 | 
											continue
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									writeLog(fmt.Sprintf("  * %s: %s (Recovering %s)", action, file.Path, bytesToHumanReadable(file.Size)))
 | 
				
			||||||
 | 
									spaceRecovered += file.Size
 | 
				
			||||||
 | 
									processed = true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	writeLog(fmt.Sprintf("  Total recoverable space: %s", bytesToHumanReadable(totalSize-bestFile.Size)))
 | 
						writeLog(fmt.Sprintf("  Total potentially recoverable space: %s", bytesToHumanReadable(spaceRecovered)))
 | 
				
			||||||
 | 
						return spaceRecovered, processed
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
@@ -262,6 +381,9 @@ func main() {
 | 
				
			|||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		writeLog("ARMED MODE: Files will be renamed and deleted!")
 | 
							writeLog("ARMED MODE: Files will be renamed and deleted!")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if forceMode {
 | 
				
			||||||
 | 
							writeLog("FORCE MODE: Will process even if releases appear different!")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	writeLog("===============================")
 | 
						writeLog("===============================")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Check mediainfo is available
 | 
						// Check mediainfo is available
 | 
				
			||||||
@@ -285,17 +407,18 @@ func main() {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var totalRecoverableSpace int64
 | 
						var totalRecoverableSpace int64
 | 
				
			||||||
 | 
						processedGroups := 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for baseFile, group := range fileGroups {
 | 
						for baseFile, group := range fileGroups {
 | 
				
			||||||
		processFileGroup(baseFile, group)
 | 
							spaceRecovered, processed := processFileGroup(baseFile, group)
 | 
				
			||||||
		for _, file := range group {
 | 
							if processed {
 | 
				
			||||||
			if file.Path != baseFile {
 | 
								processedGroups++
 | 
				
			||||||
				totalRecoverableSpace += file.Size
 | 
								totalRecoverableSpace += spaceRecovered
 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		writeLog("")
 | 
							writeLog("")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						writeLog(fmt.Sprintf("Processed %d of %d file groups", processedGroups, len(fileGroups)))
 | 
				
			||||||
	writeLog(fmt.Sprintf("Total recoverable space: %s", bytesToHumanReadable(totalRecoverableSpace)))
 | 
						writeLog(fmt.Sprintf("Total recoverable space: %s", bytesToHumanReadable(totalRecoverableSpace)))
 | 
				
			||||||
	writeLog(fmt.Sprintf("Cleanup completed at %s", getCurrentTime()))
 | 
						writeLog(fmt.Sprintf("Cleanup completed at %s", getCurrentTime()))
 | 
				
			||||||
	writeLog("===============================")
 | 
						writeLog("===============================")
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user