bel 2025-06-03 23:38:12 -06:00
parent 2cccb891bc
commit 242c74b827
1 changed files with 104 additions and 39 deletions

143
main.go
View File

@ -2,12 +2,14 @@ package main
import ( import (
"database/sql" "database/sql"
"fmt" "fmt"
"log"
"io" "io"
"io/ioutil" "io/ioutil"
"log"
"os" "os"
"path" "path"
"slices"
"strings"
_ "modernc.org/sqlite" _ "modernc.org/sqlite"
) )
@ -21,17 +23,19 @@ func main() {
data := os.Args[1] data := os.Args[1]
fromU := os.Args[2] fromU := os.Args[2]
toU := os.Args[3] toU := os.Args[3]
_ = toU _ = toU
workd, err := ioutil.TempDir(os.TempDir(), "jellyfin-user-clone.*") workd, err := ioutil.TempDir(os.TempDir(), "jellyfin-user-clone.*")
if err != nil { if err != nil {
panic(err) panic(err)
} }
if err := cp(path.Join(data, jellyDB), path.Join(workd, jellyDB)); err != nil { {
panic(err) if err := cp(path.Join(data, jellyDB), path.Join(workd, jellyDB)); err != nil {
} else if err := cp(path.Join(data, libraryDB), path.Join(workd, libraryDB)); err != nil { panic(err)
panic(err) } else if err := cp(path.Join(data, libraryDB), path.Join(workd, libraryDB)); err != nil {
panic(err)
}
} }
jellyfinDB, err := sql.Open("sqlite", path.Join(workd, jellyDB)) jellyfinDB, err := sql.Open("sqlite", path.Join(workd, jellyDB))
@ -40,17 +44,65 @@ func main() {
} }
defer jellyfinDB.Close() defer jellyfinDB.Close()
fromUUID, err := SelectOne[string](jellyfinDB, `SELECT Id FROM Users WHERE Username = $1`, fromU) fromUUID, err := SelectOne[string](jellyfinDB, `SELECT Id FROM Users WHERE Username = $1`, fromU)
if err != nil { if err != nil {
panic(err) panic(err)
} }
fromID, err := SelectOne[int](jellyfinDB, `SELECT InternalId FROM Users WHERE Id = $1`, fromUUID)
if err != nil {
panic(err)
}
log.Println(fromU, fromUUID, fromID)
fromID, err := SelectOne[int](jellyfinDB, `SELECT InternalId FROM Users WHERE Id = $1`, fromUUID) if n, err := SelectOne[int](jellyfinDB, `SELECT COUNT(*) FROM Users WHERE Username = $1`, toU); err != nil {
if err != nil { panic(err)
panic(err) } else if n == 1 {
} } else if err := Exec(jellyfinDB, `INSERT INTO Users () SELECT * FROM Users WHERE Id = $1`, fromUUID); err != nil {
panic(err)
}
log.Println(fromU, fromUUID, fromID) panic("not impl get toU, toUUID")
tables, err := Tables(jellyfinDB)
if err != nil {
panic(err)
}
for _, table := range tables {
columns, err := Columns(jellyfinDB, table)
if err != nil {
panic(err)
}
log.Println(table, columns)
userColumns := slices.DeleteFunc(slices.Clone(columns), func(s string) bool {
return !slices.Contains([]string{
"user",
"userid",
}, strings.ToLower(s))
})
if len(userColumns) == 0 {
continue
}
for _, column := range userColumns {
if n, err := SelectOne[int](jellyfinDB, fmt.Sprintf(`SELECT COUNT(*) FROM %q WHERE %q = $1`, table, column), fromID); err != nil {
panic(err)
} else if n > 0 {
notThisColumn := strings.Join(slices.DeleteFunc(slices.Clone(columns), func(s string) bool { return s == column }), ", ")
if err := Exec(jellyfinDB, fmt.Sprintf(`INSERT INTO %q (%q, %s) SELECT $2, %s FROM %q WHERE %q = $1`, table, column, notThisColumn, notThisColumn, table, column), fromID, toID); err != nil {
panic(err)
}
} else if n, err := SelectOne[int](jellyfinDB, fmt.Sprintf(`SELECT COUNT(*) FROM %q WHERE %q = $1`, table, column), fromUUID); err != nil {
panic(err)
} else if n > 0 {
panic("not impl: col is uuid")
}
}
log.Println(table, userColumns)
panic("not impl")
}
} }
func cp(from, to string) error { func cp(from, to string) error {
@ -70,36 +122,49 @@ func cp(from, to string) error {
return err return err
} }
func Tables(db *sql.DB) ([]string, error) {
return Select[string](jellyfinDB, `SELECT name FROM sqlite_schema WHERE type = 'table' AND name NOT LIKE 'sqlite_%'`)
}
func Columns(db *sql.DB, table string) ([]string, error) {
return Select[string](jellyfinDB, `SELECT name FROM PRAGMA_TABLE_INFO($1)`, table)
}
func Exec(db *sql.DB, q string, args ...any) error {
_, err := db.Exec(q, args...)
return err
}
func SelectOne[T any](db *sql.DB, q string, args ...any) (T, error) { func SelectOne[T any](db *sql.DB, q string, args ...any) (T, error) {
var some T var some T
results, err := Select[T](db, q, args...) results, err := Select[T](db, q, args...)
if err != nil { if err != nil {
return some, err return some, err
} }
if len(results) != 1 { if len(results) != 1 {
return some, fmt.Errorf("expected 1 result but got %d (%+v)", len(results), results) return some, fmt.Errorf("expected 1 result but got %d (%+v)", len(results), results)
} }
return results[0], nil return results[0], nil
} }
func Select[T any](db *sql.DB, q string, args ...any) ([]T, error) { func Select[T any](db *sql.DB, q string, args ...any) ([]T, error) {
rows, err := db.Query(q, args...) rows, err := db.Query(q, args...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
results := []T{} results := []T{}
for rows.Next() { for rows.Next() {
var some T var some T
if err := rows.Scan(&some); err != nil { if err := rows.Scan(&some); err != nil {
return nil, err return nil, err
} }
results = append(results, some) results = append(results, some)
} }
return results, rows.Err() return results, rows.Err()
} }