Added Thumbnails and saving those to db,

also added Log for Locking and unlocking Rw, because there was a Problem with that, and now it stays
This commit is contained in:
Pablu23
2024-02-23 14:59:08 +01:00
parent cd26d937aa
commit 23f96e0ab5
9 changed files with 236 additions and 29 deletions

View File

@@ -46,6 +46,7 @@ func main() {
http.HandleFunc("POST /next", s.HandleNext) http.HandleFunc("POST /next", s.HandleNext)
http.HandleFunc("POST /prev", s.HandlePrev) http.HandleFunc("POST /prev", s.HandlePrev)
http.HandleFunc("POST /exit", s.HandleExit) http.HandleFunc("POST /exit", s.HandleExit)
http.HandleFunc("POST /delete", s.HandleDelete)
fmt.Println("Server starting...") fmt.Println("Server starting...")
err = http.ListenAndServe(":8000", nil) err = http.ListenAndServe(":8000", nil)
@@ -55,7 +56,7 @@ func main() {
} }
} }
func Close(db *database.DatabaseManager) { func Close(db *database.Manager) {
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,7 +1,8 @@
create table if not exists Manga ( create table if not exists Manga (
ID integer not null primary key, ID integer not null primary key,
Title text, Title text,
TimeStampUnixEpoch int TimeStampUnixEpoch int,
Thumbnail blob
); );
create table if not exists Chapter ( create table if not exists Chapter (

View File

@@ -1,8 +1,10 @@
package database package database
import ( import (
"bytes"
"database/sql" "database/sql"
_ "embed" _ "embed"
"fmt"
"sync" "sync"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
@@ -12,6 +14,7 @@ type Manga struct {
Id int Id int
Title string Title string
TimeStampUnix int64 TimeStampUnix int64
Thumbnail *bytes.Buffer
// Not in DB // Not in DB
LatestChapter *Chapter LatestChapter *Chapter
@@ -26,7 +29,7 @@ type Chapter struct {
TimeStampUnix int64 TimeStampUnix int64
} }
type DatabaseManager struct { type Manager struct {
ConnectionString string ConnectionString string
db *sql.DB db *sql.DB
@@ -37,8 +40,8 @@ type DatabaseManager struct {
CreateIfNotExists bool CreateIfNotExists bool
} }
func NewDatabase(connectionString string, createIfNotExists bool) DatabaseManager { func NewDatabase(connectionString string, createIfNotExists bool) Manager {
return DatabaseManager{ return Manager{
ConnectionString: connectionString, ConnectionString: connectionString,
Rw: &sync.Mutex{}, Rw: &sync.Mutex{},
Mangas: make(map[int]*Manga), Mangas: make(map[int]*Manga),
@@ -47,7 +50,7 @@ func NewDatabase(connectionString string, createIfNotExists bool) DatabaseManage
} }
} }
func (dbMgr *DatabaseManager) Open() error { func (dbMgr *Manager) Open() error {
db, err := sql.Open("sqlite3", dbMgr.ConnectionString) db, err := sql.Open("sqlite3", dbMgr.ConnectionString)
if err != nil { if err != nil {
return err return err
@@ -63,7 +66,7 @@ func (dbMgr *DatabaseManager) Open() error {
return err return err
} }
func (dbMgr *DatabaseManager) Close() error { func (dbMgr *Manager) Close() error {
err := dbMgr.db.Close() err := dbMgr.db.Close()
if err != nil { if err != nil {
return err return err
@@ -75,11 +78,43 @@ func (dbMgr *DatabaseManager) Close() error {
return nil return nil
} }
func (dbMgr *DatabaseManager) Save() error { 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)
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 db := dbMgr.db
fmt.Println("Locking Rw in database.go:113")
dbMgr.Rw.Lock() dbMgr.Rw.Lock()
defer dbMgr.Rw.Unlock() defer func() {
fmt.Println("Unlocking Rw in database.go:116")
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)
@@ -88,9 +123,16 @@ func (dbMgr *DatabaseManager) Save() error {
} }
if count == 0 { if count == 0 {
_, err := db.Exec("INSERT INTO Manga(ID, Title, TimeStampUnixEpoch) values(?, ?, ?)", m.Id, m.Title, m.TimeStampUnix) if m.Thumbnail != nil {
if err != nil { _, err := db.Exec("INSERT INTO Manga(ID, Title, TimeStampUnixEpoch, Thumbnail) values(?, ?, ?, ?)", m.Id, m.Title, m.TimeStampUnix, m.Thumbnail.Bytes())
return err if err != nil {
return err
}
} else {
_, err := db.Exec("INSERT INTO Manga(ID, Title, TimeStampUnixEpoch ) values(?, ?, ?)", m.Id, m.Title, m.TimeStampUnix)
if err != nil {
return err
}
} }
} else { } else {
_, err := db.Exec("UPDATE Manga set Title = ?, TimeStampUnixEpoch = ? WHERE ID = ?", m.Title, m.TimeStampUnix, m.Id) _, err := db.Exec("UPDATE Manga set Title = ?, TimeStampUnixEpoch = ? WHERE ID = ?", m.Title, m.TimeStampUnix, m.Id)
@@ -126,16 +168,20 @@ func (dbMgr *DatabaseManager) Save() error {
//go:embed createDb.sql //go:embed createDb.sql
var createSql string var createSql string
func (dbMgr *DatabaseManager) createDatabaseIfNotExists() error { func (dbMgr *Manager) createDatabaseIfNotExists() error {
_, err := dbMgr.db.Exec(createSql) _, err := dbMgr.db.Exec(createSql)
return err return err
} }
func (dbMgr *DatabaseManager) load() error { func (dbMgr *Manager) load() error {
db := dbMgr.db db := dbMgr.db
fmt.Println("Locking Rw in database.go:180")
dbMgr.Rw.Lock() dbMgr.Rw.Lock()
defer dbMgr.Rw.Unlock() defer func() {
fmt.Println("Unlocking Rw in database.go:183")
dbMgr.Rw.Unlock()
}()
rows, err := db.Query("SELECT * FROM Manga") rows, err := db.Query("SELECT * FROM Manga")
if err != nil { if err != nil {
@@ -144,7 +190,7 @@ func (dbMgr *DatabaseManager) load() error {
for rows.Next() { for rows.Next() {
manga := Manga{} manga := Manga{}
if err = rows.Scan(&manga.Id, &manga.Title, &manga.TimeStampUnix); err != nil { if err = rows.Scan(&manga.Id, &manga.Title, &manga.TimeStampUnix, &manga.Thumbnail); err != nil {
return err return err
} }
dbMgr.Mangas[manga.Id] = &manga dbMgr.Mangas[manga.Id] = &manga

View File

@@ -108,3 +108,40 @@ func (b *Bato) GetTitleIdAndChapterId(url string) (titleId int, chapterId int, e
return t, c, err return t, c, err
} }
//func (b *Bato) GetChapterList(url string) (chapterIds []int, err error) {
//
//}
func (b *Bato) GetThumbnail(subUrl string) (thumbnailUrl string, err error) {
url := fmt.Sprintf("https://bato.to/title/%s", subUrl)
resp, err := http.Get(url)
// TODO: Testing for above 300 is dirty
if err != nil && resp.StatusCode > 300 {
return "", errors.New("could not get html")
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
fmt.Printf("Could not close body because: %v\n", err)
}
}(resp.Body)
all, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
h := string(all)
reg, err := regexp.Compile(`<img data-hk="0-1-0" .*? src="(.*?)["']`)
if err != nil {
return "", err
}
match := reg.FindStringSubmatch(h)
if len(match) <= 1 {
return "", errors.New("could not find Thumbnail url")
}
return match[1], nil
}

View File

@@ -7,4 +7,5 @@ type Provider interface {
GetPrev(html string) (url string, err error) GetPrev(html string) (url string, err error)
GetTitleAndChapter(url string) (title string, chapter string, err error) GetTitleAndChapter(url string) (title string, chapter string, err error)
GetTitleIdAndChapterId(url string) (titleId int, chapterId int, err error) GetTitleIdAndChapterId(url string) (titleId int, chapterId int, err error)
GetThumbnail(mangaId string) (thumbnailUrl string, err error)
} }

View File

@@ -39,8 +39,12 @@ func (s *Server) HandleNew(w http.ResponseWriter, r *http.Request) {
func (s *Server) HandleMenu(w http.ResponseWriter, _ *http.Request) { func (s *Server) HandleMenu(w http.ResponseWriter, _ *http.Request) {
tmpl := template.Must(view.GetViewTemplate(view.Menu)) tmpl := template.Must(view.GetViewTemplate(view.Menu))
fmt.Println("Locking Rw in handler.go:43")
s.DbMgr.Rw.Lock() s.DbMgr.Rw.Lock()
defer s.DbMgr.Rw.Unlock() defer func() {
fmt.Println("Unlocking Rw in handler.go:46")
s.DbMgr.Rw.Unlock()
}()
all := s.DbMgr.Mangas all := s.DbMgr.Mangas
l := len(all) l := len(all)
@@ -50,12 +54,20 @@ func (s *Server) HandleMenu(w http.ResponseWriter, _ *http.Request) {
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))
thumbnail, err := s.LoadThumbnail(manga.Id)
if err != nil {
continue
}
manga.Thumbnail = s.ImageBuffers[thumbnail]
mangaViewModels[counter] = view.MangaViewModel{ mangaViewModels[counter] = view.MangaViewModel{
ID: manga.Id,
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
LastTime: time.Unix(manga.TimeStampUnix, 0).Format("15:04 (02-01-06)"), LastTime: time.Unix(manga.TimeStampUnix, 0).Format("15:04 (02-01-06)"),
Url: manga.LatestChapter.Url, Url: manga.LatestChapter.Url,
ThumbnailUrl: thumbnail,
} }
counter++ counter++
} }
@@ -74,6 +86,29 @@ func (s *Server) HandleMenu(w http.ResponseWriter, _ *http.Request) {
} }
} }
func (s *Server) HandleDelete(w http.ResponseWriter, r *http.Request) {
mangaStr := r.PostFormValue("mangaId")
if mangaStr == "" {
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
return
}
mangaId, err := strconv.Atoi(mangaStr)
if err != nil {
fmt.Println(err)
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
return
}
err = s.DbMgr.Delete(mangaId)
if err != nil {
fmt.Println(err)
}
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
}
func (s *Server) HandleExit(w http.ResponseWriter, r *http.Request) { func (s *Server) HandleExit(w http.ResponseWriter, r *http.Request) {
err := s.DbMgr.Save() err := s.DbMgr.Save()
if err != nil { if err != nil {
@@ -87,8 +122,12 @@ func (s *Server) HandleExit(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(view.GetViewTemplate(view.Viewer)) tmpl := template.Must(view.GetViewTemplate(view.Viewer))
fmt.Println("Locking Rw in handler.go:125")
s.DbMgr.Rw.Lock() s.DbMgr.Rw.Lock()
defer s.DbMgr.Rw.Unlock() defer func() {
fmt.Println("Unlocking Rw in handler.go:128")
s.DbMgr.Rw.Unlock()
}()
mangaId, chapterId, err := s.Provider.GetTitleIdAndChapterId(s.CurrSubUrl) mangaId, chapterId, err := s.Provider.GetTitleIdAndChapterId(s.CurrSubUrl)
if err != nil { if err != nil {
@@ -144,6 +183,7 @@ func (s *Server) HandleCurrent(w http.ResponseWriter, _ *http.Request) {
func (s *Server) HandleImage(w http.ResponseWriter, r *http.Request) { func (s *Server) HandleImage(w http.ResponseWriter, r *http.Request) {
u := r.PathValue("url") u := r.PathValue("url")
s.Mutex.Lock() s.Mutex.Lock()
defer s.Mutex.Unlock()
buf := s.ImageBuffers[u] buf := s.ImageBuffers[u]
if buf == nil { if buf == nil {
fmt.Printf("url: %s is nil\n", u) fmt.Printf("url: %s is nil\n", u)
@@ -156,7 +196,6 @@ func (s *Server) HandleImage(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
s.Mutex.Unlock()
} }
func (s *Server) HandleNext(w http.ResponseWriter, r *http.Request) { func (s *Server) HandleNext(w http.ResponseWriter, r *http.Request) {

View File

@@ -9,6 +9,7 @@ import (
"mangaGetter/internal/view" "mangaGetter/internal/view"
"net/http" "net/http"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"sync" "sync"
) )
@@ -30,7 +31,7 @@ type Server struct {
IsFirst bool IsFirst bool
IsLast bool IsLast bool
DbMgr *database.DatabaseManager DbMgr *database.Manager
// 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
@@ -136,6 +137,27 @@ func (s *Server) LoadCurr() {
fmt.Println("Loaded current") fmt.Println("Loaded current")
} }
func (s *Server) LoadThumbnail(mangaId int) (path string, err error) {
strId := strconv.Itoa(mangaId)
s.Mutex.Lock()
defer s.Mutex.Unlock()
if s.ImageBuffers[strId] != nil {
return strId, nil
}
url, err := s.Provider.GetThumbnail(strconv.Itoa(mangaId))
if err != nil {
return "", err
}
ram, err := addFileToRam(url)
if err != nil {
return "", err
}
s.ImageBuffers[strId] = ram
return strId, nil
}
func (s *Server) AppendImagesToBuf(html string) ([]view.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 {

View File

@@ -34,12 +34,52 @@
.button-36 { .button-36 {
padding: 0 2.6rem; padding: 0 2.6rem;
} }
.button-delete{
padding: 0 2.6rem;
}
}
.button-delete{
background-image: linear-gradient(92.88deg, #f44336 9.16%, #f44336 43.89%, #f44336 64.72%);
border-radius: 8px;
border-style: none;
box-sizing: border-box;
color: #FFFFFF;
cursor: pointer;
flex-shrink: 0;
font-family: "Inter UI","SF Pro Display",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Open Sans","Helvetica Neue",sans-serif;
font-size: 16px;
font-weight: 500;
height: 4rem;
padding: 0 1.6rem;
text-align: center;
text-shadow: rgba(0, 0, 0, 0.25) 0 3px 8px;
transition: all .5s;
user-select: none;
-webkit-user-select: none;
touch-action: manipulation;
}
.button-delete:hover {
box-shadow: rgba(244, 67, 54, 0.5) 0 1px 30px;
transition-duration: .1s;
} }
.table-left{ .table-left{
text-align: left; text-align: left;
} }
.thumbnail{
border: 1px solid #ddd; /* Gray border */
border-radius: 4px; /* Rounded border */
padding: 5px; /* Some padding */
width: 150px; /* Set a small width */
}
.thumbnail:hover{
box-shadow: 0 0 2px 1px rgba(0, 140, 186, 0.5);
}
.table { .table {
width: 100%; width: 100%;
} }
@@ -54,27 +94,45 @@
</head> </head>
<body> <body>
<form method="post" action="/new/"> <form method="post" action="/new/">
{{/* <label>New Sub Url</label>*/}}
<label> <label>
New Sub Url New Sub Url
<input type="text" name="subUrl"> <input type="text" name="subUrl">
</label> </label>
<input type="submit" class="button-36"> <input type="submit" value="Open" class="button-36">
</form> </form>
<table class="table"> <table class="table">
<tr> <tr>
<th>Thumbnail</th>
<th class="table-left">Title</th> <th class="table-left">Title</th>
<th>Current Chapter</th> <th>Current Chapter</th>
<th>Last Accessed</th> <th>Last Accessed</th>
<th>Link</th> <th>Link</th>
<th>Delete</th>
</tr> </tr>
{{range .Mangas}} {{range .Mangas}}
<tr> <tr>
<td>
<a target="_blank" href="/img/{{.ThumbnailUrl}}">
<img class="thumbnail" src="/img/{{.ThumbnailUrl}}" alt="img_{{.ThumbnailUrl}}"/>
</a>
</td>
<td class="table-left">{{.Title}}</td> <td class="table-left">{{.Title}}</td>
<td>{{.Number}}</td> <td>{{.Number}}</td>
<td>{{.LastTime}}</td> <td>{{.LastTime}}</td>
<td><a href="/new/{{.Url}}}" class="button-36">Go to last Chapter</a></td> <td>
<a href="/new/{{.Url}}}">
<button class="button-36">
To chapter
</button>
</a>
</td>
<td>
<form method="post" action="/delete">
<input type="hidden" name="mangaId" value="{{.ID}}">
<input type="submit" class="button-delete" value="Delete">
</form>
</td>
</tr> </tr>
{{end}} {{end}}
</table> </table>

View File

@@ -11,10 +11,12 @@ type ImageViewModel struct {
} }
type MangaViewModel struct { type MangaViewModel struct {
Title string ID int
Number int Title string
LastTime string Number int
Url string LastTime string
Url string
ThumbnailUrl string
} }
type MenuViewModel struct { type MenuViewModel struct {