1 Commits

11 changed files with 234 additions and 115 deletions

View File

@@ -3,56 +3,55 @@ package main
import ( import (
"fmt" "fmt"
"mangaGetter/internal/database" "mangaGetter/internal/database"
"mangaGetter/internal/provider"
"mangaGetter/internal/server" "mangaGetter/internal/server"
"net/http" "net/http"
"os" "os"
"os/exec" "os/exec"
"os/signal" "os/signal"
"path/filepath"
"runtime" "runtime"
"time" "time"
) )
func main() { func main() {
dir, err := os.UserCacheDir() //dir, err := os.UserCacheDir()
if err != nil { //if err != nil {
fmt.Println(nil) // fmt.Println(nil)
return // return
} //}
dirPath := filepath.Join(dir, "MangaGetter") //
filePath := filepath.Join(dirPath, "db.sqlite") //dirPath := filepath.Join(dir, "MangaGetter")
//filePath := filepath.Join(dirPath, "db.sqlite")
//
//if _, err := os.Stat(dirPath); os.IsNotExist(err) {
// err = os.Mkdir(dirPath, os.ModePerm)
// if err != nil {
// fmt.Println(err)
// return
// }
//}
//
//if _, err := os.Stat(filePath); os.IsNotExist(err) {
// f, err := os.Create(filePath)
// if err != nil {
// fmt.Println(err)
// return
// }
// err = f.Close()
// if err != nil {
// fmt.Println(err)
// return
// }
//}
if _, err := os.Stat(dirPath); os.IsNotExist(err) { db := database.NewDatabase("db.sqlite", true)
err = os.Mkdir(dirPath, os.ModePerm) err := db.Open()
if err != nil {
fmt.Println(err)
return
}
}
if _, err := os.Stat(filePath); os.IsNotExist(err) {
f, err := os.Create(filePath)
if err != nil {
fmt.Println(err)
return
}
err = f.Close()
if err != nil {
fmt.Println(err)
return
}
}
db := database.NewDatabase(filePath, true)
err = db.Open()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
s := server.New(&provider.Bato{}, &db) s := server.New(&db)
c := make(chan os.Signal, 1) c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt) signal.Notify(c, os.Interrupt)
@@ -64,8 +63,7 @@ func main() {
}() }()
http.HandleFunc("/", s.HandleMenu) http.HandleFunc("/", s.HandleMenu)
http.HandleFunc("/new/", s.HandleNewQuery) http.HandleFunc("POST /new/", s.HandleNewQuery)
http.HandleFunc("/new/title/{title}/{chapter}", s.HandleNew)
http.HandleFunc("/current/", s.HandleCurrent) http.HandleFunc("/current/", s.HandleCurrent)
http.HandleFunc("/img/{url}/", s.HandleImage) http.HandleFunc("/img/{url}/", s.HandleImage)
http.HandleFunc("POST /next", s.HandleNext) http.HandleFunc("POST /next", s.HandleNext)

2
go.mod
View File

@@ -6,3 +6,5 @@ require (
github.com/mattn/go-sqlite3 v1.14.22 github.com/mattn/go-sqlite3 v1.14.22
golang.org/x/text v0.14.0 golang.org/x/text v0.14.0
) )
require github.com/google/uuid v1.6.0 // indirect

2
go.sum
View File

@@ -1,3 +1,5 @@
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=

View File

@@ -0,0 +1,11 @@
package database
type Chapter struct {
Id int
Manga *Manga
InternalIdentifier string
Url string
Name string
Number int
TimeStampUnix int64
}

View File

@@ -1,16 +1,27 @@
create table if not exists Manga ( drop table if exists Chapter;
ID integer not null primary key, drop table if exists Manga;
Title text,
TimeStampUnixEpoch int, create table if not exists Manga
Thumbnail blob (
ID integer not null primary key autoincrement,
Provider integer not null,
Rating integer,
Title text,
TimeStampUnixEpoch integer,
Thumbnail blob
); );
create table if not exists Chapter ( create table if not exists Chapter
ID integer not null primary key, (
MangaID integer not null, ID integer not null primary key autoincrement,
Url text not null, MangaID integer not null,
Name text null,
Number int not null, InternalIdentifier text not null,
TimeStampUnixEpoch int, Url text not null,
foreign key(MangaID) references Manga(ID) Name text null,
); Number integer not null,
TimeStampUnixEpoch integer,
foreign key (MangaID) references Manga (ID)
);

