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/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 {

View File

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

View File

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

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 (
"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
}

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
}