From 9bc483afb30d5bb10734166ba329e35d073d3c01 Mon Sep 17 00:00:00 2001 From: Pablu23 Date: Fri, 23 Feb 2024 10:59:26 +0100 Subject: [PATCH] Changed Project structure, added Hot Reload of gohtml files for Develop and embedding of gohtml files for Release --- main.go => cmd/mangaGetter/main.go | 47 ++++------ .../database/createDb.sql | 0 database.go => internal/database/database.go | 14 +-- bato.go => internal/provider/bato.go | 43 +++++++-- internal/provider/provider.go | 10 +++ server.go => internal/server/server.go | 88 ++++++++++++------- .../view/Views/menu.gohtml | 0 .../view/Views/viewer.gohtml | 0 internal/view/embedded.go | 25 ++++++ internal/view/hotreload.go | 18 ++++ internal/view/viewmodels.go | 22 +++++ internal/view/views.go | 8 ++ util.go | 64 -------------- 13 files changed, 196 insertions(+), 143 deletions(-) rename main.go => cmd/mangaGetter/main.go (50%) rename createDb.sql => internal/database/createDb.sql (100%) rename database.go => internal/database/database.go (95%) rename bato.go => internal/provider/bato.go (65%) create mode 100644 internal/provider/provider.go rename server.go => internal/server/server.go (75%) rename menu.gohtml => internal/view/Views/menu.gohtml (100%) rename viewer.gohtml => internal/view/Views/viewer.gohtml (100%) create mode 100644 internal/view/embedded.go create mode 100644 internal/view/hotreload.go create mode 100644 internal/view/viewmodels.go create mode 100644 internal/view/views.go delete mode 100644 util.go diff --git a/main.go b/cmd/mangaGetter/main.go similarity index 50% rename from main.go rename to cmd/mangaGetter/main.go index 5d956d1..4d2b8fc 100644 --- a/main.go +++ b/cmd/mangaGetter/main.go @@ -7,41 +7,24 @@ import ( "os" "os/signal" "sync" + + "mangaGetter/internal/database" + "mangaGetter/internal/provider" + "mangaGetter/internal/server" ) -type Image struct { - Path string - Index int -} - -type ImageViewModel struct { - Title string - Images []Image -} - -type MangaViewModel struct { - Title string - Number int - LastTime string - Url string -} - -type MenuViewModel struct { - Mangas []MangaViewModel -} - func main() { - db := NewDatabase("db.sqlite", true) + db := database.NewDatabase("db.sqlite", true) err := db.Open() if err != nil { return } - server := Server{ + s := server.Server{ ImageBuffers: make(map[string]*bytes.Buffer), NextReady: make(chan bool), PrevReady: make(chan bool), - Provider: &Bato{}, + Provider: &provider.Bato{}, DbMgr: &db, Mutex: &sync.Mutex{}, } @@ -55,13 +38,13 @@ func main() { } }() - http.HandleFunc("/", server.HandleMenu) - http.HandleFunc("/new/title/{title}/{chapter}", server.HandleNew) - http.HandleFunc("/current/", server.HandleCurrent) - http.HandleFunc("/img/{url}/", server.HandleImage) - http.HandleFunc("POST /next", server.HandleNext) - http.HandleFunc("POST /prev", server.HandlePrev) - http.HandleFunc("POST /exit", server.HandleExit) + http.HandleFunc("/", s.HandleMenu) + http.HandleFunc("/new/title/{title}/{chapter}", s.HandleNew) + http.HandleFunc("/current/", s.HandleCurrent) + http.HandleFunc("/img/{url}/", s.HandleImage) + http.HandleFunc("POST /next", s.HandleNext) + http.HandleFunc("POST /prev", s.HandlePrev) + http.HandleFunc("POST /exit", s.HandleExit) fmt.Println("Server starting...") err = http.ListenAndServe(":8000", nil) @@ -71,7 +54,7 @@ func main() { } } -func Close(db *DatabaseManager) { +func Close(db *database.DatabaseManager) { fmt.Println("Attempting to save and close DB") err := db.Save() if err != nil { diff --git a/createDb.sql b/internal/database/createDb.sql similarity index 100% rename from createDb.sql rename to internal/database/createDb.sql diff --git a/database.go b/internal/database/database.go similarity index 95% rename from database.go rename to internal/database/database.go index a335c17..52ae74e 100644 --- a/database.go +++ b/internal/database/database.go @@ -1,4 +1,4 @@ -package main +package database import ( "database/sql" @@ -30,7 +30,7 @@ type DatabaseManager struct { ConnectionString string db *sql.DB - rw *sync.Mutex + Rw *sync.Mutex Mangas map[int]*Manga Chapters map[int]*Chapter @@ -40,7 +40,7 @@ type DatabaseManager struct { func NewDatabase(connectionString string, createIfNotExists bool) DatabaseManager { return DatabaseManager{ ConnectionString: connectionString, - rw: &sync.Mutex{}, + Rw: &sync.Mutex{}, Mangas: make(map[int]*Manga), Chapters: make(map[int]*Chapter), CreateIfNotExists: createIfNotExists, @@ -78,8 +78,8 @@ func (dbMgr *DatabaseManager) Close() error { func (dbMgr *DatabaseManager) Save() error { db := dbMgr.db - dbMgr.rw.Lock() - defer dbMgr.rw.Unlock() + dbMgr.Rw.Lock() + defer dbMgr.Rw.Unlock() for _, m := range dbMgr.Mangas { count := 0 err := db.QueryRow("SELECT COUNT(*) FROM Manga where ID = ?", m.Id).Scan(&count) @@ -134,8 +134,8 @@ func (dbMgr *DatabaseManager) createDatabaseIfNotExists() error { func (dbMgr *DatabaseManager) load() error { db := dbMgr.db - dbMgr.rw.Lock() - defer dbMgr.rw.Unlock() + dbMgr.Rw.Lock() + defer dbMgr.Rw.Unlock() rows, err := db.Query("SELECT * FROM Manga") if err != nil { diff --git a/bato.go b/internal/provider/bato.go similarity index 65% rename from bato.go rename to internal/provider/bato.go index 63066cc..6480de1 100644 --- a/bato.go +++ b/internal/provider/bato.go @@ -1,4 +1,4 @@ -package main +package provider import ( "errors" @@ -6,15 +6,9 @@ import ( "io" "net/http" "regexp" + "strconv" ) -type Provider interface { - GetImageList(html string) (imageUrls []string, err error) - GetHtml(url string) (html string, err error) - GetNext(html string) (url string, err error) - GetPrev(html string) (url string, err error) -} - type Bato struct{} func (b *Bato) GetImageList(html string) ([]string, error) { @@ -81,3 +75,36 @@ func (b *Bato) GetPrev(html string) (subUrl string, err error) { return match[1], err } + +func (b *Bato) GetTitleAndChapter(url string) (title string, chapter string, err error) { + reg, err := regexp.Compile(`/title/\d*-(.*?)/\d*-(.*)`) + if err != nil { + return "", "", err + } + + matches := reg.FindAllStringSubmatch(url, -1) + if len(matches) <= 0 { + return "", "", errors.New("no title or chapter found") + } + + return matches[0][1], matches[0][2], nil +} + +func (b *Bato) GetTitleIdAndChapterId(url string) (titleId int, chapterId int, err error) { + reg, err := regexp.Compile(`/title/(\d*)-.*?/(\d*)-.*`) + if err != nil { + return 0, 0, err + } + + matches := reg.FindAllStringSubmatch(url, -1) + if len(matches) <= 0 { + return 0, 0, errors.New("no title or chapter found") + } + t, err := strconv.Atoi(matches[0][1]) + if err != nil { + return 0, 0, err + } + c, err := strconv.Atoi(matches[0][2]) + + return t, c, err +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go new file mode 100644 index 0000000..9192166 --- /dev/null +++ b/internal/provider/provider.go @@ -0,0 +1,10 @@ +package provider + +type Provider interface { + GetImageList(html string) (imageUrls []string, err error) + GetHtml(url string) (html string, err error) + GetNext(html string) (url string, err error) + GetPrev(html string) (url string, err error) + GetTitleAndChapter(url string) (title string, chapter string, err error) + GetTitleIdAndChapterId(url string) (titleId int, chapterId int, err error) +} diff --git a/server.go b/internal/server/server.go similarity index 75% rename from server.go rename to internal/server/server.go index 797b110..a8c874c 100644 --- a/server.go +++ b/internal/server/server.go @@ -1,10 +1,14 @@ -package main +package server import ( "bytes" "fmt" "golang.org/x/text/language" "html/template" + "io" + "mangaGetter/internal/database" + "mangaGetter/internal/provider" + "mangaGetter/internal/view" "net/http" "path/filepath" "strconv" @@ -16,9 +20,9 @@ import ( ) type Server struct { - PrevViewModel *ImageViewModel - CurrViewModel *ImageViewModel - NextViewModel *ImageViewModel + PrevViewModel *view.ImageViewModel + CurrViewModel *view.ImageViewModel + NextViewModel *view.ImageViewModel ImageBuffers map[string]*bytes.Buffer Mutex *sync.Mutex @@ -27,12 +31,12 @@ type Server struct { CurrSubUrl string PrevSubUrl string - Provider Provider + Provider provider.Provider IsFirst bool IsLast bool - DbMgr *DatabaseManager + DbMgr *database.DatabaseManager // I'm not even sure if this helps. // If you press next and then prev too fast you still lock yourself out @@ -62,7 +66,7 @@ func (s *Server) HandleNext(w http.ResponseWriter, r *http.Request) { fmt.Println("Received Next") if s.PrevViewModel != nil { - go func(viewModel ImageViewModel, s *Server) { + go func(viewModel view.ImageViewModel, s *Server) { s.Mutex.Lock() for _, img := range viewModel.Images { delete(s.ImageBuffers, img.Path) @@ -109,7 +113,7 @@ func (s *Server) LoadNext() { return } - title, chapter, err := getTitleAndChapter(next) + title, chapter, err := s.Provider.GetTitleAndChapter(next) if err != nil { title = "Unknown" chapter = "ch_?" @@ -117,7 +121,7 @@ func (s *Server) LoadNext() { full := strings.Replace(title, "-", " ", -1) + " - " + strings.Replace(chapter, "_", " ", -1) - s.NextViewModel = &ImageViewModel{Images: imagesNext, Title: full} + s.NextViewModel = &view.ImageViewModel{Images: imagesNext, Title: full} s.NextSubUrl = next fmt.Println("Loaded next") @@ -147,7 +151,7 @@ func (s *Server) LoadPrev() { return } - title, chapter, err := getTitleAndChapter(prev) + title, chapter, err := s.Provider.GetTitleAndChapter(prev) if err != nil { title = "Unknown" chapter = "ch_?" @@ -155,7 +159,7 @@ func (s *Server) LoadPrev() { full := strings.Replace(title, "-", " ", -1) + " - " + strings.Replace(chapter, "_", " ", -1) - s.PrevViewModel = &ImageViewModel{Images: imagesNext, Title: full} + s.PrevViewModel = &view.ImageViewModel{Images: imagesNext, Title: full} s.PrevSubUrl = prev fmt.Println("Loaded prev") @@ -165,7 +169,7 @@ func (s *Server) LoadPrev() { func (s *Server) HandlePrev(w http.ResponseWriter, r *http.Request) { fmt.Println("Received Prev") if s.NextViewModel != nil { - go func(viewModel ImageViewModel, s *Server) { + go func(viewModel view.ImageViewModel, s *Server) { s.Mutex.Lock() for _, img := range viewModel.Images { delete(s.ImageBuffers, img.Path) @@ -188,22 +192,22 @@ func (s *Server) HandlePrev(w http.ResponseWriter, r *http.Request) { } func (s *Server) HandleCurrent(w http.ResponseWriter, _ *http.Request) { - tmpl := template.Must(template.ParseFiles("viewer.gohtml")) + tmpl := template.Must(view.GetViewTemplate(view.Viewer)) - s.DbMgr.rw.Lock() - defer s.DbMgr.rw.Unlock() + s.DbMgr.Rw.Lock() + defer s.DbMgr.Rw.Unlock() - mangaId, chapterId, err := getMangaIdAndChapterId(s.CurrSubUrl) + mangaId, chapterId, err := s.Provider.GetTitleIdAndChapterId(s.CurrSubUrl) if err != nil { fmt.Println(err) } else { - title, chapter, err := getTitleAndChapter(s.CurrSubUrl) + title, chapter, err := s.Provider.GetTitleAndChapter(s.CurrSubUrl) if err != nil { fmt.Println(err) } else { - var manga *Manga + var manga *database.Manga if s.DbMgr.Mangas[mangaId] == nil { - manga = &Manga{ + manga = &database.Manga{ Id: mangaId, Title: title, TimeStampUnix: time.Now().Unix(), @@ -222,7 +226,7 @@ func (s *Server) HandleCurrent(w http.ResponseWriter, _ *http.Request) { number = 0 } - s.DbMgr.Chapters[chapterId] = &Chapter{ + s.DbMgr.Chapters[chapterId] = &database.Chapter{ Id: chapterId, Manga: manga, Url: s.CurrSubUrl, @@ -270,7 +274,7 @@ func (s *Server) LoadCurr() { imagesCurr, err := s.AppendImagesToBuf(html) - title, chapter, err := getTitleAndChapter(s.CurrSubUrl) + title, chapter, err := s.Provider.GetTitleAndChapter(s.CurrSubUrl) if err != nil { title = "Unknown" chapter = "ch_?" @@ -278,17 +282,17 @@ func (s *Server) LoadCurr() { full := strings.Replace(title, "-", " ", -1) + " - " + strings.Replace(chapter, "_", " ", -1) - s.CurrViewModel = &ImageViewModel{Images: imagesCurr, Title: full} + s.CurrViewModel = &view.ImageViewModel{Images: imagesCurr, Title: full} fmt.Println("Loaded current") } -func (s *Server) AppendImagesToBuf(html string) ([]Image, error) { +func (s *Server) AppendImagesToBuf(html string) ([]view.Image, error) { imgList, err := s.Provider.GetImageList(html) if err != nil { return nil, err } - images := make([]Image, len(imgList)) + images := make([]view.Image, len(imgList)) wg := sync.WaitGroup{} for i, url := range imgList { @@ -302,7 +306,7 @@ func (s *Server) AppendImagesToBuf(html string) ([]Image, error) { s.Mutex.Lock() s.ImageBuffers[name] = buf s.Mutex.Unlock() - images[i] = Image{Path: name, Index: i} + images[i] = view.Image{Path: name, Index: i} wg.Done() }(i, url, &wg) } @@ -311,21 +315,21 @@ func (s *Server) AppendImagesToBuf(html string) ([]Image, error) { return images, nil } -func (s *Server) HandleMenu(w http.ResponseWriter, r *http.Request) { - tmpl := template.Must(template.ParseFiles("menu.gohtml")) +func (s *Server) HandleMenu(w http.ResponseWriter, _ *http.Request) { + tmpl := template.Must(view.GetViewTemplate(view.Menu)) - s.DbMgr.rw.Lock() - defer s.DbMgr.rw.Unlock() + s.DbMgr.Rw.Lock() + defer s.DbMgr.Rw.Unlock() all := s.DbMgr.Mangas l := len(all) - mangaViewModels := make([]MangaViewModel, l) + mangaViewModels := make([]view.MangaViewModel, l) counter := 0 for _, manga := range all { title := cases.Title(language.English, cases.Compact).String(strings.Replace(manga.Title, "-", " ", -1)) - mangaViewModels[counter] = MangaViewModel{ + mangaViewModels[counter] = view.MangaViewModel{ Title: title, Number: manga.LatestChapter.Number, // I Hate this time Format... 15 = hh, 04 = mm, 02 = DD, 01 = MM, 06 == YY @@ -335,7 +339,7 @@ func (s *Server) HandleMenu(w http.ResponseWriter, r *http.Request) { counter++ } - menuViewModel := MenuViewModel{ + menuViewModel := view.MenuViewModel{ Mangas: mangaViewModels, } @@ -354,3 +358,23 @@ func (s *Server) HandleExit(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/", http.StatusTemporaryRedirect) } + +func addFileToRam(url string) (*bytes.Buffer, error) { + // Get the data + resp, err := http.Get(url) + if err != nil { + return nil, err + } + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + fmt.Println(err) + } + }(resp.Body) + + buf := new(bytes.Buffer) + + // Write the body to file + _, err = io.Copy(buf, resp.Body) + return buf, err +} diff --git a/menu.gohtml b/internal/view/Views/menu.gohtml similarity index 100% rename from menu.gohtml rename to internal/view/Views/menu.gohtml diff --git a/viewer.gohtml b/internal/view/Views/viewer.gohtml similarity index 100% rename from viewer.gohtml rename to internal/view/Views/viewer.gohtml diff --git a/internal/view/embedded.go b/internal/view/embedded.go new file mode 100644 index 0000000..bce7e16 --- /dev/null +++ b/internal/view/embedded.go @@ -0,0 +1,25 @@ +//go:build !Develop + +package view + +import ( + _ "embed" + "errors" + "html/template" +) + +//go:embed Views/menu.gohtml +var menu string + +//go:embed Views/viewer.gohtml +var viewer string + +func GetViewTemplate(view View) (*template.Template, error) { + switch view { + case Menu: + return template.New("menu").Parse(menu) + case Viewer: + return template.New("viewer").Parse(viewer) + } + return nil, errors.New("invalid view") +} diff --git a/internal/view/hotreload.go b/internal/view/hotreload.go new file mode 100644 index 0000000..62d0f7f --- /dev/null +++ b/internal/view/hotreload.go @@ -0,0 +1,18 @@ +//go:build Develop + +package view + +import ( + "html/template" +) + +func GetViewTemplate(view View) (*template.Template, error) { + var path string + switch view { + case Menu: + path = "internal/view/Views/menu.gohtml" + case Viewer: + path = "internal/view/Views/viewer.gohtml" + } + return template.ParseFiles(path) +} diff --git a/internal/view/viewmodels.go b/internal/view/viewmodels.go new file mode 100644 index 0000000..0817593 --- /dev/null +++ b/internal/view/viewmodels.go @@ -0,0 +1,22 @@ +package view + +type Image struct { + Path string + Index int +} + +type ImageViewModel struct { + Title string + Images []Image +} + +type MangaViewModel struct { + Title string + Number int + LastTime string + Url string +} + +type MenuViewModel struct { + Mangas []MangaViewModel +} diff --git a/internal/view/views.go b/internal/view/views.go new file mode 100644 index 0000000..6d52763 --- /dev/null +++ b/internal/view/views.go @@ -0,0 +1,8 @@ +package view + +type View int + +const ( + Menu View = iota + Viewer View = iota +) diff --git a/util.go b/util.go deleted file mode 100644 index 165e20e..0000000 --- a/util.go +++ /dev/null @@ -1,64 +0,0 @@ -package main - -import ( - "bytes" - "errors" - "fmt" - "io" - "net/http" - "regexp" - "strconv" -) - -func getTitleAndChapter(url string) (title string, chapter string, err error) { - reg, err := regexp.Compile(`/title/\d*-(.*?)/\d*-(.*)`) - if err != nil { - return "", "", err - } - - matches := reg.FindAllStringSubmatch(url, -1) - if len(matches) <= 0 { - return "", "", errors.New("no title or chapter found") - } - - return matches[0][1], matches[0][2], nil -} - -func getMangaIdAndChapterId(url string) (titleId int, chapterId int, err error) { - reg, err := regexp.Compile(`/title/(\d*)-.*?/(\d*)-.*`) - if err != nil { - return 0, 0, err - } - - matches := reg.FindAllStringSubmatch(url, -1) - if len(matches) <= 0 { - return 0, 0, errors.New("no title or chapter found") - } - t, err := strconv.Atoi(matches[0][1]) - if err != nil { - return 0, 0, err - } - c, err := strconv.Atoi(matches[0][2]) - - return t, c, err -} - -func addFileToRam(url string) (*bytes.Buffer, error) { - // Get the data - resp, err := http.Get(url) - if err != nil { - return nil, err - } - defer func(Body io.ReadCloser) { - err := Body.Close() - if err != nil { - fmt.Println(err) - } - }(resp.Body) - - buf := new(bytes.Buffer) - - // Write the body to file - _, err = io.Copy(buf, resp.Body) - return buf, err -}