v 0.0.62 (исправленна SMB отправка изменения пароля)

This commit is contained in:
DND
2026-06-08 12:35:22 +07:00
parent c248ce166b
commit f1cfcd897c
2 changed files with 242 additions and 17 deletions
+175
View File
@@ -0,0 +1,175 @@
package logger
import (
"encoding/json"
"log"
"os"
"strconv"
"sync"
"time"
)
type LogEntry struct {
ID int `json:"id"`
Timestamp time.Time `json:"timestamp"`
Level string `json:"level"`
Category string `json:"category"`
Message string `json:"message"`
Details string `json:"details,omitempty"`
}
var (
logs []LogEntry
logsMu sync.RWMutex
nextID = 1
logFile *os.File
)
func Init() {
var err error
logFile, err = os.OpenFile("logs.json", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
if err != nil {
log.Printf("Failed to open log file: %v", err)
return
}
decoder := json.NewDecoder(logFile)
for decoder.More() {
var entry LogEntry
if err := decoder.Decode(&entry); err == nil {
logs = append(logs, entry)
if entry.ID >= nextID {
nextID = entry.ID + 1
}
}
}
}
func Add(level, category, message string, details ...string) {
logsMu.Lock()
defer logsMu.Unlock()
entry := LogEntry{
ID: nextID,
Timestamp: time.Now(),
Level: level,
Category: category,
Message: message,
}
if len(details) > 0 {
entry.Details = details[0]
}
logs = append(logs, entry)
nextID++
if logFile != nil {
data, _ := json.Marshal(entry)
logFile.Write(append(data, '\n'))
}
log.Printf("[%s][%s] %s", level, category, message)
}
func AddRecording(channelID, filename string, duration int, size int64, status string) {
msg := "Запись: " + status + " | Канал: " + channelID + " | Файл: " + filename
if duration > 0 {
msg += " | Длительность: " + formatDuration(duration)
}
if size > 0 {
msg += " | Размер: " + formatBytes(size)
}
Add("INFO", "RECORDING", msg)
}
func AddSMB(filename string, success bool, errorMsg string) {
status := "УСПЕШНО"
if !success {
status = "ОШИБКА"
}
msg := "SMB отправка: " + status + " | Файл: " + filename
if errorMsg != "" {
msg += " | Ошибка: " + errorMsg
}
Add("INFO", "SMB", msg)
}
func AddSettings(channelID, setting, value string) {
msg := "Изменение настроек | Канал: " + channelID + " | " + setting + " = " + value
Add("SETTINGS", "SETTINGS", msg)
}
func AddError(category, message string, err error) {
msg := message
if err != nil {
msg += ": " + err.Error()
}
Add("ERROR", category, msg)
}
func GetLogs(limit int) []LogEntry {
logsMu.RLock()
defer logsMu.RUnlock()
start := 0
if len(logs) > limit {
start = len(logs) - limit
}
result := make([]LogEntry, len(logs)-start)
copy(result, logs[start:])
for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 {
result[i], result[j] = result[j], result[i]
}
return result
}
func GetLogsByCategory(category string, limit int) []LogEntry {
logsMu.RLock()
defer logsMu.RUnlock()
var filtered []LogEntry
for i := len(logs) - 1; i >= 0 && len(filtered) < limit; i-- {
if logs[i].Category == category {
filtered = append(filtered, logs[i])
}
}
return filtered
}
func ClearLogs() {
logsMu.Lock()
defer logsMu.Unlock()
logs = []LogEntry{}
if logFile != nil {
logFile.Truncate(0)
logFile.Seek(0, 0)
}
}
func formatDuration(seconds int) string {
if seconds < 60 {
return strconv.Itoa(seconds) + "с"
}
minutes := seconds / 60
secs := seconds % 60
if minutes < 60 {
return strconv.Itoa(minutes) + "м " + strconv.Itoa(secs) + "с"
}
hours := minutes / 60
minutes = minutes % 60
return strconv.Itoa(hours) + "ч " + strconv.Itoa(minutes) + "м"
}
func formatBytes(bytes int64) string {
const unit = 1024
if bytes < unit {
return strconv.FormatInt(bytes, 10) + " B"
}
div, exp := int64(unit), 0
for n := bytes / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return strconv.FormatInt(bytes/div, 10) + " " + []string{"KB", "MB", "GB"}[exp]
}
+67 -17
View File
@@ -14,6 +14,7 @@ import (
"time"
"audio-recorder/database"
"audio-recorder/logger"
)
type Channel struct {
@@ -168,6 +169,8 @@ func (ch *Channel) StartRecording(outputDir string, testDeviceFunc func(string)
log.Printf("Warning: Failed to save recording to DB: %v", err)
}
logger.AddRecording(ch.ID, filename, 0, 0, "НАЧАЛО")
if testDeviceFunc != nil {
if working, msg := testDeviceFunc(ch.Device); !working {
return fmt.Errorf("device %s not working: %s", ch.Device, msg)
@@ -269,9 +272,7 @@ func (ch *Channel) StartRecording(outputDir string, testDeviceFunc func(string)
if recording.ID > 0 {
db.UpdateRecordingStatus(recording.ID, "completed")
if fileInfo, err := os.Stat(currentPath); err == nil {
// Используем ch.Duration который был установлен при старте
log.Printf("Recording completed: %s, duration=%d seconds, size=%d bytes",
currentFile, ch.Duration, fileInfo.Size())
logger.AddRecording(ch.ID, currentFile, ch.Duration, fileInfo.Size(), "ЗАВЕРШЕНА")
db.UpdateRecordingStats(recording.ID, fileInfo.Size(), ch.Duration)
}
}
@@ -309,7 +310,32 @@ func (ch *Channel) StopRecording() {
ch.stopOnce.Do(func() {
log.Printf("=== STOPPING RECORDING for channel %s ===", ch.ID)
// Сохраняем информацию о файле до остановки
currentFile := ch.CurrentFile
currentPath := ""
smbFolder := ch.SMBFolder
log.Printf("DEBUG: currentFile=%s, smbFolder=%s", currentFile, smbFolder)
ch.mu.Lock()
if ch.cmd != nil && ch.cmd.Process != nil {
for _, arg := range ch.cmd.Args {
if strings.HasSuffix(arg, ".wav") {
currentPath = arg
break
}
}
}
if currentPath == "" && currentFile != "" {
db := database.GetDB()
recordingsDir, _ := db.GetSetting("recordings_dir")
if recordingsDir == "" {
recordingsDir = ch.OutputDir
}
currentPath = filepath.Join(recordingsDir, ch.ID, currentFile)
}
log.Printf("DEBUG: currentPath=%s", currentPath)
ch.mu.Unlock()
select {
case <-ch.stopChan:
@@ -330,22 +356,17 @@ func (ch *Channel) StopRecording() {
log.Printf("Process PID for channel %s: %d", ch.ID, pid)
}
ch.mu.Unlock()
if pid > 0 {
log.Printf("Killing process %d for channel %s", pid, ch.ID)
if err := syscall.Kill(pid, syscall.SIGKILL); err != nil {
log.Printf("syscall.Kill error: %v", err)
}
killCmd := exec.Command("kill", "-9", strconv.Itoa(pid))
if output, err := killCmd.CombinedOutput(); err != nil {
log.Printf("kill -9 error: %v, output: %s", err, string(output))
} else {
log.Printf("kill -9 sent successfully to PID %d", pid)
}
exec.Command("pkill", "-9", "-P", strconv.Itoa(pid)).Run()
} else {
log.Printf("No PID found, killing all arecord processes")
@@ -370,6 +391,41 @@ func (ch *Channel) StopRecording() {
ch.saveStateToDB()
// Проверяем существует ли файл
fileExists := false
if currentPath != "" {
if _, err := os.Stat(currentPath); err == nil {
fileExists = true
}
}
log.Printf("DEBUG: fileExists=%v, currentPath=%s, smbFolder=%s, onRecordingCompleted=%v",
fileExists, currentPath, smbFolder, onRecordingCompleted != nil)
// Отправляем файл на SMB если есть файл и папка SMB указана
if fileExists && currentPath != "" && smbFolder != "" && onRecordingCompleted != nil {
log.Printf("✅ Sending file to SMB: %s, folder: %s, path: %s", currentFile, smbFolder, currentPath)
db := database.GetDB()
if currentFile != "" {
recordings, _ := db.GetRecordingsByChannel(ch.ID, 1000)
for _, rec := range recordings {
if rec.Filename == currentFile {
if fileInfo, err := os.Stat(currentPath); err == nil {
logger.AddRecording(ch.ID, currentFile, ch.Duration, fileInfo.Size(), "ЗАВЕРШЕНА")
db.UpdateRecordingStats(rec.ID, fileInfo.Size(), ch.Duration)
}
db.UpdateRecordingStatus(rec.ID, "completed")
break
}
}
}
onRecordingCompleted(currentFile, currentPath, smbFolder)
} else {
log.Printf("❌ SMB отправка НЕ выполнена: fileExists=%v, currentPath=%s, smbFolder=%s, callback=%v",
fileExists, currentPath, smbFolder, onRecordingCompleted != nil)
}
log.Printf("=== RECORDING STOPPED for channel %s ===", ch.ID)
})
}
@@ -418,6 +474,8 @@ func (ch *Channel) UpdateConfig(config ChannelConfig) {
ch.AutoRestart = config.AutoRestart
ch.SMBFolder = config.SMBFolder
logger.AddSettings(ch.ID, "настройки канала", "сохранены")
log.Printf("Updated channel %s config: device=%s, duration=%d, sample_rate=%d, format=%s, smb_folder=%s",
ch.ID, ch.Device, ch.Duration, ch.SampleRate, ch.Format, ch.SMBFolder)
@@ -453,7 +511,6 @@ func (ch *Channel) GetConfig() ChannelConfig {
}
}
// StartSchedule запускает планировщик для канала
func (ch *Channel) StartSchedule() {
ch.mu.Lock()
defer ch.mu.Unlock()
@@ -539,12 +596,8 @@ func (ch *Channel) StartSchedule() {
db := database.GetDB()
db.SetChannelState(ch.ID, true)
dm := GetDeviceManager()
testFunc := func(device string) (bool, string) {
if dm != nil {
return true, "Device check skipped"
}
return true, "No device manager"
return true, "Device check skipped"
}
if err := ch.StartRecording(ch.OutputDir, testFunc); err != nil {
@@ -570,7 +623,6 @@ func (ch *Channel) StartSchedule() {
})
}
// StopSchedule останавливает планировщик
func (ch *Channel) StopSchedule() {
ch.mu.Lock()
defer ch.mu.Unlock()
@@ -581,7 +633,6 @@ func (ch *Channel) StopSchedule() {
}
}
// getAudioCommandWithContext - изменённый для правильной записи WAV
func getAudioCommandWithContext(ctx context.Context, device string, sampleRate, channels int, format, filepath string) *exec.Cmd {
args := []string{}
@@ -605,7 +656,6 @@ func getAudioCommandWithContext(ctx context.Context, device string, sampleRate,
args = append(args, "-f", "S16_LE")
}
// Используем формат wav - arecord сам правильно заполнит заголовок
args = append(args, "-t", "wav")
args = append(args, filepath)