Basics of a web interface
Some checks failed
Build / build (push) Failing after 1h10m17s

This commit is contained in:
Sebastiaan de Schaetzen 2025-03-12 08:43:07 +01:00
parent 53aaf09c76
commit 6fd1850e20
10 changed files with 165 additions and 108 deletions

View File

@ -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)

View File

@ -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

View File

@ -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()
}

3
go.mod
View File

@ -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

6
go.sum
View File

@ -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=

20
html_debug.go Normal file
View File

@ -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)
}

139
main.go
View File

@ -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()
}

9
static/style.css Normal file
View File

@ -0,0 +1,9 @@
body {
background-color: black;
color: white;
font-family: sans-serif;
}
h1 {
text-transform: uppercase;
}

11
web/index.gohtml Normal file
View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Viva++</title>
<link href="/static/style.css" rel="stylesheet">
</head>
<body>
<h1>Viva++</h1>
</body>
</html>

24
webview.go Normal file
View File

@ -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)
}
}