diff --git a/README.md b/README.md index 69642d5..4ae72ad 100644 --- a/README.md +++ b/README.md @@ -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 # Features that might get added: -- Manga / Chapter History - Searchbar - Better looking UI -- Main Screen +- Genres and Filter +- More Providers like Asuratoon +- Performance improvements # Pretext diff --git a/cmd/mangaGetter/main.go b/cmd/mangaGetter/main.go index a4efa3e..401dfed 100644 --- a/cmd/mangaGetter/main.go +++ b/cmd/mangaGetter/main.go @@ -5,7 +5,6 @@ import ( "mangaGetter/internal/database" "mangaGetter/internal/provider" "mangaGetter/internal/server" - "net/http" "os" "os/exec" "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() { time.Sleep(300 * time.Millisecond) err := open("http://localhost:8000") @@ -53,11 +41,9 @@ func main() { } }() - fmt.Println("Server starting...") - err = http.ListenAndServe(":8000", nil) + err = s.Start() if err != nil { - fmt.Println(err) - return + panic(err) } } diff --git a/internal/database/createDb.sql b/internal/database/createDb.sql index 02cf297..55e3a67 100644 --- a/internal/database/createDb.sql +++ b/internal/database/createDb.sql @@ -1,8 +1,9 @@ create table if not exists Manga ( ID integer not null primary key, Title text, - TimeStampUnixEpoch int, - Thumbnail blob + TimeStampUnixEpoch integer not null, + Thumbnail blob, + LatestAvailableChapter integer not null ); create table if not exists Chapter ( @@ -10,7 +11,7 @@ create table if not exists Chapter ( MangaID integer not null, Url text not null, Name text null, - Number int not null, - TimeStampUnixEpoch int, + Number integer not null, + TimeStampUnixEpoch integer not null, foreign key(MangaID) references Manga(ID) ); \ No newline at end of file diff --git a/internal/database/database.go b/internal/database/database.go index 0fe6924..23e6dae 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -11,14 +11,14 @@ import ( ) type Manga struct { - Id int - Title string - TimeStampUnix int64 - Thumbnail *bytes.Buffer + Id int + Title string + TimeStampUnix int64 + Thumbnail *bytes.Buffer + LastChapterNum int // Not in DB - LatestChapter *Chapter - LastChapterNum int + LatestChapter *Chapter } type Chapter struct { @@ -125,12 +125,12 @@ func (dbMgr *Manager) Save() error { if count == 0 { 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 { return err } } 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 { return err } @@ -143,12 +143,12 @@ func (dbMgr *Manager) Save() error { } 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 { return err } } 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 { return err } @@ -197,7 +197,7 @@ func (dbMgr *Manager) load() error { 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 { return err } @@ -205,7 +205,7 @@ func (dbMgr *Manager) load() error { for rows.Next() { manga := Manga{} 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 } 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 { return err } - dbMgr.Chapters[chapter.Id] = &chapter + chapter.Manga = &manga manga.LatestChapter = &chapter + dbMgr.Chapters[chapter.Id] = &chapter dbMgr.Mangas[manga.Id] = &manga } return nil diff --git a/internal/server/handler.go b/internal/server/handler.go index cb07657..24f198c 100644 --- a/internal/server/handler.go +++ b/internal/server/handler.go @@ -33,7 +33,7 @@ func (s *Server) HandleNew(w http.ResponseWriter, r *http.Request) { 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)) 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 // TODO: put this into own Method if manga.LastChapterNum == 0 { - l, err := s.Provider.GetChapterList("/title/" + strconv.Itoa(manga.Id)) + err := s.UpdateLatestAvailableChapter(manga) 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() @@ -121,9 +106,29 @@ func (s *Server) HandleMenu(w http.ResponseWriter, _ *http.Request) { n = time.Now().UnixNano() - slices.SortStableFunc(mangaViewModels, func(a, b view.MangaViewModel) int { - return cmp.Compare(a.Title, b.Title) - }) + sort := r.URL.Query().Get("sort") + + 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() fmt.Printf("Sorting took %d ms\n", (nex-n)/1000000) diff --git a/internal/server/server.go b/internal/server/server.go index f2abcc7..ee76c68 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -13,6 +13,7 @@ import ( "strconv" "strings" "sync" + "time" ) type Server struct { @@ -46,6 +47,40 @@ func New(provider provider.Provider, db *database.Manager) *Server { 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() { c, err := s.Provider.GetHtml(s.CurrSubUrl) if err != nil { @@ -157,6 +192,31 @@ func (s *Server) LoadCurr() { 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) { strId := strconv.Itoa(manga.Id) diff --git a/internal/view/Views/menu.gohtml b/internal/view/Views/menu.gohtml index f93d9e2..dc4a5c0 100644 --- a/internal/view/Views/menu.gohtml +++ b/internal/view/Views/menu.gohtml @@ -124,9 +124,9 @@
| Thumbnail | -Title | -Current Chapter | -Last Accessed | +Title | +Current Chapter | +Last Accessed | Link | Delete |
|---|