diff --git a/.gitignore b/.gitignore index d693441..05681c2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,6 @@ findings.txt test.txt h.html *.sqlite +*.bak /bin *.exe \ No newline at end of file diff --git a/cmd/mangaGetter/main.go b/cmd/mangaGetter/main.go index 4e68207..7b43fe8 100644 --- a/cmd/mangaGetter/main.go +++ b/cmd/mangaGetter/main.go @@ -66,13 +66,7 @@ func open(url string) error { func Close(db *database.Manager) { fmt.Println("Attempting to save and close DB") - err := db.Save() - if err != nil { - fmt.Println(err) - return - } - - err = db.Close() + err := db.Close() if err != nil { fmt.Println(err) } diff --git a/go.mod b/go.mod index 32ed120..36a10cd 100644 --- a/go.mod +++ b/go.mod @@ -6,3 +6,10 @@ require ( github.com/mattn/go-sqlite3 v1.14.22 golang.org/x/text v0.14.0 ) + +require ( + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + gorm.io/driver/sqlite v1.5.5 // indirect + gorm.io/gorm v1.25.10 // indirect +) diff --git a/go.sum b/go.sum index 81788cd..4e5aa55 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,12 @@ +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E= +gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE= +gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= +gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= diff --git a/internal/database/chapter.go b/internal/database/chapter.go index 034f8af..bfec341 100644 --- a/internal/database/chapter.go +++ b/internal/database/chapter.go @@ -1,57 +1,21 @@ package database -import ( - "database/sql" -) - type Chapter struct { - Id int - MangaId int + Id int `gorm:"primary_key;AUTO_INCREMENT"` Url string Name string Number string TimeStampUnix int64 + MangaId int } func NewChapter(id int, mangaId int, url string, name string, number string, timeStampUnix int64) Chapter { return Chapter{ Id: id, - MangaId: mangaId, Url: url, Name: name, Number: number, TimeStampUnix: timeStampUnix, + MangaId: mangaId, } } - -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 - } - 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 deleteChapter(db *sql.DB, key int) error { - _, err := db.Exec("DELETE from Chapter where ID = ?", key) - return err -} diff --git a/internal/database/createDb.sql b/internal/database/createDb.sql deleted file mode 100644 index 798be11..0000000 --- a/internal/database/createDb.sql +++ /dev/null @@ -1,23 +0,0 @@ -create table if not exists Manga ( - ID integer not null primary key, - Title text, - TimeStampUnixEpoch integer not null, - Thumbnail blob null, - LatestAvailableChapter text -); - -create table if not exists Chapter ( - ID integer not null primary key, - MangaID integer not null, - Url text not null, - Name text null, - Number text null, - TimeStampUnixEpoch integer not null, - foreign key(MangaID) references Manga(ID) -); - -create table if not exists Setting ( - Name text not null primary key, - Value text, - DefaultValue text not null -); \ No newline at end of file diff --git a/internal/database/database.go b/internal/database/database.go index 571febe..6609dae 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -1,115 +1,59 @@ package database import ( - "database/sql" _ "embed" - "errors" _ "github.com/mattn/go-sqlite3" + "gorm.io/driver/sqlite" + "gorm.io/gorm" ) type Manager struct { ConnectionString string - db *sql.DB - Mangas DbTable[int, Manga] - Chapters DbTable[int, Chapter] - Settings DbTable[string, Setting] + Db *gorm.DB CreateIfNotExists bool } func NewDatabase(connectionString string, createIfNotExists bool) Manager { return Manager{ ConnectionString: connectionString, - db: nil, - Mangas: NewDbTable(updateManga, insertManga, loadMangas, deleteManga), - Chapters: NewDbTable(updateChapter, insertChapter, loadChapters, deleteChapter), - Settings: NewDbTable(updateSetting, insertSetting, loadSettings, deleteSetting), + Db: nil, CreateIfNotExists: createIfNotExists, } } func (dbMgr *Manager) Open() error { - db, err := sql.Open("sqlite3", dbMgr.ConnectionString) + db, err := gorm.Open(sqlite.Open(dbMgr.ConnectionString), &gorm.Config{}) if err != nil { return err } - dbMgr.db = db + 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() + sql, err := dbMgr.Db.DB() if err != nil { return err } - - dbMgr.db = nil + err = sql.Close() + if err != nil { + return err + } + dbMgr.Db = nil return nil } -func (dbMgr *Manager) Delete(mangaId int) error { - dbMgr.Mangas.Get(mangaId) - err := dbMgr.Mangas.Delete(dbMgr.db, mangaId) - if err != nil && !errors.Is(err, IgnoreDeleteError{}) { - return err - } - - chapters := dbMgr.Chapters.All() - for i, chapter := range chapters { - if chapter.MangaId == mangaId { - err := dbMgr.Chapters.Delete(dbMgr.db, i) - if err != nil && !errors.Is(err, IgnoreDeleteError{}) { - return err - } - } - } - - return nil +func (dbMgr *Manager) Delete(mangaId int) { + dbMgr.Db.Delete(&Manga{}, mangaId) } -func (dbMgr *Manager) Save() error { - err := dbMgr.Mangas.Save(dbMgr.db) - if err != nil { - return err - } - err = dbMgr.Chapters.Save(dbMgr.db) - if err != nil { - return err - } - - return dbMgr.Settings.Save(dbMgr.db) -} - -//go:embed createDb.sql -var createSql string - func (dbMgr *Manager) createDatabaseIfNotExists() error { - _, err := dbMgr.db.Exec(createSql) + err := dbMgr.Db.AutoMigrate(&Manga{}, &Chapter{}, &Setting{}) return err } - -func (dbMgr *Manager) load() error { - err := dbMgr.Chapters.Load(dbMgr.db) - if err != nil { - return err - } - - err = dbMgr.Mangas.Load(dbMgr.db) - if err != nil { - return err - } - - err = dbMgr.Settings.Load(dbMgr.db) - if err != nil { - return err - } - initSettings(&dbMgr.Settings) - - return nil -} diff --git a/internal/database/manga.go b/internal/database/manga.go index 935d6a3..d96a22a 100644 --- a/internal/database/manga.go +++ b/internal/database/manga.go @@ -1,16 +1,13 @@ package database -import ( - "bytes" - "database/sql" -) - type Manga struct { - Id int + Id int `gorm:"primary_key;AUTO_INCREMENT"` Title string TimeStampUnix int64 - Thumbnail *bytes.Buffer + Thumbnail []byte LastChapterNum string + Chapters []Chapter + //`gorm:"foreignkey:MangaID"` } func NewManga(id int, title string, timeStampUnix int64) Manga { @@ -23,12 +20,10 @@ func NewManga(id int, title string, timeStampUnix int64) Manga { } // GetLatestChapter TODO: Cache this somehow -func (m *Manga) GetLatestChapter(chapters *DbTable[int, Chapter]) (*Chapter, bool) { - c := chapters.All() - +func (m *Manga) GetLatestChapter() (*Chapter, bool) { highest := int64(0) index := 0 - for i, chapter := range c { + for i, chapter := range m.Chapters { if chapter.MangaId == m.Id && highest < chapter.TimeStampUnix { highest = chapter.TimeStampUnix index = i @@ -39,58 +34,14 @@ func (m *Manga) GetLatestChapter(chapters *DbTable[int, Chapter]) (*Chapter, boo return nil, false } - return &c[index], true -} - -func updateManga(db *sql.DB, m *Manga) error { - const cmd = "UPDATE Manga set Title = ?, TimeStampUnixEpoch = ?, Thumbnail = ?, LatestAvailableChapter = ? WHERE ID = ?" - var err error - if m.Thumbnail == nil { - _, err = db.Exec(cmd, m.Title, m.TimeStampUnix, nil, m.LastChapterNum, m.Id) - - } else { - _, err = db.Exec(cmd, m.Title, m.TimeStampUnix, m.Thumbnail.Bytes(), m.LastChapterNum, m.Id) - } - return err -} - -func insertManga(db *sql.DB, manga *Manga) error { - const cmd = "INSERT INTO Manga(ID, Title, TimeStampUnixEpoch, Thumbnail, LatestAvailableChapter) values(?, ?, ?, ?, ?)" - var err error - if manga.Thumbnail == nil { - _, err = db.Exec(cmd, manga.Id, manga.Title, manga.TimeStampUnix, nil, manga.LastChapterNum) - - } else { - _, 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 Manga where ID = ?", key) - return err + return &m.Chapters[index], true + + //result := db.Where("manga.id = ?", m.Id).Order("TimeStampUnix desc").Take(&chapter) + //if result.Error != nil && !errors.Is(result.Error, gorm.ErrRecordNotFound) { + // return &chapter, true, result.Error + //} else if errors.Is(result.Error, gorm.ErrRecordNotFound) { + // return &chapter, false, nil + //} else { + // return &chapter, true, nil + //} } diff --git a/internal/database/setting.go b/internal/database/setting.go index cd11d33..d658dd8 100644 --- a/internal/database/setting.go +++ b/internal/database/setting.go @@ -1,11 +1,7 @@ package database -import ( - "database/sql" -) - type Setting struct { - Name string + Name string `gorm:"PRIMARY_KEY"` Value string Default string } @@ -18,57 +14,14 @@ func NewSetting(name string, defaultValue string) Setting { } } -func initSettings(settings *DbTable[string, Setting]) { - addSettingIfNotExists("theme", "white", settings) - addSettingIfNotExists("order", "title", settings) -} - -func addSettingIfNotExists(name string, value string, settings *DbTable[string, Setting]) { - _, exists := settings.Get(name) - if !exists { - settings.Set(name, NewSetting(name, value)) - } -} - -func updateSetting(db *sql.DB, s *Setting) error { - const cmd = "UPDATE Setting set Value = ? WHERE Name = ?" - _, err := db.Exec(cmd, s.Value, s.Name) - return err -} - -func insertSetting(db *sql.DB, s *Setting) error { - const cmd = "INSERT INTO Setting(Name, Value, DefaultValue) VALUES(?, ?, ?)" - _, err := db.Exec(cmd, s.Name, s.Value, s.Default) - return err -} - -func loadSettings(db *sql.DB) (map[string]Setting, error) { - const cmd = "SELECT Name, Value, DefaultValue from Setting" - rows, err := db.Query(cmd) - - res := make(map[string]Setting) - - for rows.Next() { - setting := Setting{} - if err = rows.Scan(&setting.Name, &setting.Value, &setting.Default); err != nil { - return nil, err - } - res[setting.Name] = setting - } - return res, err -} - -type IgnoreDeleteError struct{} - -func (m IgnoreDeleteError) Error() string { - return "Should ignore deletion" -} - -func deleteSetting(db *sql.DB, key string) error { - const cmd = "UPDATE Setting set Value = DefaultValue WHERE Name = ?" - _, err := db.Exec(cmd, key) - if err != nil { - return err - } - return IgnoreDeleteError{} -} +//func initSettings(settings *DbTable[string, Setting]) { +// addSettingIfNotExists("theme", "white", settings) +// addSettingIfNotExists("order", "title", settings) +//} +// +//func addSettingIfNotExists(name string, value string, settings *DbTable[string, Setting]) { +// _, exists := settings.Get(name) +// if !exists { +// settings.Set(name, NewSetting(name, value)) +// } +//} diff --git a/internal/database/table.go b/internal/database/table.go deleted file mode 100644 index c22e44b..0000000 --- a/internal/database/table.go +++ /dev/null @@ -1,156 +0,0 @@ -package database - -import ( - "database/sql" - "sync" -) - -type DbStatus uint8 - -const ( - New DbStatus = iota - Loaded - Updated -) - -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 -} - -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]) Map() map[K]T { - d.mutex.Lock() - defer d.mutex.Unlock() - res := make(map[K]T, len(d.items)) - for k, manga := range d.items { - res[k] = manga - } - return res -} - -func (d *DbTable[K, T]) First(filter func(match T) bool) (key K, value T, ok bool) { - d.mutex.Lock() - defer d.mutex.Unlock() - - for k, manga := range d.items { - if filter(manga) { - return k, manga, true - } - } - - return *new(K), *new(T), false -} - -func (d *DbTable[K, T]) Where(filter func(match T) bool) map[K]T { - d.mutex.Lock() - defer d.mutex.Unlock() - res := make(map[K]T, len(d.items)) - for k, manga := range d.items { - if filter(manga) { - res[k] = manga - } - } - return res -} - -func (d *DbTable[K, T]) Delete(db *sql.DB, key K) error { - d.mutex.Lock() - defer d.mutex.Unlock() - err := d.deleteFunc(db, key) - if err == nil { - delete(d.items, key) - } - return err -} - -func (d *DbTable[K, T]) Save(db *sql.DB) error { - d.mutex.Lock() - defer d.mutex.Unlock() - for k, status := range d.updated { - switch status { - case Loaded: - continue - case Updated: - item := d.items[k] - err := d.updateFunc(db, &item) - if err != nil { - return err - } - d.updated[k] = Loaded - case New: - item := d.items[k] - err := d.insertFunc(db, &item) - if err != nil { - return err - } - d.updated[k] = Loaded - } - } - 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 b958d39..5a1da94 100644 --- a/internal/server/handler.go +++ b/internal/server/handler.go @@ -3,11 +3,13 @@ package server import ( "cmp" _ "embed" + "errors" "fmt" "github.com/pablu23/mangaGetter/internal/database" "github.com/pablu23/mangaGetter/internal/view" "golang.org/x/text/cases" "golang.org/x/text/language" + "gorm.io/gorm" "html/template" "net/http" "slices" @@ -35,13 +37,21 @@ func (s *Server) HandleNew(w http.ResponseWriter, r *http.Request) { func (s *Server) HandleMenu(w http.ResponseWriter, _ *http.Request) { tmpl := template.Must(view.GetViewTemplate(view.Menu)) - all := s.DbMgr.Mangas.All() + var all []*database.Manga + _ = s.DbMgr.Db.Preload("Chapters").Find(&all) l := len(all) mangaViewModels := make([]view.MangaViewModel, l) counter := 0 n := time.Now().UnixNano() + var tmp []database.Setting + s.DbMgr.Db.Find(&tmp) + settings := make(map[string]database.Setting) + for _, m := range tmp { + settings[m.Name] = m + } + var thumbNs int64 = 0 var titNs int64 = 0 @@ -51,13 +61,13 @@ func (s *Server) HandleMenu(w http.ResponseWriter, _ *http.Request) { t1 := time.Now().UnixNano() - thumbnail, updated, 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 } if updated { - s.DbMgr.Mangas.Set(manga.Id, manga) + s.DbMgr.Db.Save(manga) } t2 := time.Now().UnixNano() @@ -69,12 +79,12 @@ func (s *Server) HandleMenu(w http.ResponseWriter, _ *http.Request) { // This is very slow // TODO: put this into own Method if manga.LastChapterNum == "" { - err, updated := s.UpdateLatestAvailableChapter(&manga) + err, updated := s.UpdateLatestAvailableChapter(manga) if err != nil { fmt.Println(err) } if updated { - s.DbMgr.Mangas.Set(manga.Id, manga) + s.DbMgr.Db.Save(manga) } } @@ -82,7 +92,7 @@ func (s *Server) HandleMenu(w http.ResponseWriter, _ *http.Request) { titNs += t2 - t1 - latestChapter, ok := manga.GetLatestChapter(&s.DbMgr.Chapters) + latestChapter, ok := manga.GetLatestChapter() if !ok { continue } @@ -101,24 +111,23 @@ func (s *Server) HandleMenu(w http.ResponseWriter, _ *http.Request) { } fmt.Printf("Loading Thumbnails took %d ms\n", (thumbNs)/1000000) - fmt.Printf("Loading latest Chapter took %d ms\n", (titNs)/1000000) + fmt.Printf("Loading latest Chapters took %d ms\n", (titNs)/1000000) nex := time.Now().UnixNano() fmt.Printf("Creating Viewmodels took %d ms\n", (nex-n)/1000000) n = time.Now().UnixNano() - order, ok := s.DbMgr.Settings.Get("order") - sort := order.Value - if !ok || sort == "title" { + order, ok := settings["order"] + if !ok || order.Value == "title" { slices.SortStableFunc(mangaViewModels, func(a, b view.MangaViewModel) int { return cmp.Compare(a.Title, b.Title) }) - } else if sort == "chapter" { + } else if order.Value == "chapter" { slices.SortStableFunc(mangaViewModels, func(a, b view.MangaViewModel) int { return cmp.Compare(b.Number, a.Number) }) - } else if sort == "last" { + } else if order.Value == "last" { slices.SortStableFunc(mangaViewModels, func(a, b view.MangaViewModel) int { aT, err := time.Parse("15:04 (02-01-06)", a.LastTime) if err != nil { @@ -136,7 +145,7 @@ func (s *Server) HandleMenu(w http.ResponseWriter, _ *http.Request) { fmt.Printf("Sorting took %d ms\n", (nex-n)/1000000) menuViewModel := view.MenuViewModel{ - Settings: s.DbMgr.Settings.Map(), + Settings: settings, Mangas: mangaViewModels, } @@ -161,22 +170,12 @@ func (s *Server) HandleDelete(w http.ResponseWriter, r *http.Request) { return } - err = s.DbMgr.Delete(mangaId) - if err != nil { - fmt.Println(err) - } + s.DbMgr.Delete(mangaId) http.Redirect(w, r, "/", http.StatusTemporaryRedirect) } func (s *Server) HandleExit(w http.ResponseWriter, r *http.Request) { - err := s.DbMgr.Save() - if err != nil { - fmt.Println(err) - http.Redirect(w, r, "/curr", http.StatusTemporaryRedirect) - return - } - http.Redirect(w, r, "/", http.StatusTemporaryRedirect) go func() { @@ -215,23 +214,25 @@ func (s *Server) HandleCurrent(w http.ResponseWriter, _ *http.Request) { fmt.Println(err) } - manga, ok := s.DbMgr.Mangas.Get(mangaId) - if !ok { + var manga database.Manga + result := s.DbMgr.Db.First(&manga, mangaId) + if result.Error != nil && errors.Is(result.Error, gorm.ErrRecordNotFound) { manga = database.NewManga(mangaId, title, time.Now().Unix()) } else { manga.TimeStampUnix = time.Now().Unix() } - chapter, ok := s.DbMgr.Chapters.Get(chapterId) - if !ok { + var chapter database.Chapter + result = s.DbMgr.Db.First(&chapter, chapterId) + if result.Error != nil && errors.Is(result.Error, gorm.ErrRecordNotFound) { chapterNumberStr := strings.Replace(chapterName, "ch_", "", 1) - chapter = database.NewChapter(chapterId, manga.Id, s.CurrSubUrl, chapterName, chapterNumberStr, time.Now().Unix()) + chapter = database.NewChapter(chapterId, mangaId, s.CurrSubUrl, chapterName, chapterNumberStr, time.Now().Unix()) } else { chapter.TimeStampUnix = time.Now().Unix() } - s.DbMgr.Chapters.Set(chapterId, chapter) - s.DbMgr.Mangas.Set(mangaId, manga) + s.DbMgr.Db.Save(&manga) + s.DbMgr.Db.Save(&chapter) err = tmpl.Execute(w, s.CurrViewModel) if err != nil { @@ -251,7 +252,7 @@ func (s *Server) HandleImage(w http.ResponseWriter, r *http.Request) { } w.Header().Set("Content-Type", "image/webp") - _, err := w.Write(buf.Bytes()) + _, err := w.Write(buf) if err != nil { fmt.Println(err) } @@ -284,10 +285,6 @@ func (s *Server) HandleNext(w http.ResponseWriter, r *http.Request) { if s.NextViewModel == nil || s.NextSubUrl == "" { http.Redirect(w, r, "/", http.StatusTemporaryRedirect) - err := s.DbMgr.Save() - if err != nil { - fmt.Println(err) - } return } @@ -315,10 +312,6 @@ func (s *Server) HandlePrev(w http.ResponseWriter, r *http.Request) { if s.PrevViewModel == nil || s.PrevSubUrl == "" { http.Redirect(w, r, "/", http.StatusTemporaryRedirect) - err := s.DbMgr.Save() - if err != nil { - fmt.Println(err) - } return } @@ -336,14 +329,14 @@ func (s *Server) HandleSettingSet(w http.ResponseWriter, r *http.Request) { settingName := r.PathValue("setting") settingValue := r.PathValue("value") - setting, ok := s.DbMgr.Settings.Get(settingName) - if !ok { - s.DbMgr.Settings.Set(settingName, database.NewSetting(settingName, settingValue)) + var setting database.Setting + res := s.DbMgr.Db.First(&setting, "name = ?", settingName) + + if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) { + set := database.NewSetting(settingName, settingValue) + s.DbMgr.Db.Save(&set) } else { - if setting.Value != settingValue { - setting.Value = settingValue - s.DbMgr.Settings.Set(settingName, setting) - } + s.DbMgr.Db.Model(&setting).Update("value", settingValue) } http.Redirect(w, r, "/", http.StatusTemporaryRedirect) @@ -353,14 +346,14 @@ func (s *Server) HandleSetting(w http.ResponseWriter, r *http.Request) { settingName := r.PostFormValue("setting") settingValue := r.PostFormValue(settingName) - setting, ok := s.DbMgr.Settings.Get(settingName) - if !ok { - s.DbMgr.Settings.Set(settingName, database.NewSetting(settingName, settingValue)) + var setting database.Setting + res := s.DbMgr.Db.First(&setting, "name = ?", settingName) + + if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) { + set := database.NewSetting(settingName, settingValue) + s.DbMgr.Db.Save(&set) } else { - if setting.Value != settingValue { - setting.Value = settingValue - s.DbMgr.Settings.Set(settingName, setting) - } + s.DbMgr.Db.Model(&setting).Update("value", settingValue) } http.Redirect(w, r, "/", http.StatusTemporaryRedirect) diff --git a/internal/server/server.go b/internal/server/server.go index e401c5e..a0e9532 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -21,7 +21,7 @@ type Server struct { CurrViewModel *view.ImageViewModel NextViewModel *view.ImageViewModel - ImageBuffers map[string]*bytes.Buffer + ImageBuffers map[string][]byte Mutex *sync.Mutex NextSubUrl string @@ -38,7 +38,7 @@ type Server struct { func New(provider provider.Provider, db *database.Manager) *Server { s := Server{ - ImageBuffers: make(map[string]*bytes.Buffer), + ImageBuffers: make(map[string][]byte), Provider: provider, DbMgr: db, Mutex: &sync.Mutex{}, @@ -61,16 +61,18 @@ func (s *Server) Start(port int) error { http.HandleFunc("POST /setting/", s.HandleSetting) http.HandleFunc("GET /setting/set/{setting}/{value}", s.HandleSettingSet) - // Update Latest Chapter every 5 Minutes + // Update Latest Chapters every 5 Minutes go func(s *Server) { time.AfterFunc(time.Second*10, func() { - for _, m := range s.DbMgr.Mangas.All() { - err, updated := s.UpdateLatestAvailableChapter(&m) + var all []*database.Manga + s.DbMgr.Db.Find(&all) + for _, m := range all { + err, updated := s.UpdateLatestAvailableChapter(m) if err != nil { fmt.Println(err) } if updated { - s.DbMgr.Mangas.Set(m.Id, m) + s.DbMgr.Db.Save(m) } } }) @@ -78,13 +80,15 @@ func (s *Server) Start(port int) error { for { select { case <-time.After(time.Minute * 5): - for _, m := range s.DbMgr.Mangas.All() { - err, updated := s.UpdateLatestAvailableChapter(&m) + var all []*database.Manga + s.DbMgr.Db.Find(&all) + for _, m := range all { + err, updated := s.UpdateLatestAvailableChapter(m) if err != nil { fmt.Println(err) } if updated { - s.DbMgr.Mangas.Set(m.Id, m) + s.DbMgr.Db.Save(m) } } } @@ -287,7 +291,7 @@ func (s *Server) AppendImagesToBuf(html string) ([]view.Image, error) { return images, nil } -func addFileToRam(url string) (*bytes.Buffer, error) { +func addFileToRam(url string) ([]byte, error) { // Get the data resp, err := http.Get(url) if err != nil { @@ -304,5 +308,5 @@ func addFileToRam(url string) (*bytes.Buffer, error) { // Write the body to file _, err = io.Copy(buf, resp.Body) - return buf, err + return buf.Bytes(), err }