Significantly simplified adding new Database Tables

This commit is contained in:
Pablu23
2024-04-02 18:22:54 +02:00
parent ce878efce3
commit c83a10823d
7 changed files with 291 additions and 308 deletions

View File

@@ -33,15 +33,17 @@ func main() {
} }
}() }()
port := 8080
go func() { go func() {
time.Sleep(300 * time.Millisecond) time.Sleep(300 * time.Millisecond)
err := open("http://localhost:8000") err := open(fmt.Sprintf("http://localhost:%d", port))
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
}() }()
err = s.Start() err = s.Start(port)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@@ -2,61 +2,56 @@ package database
import ( import (
"database/sql" "database/sql"
"sync"
) )
type Chapter struct { type Chapter struct {
Id int Id int
Manga *Manga MangaId int
Url string Url string
Name string Name string
Number int Number int
TimeStampUnix int64 TimeStampUnix int64
} }
type ChapterTable[K comparable] struct { func NewChapter(id int, mangaId int, url string, name string, number int, timeStampUnix int64) Chapter {
mutex sync.Mutex return Chapter{
chapters map[K]Chapter Id: id,
updated map[K]DbStatus MangaId: mangaId,
Url: url,
Name: name,
Number: number,
TimeStampUnix: timeStampUnix,
}
} }
func (c *ChapterTable[K]) Get(key K) (Chapter, bool) { func updateChapter(db *sql.DB, c *Chapter) error {
c.mutex.Lock() _, err := db.Exec("UPDATE Chapter set Name = ?, Url = ?, Number = ?, TimeStampUnixEpoch = ? where ID = ?", c.Name, c.Url, c.Number, c.TimeStampUnix, c.Id)
defer c.mutex.Unlock() return err
val, ok := c.chapters[key]
return val, ok
} }
func (c *ChapterTable[K]) Set(key K, new Chapter) { func insertChapter(db *sql.DB, c *Chapter) error {
c.mutex.Lock() _, err := db.Exec("INSERT INTO Chapter(ID, MangaID, Url, Name, Number, TimeStampUnixEpoch) VALUES (?, ?, ?, ?, ?, ?)", c.Id, c.MangaId, c.Url, c.Name, c.Number, c.TimeStampUnix)
defer c.mutex.Unlock() return err
val, ok := c.updated[key]
if ok && val == Loaded {
c.updated[key] = Updated
} else {
c.updated[key] = New
}
c.chapters[key] = new
} }
func (c *ChapterTable[K]) All() []Chapter { func loadChapters(db *sql.DB) (map[int]Chapter, error) {
c.mutex.Lock() rows, err := db.Query("SELECT Id, MangaID, Url, Name, Number, TimeStampUnixEpoch FROM Chapter")
defer c.mutex.Unlock() if err != nil {
res := make([]Chapter, len(c.chapters)) return nil, err
counter := 0
for _, chapter := range c.chapters {
res[counter] = chapter
counter++
} }
return res res := make(map[int]Chapter)
for rows.Next() {
chapter := Chapter{}
if err = rows.Scan(&chapter.Id, &chapter.MangaId, &chapter.Url, &chapter.Name, &chapter.Number, &chapter.TimeStampUnix); err != nil {
return nil, err
}
res[chapter.Id] = chapter
}
return res, err
} }
func (c *ChapterTable[K]) Save(db *sql.DB) error { func deleteChapter(db *sql.DB, key int) error {
//TODO implement me _, err := db.Exec("DELETE from Manga where ID = ?", key)
panic("implement me") return err
}
func (c *ChapterTable[K]) Load(db *sql.DB) error {
//TODO implement me
panic("implement me")
} }

View File

