diff --git a/cmd/mangaGetter/main.go b/cmd/mangaGetter/main.go index 401dfed..192310c 100644 --- a/cmd/mangaGetter/main.go +++ b/cmd/mangaGetter/main.go @@ -33,15 +33,17 @@ func main() { } }() + port := 8080 + go func() { time.Sleep(300 * time.Millisecond) - err := open("http://localhost:8000") + err := open(fmt.Sprintf("http://localhost:%d", port)) if err != nil { fmt.Println(err) } }() - err = s.Start() + err = s.Start(port) if err != nil { panic(err) } diff --git a/internal/database/chapter.go b/internal/database/chapter.go index e4f8127..e5ca4ee 100644 --- a/internal/database/chapter.go +++ b/internal/database/chapter.go @@ -2,61 +2,56 @@ package database import ( "database/sql" - "sync" ) type Chapter struct { Id int - Manga *Manga + MangaId int Url string Name string Number int TimeStampUnix int64 } -type ChapterTable[K comparable] struct { - mutex sync.Mutex - chapters map[K]Chapter - updated map[K]DbStatus -} - -func (c *ChapterTable[K]) Get(key K) (Chapter, bool) { - c.mutex.Lock() - defer c.mutex.Unlock() - val, ok := c.chapters[key] - return val, ok -} - -func (c *ChapterTable[K]) Set(key K, new Chapter) { - c.mutex.Lock() - defer c.mutex.Unlock() - val, ok := c.updated[key] - if ok && val == Loaded { - c.updated[key] = Updated - } else { - c.updated[key] = New +func NewChapter(id int, mangaId int, url string, name string, number int, timeStampUnix int64) Chapter { + return Chapter{ + Id: id, + MangaId: mangaId, + Url: url, + Name: name, + Number: number, + TimeStampUnix: timeStampUnix, } - c.chapters[key] = new } -func (c *ChapterTable[K]) All() []Chapter { - c.mutex.Lock() - defer c.mutex.Unlock() - res := make([]Chapter, len(c.chapters)) - counter := 0 - for _, chapter := range c.chapters { - res[counter] = chapter - counter++ +func updateChapter(db *sql.DB, c *Chapter) error { + _, err := db.Exec("UPDATE Chapter set Name = ?, Url = ?, Number = ?, TimeStampUnixEpoch = ? where ID = ?", c.Name, c.Url, c.Number, c.TimeStampUnix, c.Id) + return err +} + +func insertChapter(db *sql.DB, c *Chapter) error { + _, 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) + return err +} + +func loadChapters(db *sql.DB) (map[int]Chapter, error) { + rows, err := db.Query("SELECT Id, MangaID, Url, Name, Number, TimeStampUnixEpoch FROM Chapter") + if err != nil { + return nil, err } - 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 { - //TODO implement me - panic("implement me") -} - -func (c *ChapterTable[K]) Load(db *sql.DB) error { - //TODO implement me - panic("implement me") +func deleteChapter(db *sql.DB, key int) error { + _, err := db.Exec("DELETE from Manga where ID = ?", key) + return err } diff --git a/internal/database/database.go b/internal/database/database.go index 57cd82d..1cdabd9 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -1,32 +1,25 @@ package database import ( - "bytes" "database/sql" _ "embed" - "fmt" - "sync" - _ "github.com/mattn/go-sqlite3" ) type Manager struct { - ConnectionString string - db *sql.DB - - Rw *sync.Mutex - Mangas map[int]*Manga - Chapters map[int]*Chapter - + ConnectionString string + db *sql.DB + Mangas DbTable[int, Manga] + Chapters DbTable[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), + db: nil, + Mangas: NewDbTable[int, Manga](updateManga, insertManga, loadMangas, deleteManga), + Chapters: NewDbTable[int, Chapter](updateChapter, insertChapter, loadChapters, deleteChapter), CreateIfNotExists: createIfNotExists, } } @@ -53,103 +46,21 @@ func (dbMgr *Manager) Close() error { 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) + dbMgr.Mangas.Get(mangaId) + err := dbMgr.Mangas.Delete(dbMgr.db, 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, 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) + chapters := dbMgr.Chapters.All() + for i, chapter := range chapters { + if chapter.MangaId == mangaId { + err := dbMgr.Chapters.Delete(dbMgr.db, i) if err != nil { return err } @@ -159,6 +70,15 @@ func (dbMgr *Manager) Save() error { return nil } +func (dbMgr *Manager) Save() error { + err := dbMgr.Mangas.Save(dbMgr.db) + if err != nil { + return err + } + + return dbMgr.Chapters.Save(dbMgr.db) +} + //go:embed createDb.sql var createSql string @@ -168,39 +88,15 @@ func (dbMgr *Manager) createDatabaseIfNotExists() error { } 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 Id, Title, TimeStampUnixEpoch, Thumbnail, LatestAvailableChapter FROM Manga") + err := dbMgr.Chapters.Load(dbMgr.db) if err != nil { return err } - for rows.Next() { - manga := Manga{} - var thumbnail []byte - if err = rows.Scan(&manga.Id, &manga.Title, &manga.TimeStampUnix, &thumbnail, &manga.LastChapterNum); err != nil { - 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 + err = dbMgr.Mangas.Load(dbMgr.db) + if err != nil { + return err } + return nil } diff --git a/internal/database/manga.go b/internal/database/manga.go index 19ca9b9..e61cfc1 100644 --- a/internal/database/manga.go +++ b/internal/database/manga.go @@ -2,8 +2,9 @@ package database import ( "bytes" + "cmp" "database/sql" - "sync" + "slices" ) type Manga struct { @@ -12,73 +13,31 @@ type Manga struct { TimeStampUnix int64 Thumbnail *bytes.Buffer LastChapterNum int - - // Not in DB - LatestChapter *Chapter } -type MangaTable[K comparable] struct { - mutex sync.Mutex - mangas map[K]Manga - updated map[K]DbStatus -} - -func (m *MangaTable[K]) Get(key K) (Manga, bool) { - m.mutex.Lock() - defer m.mutex.Unlock() - val, ok := m.mangas[key] - return val, ok -} - -func (m *MangaTable[K]) Set(key K, new Manga) { - 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 +func NewManga(id int, title string, timeStampUnix int64) Manga { + return Manga{ + Id: id, + Title: title, + TimeStampUnix: timeStampUnix, } - 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 -} +// GetLatestChapter TODO: Cache this somehow +func (m *Manga) GetLatestChapter(chapters *DbTable[int, Chapter]) (*Chapter, bool) { + c := chapters.All() -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 - } + 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 } } - return nil -} -func (m *MangaTable[K]) Load(db *sql.DB) error { - panic("") + return nil, false } 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) 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 +} diff --git a/internal/database/table.go b/internal/database/table.go index 1cc787c..d259bd3 100644 --- a/internal/database/table.go +++ b/internal/database/table.go @@ -1,6 +1,9 @@ package database -import "database/sql" +import ( + "database/sql" + "sync" +) type DbStatus int @@ -10,11 +13,121 @@ const ( Updated ) -// Table TODO: This Could probably be a generic instead of interface / both -type Table[K comparable, T any] interface { - Get(key K) (T, bool) - Set(key K, new T) - All() []T - Save(db *sql.DB) error - Load(db *sql.DB) error +//type Table[K comparable, T any] interface { +// Get(key K) (T, bool) +// Set(key K, new T) +// All() []T +// Delete(key K) error +// Save(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 } diff --git a/internal/server/handler.go b/internal/server/handler.go index 24f198c..36c820c 100644 --- a/internal/server/handler.go +++ b/internal/server/handler.go @@ -35,15 +35,7 @@ func (s *Server) HandleNew(w http.ResponseWriter, r *http.Request) { func (s *Server) HandleMenu(w http.ResponseWriter, r *http.Request) { tmpl := template.Must(view.GetViewTemplate(view.Menu)) - - 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 + all := s.DbMgr.Mangas.All() l := len(all) mangaViewModels := make([]view.MangaViewModel, l) counter := 0 @@ -59,12 +51,14 @@ func (s *Server) HandleMenu(w http.ResponseWriter, r *http.Request) { 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 if err != nil { continue } - manga.Thumbnail = s.ImageBuffers[thumbnail] + if updated { + s.DbMgr.Mangas.Set(manga.Id, manga) + } t2 := time.Now().UnixNano() @@ -75,24 +69,32 @@ func (s *Server) HandleMenu(w http.ResponseWriter, r *http.Request) { // This is very slow // TODO: put this into own Method if manga.LastChapterNum == 0 { - err := s.UpdateLatestAvailableChapter(manga) + err, updated := s.UpdateLatestAvailableChapter(&manga) if err != nil { fmt.Println(err) } + if updated { + s.DbMgr.Mangas.Set(manga.Id, manga) + } } t2 = time.Now().UnixNano() titNs += t2 - t1 + latestChapter, ok := manga.GetLatestChapter(&s.DbMgr.Chapters) + if !ok { + continue + } + mangaViewModels[counter] = view.MangaViewModel{ ID: manga.Id, Title: title, - Number: manga.LatestChapter.Number, + Number: latestChapter.Number, LastNumber: manga.LastChapterNum, // 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)"), - Url: manga.LatestChapter.Url, + Url: latestChapter.Url, ThumbnailUrl: thumbnail, } counter++ @@ -201,56 +203,37 @@ func (s *Server) HandleExit(w http.ResponseWriter, r *http.Request) { func (s *Server) HandleCurrent(w http.ResponseWriter, _ *http.Request) { 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) if err != nil { fmt.Println(err) } else { - title, chapter, err := s.Provider.GetTitleAndChapter(s.CurrSubUrl) + title, chapterName, err := s.Provider.GetTitleAndChapter(s.CurrSubUrl) if err != nil { fmt.Println(err) } else { - var manga *database.Manga - if s.DbMgr.Mangas[mangaId] == nil { - manga = &database.Manga{ - Id: mangaId, - Title: title, - TimeStampUnix: time.Now().Unix(), - } - s.DbMgr.Mangas[mangaId] = manga + manga, ok := s.DbMgr.Mangas.Get(mangaId) + if !ok { + manga = database.NewManga(mangaId, title, time.Now().Unix()) } else { - manga = s.DbMgr.Mangas[mangaId] - s.DbMgr.Mangas[mangaId].TimeStampUnix = time.Now().Unix() + manga.TimeStampUnix = time.Now().Unix() } - if s.DbMgr.Chapters[chapterId] == nil { - chapterNumberStr := strings.Replace(chapter, "ch_", "", 1) + chapter, ok := s.DbMgr.Chapters.Get(chapterId) + if !ok { + chapterNumberStr := strings.Replace(chapterName, "ch_", "", 1) number, err := strconv.Atoi(chapterNumberStr) if err != nil { fmt.Println(err) number = 0 } - s.DbMgr.Chapters[chapterId] = &database.Chapter{ - Id: chapterId, - Manga: manga, - Url: s.CurrSubUrl, - Name: chapter, - Number: number, - TimeStampUnix: time.Now().Unix(), - } + chapter = database.NewChapter(chapterId, manga.Id, s.CurrSubUrl, chapterName, number, time.Now().Unix()) } 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) } } diff --git a/internal/server/server.go b/internal/server/server.go index 50ee4a0..a75db0b 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -47,7 +47,7 @@ func New(provider provider.Provider, db *database.Manager) *Server { return &s } -func (s *Server) Start() error { +func (s *Server) Start(port int) error { http.HandleFunc("/", s.HandleMenu) http.HandleFunc("/new/", s.HandleNewQuery) http.HandleFunc("/new/title/{title}/{chapter}", s.HandleNew) @@ -62,33 +62,35 @@ func (s *Server) Start() error { // Update Latest Chapter every 5 Minutes go func(s *Server) { time.AfterFunc(time.Second*10, func() { - s.DbMgr.Rw.Lock() - for _, m := range s.DbMgr.Mangas { - err := s.UpdateLatestAvailableChapter(m) + for _, m := range s.DbMgr.Mangas.All() { + err, updated := s.UpdateLatestAvailableChapter(&m) if err != nil { fmt.Println(err) } + if updated { + s.DbMgr.Mangas.Set(m.Id, m) + } } - s.DbMgr.Rw.Unlock() }) for { select { case <-time.After(time.Minute * 5): - s.DbMgr.Rw.Lock() - for _, m := range s.DbMgr.Mangas { - err := s.UpdateLatestAvailableChapter(m) + for _, m := range s.DbMgr.Mangas.All() { + err, updated := s.UpdateLatestAvailableChapter(&m) if err != nil { fmt.Println(err) } + if updated { + s.DbMgr.Mangas.Set(m.Id, m) + } } - s.DbMgr.Rw.Unlock() } } }(s) fmt.Println("Server starting...") - err := http.ListenAndServe(":8000", nil) + err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil) return err } @@ -203,56 +205,60 @@ func (s *Server) LoadCurr() { 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) l, err := s.Provider.GetChapterList("/title/" + strconv.Itoa(manga.Id)) if err != nil { - return err + return err, false } le := len(l) _, c, err := s.Provider.GetTitleAndChapter(l[le-1]) if err != nil { - return err + return err, false } chapterNumberStr := strings.Replace(c, "ch_", "", 1) i, err := strconv.Atoi(chapterNumberStr) if err != nil { - return err + return err, false } - manga.LastChapterNum = i - return nil + if manga.LastChapterNum == i { + return nil, false + } else { + manga.LastChapterNum = i + 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) s.Mutex.Lock() defer s.Mutex.Unlock() if s.ImageBuffers[strId] != nil { - return strId, nil + return strId, false, nil } if manga.Thumbnail != nil { s.ImageBuffers[strId] = manga.Thumbnail - return strId, nil + return strId, false, nil } url, err := s.Provider.GetThumbnail(strId) if err != nil { - return "", err + return "", false, err } ram, err := addFileToRam(url) if err != nil { - return "", err + return "", false, err } manga.Thumbnail = ram s.ImageBuffers[strId] = ram - return strId, nil + return strId, true, nil } func (s *Server) AppendImagesToBuf(html string) ([]view.Image, error) {