Added saving of LatestAvailableChapter and updating it in goroutine every 5 Minutes, also Added filtering to menu
This commit is contained in:
@@ -8,10 +8,11 @@ That's, why I created this simple pre Loader, right now it's not really user-fri
|
|||||||
a few more features
|
a few more features
|
||||||
|
|
||||||
# Features that might get added:
|
# Features that might get added:
|
||||||
- Manga / Chapter History
|
|
||||||
- Searchbar
|
- Searchbar
|
||||||
- Better looking UI
|
- Better looking UI
|
||||||
- Main Screen
|
- Genres and Filter
|
||||||
|
- More Providers like Asuratoon
|
||||||
|
- Performance improvements
|
||||||
|
|
||||||
# Pretext
|
# Pretext
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"mangaGetter/internal/database"
|
"mangaGetter/internal/database"
|
||||||
"mangaGetter/internal/provider"
|
"mangaGetter/internal/provider"
|
||||||
"mangaGetter/internal/server"
|
"mangaGetter/internal/server"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@@ -34,17 +33,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
http.HandleFunc("/", s.HandleMenu)
|
|
||||||
http.HandleFunc("/new/", s.HandleNewQuery)
|
|
||||||
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)
|
|
||||||
http.HandleFunc("POST /delete", s.HandleDelete)
|
|
||||||
http.HandleFunc("/favicon.ico", s.HandleFavicon)
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(300 * time.Millisecond)
|
time.Sleep(300 * time.Millisecond)
|
||||||
err := open("http://localhost:8000")
|
err := open("http://localhost:8000")
|
||||||
@@ -53,11 +41,9 @@ func main() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
fmt.Println("Server starting...")
|
err = s.Start()
|
||||||
err = http.ListenAndServe(":8000", nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
panic(err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
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 integer not null,
|
||||||
Thumbnail blob
|
Thumbnail blob,
|
||||||
|
LatestAvailableChapter integer not null
|
||||||
);
|
);
|
||||||
|
|
||||||
create table if not exists Chapter (
|
create table if not exists Chapter (
|
||||||
@@ -10,7 +11,7 @@ create table if not exists Chapter (
|
|||||||
MangaID integer not null,
|
MangaID integer not null,
|
||||||
Url text not null,
|
Url text not null,
|
||||||
Name text null,
|
Name text null,
|
||||||
Number int not null,
|
Number integer not null,
|
||||||
TimeStampUnixEpoch int,
|
TimeStampUnixEpoch integer not null,
|
||||||
foreign key(MangaID) references Manga(ID)
|
foreign key(MangaID) references Manga(ID)
|
||||||
);
|
);
|
||||||
@@ -11,14 +11,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Manga struct {
|
type Manga struct {
|
||||||
Id int
|
Id int
|
||||||
Title string
|
Title string
|
||||||
TimeStampUnix int64
|
TimeStampUnix int64
|
||||||
Thumbnail *bytes.Buffer
|
Thumbnail *bytes.Buffer
|
||||||
|
LastChapterNum int
|
||||||
|
|
||||||
// Not in DB
|
// Not in DB
|
||||||
LatestChapter *Chapter
|
LatestChapter *Chapter
|
||||||
LastChapterNum int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Chapter struct {
|
type Chapter struct {
|
||||||
@@ -125,12 +125,12 @@ func (dbMgr *Manager) Save() error {
|
|||||||
|
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
if m.Thumbnail != nil {
|
if m.Thumbnail != nil {
|
||||||
_, err := db.Exec("INSERT INTO Manga(ID, Title, TimeStampUnixEpoch, Thumbnail) values(?, ?, ?, ?)", m.Id, m.Title, m.TimeStampUnix, m.Thumbnail.Bytes())
|
_, err := db.Exec("INSERT INTO Manga(ID, Title, TimeStampUnixEpoch, Thumbnail, LatestAvailableChapter) values(?, ?, ?, ?, ?)", m.Id, m.Title, m.TimeStampUnix, m.Thumbnail.Bytes(), m.LastChapterNum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_, err := db.Exec("INSERT INTO Manga(ID, Title, TimeStampUnixEpoch ) values(?, ?, ?)", m.Id, m.Title, m.TimeStampUnix)
|
_, err := db.Exec("INSERT INTO Manga(ID, Title, TimeStampUnixEpoch, LatestAvailableChapter) values(?, ?, ?, ?)", m.Id, m.Title, m.TimeStampUnix, m.LastChapterNum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -143,12 +143,12 @@ func (dbMgr *Manager) Save() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if tSet != 0 {
|
if tSet != 0 {
|
||||||
_, err = db.Exec("UPDATE Manga set Title = ?, TimeStampUnixEpoch = ? WHERE ID = ?", m.Title, m.TimeStampUnix, m.Id)
|
_, err = db.Exec("UPDATE Manga set Title = ?, TimeStampUnixEpoch = ?, LatestAvailableChapter = ? WHERE ID = ?", m.Title, m.TimeStampUnix, m.LastChapterNum, m.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_, err = db.Exec("UPDATE Manga set Title = ?, TimeStampUnixEpoch = ?, Thumbnail = ? WHERE ID = ?", m.Title, m.TimeStampUnix, m.Thumbnail.Bytes(), m.Id)
|
_, err = db.Exec("UPDATE Manga set Title = ?, TimeStampUnixEpoch = ?, Thumbnail = ?, LatestAvailableChapter = ? WHERE ID = ?", m.Title, m.TimeStampUnix, m.Thumbnail.Bytes(), m.LastChapterNum, m.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -197,7 +197,7 @@ func (dbMgr *Manager) load() error {
|
|||||||
dbMgr.Rw.Unlock()
|
dbMgr.Rw.Unlock()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
rows, err := db.Query("SELECT * FROM Manga")
|
rows, err := db.Query("SELECT Id, Title, TimeStampUnixEpoch, Thumbnail, LatestAvailableChapter FROM Manga")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -205,7 +205,7 @@ func (dbMgr *Manager) load() error {
|
|||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
manga := Manga{}
|
manga := Manga{}
|
||||||
var thumbnail []byte
|
var thumbnail []byte
|
||||||
if err = rows.Scan(&manga.Id, &manga.Title, &manga.TimeStampUnix, &thumbnail); err != nil {
|
if err = rows.Scan(&manga.Id, &manga.Title, &manga.TimeStampUnix, &thumbnail, &manga.LastChapterNum); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(thumbnail) != 0 {
|
if len(thumbnail) != 0 {
|
||||||
@@ -217,8 +217,9 @@ func (dbMgr *Manager) load() error {
|
|||||||
if err = latestChapter.Scan(&chapter.Id, &chapter.Url, &chapter.Name, &chapter.Number, &chapter.TimeStampUnix); err != nil {
|
if err = latestChapter.Scan(&chapter.Id, &chapter.Url, &chapter.Name, &chapter.Number, &chapter.TimeStampUnix); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
dbMgr.Chapters[chapter.Id] = &chapter
|
chapter.Manga = &manga
|
||||||
manga.LatestChapter = &chapter
|
manga.LatestChapter = &chapter
|
||||||
|
dbMgr.Chapters[chapter.Id] = &chapter
|
||||||
dbMgr.Mangas[manga.Id] = &manga
|
dbMgr.Mangas[manga.Id] = &manga
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func (s *Server) HandleNew(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Redirect(w, r, "/current/", http.StatusTemporaryRedirect)
|
http.Redirect(w, r, "/current/", http.StatusTemporaryRedirect)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) HandleMenu(w http.ResponseWriter, _ *http.Request) {
|
func (s *Server) HandleMenu(w http.ResponseWriter, r *http.Request) {
|
||||||
tmpl := template.Must(view.GetViewTemplate(view.Menu))
|
tmpl := template.Must(view.GetViewTemplate(view.Menu))
|
||||||
|
|
||||||
fmt.Println("Locking Rw in handler.go:43")
|
fmt.Println("Locking Rw in handler.go:43")
|
||||||
@@ -75,25 +75,10 @@ func (s *Server) HandleMenu(w http.ResponseWriter, _ *http.Request) {
|
|||||||
// This is very slow
|
// This is very slow
|
||||||
// TODO: put this into own Method
|
// TODO: put this into own Method
|
||||||
if manga.LastChapterNum == 0 {
|
if manga.LastChapterNum == 0 {
|
||||||
l, err := s.Provider.GetChapterList("/title/" + strconv.Itoa(manga.Id))
|
err := s.UpdateLatestAvailableChapter(manga)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
le := len(l)
|
|
||||||
_, c, err := s.Provider.GetTitleAndChapter(l[le-1])
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
chapterNumberStr := strings.Replace(c, "ch_", "", 1)
|
|
||||||
|
|
||||||
i, err := strconv.Atoi(chapterNumberStr)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
manga.LastChapterNum = i
|
|
||||||
}
|
}
|
||||||
|
|
||||||
t2 = time.Now().UnixNano()
|
t2 = time.Now().UnixNano()
|
||||||
@@ -121,9 +106,29 @@ func (s *Server) HandleMenu(w http.ResponseWriter, _ *http.Request) {
|
|||||||
|
|
||||||
n = time.Now().UnixNano()
|
n = time.Now().UnixNano()
|
||||||
|
|
||||||
slices.SortStableFunc(mangaViewModels, func(a, b view.MangaViewModel) int {
|
sort := r.URL.Query().Get("sort")
|
||||||
return cmp.Compare(a.Title, b.Title)
|
|
||||||
})
|
if sort == "" || sort == "title" {
|
||||||
|
slices.SortStableFunc(mangaViewModels, func(a, b view.MangaViewModel) int {
|
||||||
|
return cmp.Compare(a.Title, b.Title)
|
||||||
|
})
|
||||||
|
} else if sort == "chapter" {
|
||||||
|
slices.SortStableFunc(mangaViewModels, func(a, b view.MangaViewModel) int {
|
||||||
|
return cmp.Compare(b.Number, a.Number)
|
||||||
|
})
|
||||||
|
} else if sort == "last" {
|
||||||
|
slices.SortStableFunc(mangaViewModels, func(a, b view.MangaViewModel) int {
|
||||||
|
aT, err := time.Parse("15:04 (02-01-06)", a.LastTime)
|
||||||
|
if err != nil {
|
||||||
|
return cmp.Compare(a.Title, b.Title)
|
||||||
|
}
|
||||||
|
bT, err := time.Parse("15:04 (02-01-06)", b.LastTime)
|
||||||
|
if err != nil {
|
||||||
|
return cmp.Compare(a.Title, b.Title)
|
||||||
|
}
|
||||||
|
return bT.Compare(aT)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
nex = time.Now().UnixNano()
|
nex = time.Now().UnixNano()
|
||||||
fmt.Printf("Sorting took %d ms\n", (nex-n)/1000000)
|
fmt.Printf("Sorting took %d ms\n", (nex-n)/1000000)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
@@ -46,6 +47,40 @@ func New(provider provider.Provider, db *database.Manager) *Server {
|
|||||||
return &s
|
return &s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) Start() error {
|
||||||
|
http.HandleFunc("/", s.HandleMenu)
|
||||||
|
http.HandleFunc("/new/", s.HandleNewQuery)
|
||||||
|
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)
|
||||||
|
http.HandleFunc("POST /delete", s.HandleDelete)
|
||||||
|
http.HandleFunc("/favicon.ico", s.HandleFavicon)
|
||||||
|
|
||||||
|
// Update Latest Chapter every 5 Minutes
|
||||||
|
go func(s *Server) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-time.After(time.Minute * 5):
|
||||||
|
s.DbMgr.Rw.Lock()
|
||||||
|
for _, m := range s.DbMgr.Mangas {
|
||||||
|
err := s.UpdateLatestAvailableChapter(m)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.DbMgr.Rw.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(s)
|
||||||
|
|
||||||
|
fmt.Println("Server starting...")
|
||||||
|
err := http.ListenAndServe(":8000", nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) LoadNext() {
|
func (s *Server) LoadNext() {
|
||||||
c, err := s.Provider.GetHtml(s.CurrSubUrl)
|
c, err := s.Provider.GetHtml(s.CurrSubUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -157,6 +192,31 @@ func (s *Server) LoadCurr() {
|
|||||||
fmt.Println("Loaded current")
|
fmt.Println("Loaded current")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) UpdateLatestAvailableChapter(manga *database.Manga) error {
|
||||||
|
fmt.Printf("Updating Manga: %s\n", manga.Title)
|
||||||
|
|
||||||
|
l, err := s.Provider.GetChapterList("/title/" + strconv.Itoa(manga.Id))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
le := len(l)
|
||||||
|
_, c, err := s.Provider.GetTitleAndChapter(l[le-1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
chapterNumberStr := strings.Replace(c, "ch_", "", 1)
|
||||||
|
|
||||||
|
i, err := strconv.Atoi(chapterNumberStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
manga.LastChapterNum = i
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) LoadThumbnail(manga *database.Manga) (path string, err error) {
|
func (s *Server) LoadThumbnail(manga *database.Manga) (path string, err error) {
|
||||||
strId := strconv.Itoa(manga.Id)
|
strId := strconv.Itoa(manga.Id)
|
||||||
|
|
||||||
|
|||||||
@@ -124,9 +124,9 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Thumbnail</th>
|
<th>Thumbnail</th>
|
||||||
<th class="table-left">Title</th>
|
<th class="table-left"><a href="?sort=title">Title</a></th>
|
||||||
<th>Current Chapter</th>
|
<th><a href="?sort=chapter">Current Chapter</a></th>
|
||||||
<th>Last Accessed</th>
|
<th><a href="?sort=last">Last Accessed</a></th>
|
||||||
<th>Link</th>
|
<th>Link</th>
|
||||||
<th>Delete</th>
|
<th>Delete</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
Reference in New Issue
Block a user