View File

@@ -5,30 +5,12 @@ import (
"database/sql" "database/sql"
_ "embed" _ "embed"
"fmt" "fmt"
"mangaGetter/internal/provider"
"sync" "sync"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
) )
type Manga struct {
Id int
Title string
TimeStampUnix int64
Thumbnail *bytes.Buffer
// Not in DB
LatestChapter *Chapter
}
type Chapter struct {
Id int
Manga *Manga
Url string
Name string
Number int
TimeStampUnix int64
}
type Manager struct { type Manager struct {
ConnectionString string ConnectionString string
db *sql.DB db *sql.DB
@@ -183,7 +165,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, Provider, Title, TimeStampUnixEpoch, Thumbnail, Rating FROM Manga")
if err != nil { if err != nil {
return err return err
} }
@@ -191,14 +173,16 @@ 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 { var providerId int
if err = rows.Scan(&manga.Id, &providerId, &manga.Title, &manga.TimeStampUnix, &thumbnail, &manga.Rating); err != nil {
return err return err
} }
manga.Thumbnail = bytes.NewBuffer(thumbnail) manga.Thumbnail = bytes.NewBuffer(thumbnail)
manga.Provider = provider.GetProviderByType(provider.ProviderType(providerId))
dbMgr.Mangas[manga.Id] = &manga dbMgr.Mangas[manga.Id] = &manga
} }
rows, err = db.Query("SELECT * FROM Chapter") rows, err = db.Query("SELECT ID, MangaID, Url, Name, Number, TimeStampUnixEpoch, InternalIdentifier FROM Chapter")
if err != nil { if err != nil {
return err return err
} }
@@ -206,7 +190,7 @@ func (dbMgr *Manager) load() error {
for rows.Next() { for rows.Next() {
chapter := Chapter{} chapter := Chapter{}
var mangaID int var mangaID int
if err = rows.Scan(&chapter.Id, &mangaID, &chapter.Url, &chapter.Name, &chapter.Number, &chapter.TimeStampUnix); err != nil { if err = rows.Scan(&chapter.Id, &mangaID, &chapter.Url, &chapter.Name, &chapter.Number, &chapter.TimeStampUnix, &chapter.InternalIdentifier); err != nil {
return err return err
} }

View File

@@ -0,0 +1,18 @@
package database
import (
"bytes"
"mangaGetter/internal/provider"
)
type Manga struct {
Id int
Provider provider.Provider
Rating int
Title string
TimeStampUnix int64
Thumbnail *bytes.Buffer
// Not in DB
LatestChapter *Chapter
}

View File

@@ -0,0 +1,100 @@
package provider
import (
"errors"
"fmt"
"io"
"net/http"
"regexp"
"strconv"
)
type Asura struct{}
func (a Asura) GetImageList(html string) (imageUrls []string, err error) {
reg, err := regexp.Compile(`<img decoding="async" class="ts-main-image " src="(.*?)"`)
if err != nil {
return nil, err
}
m := reg.FindAllStringSubmatch(html, -1)
l := len(m)
result := make([]string, l)
for i, match := range m {
result[i] = match[1]
}
return result, nil
}
func (a Asura) GetHtml(url string) (html string, err error) {
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)
return h, nil
}
func (a Asura) GetNext(html string) (url string, err error) {
//TODO implement me
return "#/next/", nil
}
func (a Asura) GetPrev(html string) (url string, err error) {
//TODO implement me
return "#/prev/", nil
}
func (a Asura) GetTitleAndChapter(url string) (title string, chapter string, err error) {
//TODO implement me
reg, err := regexp.Compile(`\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 (a Asura) GetTitleIdAndChapterId(url string) (titleId int, chapterId int, err error) {
//TODO implement me
reg, err := regexp.Compile(`(\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, nil
}
func (a Asura) GetThumbnail(mangaId string) (thumbnailUrl string, err error) {
//TODO implement me
panic("implement me")
}

View File

@@ -0,0 +1,18 @@
package provider
type ProviderType int
const (
BatoId ProviderType = iota
AsuraId ProviderType = iota
)
func GetProviderByType(typeId ProviderType) Provider {
switch typeId {
case BatoId:
return &Bato{}
case AsuraId:
return &Asura{}
}
return nil
}

View File

@@ -17,26 +17,6 @@ import (
"time" "time"
) )
func (s *Server) HandleNew(w http.ResponseWriter, r *http.Request) {
title := r.PathValue("title")
chapter := r.PathValue("chapter")
url := fmt.Sprintf("/title/%s/%s", title, chapter)
s.Mutex.Lock()
s.ImageBuffers = make(map[string]*bytes.Buffer)
s.Mutex.Unlock()
s.CurrSubUrl = url
s.PrevSubUrl = ""
s.NextSubUrl = ""
s.LoadCurr()
go s.LoadNext()
go s.LoadPrev()
http.Redirect(w, r, "/current/", http.StatusTemporaryRedirect)
}
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))
@@ -277,8 +257,6 @@ func (s *Server) HandlePrev(w http.ResponseWriter, r *http.Request) {
func (s *Server) HandleNewQuery(w http.ResponseWriter, r *http.Request) { func (s *Server) HandleNewQuery(w http.ResponseWriter, r *http.Request) {
sub := r.PostFormValue("subUrl") sub := r.PostFormValue("subUrl")
url := fmt.Sprintf("/title/%s", sub)
s.Mutex.Lock() s.Mutex.Lock()
s.ImageBuffers = make(map[string]*bytes.Buffer) s.ImageBuffers = make(map[string]*bytes.Buffer)
s.Mutex.Unlock() s.Mutex.Unlock()

View File

@@ -4,18 +4,18 @@ import (
"bytes" "bytes"
_ "embed" _ "embed"
"fmt" "fmt"
"github.com/google/uuid"
"io" "io"
"mangaGetter/internal/database" "mangaGetter/internal/database"
"mangaGetter/internal/provider"
"mangaGetter/internal/view" "mangaGetter/internal/view"
"net/http" "net/http"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
) )
type Server struct { type Server struct {
ContextManga *database.Manga
PrevViewModel *view.ImageViewModel PrevViewModel *view.ImageViewModel
CurrViewModel *view.ImageViewModel CurrViewModel *view.ImageViewModel
NextViewModel *view.ImageViewModel NextViewModel *view.ImageViewModel
@@ -27,18 +27,15 @@ type Server struct {
CurrSubUrl string CurrSubUrl string
PrevSubUrl string PrevSubUrl string
Provider provider.Provider
IsFirst bool IsFirst bool
IsLast bool IsLast bool
DbMgr *database.Manager DbMgr *database.Manager
} }
func New(provider provider.Provider, db *database.Manager) *Server { func New(db *database.Manager) *Server {
s := Server{ s := Server{
ImageBuffers: make(map[string]*bytes.Buffer), ImageBuffers: make(map[string]*bytes.Buffer),
Provider: provider,
DbMgr: db, DbMgr: db,
Mutex: &sync.Mutex{}, Mutex: &sync.Mutex{},
} }
@@ -47,7 +44,7 @@ func New(provider provider.Provider, db *database.Manager) *Server {
} }
func (s *Server) LoadNext() { func (s *Server) LoadNext() {
c, err := s.Provider.GetHtml(s.CurrSubUrl) c, err := s.ContextManga.Provider.GetHtml(s.CurrSubUrl)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
s.NextSubUrl = "" s.NextSubUrl = ""
@@ -55,7 +52,7 @@ func (s *Server) LoadNext() {
return return
} }
next, err := s.Provider.GetNext(c) next, err := s.ContextManga.Provider.GetNext(c)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
s.NextSubUrl = "" s.NextSubUrl = ""
@@ -63,7 +60,7 @@ func (s *Server) LoadNext() {
return return
} }
html, err := s.Provider.GetHtml(next) html, err := s.ContextManga.Provider.GetHtml(next)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
s.NextSubUrl = "" s.NextSubUrl = ""
@@ -79,7 +76,7 @@ func (s *Server) LoadNext() {
return return
} }
title, chapter, err := s.Provider.GetTitleAndChapter(next) title, chapter, err := s.ContextManga.Provider.GetTitleAndChapter(next)
if err != nil { if err != nil {
title = "Unknown" title = "Unknown"
chapter = "ch_?" chapter = "ch_?"
@@ -93,21 +90,21 @@ func (s *Server) LoadNext() {
} }
func (s *Server) LoadPrev() { func (s *Server) LoadPrev() {
c, err := s.Provider.GetHtml(s.CurrSubUrl) c, err := s.ContextManga.Provider.GetHtml(s.CurrSubUrl)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
s.PrevSubUrl = "" s.PrevSubUrl = ""
s.PrevViewModel = nil s.PrevViewModel = nil
return return
} }
prev, err := s.Provider.GetPrev(c) prev, err := s.ContextManga.Provider.GetPrev(c)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
s.PrevSubUrl = "" s.PrevSubUrl = ""
s.PrevViewModel = nil s.PrevViewModel = nil
return return
} }
html, err := s.Provider.GetHtml(prev) html, err := s.ContextManga.Provider.GetHtml(prev)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
s.PrevSubUrl = "" s.PrevSubUrl = ""
@@ -123,7 +120,7 @@ func (s *Server) LoadPrev() {
return return
} }
title, chapter, err := s.Provider.GetTitleAndChapter(prev) title, chapter, err := s.ContextManga.Provider.GetTitleAndChapter(prev)
if err != nil { if err != nil {
title = "Unknown" title = "Unknown"
chapter = "ch_?" chapter = "ch_?"
@@ -138,14 +135,14 @@ func (s *Server) LoadPrev() {
} }
func (s *Server) LoadCurr() { func (s *Server) LoadCurr() {
html, err := s.Provider.GetHtml(s.CurrSubUrl) html, err := s.ContextManga.Provider.GetHtml(s.CurrSubUrl)
if err != nil { if err != nil {
panic(err) panic(err)
} }
imagesCurr, err := s.AppendImagesToBuf(html) imagesCurr, err := s.AppendImagesToBuf(html)
title, chapter, err := s.Provider.GetTitleAndChapter(s.CurrSubUrl) title, chapter, err := s.ContextManga.Provider.GetTitleAndChapter(s.CurrSubUrl)
if err != nil { if err != nil {
title = "Unknown" title = "Unknown"
chapter = "ch_?" chapter = "ch_?"
@@ -166,7 +163,7 @@ func (s *Server) LoadThumbnail(mangaId int) (path string, err error) {
return strId, nil return strId, nil
} }
url, err := s.Provider.GetThumbnail(strconv.Itoa(mangaId)) url, err := s.ContextManga.Provider.GetThumbnail(strconv.Itoa(mangaId))
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -179,7 +176,7 @@ func (s *Server) LoadThumbnail(mangaId int) (path string, err error) {
} }
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.ContextManga.Provider.GetImageList(html)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -194,11 +191,11 @@ func (s *Server) AppendImagesToBuf(html string) ([]view.Image, error) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
name := filepath.Base(url) g := uuid.New()
s.Mutex.Lock() s.Mutex.Lock()
s.ImageBuffers[name] = buf s.ImageBuffers[g.String()] = buf
s.Mutex.Unlock() s.Mutex.Unlock()
images[i] = view.Image{Path: name, Index: i} images[i] = view.Image{Path: g.String(), Index: i}
wg.Done() wg.Done()
}(i, url, &wg) }(i, url, &wg)
} }