Files
mangaGetter/internal/database/database.go
Pablu23 a8ba8728dc Added Last Chapter functionality to see how many more chapters you have, also kinda works as update Status bar
changed it to only load latest chapter instead of all chapters for manga
Added develop and release db from different locations
Added stopwatch for future performance improvement metrics
2024-03-01 14:31:38 +01:00

210 lines
4.6 KiB
Go

package database
import (
"bytes"
"database/sql"
_ "embed"
"fmt"
"sync"
_ "github.com/mattn/go-sqlite3"
)
type Manga struct {
Id int
Title string
TimeStampUnix int64
Thumbnail *bytes.Buffer
// Not in DB
LatestChapter *Chapter
}
type Chapter struct {
Id int
Manga *Manga
Url string
Name string
Number int
TimeStampUnix int64
}
type Manager struct {
ConnectionString string
db *sql.DB
Rw *sync.Mutex
Mangas map[int]*Manga
Chapters map[int]*Chapter
CreateIfNotExists bool
}
func NewDatabase(connectionString string, createIfNotExists bool) Manager {
return Manager{
ConnectionString: connectionString,
Rw: &sync.Mutex{},
Mangas: make(map[int]*Manga),
Chapters: make(map[int]*Chapter),
CreateIfNotExists: createIfNotExists,
}
}
func (dbMgr *Manager) Open() error {
db, err := sql.Open("sqlite3", dbMgr.ConnectionString)
if err != nil {
return err
}
dbMgr.db = db
if dbMgr.CreateIfNotExists {
err = dbMgr.createDatabaseIfNotExists()
if err != nil {
return err
}
}
err = dbMgr.load()
return err
}
func (dbMgr *Manager) Close() error {
err := dbMgr.db.Close()
if err != nil {
return err
}
dbMgr.Mangas = nil
dbMgr.Chapters = nil
dbMgr.db = nil
return nil
}
func (dbMgr *Manager) Delete(mangaId int) error {
db := dbMgr.db
fmt.Println("Locking Rw in database.go:84")
dbMgr.Rw.Lock()
defer func() {
fmt.Println("Unlocking Rw in database.go:87")
dbMgr.Rw.Unlock()
}()
_, err := db.Exec("DELETE from Chapter where MangaID = ?", mangaId)
if err != nil {
return err
}
_, err = db.Exec("DELETE from Manga where ID = ?", mangaId)
if err != nil {
return err
}
for i, chapter := range dbMgr.Chapters {
if chapter.Manga.Id == mangaId {
delete(dbMgr.Chapters, i)
}
}
delete(dbMgr.Mangas, mangaId)
return nil
}
func (dbMgr *Manager) Save() error {
db := dbMgr.db
fmt.Println("Locking Rw in database.go:113")
dbMgr.Rw.Lock()
defer func() {
fmt.Println("Unlocking Rw in database.go:116")
dbMgr.Rw.Unlock()
}()
for _, m := range dbMgr.Mangas {
count := 0
err := db.QueryRow("SELECT COUNT(*) FROM Manga where ID = ?", m.Id).Scan(&count)
if err != nil {
return err
}
if count == 0 {
if m.Thumbnail != nil {
_, err := db.Exec("INSERT INTO Manga(ID, Title, TimeStampUnixEpoch, Thumbnail) values(?, ?, ?, ?)", m.Id, m.Title, m.TimeStampUnix, m.Thumbnail.Bytes())
if err != nil {
return err
}
} else {
_, err := db.Exec("INSERT INTO Manga(ID, Title, TimeStampUnixEpoch ) values(?, ?, ?)", m.Id, m.Title, m.TimeStampUnix)
if err != nil {
return err
}
}
} else {
_, err := db.Exec("UPDATE Manga set Title = ?, TimeStampUnixEpoch = ? WHERE ID = ?", m.Title, m.TimeStampUnix, m.Id)
if err != nil {
return err
}
}
}
for _, c := range dbMgr.Chapters {
count := 0
err := db.QueryRow("SELECT COUNT(*) FROM Chapter where ID = ?", c.Id).Scan(&count)
if err != nil {
return err
}
if count == 0 {
_, err := db.Exec("INSERT INTO Chapter(ID, MangaID, Url, Name, Number, TimeStampUnixEpoch) VALUES (?, ?, ?, ?, ?, ?)", c.Id, c.Manga.Id, c.Url, c.Name, c.Number, c.TimeStampUnix)
if err != nil {
return err
}
} else {
_, err = db.Exec("UPDATE Chapter set Name = ?, Url = ?, Number = ?, TimeStampUnixEpoch = ? where ID = ?", c.Name, c.Url, c.Number, c.TimeStampUnix, c.Id)
if err != nil {
return err
}
}
}
return nil
}
//go:embed createDb.sql
var createSql string
func (dbMgr *Manager) createDatabaseIfNotExists() error {
_, err := dbMgr.db.Exec(createSql)
return err
}
func (dbMgr *Manager) load() error {
db := dbMgr.db
fmt.Println("Locking Rw in database.go:180")
dbMgr.Rw.Lock()
defer func() {
fmt.Println("Unlocking Rw in database.go:183")
dbMgr.Rw.Unlock()
}()
rows, err := db.Query("SELECT * FROM Manga")
if err != nil {
return err
}
for rows.Next() {
manga := Manga{}
var thumbnail []byte
if err = rows.Scan(&manga.Id, &manga.Title, &manga.TimeStampUnix, &thumbnail); err != nil {
return err
}
manga.Thumbnail = bytes.NewBuffer(thumbnail)
latestChapter := db.QueryRow("SELECT Id, Url, Name, Number, TimeStampUnixEpoch FROM Chapter where MangaID = ? ORDER BY TimeStampUnixEpoch desc LIMIT 1", manga.Id)
chapter := Chapter{}
if err = latestChapter.Scan(&chapter.Id, &chapter.Url, &chapter.Name, &chapter.Number, &chapter.TimeStampUnix); err != nil {
return err
}
dbMgr.Chapters[chapter.Id] = &chapter
manga.LatestChapter = &chapter
dbMgr.Mangas[manga.Id] = &manga
}
return nil
}