-
-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add directory watch functionality and enhance audio file proces… #401
Conversation
…sing - Introduced a new flag for the directory command to enable watching for new files. - Implemented a directory scanning mechanism that processes .wav and .flac files, including checks for file readiness and processing locks. - Enhanced error handling and logging for file analysis, ensuring better feedback during processing. - Updated configuration to include a watch option in the input settings, allowing for dynamic file monitoring. These changes improve the usability of the directory command by enabling real-time file processing and enhancing the overall robustness of audio file handling.
Caution Review failedThe pull request is closed. WalkthroughThis pull request introduces enhancements to the command functions across multiple files, focusing on context management for graceful shutdowns and improved error handling. A new Changes
Possibly related PRs
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (2)
Finishing Touches
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (4)
internal/analysis/directory.go (2)
206-227
: Optimize file size stability check to reduce processing timeThe
isFileSizeStable
function currently checks the file size three times with a 2-second sleep in between, totaling at least 4 seconds per file. This could slow down the processing if there are many files. Consider reducing the number of checks or the sleep duration, or implementing a more efficient mechanism to detect if a file is still being written to.
284-290
: Simplify file extension handling withstrings.TrimSuffix
The code for removing the
.wav
and.flac
extensions from the output path can be simplified usingstrings.TrimSuffix
. This improves code readability and reduces potential errors.Apply this diff to simplify extension handling:
outputPath := filepath.Join(settings.Output.File.Path, filepath.Base(path)) ext := strings.ToLower(filepath.Ext(outputPath)) - if ext == ".wav" { - outputPath = outputPath[:len(outputPath)-4] - } else if ext == ".flac" { - outputPath = outputPath[:len(outputPath)-5] + outputPath = strings.TrimSuffix(outputPath, ext)internal/myaudio/readfile.go (1)
64-64
: Refactor to avoid redundancy in extension handlingThe code for obtaining the lowercase file extension is repeated. Consider creating a helper function to avoid redundancy and enhance maintainability.
Apply this diff to refactor the extension handling:
+func getLowercaseExt(path string) string { + return strings.ToLower(filepath.Ext(path)) +} ... file, err := os.Open(settings.Input.Path) if err != nil { return err } defer file.Close() - ext := strings.ToLower(filepath.Ext(settings.Input.Path)) + ext := getLowercaseExt(settings.Input.Path)internal/analysis/file.go (1)
68-72
: LGTM! Consider enhancing the error message.The case-insensitive extension check is a good approach. However, the error message could be more user-friendly by listing the supported formats.
- return fmt.Errorf("\033[31m❌ Invalid audio file %s: unsupported audio format: %s\033[0m", filepath.Base(filePath), filepath.Ext(filePath)) + return fmt.Errorf("\033[31m❌ Invalid audio file %s: unsupported audio format: %s (supported formats: .wav, .flac)\033[0m", filepath.Base(filePath), filepath.Ext(filePath))
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
cmd/directory/directory.go
(1 hunks)internal/analysis/directory.go
(2 hunks)internal/analysis/file.go
(2 hunks)internal/conf/config.go
(1 hunks)internal/myaudio/readfile.go
(3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Analyze (go)
🔇 Additional comments (8)
internal/analysis/directory.go (3)
396-399
: Ensure proper cleanup and cancellation upon receiving signalsIn the signal handling goroutine, after calling
cleanupProcessingFiles()
, you invokecancel()
. Verify that all resources are properly released and that there are no race conditions or deadlocks caused by the context cancellation.
99-104
: 🛠️ Refactor suggestionReconsider the use of
os.OpenFile
for file locking detectionThe
isFileLocked
function currently determines if a file is locked by attempting to open it with read-write permissions. However, failure to open a file might not necessarily indicate that the file is locked; it could also be due to permission issues or the file being deleted. Consider a more reliable method to check if the file is being written to, such as checking if the file size is stable or using platform-specific file locking mechanisms.
369-370
:⚠️ Potential issueReturn after logging error when creating output directory
In the error handling at line 370, you log that you failed to create the output directory but continue execution. This could lead to further errors down the line. Consider returning the error after logging to prevent execution when the output directory cannot be created.
Apply this diff to return the error:
log.Printf("Failed to create output directory: %v", err) + return err
Likely invalid or redundant comment.
cmd/directory/directory.go (1)
39-39
: Verify there are no flag conflicts with the-w
shorthandThe shorthand
-w
for the--watch
flag might conflict with other flags in your application or dependencies. Ensure that-w
is not already used elsewhere to prevent unexpected behavior.internal/myaudio/readfile.go (1)
44-44
: Standardize file extension handling for case-insensitive comparisonConverting file extensions to lowercase using
strings.ToLower
ensures consistent handling of files regardless of the case of the extension. This change improves robustness in processing audio files.internal/analysis/file.go (2)
Line range hint
74-89
: LGTM! Thorough audio validation.The validation logic is comprehensive, checking file accessibility, audio format, and sample data. Good use of defer for file closing and clear error messages.
Line range hint
41-47
: LGTM! Excellent error handling.The error handling is robust, with proper error wrapping and user-friendly feedback. Good practice to save partial results before exiting.
internal/conf/config.go (1)
198-198
: LGTM! Well-documented configuration field.The addition of the
Watch
field with theyaml:"-"
tag is appropriate for runtime configuration. The field's purpose is clearly documented.
internal/analysis/directory.go
Outdated
expectedSize := int64(wavHeader[0]) | int64(wavHeader[1])<<8 | int64(wavHeader[2])<<16 | int64(wavHeader[3])<<24 | ||
expectedSize += 8 // Add 8 bytes for RIFF header |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle potential integer overflow when calculating expectedSize
The calculation of expectedSize
could result in an integer overflow if the WAV file is larger than 2GB. Ensure that the variables used can accommodate large file sizes without overflowing by using uint64
where appropriate.
internal/analysis/directory.go
Outdated
valid, reason, err := verifyAudioFile(path) | ||
if err != nil { | ||
log.Printf("Error verifying audio file %s: %v", filepath.Base(path), err) | ||
return false, nil | ||
} | ||
if !valid { | ||
log.Printf("File %s is not ready: %s", filepath.Base(path), reason) | ||
return false, nil | ||
} | ||
|
||
// Check if file is locked | ||
if isFileLocked(path) { | ||
log.Printf("File %s is locked, skipping...", filepath.Base(path)) | ||
return false, nil | ||
} | ||
|
||
// Check modification time | ||
info, err := os.Stat(path) | ||
if err != nil { | ||
return false, err | ||
} | ||
if time.Since(info.ModTime()) < 30*time.Second { | ||
log.Printf("File %s was modified too recently, waiting...", filepath.Base(path)) | ||
return false, nil | ||
} | ||
|
||
// Check file size stability | ||
stable, err := isFileSizeStable(path) | ||
if err != nil || !stable { | ||
return false, err | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle errors consistently in isFileReadyForProcessing
In isFileReadyForProcessing
, when verifyAudioFile
returns an error, the function logs the error but does not return it, instead returning false, nil
. Similarly, when isFileSizeStable
returns false
, the function returns false, err
, where err
could be nil
. This inconsistency could lead to suppressed errors. Ensure that all errors are appropriately returned and handled.
Apply this diff to return and handle errors correctly:
return false, nil
- }
+ }
+ return false, err
}
if !valid {
log.Printf("File %s is not ready: %s", filepath.Base(path), reason)
return false, nil
}
Committable suggestion skipped: line range outside the PR's diff.
internal/analysis/directory.go
Outdated
// Since we've already read the first 4 bytes, we need to use the first 4 bytes of wavHeader | ||
expectedSize := int64(wavHeader[0]) | int64(wavHeader[1])<<8 | int64(wavHeader[2])<<16 | int64(wavHeader[3])<<24 | ||
expectedSize += 8 // Add 8 bytes for RIFF header | ||
|
||
// If file is smaller than header indicates, it's definitely incomplete | ||
if actualSize < expectedSize { | ||
return false, fmt.Sprintf("incomplete WAV file: expected %d bytes, got %d", expectedSize, actualSize), nil | ||
} | ||
|
||
// Allow for small differences (up to 1KB) due to block sizes | ||
sizeDiff := actualSize - expectedSize | ||
if sizeDiff > 1024 { | ||
return false, fmt.Sprintf("WAV file size mismatch: expected %d bytes, got %d (difference too large: %d bytes)", | ||
expectedSize, actualSize, sizeDiff), nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider endianness when reading WAV header fields
The calculation of expectedSize
in verifyAudioFile
reads bytes directly to form an integer. This assumes little-endian byte order. However, WAV files are typically stored in little-endian format, but it's safer to explicitly specify endianness. Use binary.Read
with the appropriate endianness to correctly parse the header fields.
Apply this diff to use binary.Read
for correct parsing:
+import "encoding/binary"
...
- expectedSize := int64(wavHeader[0]) | int64(wavHeader[1])<<8 | int64(wavHeader[2])<<16 | int64(wavHeader[3])<<24
+ var chunkSize uint32
+ err = binary.Read(bytes.NewReader(wavHeader[0:4]), binary.LittleEndian, &chunkSize)
+ if err != nil {
+ return false, "", fmt.Errorf("failed to read chunk size: %v", err)
+ }
+ expectedSize := int64(chunkSize) + 8 // Add 8 bytes for RIFF header
Committable suggestion skipped: line range outside the PR's diff.
… error handling - Added context cancellation support to audio file analysis, allowing for graceful shutdowns during processing. - Implemented signal handling for user-initiated cancellations, ensuring clean exits without errors. - Enhanced error handling in file and directory analysis, providing clearer feedback on issues encountered during processing. - Updated the WAV and FLAC file validation logic to improve robustness and accuracy in file readiness checks. These changes improve the user experience by allowing for more responsive and controlled audio processing, especially in long-running tasks.
@coderabbitai full review |
✅ Actions performedFull review triggered. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Nitpick comments (6)
internal/analysis/directory.go (3)
91-92
: Handle errors when removing stale lock filesThe error returned by
os.Remove(outputPathProcessing)
is not being checked. Ignoring errors from file operations can lead to unintended behavior if the file cannot be removed.Apply this diff to handle the error:
if isLockFileStale(outputPathProcessing) { log.Printf("Removing stale lock file: %s", outputPathProcessing) - os.Remove(outputPathProcessing) + if err := os.Remove(outputPathProcessing); err != nil { + log.Printf("Error removing stale lock file %s: %v", outputPathProcessing, err) + } return false }
338-344
: Simplify file extension handling usingstrings.TrimSuffix
Currently, the code manually strips the file extension based on its length, which can be error-prone and less readable. Using
strings.TrimSuffix
simplifies the code and makes it more maintainable.Apply this diff to simplify the extension handling:
outputPath := filepath.Join(settings.Output.File.Path, filepath.Base(path)) ext := strings.ToLower(filepath.Ext(outputPath)) - if ext == ".wav" { - outputPath = outputPath[:len(outputPath)-4] - } else if ext == ".flac" { - outputPath = outputPath[:len(outputPath)-5] - } + outputPath = strings.TrimSuffix(outputPath, ext)
412-414
: Continue directory traversal on access errorsReturning an error in the
filepath.WalkDir
function will terminate the directory traversal. To ensure all accessible files are processed, log the error and continue with the traversal when an access error occurs.Apply this diff to handle errors gracefully:
if err != nil { - return fmt.Errorf("error accessing path %s: %w", path, err) + log.Printf("Error accessing path %s: %v", path, err) + return nil }internal/analysis/file.go (3)
36-41
: Consider removing redundant context check before writeResults.While the context check before processing is crucial, the second check before writeResults might be unnecessary as:
- Writing results is typically a quick operation
- If cancellation occurs during writing, it's better to complete the write than leave partial results
- // Check for context cancellation before writing results - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - return writeResults(settings, notes)Also applies to: 56-61
83-89
: Optimize file handling to avoid double open.The file is being opened twice:
- First to check validity
- Again in GetAudioInfo
Consider removing the explicit file open and rely on GetAudioInfo for validation.// Check file extension (case-insensitive) ext := strings.ToLower(filepath.Ext(filePath)) if ext != ".wav" && ext != ".flac" { return fmt.Errorf("\033[31m❌ Invalid audio file %s: unsupported audio format: %s\033[0m", filepath.Base(filePath), filepath.Ext(filePath)) } - // Open the file to check if it's a valid audio file - file, err := os.Open(filePath) - if err != nil { - return fmt.Errorf("\033[31m❌ Error opening file %s: %w\033[0m", filepath.Base(filePath), err) - } - defer file.Close()
301-302
: Enhance error context for cancellation.Consider wrapping the context error with additional information about the processing state.
case <-ctx.Done(): - return ctx.Err() + return fmt.Errorf("analysis cancelled at chunk position %v: %w", filePosition, ctx.Err())
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
cmd/file/file.go
(2 hunks)internal/analysis/directory.go
(2 hunks)internal/analysis/file.go
(10 hunks)main.go
(1 hunks)
🔇 Additional comments (3)
internal/analysis/file.go (3)
4-4
: LGTM! Good addition of context support.The addition of context handling will enable proper cancellation of long-running analysis tasks.
Also applies to: 8-8, 20-20
111-111
: LGTM! Well-implemented context handling in worker goroutine.The context check is properly placed within the worker loop to enable quick cancellation response.
Also applies to: 162-168
205-207
: LGTM! Good cleanup handling in progress monitor.The progress monitoring goroutine properly handles cancellation and ensures cleanup.
internal/analysis/directory.go
Outdated
sleepTime := 30 + rand.Intn(15) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seed the random number generator for true randomness
The rand.Intn(15)
function uses the default seed, resulting in the same sequence each time the application runs. To ensure that the sleep intervals vary between runs, seed the random number generator.
Apply this diff to seed the random number generator at the start of DirectoryAnalysis
:
+ // Seed the random number generator
+ rand.Seed(time.Now().UnixNano())
Place this code before the watch loop begins.
Committable suggestion skipped: line range outside the PR's diff.
main.go
Outdated
if err.Error() == "error processing audio: context canceled" { | ||
// Clean exit for user-initiated cancellation | ||
os.Exit(0) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid comparing error messages as strings
Comparing error messages as strings is fragile and can break if the error message changes. Instead, use errors.Is
to compare errors, which is more reliable and idiomatic in Go.
Apply this diff to use errors.Is
for error comparison:
+ "errors"
// Other imports...
if err := rootCmd.Execute(); err != nil {
- if err.Error() == "error processing audio: context canceled" {
+ if errors.Is(err, context.Canceled) {
// Clean exit for user-initiated cancellation
os.Exit(0)
}
Committable suggestion skipped: line range outside the PR's diff.
cmd/file/file.go
Outdated
if err == context.Canceled { | ||
// Return nil for user-initiated cancellation | ||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Use errors.Is
to compare errors with context.Canceled
Directly comparing errors using ==
may not always work as expected due to error wrapping. Use errors.Is
to reliably check for context.Canceled
.
Apply this diff to improve error comparison:
+ "errors"
// Other imports...
err := analysis.FileAnalysis(settings, ctx)
- if err == context.Canceled {
+ if errors.Is(err, context.Canceled) {
// Return nil for user-initiated cancellation
return nil
}
Committable suggestion skipped: line range outside the PR's diff.
internal/analysis/file.go
Outdated
case <-ctx.Done(): | ||
processingError = ctx.Err() | ||
close(doneChan) | ||
return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider adding mutex for processingError access.
There's a potential race condition between setting processingError and closing doneChan. Consider using a mutex to synchronize access to processingError.
var processingError error
+ var processingErrorMutex sync.Mutex
go func() {
if settings.Debug {
fmt.Println("DEBUG: Result collector started")
}
for i := 1; i <= totalChunks; i++ {
select {
case <-ctx.Done():
+ processingErrorMutex.Lock()
processingError = ctx.Err()
+ processingErrorMutex.Unlock()
close(doneChan)
return
Committable suggestion skipped: line range outside the PR's diff.
- Added a new error type, ErrAnalysisCanceled, to handle user-initiated cancellations during audio analysis. - Updated error handling in main.go and directory.go to utilize the new error type for clearer cancellation feedback. - Enhanced synchronization in file.go to ensure safe access to processing errors across goroutines. These changes enhance the robustness of audio processing by providing clearer error reporting and supporting graceful cancellation of analysis tasks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (2)
cmd/file/file.go (1)
44-47
: 🛠️ Refactor suggestionUse
errors.Is
for error comparison withcontext.Canceled
Directly comparing errors using
==
may not reliably detect if an error iscontext.Canceled
, especially if the error has been wrapped. Useerrors.Is
to check for context cancellation.Apply this diff to improve error handling:
+ "errors" // Other imports... err := analysis.FileAnalysis(settings, ctx) - if err == context.Canceled { + if errors.Is(err, context.Canceled) { // Return nil for user-initiated cancellation return nil }main.go (1)
71-74
: 🛠️ Refactor suggestionUse errors.Is for robust error comparison.
Comparing error messages as strings is fragile. Use
errors.Is
with proper error types for more reliable error handling.Apply this diff to improve error handling:
+ "context" + "errors" // Other imports... if err := rootCmd.Execute(); err != nil { - if err.Error() == "error processing audio: context canceled" { + if errors.Is(err, context.Canceled) { // Clean exit for user-initiated cancellation os.Exit(0) }
🧹 Nitpick comments (5)
cmd/file/file.go (1)
25-39
: Ensure signal handling cleans up resources properlyThe current signal handling setup is good for graceful shutdowns. However, consider handling additional signals like
os.Kill
andos.Quit
to cover more termination scenarios. Also, ensure that any other resources or goroutines are properly cleaned up upon cancellation.internal/analysis/file.go (1)
56-62
: Avoid redundant context cancellation checksThe context cancellation check before writing results may be unnecessary since context cancellation would already have been handled during processing. Ensure that the extra check is needed, or remove it to simplify the code.
internal/analysis/directory.go (2)
101-127
: Improve file lock detection on non-Windows systemsOn non-Windows systems,
syscall.O_NONBLOCK
may not have the desired effect when checking file locks. Consider usingflock
or other file locking mechanisms to more accurately detect if a file is in use.
484-512
: Ensure clean shutdown and resource cleanupWhile the signal handling and context cancellation are implemented, consider handling all possible signals that could terminate the program. Additionally, make sure that all goroutines are properly terminated to prevent resource leaks.
internal/myaudio/readfile.go (1)
Line range hint
44-52
: Extract duplicate file extension handling logic.The file extension handling logic is duplicated between
GetAudioInfo
andReadAudioFileBuffered
. Consider extracting it into a helper function.+func getSupportedAudioFormat(filePath string) (string, error) { + ext := strings.ToLower(filepath.Ext(filePath)) + switch ext { + case ".wav", ".flac": + return ext, nil + default: + return "", fmt.Errorf("unsupported audio format: %s", ext) + } +} func GetAudioInfo(filePath string) (AudioInfo, error) { file, err := os.Open(filePath) if err != nil { return AudioInfo{}, err } defer file.Close() - ext := strings.ToLower(filepath.Ext(filePath)) - switch ext { - case ".wav": + ext, err := getSupportedAudioFormat(filePath) + if err != nil { + return AudioInfo{}, err + } + switch ext { + case ".wav": return readWAVInfo(file) - case ".flac": + case ".flac": return readFLACInfo(file) - default: - return AudioInfo{}, fmt.Errorf("unsupported audio format: %s", ext) } + return AudioInfo{}, fmt.Errorf("unhandled audio format: %s", ext) }Also applies to: 64-72
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
cmd/directory/directory.go
(1 hunks)cmd/file/file.go
(2 hunks)internal/analysis/directory.go
(2 hunks)internal/analysis/file.go
(10 hunks)internal/conf/config.go
(1 hunks)internal/myaudio/readfile.go
(3 hunks)main.go
(1 hunks)
🔇 Additional comments (6)
internal/analysis/file.go (1)
84-89
: Enhance audio file validation beyond file extensionsRelying solely on file extensions to validate audio files may not be sufficient, as files could have incorrect extensions. Consider inspecting the file content or using a library to verify that the file is indeed a valid WAV or FLAC file.
Apply this diff to improve file validation:
func validateAudioFile(filePath string) error { fileInfo, err := os.Stat(filePath) if err != nil { return fmt.Errorf("\033[31m❌ Error accessing file %s: %w\033[0m", filepath.Base(filePath), err) } - // Check file extension (case-insensitive) - ext := strings.ToLower(filepath.Ext(filePath)) - if ext != ".wav" && ext != ".flac" { - return fmt.Errorf("\033[31m❌ Invalid audio file %s: unsupported audio format: %s\033[0m", filepath.Base(filePath), filepath.Ext(filePath)) - } // Open the file to check if it's a valid audio file file, err := os.Open(filePath) if err != nil { return fmt.Errorf("\033[31m❌ Error opening file %s: %w\033[0m", filepath.Base(filePath), err) }This change removes the extension check and relies on attempting to read the file, which provides a more reliable validation.
internal/analysis/directory.go (2)
527-528
: Seed the random number generator for true randomnessThe use of
rand.Intn(15)
without seeding the random number generator results in the same sequence of sleep intervals each time the application runs. Seed the random number generator to ensure variability in sleep durations.Apply this diff to seed the random number generator:
+ // Seed the random number generator + rand.Seed(time.Now().UnixNano()) // Watch mode - continuously scan with random intervals log.Printf("Starting directory watch on %s (Press Ctrl+C to stop)", watchDir)
273-309
: 🛠️ Refactor suggestionHandle errors consistently in file readiness checks
In the
isFileReadyForProcessing
function, error handling could be improved for consistency:
- When
verifyAudioFile
returns an error, consider returning the error instead of suppressing it with a log message.- When
isFileSizeStable
returnsfalse
with anil
error, ensure that this is the intended behavior.Apply this diff to improve error handling:
// Check if file is ready for processing isFileReadyForProcessing := func(path string) (bool, error) { // First verify the audio file headers and size valid, reason, err := verifyAudioFile(path) if err != nil { - return false, fmt.Errorf("error verifying audio file %s: %w", filepath.Base(path), err) + log.Printf("Error verifying audio file %s: %v", filepath.Base(path), err) + return false, nil } if !valid { log.Printf("File %s is not ready: %s", filepath.Base(path), reason) return false, nil } // Check file size stability stable, err := isFileSizeStable(path) if err != nil { - return false, fmt.Errorf("error checking file size stability for %s: %w", filepath.Base(path), err) + log.Printf("Error checking file size stability for %s: %v", filepath.Base(path), err) + return false, nil }This adjustment ensures that errors are logged and processing can continue with other files.
Likely invalid or redundant comment.
cmd/directory/directory.go (1)
39-39
: LGTM! Clean implementation of the watch flag.The watch flag is properly integrated into the directory command using the standard Go flag patterns.
internal/conf/config.go (1)
198-198
: LGTM! Consistent configuration field addition.The Watch field is properly added with the
yaml:"-"
tag, consistent with other runtime-only configuration fields in the struct.internal/myaudio/readfile.go (1)
44-44
: LGTM! Improved file extension handling.Case-insensitive file extension comparison makes the code more robust and user-friendly.
Also applies to: 64-64
…ocessing - Removed unnecessary log statements in favor of direct fmt.Printf for signal handling during graceful shutdowns. - Enhanced error handling in audio analysis to prioritize cancellation errors and ensure proper reporting of partial results. - Improved synchronization in goroutines to prevent race conditions when sending errors and results, ensuring robust processing flow. These changes contribute to cleaner code and improved responsiveness during audio analysis, enhancing overall user experience.
… handling - Added context cancellation support to the directory analysis process, allowing for graceful shutdowns during file processing. - Implemented signal handling for SIGINT and SIGTERM to manage user-initiated cancellations effectively. - Updated the DirectoryAnalysis function to include a context parameter, ensuring that analysis can be interrupted cleanly. - Enhanced error handling to provide clearer feedback on cancellation and processing issues. - Introduced a new watchDirectory function to continuously monitor for new audio files, improving real-time processing capabilities. These changes improve the responsiveness and robustness of the audio analysis workflow, enhancing user experience during long-running tasks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
internal/analysis/file.go (1)
169-178
: Consider using a buffered error channel.The error channel might block if multiple workers encounter errors simultaneously. Consider using a buffered channel to prevent potential goroutine leaks.
- errorChan := make(chan error, 1) + errorChan := make(chan error, numWorkers)internal/analysis/directory.go (2)
19-84
: Consider parameterizing lock file timeouts.The lock file staleness (5 minutes) and cleanup (60 minutes) timeouts are hardcoded. Consider making these configurable via settings.
+// Default timeout values +const ( + DefaultLockStalenessTimeout = 5 * time.Minute + DefaultLockCleanupTimeout = 60 * time.Minute +) // isLockFileStale checks if a lock file is older than 5 minutes func isLockFileStale(path string) bool { info, err := os.Stat(path) if err != nil { return false } - return time.Since(info.ModTime()) > 5*time.Minute + return time.Since(info.ModTime()) > settings.LockStalenessTimeout }
326-356
: Add jitter to watch interval.The random interval (30 + rand.Intn(15)) might not provide enough jitter to prevent thundering herd problems in large deployments.
- sleepTime := 30 + rand.Intn(15) + // Base interval of 30 seconds with ±50% jitter + baseInterval := 30 + jitter := rand.Float64()*float64(baseInterval) - float64(baseInterval)/2 + sleepTime := baseInterval + int(jitter)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
cmd/directory/directory.go
(3 hunks)cmd/file/file.go
(2 hunks)internal/analysis/directory.go
(1 hunks)internal/analysis/errors.go
(1 hunks)internal/analysis/file.go
(11 hunks)main.go
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- main.go
- cmd/file/file.go
🔇 Additional comments (12)
internal/analysis/errors.go (1)
1-6
: LGTM! Well-documented error variable.The error variable is properly defined and documented, providing clear context for analysis cancellation scenarios.
cmd/directory/directory.go (4)
24-38
: LGTM! Robust signal handling implementation.The implementation properly handles SIGTERM and SIGINT signals for graceful shutdown, with clear user feedback.
47-54
: LGTM! Proper error handling for cancellation.The error handling correctly distinguishes between cancellation and other errors, ensuring appropriate exit behavior.
58-61
: LGTM! Command configuration enhancement.Disabling automatic usage messages on errors improves the user experience by preventing redundant output.
74-74
: LGTM! Watch flag implementation.The watch flag is properly integrated with the existing flag system and aligns with the PR objectives.
internal/analysis/file.go (3)
75-79
: LGTM! Case-insensitive file extension validation.The implementation properly handles both .wav and .flac extensions in a case-insensitive manner.
277-281
: LGTM! Thread-safe error handling in result collector.The implementation properly uses mutex to ensure thread-safe access to the processing error.
359-361
: LGTM! Proper error handling for cancellation.The implementation correctly distinguishes between cancellation and other errors, returning appropriate error types.
Also applies to: 372-374
internal/analysis/directory.go (4)
86-178
: LGTM! Comprehensive file readiness checks.The implementation includes thorough checks for file locks, modification time, and size stability, with proper error handling and logging.
180-262
: LGTM! Robust file processing implementation.The implementation properly handles file locking, processing state, and cleanup, with appropriate error handling for cancellation.
264-324
: LGTM! Efficient directory scanning.The implementation properly handles recursive scanning and provides good progress feedback.
358-395
: LGTM! Well-structured main analysis function.The implementation properly initializes resources, handles initial scanning, and manages watch mode appropriately.
- Replaced direct comparison of error types with errors.Is for better error handling in both directory.go and file.go. - This change enhances the clarity and robustness of cancellation handling during analysis processes, ensuring that user-initiated cancellations are managed more effectively.
…sing
These changes improve the usability of the directory command by enabling real-time file processing and enhancing the overall robustness of audio file handling.