initial Go port of streamrip

This commit is contained in:
2026-04-19 21:11:38 +02:00
commit 97e8b758b3
32 changed files with 7008 additions and 0 deletions

89
internal/store/sqlite.go Normal file
View File

@@ -0,0 +1,89 @@
package store
import (
"context"
"database/sql"
"sync"
_ "modernc.org/sqlite"
)
type SQLite struct {
db *sql.DB
mu sync.Mutex
}
func NewSQLite(path string) (*SQLite, error) {
db, err := sql.Open("sqlite", path)
if err != nil {
return nil, err
}
s := &SQLite{db: db}
if err = s.init(); err != nil {
_ = db.Close()
return nil, err
}
return s, nil
}
func (s *SQLite) init() error {
queries := []string{
`CREATE TABLE IF NOT EXISTS downloads (id TEXT PRIMARY KEY)`,
`CREATE TABLE IF NOT EXISTS failed_downloads (
source TEXT NOT NULL,
media_type TEXT NOT NULL,
id TEXT NOT NULL,
PRIMARY KEY (source, media_type, id)
)`,
}
for _, q := range queries {
if _, err := s.db.Exec(q); err != nil {
return err
}
}
return nil
}
func (s *SQLite) IsDownloaded(ctx context.Context, id string) (bool, error) {
s.mu.Lock()
defer s.mu.Unlock()
var count int
err := s.db.QueryRowContext(ctx, `SELECT COUNT(1) FROM downloads WHERE id = ?`, id).Scan(&count)
if err != nil {
return false, err
}
return count > 0, nil
}
func (s *SQLite) MarkDownloaded(ctx context.Context, id string) error {
s.mu.Lock()
defer s.mu.Unlock()
_, err := s.db.ExecContext(ctx, `INSERT OR IGNORE INTO downloads(id) VALUES (?)`, id)
return err
}
func (s *SQLite) MarkFailed(ctx context.Context, source, mediaType, id string) error {
s.mu.Lock()
defer s.mu.Unlock()
_, err := s.db.ExecContext(
ctx,
`INSERT OR IGNORE INTO failed_downloads(source, media_type, id) VALUES (?, ?, ?)`,
source,
mediaType,
id,
)
return err
}
func (s *SQLite) Close() error {
if s.db == nil {
return nil
}
return s.db.Close()
}

View File

@@ -0,0 +1,42 @@
package store
import (
"context"
"path/filepath"
"testing"
)
func TestSQLiteStore(t *testing.T) {
ctx := context.Background()
path := filepath.Join(t.TempDir(), "test.db")
s, err := NewSQLite(path)
if err != nil {
t.Fatalf("NewSQLite() error = %v", err)
}
defer func() { _ = s.Close() }()
ok, err := s.IsDownloaded(ctx, "a")
if err != nil {
t.Fatalf("IsDownloaded() error = %v", err)
}
if ok {
t.Fatalf("expected not downloaded")
}
if err = s.MarkDownloaded(ctx, "a"); err != nil {
t.Fatalf("MarkDownloaded() error = %v", err)
}
ok, err = s.IsDownloaded(ctx, "a")
if err != nil {
t.Fatalf("IsDownloaded() error = %v", err)
}
if !ok {
t.Fatalf("expected downloaded")
}
if err = s.MarkFailed(ctx, "qobuz", "track", "1"); err != nil {
t.Fatalf("MarkFailed() error = %v", err)
}
}

32
internal/store/store.go Normal file
View File

@@ -0,0 +1,32 @@
package store
import "context"
type Database interface {
IsDownloaded(ctx context.Context, id string) (bool, error)
MarkDownloaded(ctx context.Context, id string) error
MarkFailed(ctx context.Context, source, mediaType, id string) error
Close() error
}
type Dummy struct{}
func NewDummy() *Dummy {
return &Dummy{}
}
func (d *Dummy) IsDownloaded(context.Context, string) (bool, error) {
return false, nil
}
func (d *Dummy) MarkDownloaded(context.Context, string) error {
return nil
}
func (d *Dummy) MarkFailed(context.Context, string, string, string) error {
return nil
}
func (d *Dummy) Close() error {
return nil
}