mirror of
https://git.sr.ht/~joren/streamrip-go
synced 2026-06-17 15:05:39 +02:00
fix source-aware download tracking and filter/path regressions
Make download dedupe source-specific to prevent cross-provider ID collisions. Also correct non-remaster filtering, avoid FLAC tagging on non-FLAC files, and use album IDs for singles folder templating.
This commit is contained in:
@@ -28,8 +28,11 @@ func NewSQLite(path string) (*SQLite, error) {
|
||||
}
|
||||
|
||||
func (s *SQLite) init() error {
|
||||
if err := s.ensureDownloadsSchema(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -47,23 +50,101 @@ func (s *SQLite) init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLite) IsDownloaded(ctx context.Context, id string) (bool, error) {
|
||||
func (s *SQLite) ensureDownloadsSchema() error {
|
||||
rows, err := s.db.Query(`PRAGMA table_info(downloads)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
hasTable := false
|
||||
hasSource := false
|
||||
for rows.Next() {
|
||||
hasTable = true
|
||||
var (
|
||||
cid int
|
||||
name string
|
||||
colType string
|
||||
notNull int
|
||||
defaultV sql.NullString
|
||||
pkOrdinal int
|
||||
)
|
||||
if err = rows.Scan(&cid, &name, &colType, ¬Null, &defaultV, &pkOrdinal); err != nil {
|
||||
return err
|
||||
}
|
||||
if name == "source" {
|
||||
hasSource = true
|
||||
}
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !hasTable {
|
||||
_, err = s.db.Exec(`CREATE TABLE downloads (
|
||||
source TEXT NOT NULL,
|
||||
id TEXT NOT NULL,
|
||||
PRIMARY KEY (source, id)
|
||||
)`)
|
||||
return err
|
||||
}
|
||||
|
||||
if hasSource {
|
||||
_, err = s.db.Exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_downloads_source_id ON downloads(source, id)`)
|
||||
return err
|
||||
}
|
||||
|
||||
tx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
}
|
||||
}()
|
||||
|
||||
if _, err = tx.Exec(`DROP TABLE IF EXISTS downloads_legacy`); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = tx.Exec(`ALTER TABLE downloads RENAME TO downloads_legacy`); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = tx.Exec(`CREATE TABLE downloads (
|
||||
source TEXT NOT NULL,
|
||||
id TEXT NOT NULL,
|
||||
PRIMARY KEY (source, id)
|
||||
)`); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = tx.Exec(`INSERT OR IGNORE INTO downloads(source, id) SELECT '', id FROM downloads_legacy`); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = tx.Exec(`DROP TABLE downloads_legacy`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SQLite) IsDownloaded(ctx context.Context, source, 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)
|
||||
err := s.db.QueryRowContext(ctx, `SELECT COUNT(1) FROM downloads WHERE source = ? AND id = ?`, source, id).Scan(&count)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func (s *SQLite) MarkDownloaded(ctx context.Context, id string) error {
|
||||
func (s *SQLite) MarkDownloaded(ctx context.Context, source, id string) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
_, err := s.db.ExecContext(ctx, `INSERT OR IGNORE INTO downloads(id) VALUES (?)`, id)
|
||||
_, err := s.db.ExecContext(ctx, `INSERT OR IGNORE INTO downloads(source, id) VALUES (?, ?)`, source, id)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ func TestSQLiteStore(t *testing.T) {
|
||||
}
|
||||
defer func() { _ = s.Close() }()
|
||||
|
||||
ok, err := s.IsDownloaded(ctx, "a")
|
||||
ok, err := s.IsDownloaded(ctx, "qobuz", "a")
|
||||
if err != nil {
|
||||
t.Fatalf("IsDownloaded() error = %v", err)
|
||||
}
|
||||
@@ -24,11 +24,11 @@ func TestSQLiteStore(t *testing.T) {
|
||||
t.Fatalf("expected not downloaded")
|
||||
}
|
||||
|
||||
if err = s.MarkDownloaded(ctx, "a"); err != nil {
|
||||
if err = s.MarkDownloaded(ctx, "qobuz", "a"); err != nil {
|
||||
t.Fatalf("MarkDownloaded() error = %v", err)
|
||||
}
|
||||
|
||||
ok, err = s.IsDownloaded(ctx, "a")
|
||||
ok, err = s.IsDownloaded(ctx, "qobuz", "a")
|
||||
if err != nil {
|
||||
t.Fatalf("IsDownloaded() error = %v", err)
|
||||
}
|
||||
@@ -36,6 +36,14 @@ func TestSQLiteStore(t *testing.T) {
|
||||
t.Fatalf("expected downloaded")
|
||||
}
|
||||
|
||||
ok, err = s.IsDownloaded(ctx, "tidal", "a")
|
||||
if err != nil {
|
||||
t.Fatalf("IsDownloaded() error = %v", err)
|
||||
}
|
||||
if ok {
|
||||
t.Fatalf("expected source-specific download tracking")
|
||||
}
|
||||
|
||||
if err = s.MarkFailed(ctx, "qobuz", "track", "1"); err != nil {
|
||||
t.Fatalf("MarkFailed() error = %v", err)
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ package store
|
||||
import "context"
|
||||
|
||||
type Database interface {
|
||||
IsDownloaded(ctx context.Context, id string) (bool, error)
|
||||
MarkDownloaded(ctx context.Context, id string) error
|
||||
IsDownloaded(ctx context.Context, source, id string) (bool, error)
|
||||
MarkDownloaded(ctx context.Context, source, id string) error
|
||||
MarkFailed(ctx context.Context, source, mediaType, id string) error
|
||||
Close() error
|
||||
}
|
||||
@@ -15,11 +15,11 @@ func NewDummy() *Dummy {
|
||||
return &Dummy{}
|
||||
}
|
||||
|
||||
func (d *Dummy) IsDownloaded(context.Context, string) (bool, error) {
|
||||
func (d *Dummy) IsDownloaded(context.Context, string, string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (d *Dummy) MarkDownloaded(context.Context, string) error {
|
||||
func (d *Dummy) MarkDownloaded(context.Context, string, string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user