Changed Project structure, added Hot Reload of gohtml files for Develop and embedding of gohtml files for Release

This commit is contained in:
Pablu23
2024-02-23 10:59:26 +01:00
parent cc7009c106
commit 9bc483afb3
13 changed files with 196 additions and 143 deletions

View File

@@ -7,41 +7,24 @@ import (
"os" "os"
"os/signal" "os/signal"
"sync" "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() { func main() {
db := NewDatabase("db.sqlite", true) db := database.NewDatabase("db.sqlite", true)
err := db.Open() err := db.Open()
if err != nil { if err != nil {
return return
} }
server := Server{ s := server.Server{
ImageBuffers: make(map[string]*bytes.Buffer), ImageBuffers: make(map[string]*bytes.Buffer),
NextReady: make(chan bool), NextReady: make(chan bool),
PrevReady: make(chan bool), PrevReady: make(chan bool),
Provider: &Bato{}, Provider: &provider.Bato{},
DbMgr: &db, DbMgr: &db,
Mutex: &sync.Mutex{}, Mutex: &sync.Mutex{},
} }
@@ -55,13 +38,13 @@ func main() {
} }
}() }()
http.HandleFunc("/", server.HandleMenu) http.HandleFunc("/", s.HandleMenu)
http.HandleFunc("/new/title/{title}/{chapter}", server.HandleNew) http.HandleFunc("/new/title/{title}/{chapter}", s.HandleNew)
http.HandleFunc("/current/", server.HandleCurrent) http.HandleFunc("/current/", s.HandleCurrent)
http.HandleFunc("/img/{url}/", server.HandleImage) http.HandleFunc("/img/{url}/", s.HandleImage)
http.HandleFunc("POST /next", server.HandleNext) http.HandleFunc("POST /next", s.HandleNext)
http.HandleFunc("POST /prev", server.HandlePrev) http.HandleFunc("POST /prev", s.HandlePrev)
http.HandleFunc("POST /exit", server.HandleExit) http.HandleFunc("POST /exit", s.HandleExit)
fmt.Println("Server starting...") fmt.Println("Server starting...")
err = http.ListenAndServe(":8000", nil) 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") fmt.Println("Attempting to save and close DB")
err := db.Save() err := db.Save()
if err != nil { if err != nil {

View File

@@ -1,4 +1,4 @@
package main package database
import ( import (
"database/sql" "database/sql"
@@ -30,7 +30,7 @@ type DatabaseManager struct {
ConnectionString string ConnectionString string
db *sql.DB db *sql.DB
rw *sync.Mutex Rw *sync.Mutex
Mangas map[int]*Manga Mangas map[int]*Manga
Chapters map[int]*Chapter Chapters map[int]*Chapter
@@ -40,7 +40,7 @@ type DatabaseManager struct {
func NewDatabase(connectionString string, createIfNotExists bool) DatabaseManager { func NewDatabase(connectionString string, createIfNotExists bool) DatabaseManager {
return DatabaseManager{ return DatabaseManager{
ConnectionString: connectionString, ConnectionString: connectionString,
rw: &sync.Mutex{}, Rw: &sync.Mutex{},
Mangas: make(map[int]*Manga), Mangas: make(map[int]*Manga),
Chapters: make(map[int]*Chapter), Chapters: make(map[int]*Chapter),
CreateIfNotExists: createIfNotExists, CreateIfNotExists: createIfNotExists,
@@ -78,8 +78,8 @@ func (dbMgr *DatabaseManager) Close() error {
func (dbMgr *DatabaseManager) Save() error { func (dbMgr *DatabaseManager) Save() error {
db := dbMgr.db db := dbMgr.db
dbMgr.rw.Lock() dbMgr.Rw.Lock()
defer dbMgr.rw.Unlock() defer dbMgr.Rw.Unlock()
for _, m := range dbMgr.Mangas { for _, m := range dbMgr.Mangas {
count := 0 count := 0
err := db.QueryRow("SELECT COUNT(*) FROM Manga where ID = ?", m.Id).Scan(&count) 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 { func (dbMgr *DatabaseManager) load() error {
db := dbMgr.db db := dbMgr.db
dbMgr.rw.Lock() dbMgr.Rw.Lock()
defer dbMgr.rw.Unlock() defer dbMgr.Rw.Unlock()
rows, err := db.Query("SELECT * FROM Manga") rows, err := db.Query("SELECT * FROM Manga")
if err != nil { if err != nil {

View File

@@ -1,4 +1,4 @@
package main package provider
import ( import (
"errors" "errors"
@@ -6,15 +6,9 @@ import (
"io" "io"
"net/http" "net/http"
"regexp" "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{} type Bato struct{}
func (b *Bato) GetImageList(html string) ([]string, error) { 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 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
}

View File

@@ -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)
}

View File

@@ -1,10 +1,14 @@
package main package server
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"golang.org/x/text/language" "golang.org/x/text/language"
"html/template" "html/template"
"io"
"mangaGetter/internal/database"
"mangaGetter/internal/provider"
"mangaGetter/internal/view"
"net/http" "net/http"
"path/filepath" "path/filepath"
"strconv" "strconv"
@@ -16,9 +20,9 @@ import (
) )
type Server struct { type Server struct {
PrevViewModel *ImageViewModel PrevViewModel *view.ImageViewModel
CurrViewModel *ImageViewModel CurrViewModel *view.ImageViewModel
NextViewModel *ImageViewModel NextViewModel *view.ImageViewModel
ImageBuffers map[string]*bytes.Buffer ImageBuffers map[string]*bytes.Buffer
Mutex *sync.Mutex Mutex *sync.Mutex
@@ -27,12 +31,12 @@ type Server struct {
CurrSubUrl string CurrSubUrl string
PrevSubUrl string PrevSubUrl string
Provider Provider Provider provider.Provider
IsFirst bool IsFirst bool
IsLast bool IsLast bool
DbMgr *DatabaseManager DbMgr *database.DatabaseManager
// I'm not even sure if this helps. // I'm not even sure if this helps.
// If you press next and then prev too fast you still lock yourself out // 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") fmt.Println("Received Next")
if s.PrevViewModel != nil { if s.PrevViewModel != nil {
go func(viewModel ImageViewModel, s *Server) { go func(viewModel view.ImageViewModel, s *Server) {
s.Mutex.Lock() s.Mutex.Lock()
for _, img := range viewModel.Images { for _, img := range viewModel.Images {
delete(s.ImageBuffers, img.Path) delete(s.ImageBuffers, img.Path)
@@ -109,7 +113,7 @@ func (s *Server) LoadNext() {
return return
} }
title, chapter, err := getTitleAndChapter(next) title, chapter, err := s.Provider.GetTitleAndChapter(next)
if err != nil { if err != nil {
title = "Unknown" title = "Unknown"
chapter = "ch_?" chapter = "ch_?"
@@ -117,7 +121,7 @@ func (s *Server) LoadNext() {
full := strings.Replace(title, "-", " ", -1) + " - " + strings.Replace(chapter, "_", " ", -1) 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 s.NextSubUrl = next
fmt.Println("Loaded next") fmt.Println("Loaded next")
@@ -147,7 +151,7 @@ func (s *Server) LoadPrev() {
return return
} }
title, chapter, err := getTitleAndChapter(prev) title, chapter, err := s.Provider.GetTitleAndChapter(prev)
if err != nil { if err != nil {
title = "Unknown" title = "Unknown"
chapter = "ch_?" chapter = "ch_?"
@@ -155,7 +159,7 @@ func (s *Server) LoadPrev() {
full := strings.Replace(title, "-", " ", -1) + " - " + strings.Replace(chapter, "_", " ", -1) 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 s.PrevSubUrl = prev
fmt.Println("Loaded prev") fmt.Println("Loaded prev")
@@ -165,7 +169,7 @@ func (s *Server) LoadPrev() {
func (s *Server) HandlePrev(w http.ResponseWriter, r *http.Request) { func (s *Server) HandlePrev(w http.ResponseWriter, r *http.Request) {
fmt.Println("Received Prev") fmt.Println("Received Prev")
if s.NextViewModel != nil { if s.NextViewModel != nil {
go func(viewModel ImageViewModel, s *Server) { go func(viewModel view.ImageViewModel, s *Server) {
s.Mutex.Lock() s.Mutex.Lock()
for _, img := range viewModel.Images { for _, img := range viewModel.Images {
delete(s.ImageBuffers, img.Path) 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) { 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() s.DbMgr.Rw.Lock()
defer s.DbMgr.rw.Unlock() defer s.DbMgr.Rw.Unlock()
mangaId, chapterId, err := getMangaIdAndChapterId(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 := getTitleAndChapter(s.CurrSubUrl) title, chapter, err := s.Provider.GetTitleAndChapter(s.CurrSubUrl)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} else { } else {
var manga *Manga var manga *database.Manga
if s.DbMgr.Mangas[mangaId] == nil { if s.DbMgr.Mangas[mangaId] == nil {
manga = &Manga{ manga = &database.Manga{
Id: mangaId, Id: mangaId,
Title: title, Title: title,
TimeStampUnix: time.Now().Unix(), TimeStampUnix: time.Now().Unix(),
@@ -222,7 +226,7 @@ func (s *Server) HandleCurrent(w http.ResponseWriter, _ *http.Request) {
number = 0 number = 0
} }
s.DbMgr.Chapters[chapterId] = &Chapter{ s.DbMgr.Chapters[chapterId] = &database.Chapter{
Id: chapterId, Id: chapterId,
Manga: manga, Manga: manga,
Url: s.CurrSubUrl, Url: s.CurrSubUrl,
@@ -270,7 +274,7 @@ func (s *Server) LoadCurr() {
imagesCurr, err := s.AppendImagesToBuf(html) imagesCurr, err := s.AppendImagesToBuf(html)
title, chapter, err := getTitleAndChapter(s.CurrSubUrl) title, chapter, err := s.Provider.GetTitleAndChapter(s.CurrSubUrl)
if err != nil { if err != nil {
title = "Unknown" title = "Unknown"
chapter = "ch_?" chapter = "ch_?"
@@ -278,17 +282,17 @@ func (s *Server) LoadCurr() {
full := strings.Replace(title, "-", " ", -1) + " - " + strings.Replace(chapter, "_", " ", -1) 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") 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) imgList, err := s.Provider.GetImageList(html)
if err != nil { if err != nil {
return nil, err return nil, err
} }
images := make([]Image, len(imgList)) images := make([]view.Image, len(imgList))
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
for i, url := range imgList { for i, url := range imgList {
@@ -302,7 +306,7 @@ func (s *Server) AppendImagesToBuf(html string) ([]Image, error) {
s.Mutex.Lock() s.Mutex.Lock()
s.ImageBuffers[name] = buf s.ImageBuffers[name] = buf
s.Mutex.Unlock() s.Mutex.Unlock()
images[i] = Image{Path: name, Index: i} images[i] = view.Image{Path: name, Index: i}
wg.Done() wg.Done()
}(i, url, &wg) }(i, url, &wg)
} }
@@ -311,21 +315,21 @@ func (s *Server) AppendImagesToBuf(html string) ([]Image, error) {
return images, nil return images, nil
} }
func (s *Server) HandleMenu(w http.ResponseWriter, r *http.Request) { func (s *Server) HandleMenu(w http.ResponseWriter, _ *http.Request) {
tmpl := template.Must(template.ParseFiles("menu.gohtml")) tmpl := template.Must(view.GetViewTemplate(view.Menu))
s.DbMgr.rw.Lock() s.DbMgr.Rw.Lock()
defer s.DbMgr.rw.Unlock() defer s.DbMgr.Rw.Unlock()
all := s.DbMgr.Mangas all := s.DbMgr.Mangas
l := len(all) l := len(all)
mangaViewModels := make([]MangaViewModel, l) mangaViewModels := make([]view.MangaViewModel, l)
counter := 0 counter := 0
for _, manga := range all { for _, manga := range all {
title := cases.Title(language.English, cases.Compact).String(strings.Replace(manga.Title, "-", " ", -1)) title := cases.Title(language.English, cases.Compact).String(strings.Replace(manga.Title, "-", " ", -1))
mangaViewModels[counter] = MangaViewModel{ mangaViewModels[counter] = view.MangaViewModel{
Title: title, Title: title,
Number: manga.LatestChapter.Number, Number: manga.LatestChapter.Number,
// 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
@@ -335,7 +339,7 @@ func (s *Server) HandleMenu(w http.ResponseWriter, r *http.Request) {
counter++ counter++
} }
menuViewModel := MenuViewModel{ menuViewModel := view.MenuViewModel{
Mangas: mangaViewModels, Mangas: mangaViewModels,
} }
@@ -354,3 +358,23 @@ func (s *Server) HandleExit(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/", http.StatusTemporaryRedirect) 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
}

25
internal/view/embedded.go Normal file
View File

@@ -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")
}

View File

@@ -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)
}

View File

@@ -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
}

8
internal/view/views.go Normal file
View File

@@ -0,0 +1,8 @@
package view
type View int
const (
Menu View = iota
Viewer View = iota
)

64
util.go
View File

@@ -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
}