diff --git a/internal/config/config.go b/internal/config/config.go index 2c8f8a7..afc1103 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -27,7 +27,7 @@ func LoadConfig() (*Config, error) { "STREAM_URL": "", "CHECK_INTERVAL": "1m", "RECORDINGS_PATH": "./recordings", - "TEMP_PATH": "./temp", + "TEMP_PATH": "/tmp", "SERVER_ADDRESS": ":8080", "RSS_FEED_URL": "http://localhost:8080/rss", "LOG_LEVEL": "info", diff --git a/internal/recorder/recorder.go b/internal/recorder/recorder.go index 2553e41..50d1f33 100644 --- a/internal/recorder/recorder.go +++ b/internal/recorder/recorder.go @@ -81,6 +81,7 @@ func (r *Recorder) StopRecording() { func (r *Recorder) recordStream(ctx context.Context, streamURL string) { startTime := time.Now() var tempFilePath string + var moveSuccessful bool defer func() { r.mu.Lock() @@ -89,13 +90,16 @@ func (r *Recorder) recordStream(ctx context.Context, streamURL string) { r.mu.Unlock() slog.Info("Recording process finished") - if tempFilePath != "" { + // Only clean up temp file if it was successfully moved to final location + if tempFilePath != "" && moveSuccessful { if _, err := os.Stat(tempFilePath); err == nil { - slog.Warn("Cleaning up temporary file", "path", tempFilePath) + slog.Debug("Cleaning up temporary file", "path", tempFilePath) if err := os.Remove(tempFilePath); err != nil { slog.Error("Failed to remove temporary file", "error", err) } } + } else if tempFilePath != "" && !moveSuccessful { + slog.Warn("Temporary file preserved for manual inspection", "path", tempFilePath) } }() @@ -139,15 +143,47 @@ func (r *Recorder) recordStream(ctx context.Context, streamURL string) { finalFilename = sanitizeFilename(finalFilename) finalPath := filepath.Join(r.recordingsPath, finalFilename) + // Try rename first (fastest) if err := os.Rename(tempFilePath, finalPath); err != nil { - slog.Error("Failed to move recording to final location", "error", err) - return + slog.Warn("Failed to move recording with rename, trying copy fallback", "error", err) + + // Fallback to manual copy + if err := copyFile(tempFilePath, finalPath); err != nil { + slog.Error("Failed to move recording to final location", "error", err) + return + } + + // Copy successful, mark for cleanup + moveSuccessful = true + slog.Info("Recording copied successfully using fallback method", "path", finalPath) + } else { + moveSuccessful = true } - tempFilePath = "" // Prevent cleanup in defer slog.Info("Recording saved", "path", finalPath, "size", bytesWritten, "duration", duration) } +// copyFile copies a file from src to dst +func copyFile(src, dst string) error { + sourceFile, err := os.Open(src) + if err != nil { + return fmt.Errorf("failed to open source file: %w", err) + } + defer sourceFile.Close() + + destFile, err := os.Create(dst) + if err != nil { + return fmt.Errorf("failed to create destination file: %w", err) + } + defer destFile.Close() + + if _, err := io.Copy(destFile, sourceFile); err != nil { + return fmt.Errorf("failed to copy file contents: %w", err) + } + + return nil +} + func (r *Recorder) downloadStream(ctx context.Context, streamURL string, writer io.Writer) (int64, error) { req, err := http.NewRequestWithContext(ctx, http.MethodGet, streamURL, nil) if err != nil {