Go web application for filling USB drives with media files. Runs in Docker on Unraid with /media, /mnt/usb, /config volumes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
93 lines
1.9 KiB
Go
93 lines
1.9 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"time"
|
|
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
type DB struct {
|
|
sql *sql.DB
|
|
}
|
|
|
|
type CopyRecord struct {
|
|
DiskID string
|
|
SourcePath string
|
|
FileSize int64
|
|
CopiedAt time.Time
|
|
}
|
|
|
|
func Open(path string) (*DB, error) {
|
|
conn, err := sql.Open("sqlite", path+"?_journal=WAL&_timeout=5000")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conn.SetMaxOpenConns(1)
|
|
d := &DB{sql: conn}
|
|
if err := d.migrate(); err != nil {
|
|
conn.Close()
|
|
return nil, err
|
|
}
|
|
return d, nil
|
|
}
|
|
|
|
func (d *DB) Close() error {
|
|
return d.sql.Close()
|
|
}
|
|
|
|
func (d *DB) migrate() error {
|
|
_, err := d.sql.Exec(`
|
|
CREATE TABLE IF NOT EXISTS copy_history (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
disk_id TEXT NOT NULL,
|
|
source_path TEXT NOT NULL,
|
|
file_size INTEGER NOT NULL DEFAULT 0,
|
|
copied_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ','now'))
|
|
);
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_copy_history_disk_path
|
|
ON copy_history (disk_id, source_path);
|
|
`)
|
|
return err
|
|
}
|
|
|
|
func (d *DB) WasCopied(diskID, sourcePath string) (bool, error) {
|
|
var n int
|
|
err := d.sql.QueryRow(
|
|
`SELECT COUNT(*) FROM copy_history WHERE disk_id=? AND source_path=?`,
|
|
diskID, sourcePath,
|
|
).Scan(&n)
|
|
return n > 0, err
|
|
}
|
|
|
|
func (d *DB) RecordCopy(rec CopyRecord) error {
|
|
t := rec.CopiedAt
|
|
if t.IsZero() {
|
|
t = time.Now().UTC()
|
|
}
|
|
_, err := d.sql.Exec(
|
|
`INSERT OR IGNORE INTO copy_history (disk_id, source_path, file_size, copied_at) VALUES (?,?,?,?)`,
|
|
rec.DiskID, rec.SourcePath, rec.FileSize, t.Format(time.RFC3339),
|
|
)
|
|
return err
|
|
}
|
|
|
|
func (d *DB) CopiedPaths(diskID string) (map[string]struct{}, error) {
|
|
rows, err := d.sql.Query(
|
|
`SELECT source_path FROM copy_history WHERE disk_id=?`, diskID,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
m := make(map[string]struct{})
|
|
for rows.Next() {
|
|
var p string
|
|
if err := rows.Scan(&p); err != nil {
|
|
return nil, err
|
|
}
|
|
m[p] = struct{}{}
|
|
}
|
|
return m, rows.Err()
|
|
}
|