@@ -1,32 +1,25 @@
package database package database
import ( import (
"bytes"
"database/sql" "database/sql"
_ "embed" _ "embed"
"fmt"
"sync"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
) )
type Manager struct { type Manager struct {
ConnectionString string ConnectionString string
db *sql.DB db *sql.DB
Mangas DbTable[int, Manga]
Rw *sync.Mutex Chapters DbTable[int, Chapter]
Mangas map[int]*Manga
Chapters map[int]*Chapter
CreateIfNotExists bool CreateIfNotExists bool
} }
func NewDatabase(connectionString string, createIfNotExists bool) Manager { func NewDatabase(connectionString string, createIfNotExists bool) Manager {
return Manager{ return Manager{
ConnectionString: connectionString, ConnectionString: connectionString,
Rw: &sync.Mutex{}, db: nil,
Mangas: make(map[int]*Manga), Mangas: NewDbTable[int, Manga](updateManga, insertManga, loadMangas, deleteManga),
Chapters: make(map[int]*Chapter), Chapters: NewDbTable[int, Chapter](updateChapter, insertChapter, loadChapters, deleteChapter),
CreateIfNotExists: createIfNotExists, CreateIfNotExists: createIfNotExists,
} }
} }
@@ -53,110 +46,37 @@ func (dbMgr *Manager) Close() error {
return err return err
} }
dbMgr.Mangas = nil
dbMgr.Chapters = nil
dbMgr.db = nil dbMgr.db = nil
return nil return nil
} }
func (dbMgr *Manager) Delete(mangaId int) error { func (dbMgr *Manager) Delete(mangaId int) error {
db := dbMgr.db dbMgr.Mangas.Get(mangaId)
fmt.Println("Locking Rw in database.go:84") err := dbMgr.Mangas.Delete(dbMgr.db, mangaId)
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 { if err != nil {
return err return err
} }
_, err = db.Exec("DELETE from Manga where ID = ?", mangaId) chapters := dbMgr.Chapters.All()
for i, chapter := range chapters {
if chapter.MangaId == mangaId {
err := dbMgr.Chapters.Delete(dbMgr.db, i)
if err != nil { if err != nil {
return err return err
} }
}
}
for i, chapter := range dbMgr.Chapters {
if chapter.Manga.Id == mangaId {
delete(dbMgr.Chapters, i)
}
}
delete(dbMgr.Mangas, mangaId)
return nil return nil
} }
func (dbMgr *Manager) Save() error { func (dbMgr *Manager) Save() error {
db := dbMgr.db err := dbMgr.Mangas.Save(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 { if err != nil {
return err return err
} }
if count == 0 { return dbMgr.Chapters.Save(dbMgr.db)
if m.Thumbnail != nil {
_, err := db.Exec("INSERT INTO Manga(ID, Title, TimeStampUnixEpoch, Thumbnail, LatestAvailableChapter) values(?, ?, ?, ?, ?)", m.Id, m.Title, m.TimeStampUnix, m.Thumbnail.Bytes(), m.LastChapterNum)
if err != nil {
return err
}
} else {
_, err := db.Exec("INSERT INTO Manga(ID, Title, TimeStampUnixEpoch, LatestAvailableChapter) values(?, ?, ?, ?)", m.Id, m.Title, m.TimeStampUnix, m.LastChapterNum)
if err != nil {
return err
}
}
} else {
tSet := 0
err := db.QueryRow("SELECT COUNT(*) from Manga where ID = ? and Thumbnail IS NOT NULL", m.Id).Scan(&tSet)
if err != nil {
return err
}
if tSet != 0 {
_, err = db.Exec("UPDATE Manga set Title = ?, TimeStampUnixEpoch = ?, LatestAvailableChapter = ? WHERE ID = ?", m.Title, m.TimeStampUnix, m.LastChapterNum, m.Id)
if err != nil {
return err
}
} else {
_, err = db.Exec("UPDATE Manga set Title = ?, TimeStampUnixEpoch = ?, Thumbnail = ?, LatestAvailableChapter = ? WHERE ID = ?", m.Title, m.TimeStampUnix, m.Thumbnail.Bytes(), m.LastChapterNum, 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 //go:embed createDb.sql
@@ -168,39 +88,15 @@ func (dbMgr *Manager) createDatabaseIfNotExists() error {
} }
func (dbMgr *Manager) load() error { func (dbMgr *Manager) load() error {
db := dbMgr.db err := dbMgr.Chapters.Load(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 Id, Title, TimeStampUnixEpoch, Thumbnail, LatestAvailableChapter FROM Manga")
if err != nil { if err != nil {
return err return err
} }
for rows.Next() { err = dbMgr.Mangas.Load(dbMgr.db)
manga := Manga{} if err != nil {
var thumbnail []byte
if err = rows.Scan(&manga.Id, &manga.Title, &manga.TimeStampUnix, &thumbnail, &manga.LastChapterNum); err != nil {
return err return err
} }
if len(thumbnail) != 0 {
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
}
chapter.Manga = &manga
manga.LatestChapter = &chapter
dbMgr.Chapters[chapter.Id] = &chapter
dbMgr.Mangas[manga.Id] = &manga
}
return nil return nil
} }

View File

@@ -2,8 +2,9 @@ package database
import ( import (
"bytes" "bytes"
"cmp"
"database/sql" "database/sql"
"sync" "slices"
) )
type Manga struct { type Manga struct {
@@ -12,73 +13,31 @@ type Manga struct {
TimeStampUnix int64 TimeStampUnix int64
Thumbnail *bytes.Buffer Thumbnail *bytes.Buffer
LastChapterNum int LastChapterNum int
// Not in DB
LatestChapter *Chapter
} }
type MangaTable[K comparable] struct { func NewManga(id int, title string, timeStampUnix int64) Manga {
mutex sync.Mutex return Manga{
mangas map[K]Manga Id: id,
updated map[K]DbStatus Title: title,
TimeStampUnix: timeStampUnix,
}
} }
func (m *MangaTable[K]) Get(key K) (Manga, bool) { // GetLatestChapter TODO: Cache this somehow
m.mutex.Lock() func (m *Manga) GetLatestChapter(chapters *DbTable[int, Chapter]) (*Chapter, bool) {
defer m.mutex.Unlock() c := chapters.All()
val, ok := m.mangas[key]
return val, ok slices.SortStableFunc(c, func(a, b Chapter) int {
return cmp.Compare(b.TimeStampUnix, a.TimeStampUnix)
})
for _, chapter := range c {
if chapter.MangaId == m.Id {
return &chapter, true
}
} }
func (m *MangaTable[K]) Set(key K, new Manga) { return nil, false
m.mutex.Lock()
defer m.mutex.Unlock()
val, ok := m.updated[key]
if ok && val == Loaded {
m.updated[key] = Updated
} else {
m.updated[key] = New
}
m.mangas[key] = new
}
func (m *MangaTable[K]) All() []Manga {
m.mutex.Lock()
defer m.mutex.Unlock()
res := make([]Manga, len(m.mangas))
counter := 0
for _, manga := range m.mangas {
res[counter] = manga
counter++
}
return res
}
func (m *MangaTable[K]) Save(db *sql.DB) error {
m.mutex.Lock()
defer m.mutex.Unlock()
for k, status := range m.updated {
if status == Loaded {
continue
} else if status == Updated {
manga := m.mangas[k]
err := updateManga(db, &manga)
if err != nil {
return err
}
} else {
manga := m.mangas[k]
err := insertManga(db, &manga)
if err != nil {
return err
}
}
}
return nil
}
func (m *MangaTable[K]) Load(db *sql.DB) error {
panic("")
} }
func updateManga(db *sql.DB, m *Manga) error { func updateManga(db *sql.DB, m *Manga) error {
@@ -92,3 +51,32 @@ func insertManga(db *sql.DB, manga *Manga) error {
_, err := db.Exec(cmd, manga.Id, manga.Title, manga.TimeStampUnix, manga.Thumbnail.Bytes(), manga.LastChapterNum) _, err := db.Exec(cmd, manga.Id, manga.Title, manga.TimeStampUnix, manga.Thumbnail.Bytes(), manga.LastChapterNum)
return err return err
} }
func loadMangas(db *sql.DB) (map[int]Manga, error) {
rows, err := db.Query("SELECT Id, Title, TimeStampUnixEpoch, Thumbnail, LatestAvailableChapter FROM Manga")
if err != nil {
return nil, err
}
res := make(map[int]Manga)
for rows.Next() {
manga := Manga{}
var thumbnail []byte
if err = rows.Scan(&manga.Id, &manga.Title, &manga.TimeStampUnix, &thumbnail, &manga.LastChapterNum); err != nil {
return nil, err
}
if len(thumbnail) != 0 {
manga.Thumbnail = bytes.NewBuffer(thumbnail)
}
res[manga.Id] = manga
}
return res, nil
}
func deleteManga(db *sql.DB, key int) error {
_, err := db.Exec("DELETE from Chapter where ID = ?", key)
return err
}

View File

@@ -1,6 +1,9 @@
package database package database
import "database/sql" import (
"database/sql"
"sync"
)
type DbStatus int type DbStatus int
@@ -10,11 +13,121 @@ const (
Updated Updated
) )
// Table TODO: This Could probably be a generic instead of interface / both //type Table[K comparable, T any] interface {
type Table[K comparable, T any] interface { // Get(key K) (T, bool)
Get(key K) (T, bool) // Set(key K, new T)
Set(key K, new T) // All() []T
All() []T // Delete(key K) error
Save(db *sql.DB) error // Save(db *sql.DB) error
Load(db *sql.DB) error // Load(db *sql.DB) error
// Connect(key K, value *any) bool
//}
type DbTable[K comparable, T any] struct {
mutex sync.Mutex
items map[K]T
updated map[K]DbStatus
updateFunc func(db *sql.DB, value *T) error
insertFunc func(db *sql.DB, value *T) error
loadFunc func(db *sql.DB) (map[K]T, error)
deleteFunc func(db *sql.DB, key K) error
}
func NewDbTable[K comparable, T any](updateFunc func(db *sql.DB, value *T) error,
insertFunc func(db *sql.DB, value *T) error,
loadFunc func(db *sql.DB) (map[K]T, error),
deleteFunc func(db *sql.DB, key K) error,
) DbTable[K, T] {
return DbTable[K, T]{
mutex: sync.Mutex{},
items: make(map[K]T),
updated: make(map[K]DbStatus),
updateFunc: updateFunc,
insertFunc: insertFunc,
loadFunc: loadFunc,
deleteFunc: deleteFunc,
}
}
func (d *DbTable[K, T]) Get(key K) (T, bool) {
d.mutex.Lock()
defer d.mutex.Unlock()
val, ok := d.items[key]
return val, ok
}
// GetRef unsafe
func (d *DbTable[K, T]) getRef(key K) (*T, bool) {
d.mutex.Lock()
defer d.mutex.Unlock()
val, ok := d.items[key]
return &val, ok
}
func (d *DbTable[K, T]) Set(key K, new T) {
d.mutex.Lock()
defer d.mutex.Unlock()
val, ok := d.updated[key]
if ok && val == Loaded || val == Updated {
d.updated[key] = Updated
} else {
d.updated[key] = New
}
d.items[key] = new
}
func (d *DbTable[K, T]) All() []T {
d.mutex.Lock()
defer d.mutex.Unlock()
res := make([]T, len(d.items))
counter := 0
for _, manga := range d.items {
res[counter] = manga
counter++
}
return res
}
func (d *DbTable[K, T]) Delete(db *sql.DB, key K) error {
d.mutex.Lock()
defer d.mutex.Unlock()
delete(d.items, key)
return d.deleteFunc(db, key)
}
func (d *DbTable[K, T]) Save(db *sql.DB) error {
d.mutex.Lock()
defer d.mutex.Unlock()
for k, status := range d.updated {
if status == Loaded {
continue
} else if status == Updated {
item := d.items[k]
err := d.updateFunc(db, &item)
if err != nil {
return err
}
} else {
item := d.items[k]
err := d.insertFunc(db, &item)
if err != nil {
return err
}
}
}
return nil
}
func (d *DbTable[K, T]) Load(db *sql.DB) error {
d.mutex.Lock()
defer d.mutex.Unlock()
res, err := d.loadFunc(db)
if err != nil {
return err
}
d.items = res
for k := range d.items {
d.updated[k] = Loaded
}
return nil
} }

View File

@@ -35,15 +35,7 @@ func (s *Server) HandleNew(w http.ResponseWriter, r *http.Request) {
func (s *Server) HandleMenu(w http.ResponseWriter, r *http.Request) { func (s *Server) HandleMenu(w http.ResponseWriter, r *http.Request) {
tmpl := template.Must(view.GetViewTemplate(view.Menu)) tmpl := template.Must(view.GetViewTemplate(view.Menu))
all := s.DbMgr.Mangas.All()
fmt.Println("Locking Rw in handler.go:43")
s.DbMgr.Rw.Lock()
defer func() {
fmt.Println("Unlocking Rw in handler.go:46")
s.DbMgr.Rw.Unlock()
}()
all := s.DbMgr.Mangas
l := len(all) l := len(all)
mangaViewModels := make([]view.MangaViewModel, l) mangaViewModels := make([]view.MangaViewModel, l)
counter := 0 counter := 0
@@ -59,12 +51,14 @@ func (s *Server) HandleMenu(w http.ResponseWriter, r *http.Request) {
t1 := time.Now().UnixNano() t1 := time.Now().UnixNano()
thumbnail, err := s.LoadThumbnail(manga) thumbnail, updated, err := s.LoadThumbnail(&manga)
//TODO: Add default picture instead of not showing Manga at all //TODO: Add default picture instead of not showing Manga at all
if err != nil { if err != nil {
continue continue
} }
manga.Thumbnail = s.ImageBuffers[thumbnail] if updated {
s.DbMgr.Mangas.Set(manga.Id, manga)
}
t2 := time.Now().UnixNano() t2 := time.Now().UnixNano()
@@ -75,24 +69,32 @@ func (s *Server) HandleMenu(w http.ResponseWriter, r *http.Request) {
// This is very slow // This is very slow
// TODO: put this into own Method // TODO: put this into own Method
if manga.LastChapterNum == 0 { if manga.LastChapterNum == 0 {
err := s.UpdateLatestAvailableChapter(manga) err, updated := s.UpdateLatestAvailableChapter(&manga)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
if updated {
s.DbMgr.Mangas.Set(manga.Id, manga)
}
} }
t2 = time.Now().UnixNano() t2 = time.Now().UnixNano()
titNs += t2 - t1 titNs += t2 - t1
latestChapter, ok := manga.GetLatestChapter(&s.DbMgr.Chapters)
if !ok {
continue
}
mangaViewModels[counter] = view.MangaViewModel{ mangaViewModels[counter] = view.MangaViewModel{
ID: manga.Id, ID: manga.Id,
Title: title, Title: title,
Number: manga.LatestChapter.Number, Number: latestChapter.Number,
LastNumber: manga.LastChapterNum, LastNumber: manga.LastChapterNum,
// I Hate this time Format... 15 = hh, 04 = mm, 02 = DD, 01 = MM, 06 == YY // I Hate this time Format... 15 = hh, 04 = mm, 02 = DD, 01 = MM, 06 == YY
LastTime: time.Unix(manga.TimeStampUnix, 0).Format("15:04 (02-01-06)"), LastTime: time.Unix(manga.TimeStampUnix, 0).Format("15:04 (02-01-06)"),
Url: manga.LatestChapter.Url, Url: latestChapter.Url,
ThumbnailUrl: thumbnail, ThumbnailUrl: thumbnail,
} }
counter++ counter++
@@ -201,56 +203,37 @@ func (s *Server) HandleExit(w http.ResponseWriter, r *http.Request) {
func (s *Server) HandleCurrent(w http.ResponseWriter, _ *http.Request) { func (s *Server) HandleCurrent(w http.ResponseWriter, _ *http.Request) {
tmpl := template.Must(view.GetViewTemplate(view.Viewer)) tmpl := template.Must(view.GetViewTemplate(view.Viewer))
fmt.Println("Locking Rw in handler.go:125")
s.DbMgr.Rw.Lock()
defer func() {
fmt.Println("Unlocking Rw in handler.go:128")
s.DbMgr.Rw.Unlock()
}()
mangaId, chapterId, err := s.Provider.GetTitleIdAndChapterId(s.CurrSubUrl) mangaId, chapterId, err := s.Provider.GetTitleIdAndChapterId(s.CurrSubUrl)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} else { } else {
title, chapter, err := s.Provider.GetTitleAndChapter(s.CurrSubUrl) title, chapterName, err := s.Provider.GetTitleAndChapter(s.CurrSubUrl)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} else { } else {
var manga *database.Manga manga, ok := s.DbMgr.Mangas.Get(mangaId)
if s.DbMgr.Mangas[mangaId] == nil { if !ok {
manga = &database.Manga{ manga = database.NewManga(mangaId, title, time.Now().Unix())
Id: mangaId,
Title: title,
TimeStampUnix: time.Now().Unix(),
}
s.DbMgr.Mangas[mangaId] = manga
} else { } else {
manga = s.DbMgr.Mangas[mangaId] manga.TimeStampUnix = time.Now().Unix()
s.DbMgr.Mangas[mangaId].TimeStampUnix = time.Now().Unix()
} }
if s.DbMgr.Chapters[chapterId] == nil { chapter, ok := s.DbMgr.Chapters.Get(chapterId)
chapterNumberStr := strings.Replace(chapter, "ch_", "", 1) if !ok {
chapterNumberStr := strings.Replace(chapterName, "ch_", "", 1)
number, err := strconv.Atoi(chapterNumberStr) number, err := strconv.Atoi(chapterNumberStr)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
number = 0 number = 0
} }
s.DbMgr.Chapters[chapterId] = &database.Chapter{ chapter = database.NewChapter(chapterId, manga.Id, s.CurrSubUrl, chapterName, number, time.Now().Unix())
Id: chapterId,
Manga: manga,
Url: s.CurrSubUrl,
Name: chapter,
Number: number,
TimeStampUnix: time.Now().Unix(),
}
} else { } else {
s.DbMgr.Chapters[chapterId].TimeStampUnix = time.Now().Unix() chapter.TimeStampUnix = time.Now().Unix()
} }
s.DbMgr.Mangas[mangaId].LatestChapter = s.DbMgr.Chapters[chapterId] s.DbMgr.Chapters.Set(chapterId, chapter)
s.DbMgr.Mangas.Set(mangaId, manga)
} }
} }

View File

@@ -47,7 +47,7 @@ func New(provider provider.Provider, db *database.Manager) *Server {
return &s return &s
} }
func (s *Server) Start() error { func (s *Server) Start(port int) error {
http.HandleFunc("/", s.HandleMenu) http.HandleFunc("/", s.HandleMenu)
http.HandleFunc("/new/", s.HandleNewQuery) http.HandleFunc("/new/", s.HandleNewQuery)
http.HandleFunc("/new/title/{title}/{chapter}", s.HandleNew) http.HandleFunc("/new/title/{title}/{chapter}", s.HandleNew)
@@ -62,33 +62,35 @@ func (s *Server) Start() error {
// Update Latest Chapter every 5 Minutes // Update Latest Chapter every 5 Minutes
go func(s *Server) { go func(s *Server) {
time.AfterFunc(time.Second*10, func() { time.AfterFunc(time.Second*10, func() {
s.DbMgr.Rw.Lock() for _, m := range s.DbMgr.Mangas.All() {
for _, m := range s.DbMgr.Mangas { err, updated := s.UpdateLatestAvailableChapter(&m)
err := s.UpdateLatestAvailableChapter(m)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
if updated {
s.DbMgr.Mangas.Set(m.Id, m)
}
} }
s.DbMgr.Rw.Unlock()
}) })
for { for {
select { select {
case <-time.After(time.Minute * 5): case <-time.After(time.Minute * 5):
s.DbMgr.Rw.Lock() for _, m := range s.DbMgr.Mangas.All() {
for _, m := range s.DbMgr.Mangas { err, updated := s.UpdateLatestAvailableChapter(&m)
err := s.UpdateLatestAvailableChapter(m)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
if updated {
s.DbMgr.Mangas.Set(m.Id, m)
}
} }
s.DbMgr.Rw.Unlock()
} }
} }
}(s) }(s)
fmt.Println("Server starting...") fmt.Println("Server starting...")
err := http.ListenAndServe(":8000", nil) err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
return err return err
} }
@@ -203,56 +205,60 @@ func (s *Server) LoadCurr() {
fmt.Println("Loaded current") fmt.Println("Loaded current")
} }
func (s *Server) UpdateLatestAvailableChapter(manga *database.Manga) error { func (s *Server) UpdateLatestAvailableChapter(manga *database.Manga) (error, bool) {
fmt.Printf("Updating Manga: %s\n", manga.Title) fmt.Printf("Updating Manga: %s\n", manga.Title)
l, err := s.Provider.GetChapterList("/title/" + strconv.Itoa(manga.Id)) l, err := s.Provider.GetChapterList("/title/" + strconv.Itoa(manga.Id))
if err != nil { if err != nil {
return err return err, false
} }
le := len(l) le := len(l)
_, c, err := s.Provider.GetTitleAndChapter(l[le-1]) _, c, err := s.Provider.GetTitleAndChapter(l[le-1])
if err != nil { if err != nil {
return err return err, false
} }
chapterNumberStr := strings.Replace(c, "ch_", "", 1) chapterNumberStr := strings.Replace(c, "ch_", "", 1)
i, err := strconv.Atoi(chapterNumberStr) i, err := strconv.Atoi(chapterNumberStr)
if err != nil { if err != nil {
return err return err, false
} }
if manga.LastChapterNum == i {
return nil, false
} else {
manga.LastChapterNum = i manga.LastChapterNum = i
return nil return nil, true
}
} }
func (s *Server) LoadThumbnail(manga *database.Manga) (path string, err error) { func (s *Server) LoadThumbnail(manga *database.Manga) (path string, updated bool, err error) {
strId := strconv.Itoa(manga.Id) strId := strconv.Itoa(manga.Id)
s.Mutex.Lock() s.Mutex.Lock()
defer s.Mutex.Unlock() defer s.Mutex.Unlock()
if s.ImageBuffers[strId] != nil { if s.ImageBuffers[strId] != nil {
return strId, nil return strId, false, nil
} }
if manga.Thumbnail != nil { if manga.Thumbnail != nil {
s.ImageBuffers[strId] = manga.Thumbnail s.ImageBuffers[strId] = manga.Thumbnail
return strId, nil return strId, false, nil
} }
url, err := s.Provider.GetThumbnail(strId) url, err := s.Provider.GetThumbnail(strId)
if err != nil { if err != nil {
return "", err return "", false, err
} }
ram, err := addFileToRam(url) ram, err := addFileToRam(url)
if err != nil { if err != nil {
return "", err return "", false, err
} }
manga.Thumbnail = ram manga.Thumbnail = ram
s.ImageBuffers[strId] = ram s.ImageBuffers[strId] = ram
return strId, nil return strId, true, nil
} }
func (s *Server) AppendImagesToBuf(html string) ([]view.Image, error) { func (s *Server) AppendImagesToBuf(html string) ([]view.Image, error) {