Files
icecast-ripper/internal/database/database.go
Dmitrii Andreev 42aed9decb linter
2025-04-07 11:32:14 +03:00

119 lines
3.4 KiB
Go

package database
import (
"database/sql"
"fmt"
"log/slog"
"time"
_ "github.com/mattn/go-sqlite3" // SQLite driver
)
// DB represents the database connection.
type DB struct {
*sql.DB
}
// RecordedFile represents a row in the recorded_files table.
type RecordedFile struct {
ID int64
Filename string
Hash string
FileSize int64 // Size in bytes
Duration time.Duration // Duration of the recording
RecordedAt time.Time
}
// InitDB initializes the SQLite database connection and creates tables if they don't exist.
func InitDB(dataSourceName string) (*DB, error) {
slog.Info("Initializing database connection", "path", dataSourceName)
db, err := sql.Open("sqlite3", dataSourceName)
if err != nil {
return nil, fmt.Errorf("failed to open database: %w", err)
}
if err = db.Ping(); err != nil {
return nil, fmt.Errorf("failed to connect to database: %w", err)
}
// Create table if not exists
query := `
CREATE TABLE IF NOT EXISTS recorded_files (
id INTEGER PRIMARY KEY AUTOINCREMENT,
filename TEXT NOT NULL UNIQUE,
hash TEXT NOT NULL UNIQUE,
filesize INTEGER NOT NULL,
duration INTEGER NOT NULL, -- Store duration in seconds or milliseconds
recorded_at DATETIME NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_recorded_at ON recorded_files (recorded_at);
`
if _, err = db.Exec(query); err != nil {
return nil, fmt.Errorf("failed to create table: %w", err)
}
slog.Info("Database initialized successfully")
return &DB{db}, nil
}
// AddRecordedFile inserts a new record into the database.
func (db *DB) AddRecordedFile(filename, hash string, fileSize int64, duration time.Duration, recordedAt time.Time) (int64, error) {
result, err := db.Exec(
"INSERT INTO recorded_files (filename, hash, filesize, duration, recorded_at) VALUES (?, ?, ?, ?, ?)",
filename, hash, fileSize, int64(duration.Seconds()), recordedAt, // Store duration as seconds
)
if err != nil {
return 0, fmt.Errorf("failed to insert recorded file: %w", err)
}
id, err := result.LastInsertId()
if err != nil {
return 0, fmt.Errorf("failed to get last insert ID: %w", err)
}
slog.Debug("Added recorded file to database", "id", id, "filename", filename, "hash", hash)
return id, nil
}
// GetRecordedFiles retrieves all recorded files, ordered by recording date descending.
func (db *DB) GetRecordedFiles(limit int) ([]RecordedFile, error) {
query := "SELECT id, filename, hash, filesize, duration, recorded_at FROM recorded_files ORDER BY recorded_at DESC"
args := []interface{}{}
if limit > 0 {
query += " LIMIT ?"
args = append(args, limit)
}
rows, err := db.Query(query, args...)
if err != nil {
return nil, fmt.Errorf("failed to query recorded files: %w", err)
}
defer func() {
if err := rows.Close(); err != nil {
slog.Error("Failed to close rows", "error", err)
}
}()
var files []RecordedFile
for rows.Next() {
var rf RecordedFile
var durationSeconds int64
if err := rows.Scan(&rf.ID, &rf.Filename, &rf.Hash, &rf.FileSize, &durationSeconds, &rf.RecordedAt); err != nil {
return nil, fmt.Errorf("failed to scan recorded file row: %w", err)
}
rf.Duration = time.Duration(durationSeconds) * time.Second
files = append(files, rf)
}
if err = rows.Err(); err != nil {
return nil, fmt.Errorf("error iterating over recorded file rows: %w", err)
}
return files, nil
}
// Close closes the database connection.
func (db *DB) Close() error {
slog.Info("Closing database connection")
return db.DB.Close()
}