diff --git a/database.go b/database.go index 8a9136f..1c94b9a 100644 --- a/database.go +++ b/database.go @@ -17,6 +17,10 @@ func openDatabase() *mysqlite.Db { databaseSource = "videos.db3" } + return openDatabaseSource(databaseSource) +} + +func openDatabaseSource(databaseSource string) *mysqlite.Db { db, err := mysqlite.OpenDb(databaseSource) if err != nil { log.Fatalf("error opening database: %v", err) diff --git a/episodes.go b/episodes.go index b5fe929..05a5a8e 100644 --- a/episodes.go +++ b/episodes.go @@ -1,12 +1,12 @@ package main import ( - "database/sql" "fmt" + "gitea.seeseepuff.be/seeseemelk/mysqlite" "log" ) -func CalculateEpisodeNumbers(db *sql.DB) error { +func CalculateEpisodeNumbers(db *mysqlite.Db) error { log.Printf("Calculating episode numbers") // Start a transaction @@ -14,50 +14,50 @@ func CalculateEpisodeNumbers(db *sql.DB) error { if err != nil { return fmt.Errorf("error starting transaction: %w", err) } - defer tx.Rollback() + defer tx.MustRollback() - // First find all days that still need episodes numbers - rows, err := tx.Query(` + query := ` select v.upload_date, ifnull( (select max(episode) from videos where upload_date = v.upload_date), substr(upload_date, 6, 2) || substr(upload_date, 9, 2) || '00') - from videos v where episode is null group by upload_date`) - if err != nil { - return fmt.Errorf("error retrieving rows: %w", err) - } + from videos v where episode is null group by upload_date + ` // Loop over each year and set the missing episode numbers - for rows.Next() { + for row := range tx.Query(query).Range(&err) { var uploadDate string var lastEpisode int - err = rows.Scan(&uploadDate, &lastEpisode) + err = row.Scan(&uploadDate, &lastEpisode) log.Printf("Last episode for %s is %d", uploadDate, lastEpisode) if err != nil { return fmt.Errorf("error getting row: %w", err) } // Find all episodes that need updating - episodes, err := tx.Query("select id from videos where upload_date = ? and episode is null order by run, id desc", uploadDate) - if err != nil { - return fmt.Errorf("error retrieving episodes: %w", err) - } + query := tx.Query("select id from videos where upload_date = ? and episode is null order by run, id desc").Bind(uploadDate) // Update each episode - for episodes.Next() { + for episode := range query.Range(&err) { var episodeId int - err = episodes.Scan(&episodeId) + err = episode.Scan(&episodeId) if err != nil { return fmt.Errorf("error retrieving episode id: %w", err) } // Set the episode ID lastEpisode++ - _, err = tx.Exec("update videos set episode = ? where id = ?", &lastEpisode, &episodeId) + err = tx.Query("update videos set episode = ? where id = ?").Bind(&lastEpisode, &episodeId).Exec() if err != nil { return fmt.Errorf("error updating episode id: %w", err) } } + if err != nil { + return fmt.Errorf("error retrieving episodes: %w", err) + } + } + if err != nil { + return fmt.Errorf("error fetching episodes: %w", err) } // Commit all changes diff --git a/episodes_test.go b/episodes_test.go index ab569d2..c07f12a 100644 --- a/episodes_test.go +++ b/episodes_test.go @@ -6,28 +6,22 @@ func TestCalculateEpisodeNumbers(t *testing.T) { db := openDatabaseSource(":memory:") // Ensure the DB is empty - _, err := db.Exec("delete from videos") - if err != nil { - t.Error(err) - } + db.Query("delete from videos").MustExec() - _, err = db.Exec(`insert into videos (title, url, upload_date, year, episode, run) values + db.Query(`insert into videos (title, url, upload_date, year, episode, run) values ('Video A', 'a', '2025-01-04', 2025, 10401, 1), ('Video D', 'd', '2025-01-05', 2025, null, 2), ('Video C', 'c', '2025-01-04', 2025, null, 2), ('Video B', 'b', '2025-01-04', 2025, null, 2), ('Video E', 'e', '2025-10-01', 2025, null, 3), - ('Old Video', 'z', '2020-12-31', 2020, 100, 1)`) + ('Old Video', 'z', '2020-12-31', 2020, 100, 1)`).MustExec() + + err := CalculateEpisodeNumbers(db) if err != nil { t.Error(err) } - err = CalculateEpisodeNumbers(db) - if err != nil { - t.Error(err) - } - - rows, err := db.Query("select title, episode from videos where year = 2025") + rows, err := db.Query("select title, episode from videos where year = 2025").ScanMulti() if err != nil { t.Error(err) } @@ -46,7 +40,7 @@ func TestCalculateEpisodeNumbers(t *testing.T) { for _, expected := range expectedList { var title string var episode int - rows.Next() + rows.MustNext() err = rows.Scan(&title, &episode) if err != nil { t.Error(err) @@ -59,4 +53,5 @@ func TestCalculateEpisodeNumbers(t *testing.T) { t.Errorf("Episode is %d, expected %d for video %s", episode, expected.Episode, title) } } + rows.MustFinish() } diff --git a/go.mod b/go.mod index d0576b3..f86bbda 100644 --- a/go.mod +++ b/go.mod @@ -5,12 +5,13 @@ go 1.24 toolchain go1.24.0 require ( - gitea.seeseepuff.be/seeseemelk/mysqlite v0.3.0 + gitea.seeseepuff.be/seeseemelk/mysqlite v0.4.0 github.com/playwright-community/playwright-go v0.4902.0 ) require ( github.com/deckarep/golang-set/v2 v2.7.0 // indirect + github.com/donseba/go-htmx v1.12.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/go-jose/go-jose/v3 v3.0.3 // indirect github.com/go-stack/stack v1.8.1 // indirect diff --git a/go.sum b/go.sum index fc2cb2b..e51182c 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,12 @@ -gitea.seeseepuff.be/seeseemelk/mysqlite v0.3.0 h1:T2TjZj7hvb3KF0zYf/uzidprcDJIBPFi3QcN+gmyutM= -gitea.seeseepuff.be/seeseemelk/mysqlite v0.3.0/go.mod h1:cgswydOxJjMlNwfcBIXnKjr47LwXnMT9BInkiHb0tXE= +gitea.seeseepuff.be/seeseemelk/mysqlite v0.4.0 h1:jBj4qsSJCz7XOBakh2Rl4Pggvv5hPpR8iLcvxwdQ4NQ= +gitea.seeseepuff.be/seeseemelk/mysqlite v0.4.0/go.mod h1:cgswydOxJjMlNwfcBIXnKjr47LwXnMT9BInkiHb0tXE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set/v2 v2.7.0 h1:gIloKvD7yH2oip4VLhsv3JyLLFnC0Y2mlusgcvJYW5k= github.com/deckarep/golang-set/v2 v2.7.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/donseba/go-htmx v1.12.0 h1:7tESER0uxaqsuGMv3yP3pK1drfBUXM6apG4H7/3+IgE= +github.com/donseba/go-htmx v1.12.0/go.mod h1:8PTAYvNKf8+QYis+DpAsggKz+sa2qljtMgvdAeNBh5s= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= diff --git a/html_debug.go b/html_debug.go new file mode 100644 index 0000000..f7bec19 --- /dev/null +++ b/html_debug.go @@ -0,0 +1,20 @@ +// / +build !release +package main + +import ( + "html/template" + "io" + "net/http" +) + +func staticFileServer() http.Handler { + return http.FileServer(http.Dir("./static")) +} + +func renderTemplate(wr io.Writer) error { + templates, err := template.ParseGlob("web/*.gohtml") + if err != nil { + return err + } + return templates.Execute(wr, nil) +} diff --git a/main.go b/main.go index 2ca8949..8f27242 100644 --- a/main.go +++ b/main.go @@ -1,78 +1,69 @@ package main -import ( - "encoding/base64" - "flag" - "github.com/playwright-community/playwright-go" - "log" - "os" - "strconv" - "time" -) - func main() { - var err error - var onlyInstall = flag.Bool("install", false, "install the required browser and do nothing else") - flag.Parse() - - options := &playwright.RunOptions{ - Browsers: []string{"firefox"}, - } - err = playwright.Install(options) - if err != nil { - log.Panicf("error installing playwright: %v", err) - } - - if *onlyInstall { - return - } - - db := openDatabase() - defer db.Close() - - sleepTimeStr := os.Getenv("VIVAPLUS_SLEEPTIME") - sleepTime := 15 - if sleepTimeStr != "" { - sleepTime, err = strconv.Atoi(sleepTimeStr) - if err != nil { - log.Fatalf("error parsing sleep time: %v", err) - } - } - - w := NewWebClient(options) - username := os.Getenv("VIVAPLUS_USER") - password, err := base64.StdEncoding.DecodeString(os.Getenv("VIVAPLUS_PASS")) - if err != nil { - log.Fatalf("error decoding password: %v", err) - } - - err = w.Login(username, string(password)) - if err != nil { - log.Fatalf("error login in: %v", err) - } - - for { - err = w.DiscoverAllVideos(db) - if err != nil { - panic(err) - } - - err = w.FetchVideoMetadata(db) - if err != nil { - panic(err) - } - - err = CalculateEpisodeNumbers(db) - if err != nil { - panic(err) - } - - err = DownloadAllVideos(db) - if err != nil { - panic(err) - } - - log.Printf("Sleeping %d minutes until next run", sleepTime) - time.Sleep(time.Duration(sleepTime) * time.Minute) - } + //var err error + //var onlyInstall = flag.Bool("install", false, "install the required browser and do nothing else") + //flag.Parse() + // + //options := &playwright.RunOptions{ + // Browsers: []string{"firefox"}, + //} + //err = playwright.Install(options) + //if err != nil { + // log.Panicf("error installing playwright: %v", err) + //} + // + //if *onlyInstall { + // return + //} + // + //db := openDatabase() + //defer db.Close() + // + //sleepTimeStr := os.Getenv("VIVAPLUS_SLEEPTIME") + //sleepTime := 15 + //if sleepTimeStr != "" { + // sleepTime, err = strconv.Atoi(sleepTimeStr) + // if err != nil { + // log.Fatalf("error parsing sleep time: %v", err) + // } + //} + // + //w := NewWebClient(options) + //username := os.Getenv("VIVAPLUS_USER") + //password, err := base64.StdEncoding.DecodeString(os.Getenv("VIVAPLUS_PASS")) + //if err != nil { + // log.Fatalf("error decoding password: %v", err) + //} + // + //err = w.Login(username, string(password)) + //if err != nil { + // log.Fatalf("error login in: %v", err) + //} + // + //for { + // err = w.DiscoverAllVideos(db) + // if err != nil { + // panic(err) + // } + // + // err = w.FetchVideoMetadata(db) + // if err != nil { + // panic(err) + // } + // + // err = CalculateEpisodeNumbers(db) + // if err != nil { + // panic(err) + // } + // + // err = DownloadAllVideos(db) + // if err != nil { + // panic(err) + // } + // + // log.Printf("Sleeping %d minutes until next run", sleepTime) + // time.Sleep(time.Duration(sleepTime) * time.Minute) + //} + serveWebview() } diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..494f64a --- /dev/null +++ b/static/style.css @@ -0,0 +1,9 @@ +body { + background-color: black; + color: white; + font-family: sans-serif; +} + +h1 { + text-transform: uppercase; +} diff --git a/web/index.gohtml b/web/index.gohtml new file mode 100644 index 0000000..86f28c8 --- /dev/null +++ b/web/index.gohtml @@ -0,0 +1,11 @@ + + + + + Viva++ + + + +

Viva++

+ + diff --git a/webview.go b/webview.go new file mode 100644 index 0000000..827b609 --- /dev/null +++ b/webview.go @@ -0,0 +1,24 @@ +package main + +import ( + "log" + "net/http" +) + +func serveWebview() { + addr := "localhost:8081" + + log.Printf("Listening on %s", addr) + http.Handle("/static/", http.StripPrefix("/static/", staticFileServer())) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + err := renderTemplate(w) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + }) + + err := http.ListenAndServe(addr, nil) + if err != nil { + panic(err) + } +}