Naming refactor. Added comments.

This commit is contained in:
Felipe M 2021-02-13 12:49:16 +01:00
parent a3cf924918
commit fc0e662409
Signed by: fmartingr
GPG Key ID: 716BC147715E716F
4 changed files with 61 additions and 22 deletions

View File

@ -11,15 +11,18 @@ import (
var cacheEnabled bool = true var cacheEnabled bool = true
// EnableCache enables request caching for reads
func EnableCache() { func EnableCache() {
cacheEnabled = true cacheEnabled = true
} }
// DisableCache disables cache reads
func DisableCache() { func DisableCache() {
cacheEnabled = false cacheEnabled = false
} }
func getCachePath() string { // getBaseCachePath returns the base path for the cache storage
func getBaseCachePath() string {
userCacheDir, errCache := os.UserCacheDir() userCacheDir, errCache := os.UserCacheDir()
if errCache != nil { if errCache != nil {
logrus.Fatalf("Unable to retrieve cache directory: %s", errCache) logrus.Fatalf("Unable to retrieve cache directory: %s", errCache)
@ -27,25 +30,30 @@ func getCachePath() string {
return filepath.Join(userCacheDir, "go-mangadex") return filepath.Join(userCacheDir, "go-mangadex")
} }
func getCachePathFor(mangadexURL *url.URL) string { // getCachePath returns the absolute path to the files cache for the provided URL
func getCachePath(mangadexURL *url.URL) string {
fileName := getCacheFilename(mangadexURL) fileName := getCacheFilename(mangadexURL)
return filepath.Join(getCachePath(), fileName) return filepath.Join(getBaseCachePath(), fileName)
} }
// getCacheFilename generates a cache filename based on the URL of the request
// TODO: Use query arguments as well
func getCacheFilename(mangadexURL *url.URL) string { func getCacheFilename(mangadexURL *url.URL) string {
return strings.ReplaceAll(mangadexURL.Path, "/", "_") return strings.ReplaceAll(mangadexURL.Path, "/", "_")
} }
// cacheExists checks that the cache for a certain URL exists or not
func cacheExists(mangadexURL *url.URL) bool { func cacheExists(mangadexURL *url.URL) bool {
stat, err := os.Stat(getCachePathFor(mangadexURL)) stat, err := os.Stat(getCachePath(mangadexURL))
if os.IsNotExist(err) { if os.IsNotExist(err) {
return false return false
} }
return !stat.IsDir() return !stat.IsDir()
} }
// initCache makes sure that the cache directory exists so reads and writes on cache folders won't fail
func initCache() { func initCache() {
cachePath := getCachePath() cachePath := getBaseCachePath()
_, err := os.Stat(cachePath) _, err := os.Stat(cachePath)
if os.IsNotExist(err) { if os.IsNotExist(err) {
logrus.Infof("Cache directory does not exist, creating. [%s]", cachePath) logrus.Infof("Cache directory does not exist, creating. [%s]", cachePath)

14
http.go
View File

@ -11,19 +11,18 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
const APIBaseURL = "https://api.mangadex.org/v2/" const apiBaseURL = "https://api.mangadex.org/v2/"
func doRequest(method string, requestURL string) (*MangaDexResponse, error) { func doRequest(method string, requestURL string) (*Response, error) {
result := MangaDexResponse{} result := Response{}
parsedURL, errParse := url.Parse(requestURL) parsedURL, errParse := url.Parse(requestURL)
if errParse != nil { if errParse != nil {
return &result, errParse return &result, errParse
} }
if cacheEnabled { if cacheEnabled {
initCache()
if cacheExists(parsedURL) { if cacheExists(parsedURL) {
cacheData, errRead := ioutil.ReadFile(getCachePathFor(parsedURL)) cacheData, errRead := ioutil.ReadFile(getCachePath(parsedURL))
if errRead != nil { if errRead != nil {
logrus.Fatalf("Error reading cache for URL: %s [%s]: %s", parsedURL.String(), getCacheFilename(parsedURL), errRead) logrus.Fatalf("Error reading cache for URL: %s [%s]: %s", parsedURL.String(), getCacheFilename(parsedURL), errRead)
} }
@ -33,9 +32,8 @@ func doRequest(method string, requestURL string) (*MangaDexResponse, error) {
} }
logrus.Debugf("Request loaded from cache: %s", parsedURL.String()) logrus.Debugf("Request loaded from cache: %s", parsedURL.String())
return &result, nil return &result, nil
} else {
logrus.Debugf("Cache not found for %s", parsedURL.String())
} }
logrus.Debugf("Cache not found for %s", parsedURL.String())
} }
logrus.Tracef("Making request %s", parsedURL) logrus.Tracef("Making request %s", parsedURL)
@ -76,7 +74,7 @@ func doRequest(method string, requestURL string) (*MangaDexResponse, error) {
// Write cache // Write cache
logrus.Infof("Writting cache for %s", parsedURL.String()) logrus.Infof("Writting cache for %s", parsedURL.String())
logrus.Infof("Writting cache to: %s", getCacheFilename(parsedURL)) logrus.Infof("Writting cache to: %s", getCacheFilename(parsedURL))
errWriteCache := ioutil.WriteFile(getCachePathFor(parsedURL), body, 0644) errWriteCache := ioutil.WriteFile(getCachePath(parsedURL), body, 0644)
if errWriteCache != nil { if errWriteCache != nil {
logrus.Warnf("Can't write to cache: %s", errWriteCache) logrus.Warnf("Can't write to cache: %s", errWriteCache)
} }

View File

@ -16,13 +16,13 @@ func (manga *Manga) GetChapters(params ChaptersParams) ([]MangaChapterList, []Ma
var mangaGroupsResult []MangaGroup var mangaGroupsResult []MangaGroup
params.validate() params.validate()
response, errRequest := doRequest("GET", APIBaseURL+path.Join("manga", strconv.Itoa(manga.ID), "chapters")+"?"+params.asQueryParams().Encode()) response, errRequest := doRequest("GET", apiBaseURL+path.Join("manga", strconv.Itoa(manga.ID), "chapters")+"?"+params.asQueryParams().Encode())
if errRequest != nil { if errRequest != nil {
logrus.Errorf("Request error: %s", errRequest) logrus.Errorf("Request error: %s", errRequest)
return mangaChaptersResult, mangaGroupsResult, errRequest return mangaChaptersResult, mangaGroupsResult, errRequest
} }
var mangaDexChaptersResponse MangaDexChaptersResponse var mangaDexChaptersResponse ChaptersResponse
errJSON := json.Unmarshal(response.Data, &mangaDexChaptersResponse) errJSON := json.Unmarshal(response.Data, &mangaDexChaptersResponse)
if errJSON != nil { if errJSON != nil {
@ -39,7 +39,7 @@ func (manga *Manga) GetChapters(params ChaptersParams) ([]MangaChapterList, []Ma
func (manga *Manga) GetChapter(chapter string) (MangaChapterDetail, error) { func (manga *Manga) GetChapter(chapter string) (MangaChapterDetail, error) {
var result MangaChapterDetail var result MangaChapterDetail
response, errRequest := doRequest("GET", APIBaseURL+path.Join("chapter", chapter)) response, errRequest := doRequest("GET", apiBaseURL+path.Join("chapter", chapter))
if errRequest != nil { if errRequest != nil {
logrus.Errorf("Request error: %s", errRequest) logrus.Errorf("Request error: %s", errRequest)
return result, errRequest return result, errRequest
@ -57,7 +57,7 @@ func (manga *Manga) GetChapter(chapter string) (MangaChapterDetail, error) {
// GetCovers requests the covers for the provided manga // GetCovers requests the covers for the provided manga
func (manga *Manga) GetCovers() ([]MangaCover, error) { func (manga *Manga) GetCovers() ([]MangaCover, error) {
var result []MangaCover var result []MangaCover
response, errRequest := doRequest("GET", APIBaseURL+path.Join("manga", strconv.Itoa(manga.ID), "covers")) response, errRequest := doRequest("GET", apiBaseURL+path.Join("manga", strconv.Itoa(manga.ID), "covers"))
if errRequest != nil { if errRequest != nil {
logrus.Errorf("Request error: %s", errRequest) logrus.Errorf("Request error: %s", errRequest)
return result, errRequest return result, errRequest
@ -73,8 +73,9 @@ func (manga *Manga) GetCovers() ([]MangaCover, error) {
// GetManga retrieves the manga information for the provided ID. // GetManga retrieves the manga information for the provided ID.
func GetManga(mangaID int) (Manga, error) { func GetManga(mangaID int) (Manga, error) {
initCache()
result := Manga{} result := Manga{}
response, errRequest := doRequest("GET", APIBaseURL+path.Join("manga", strconv.Itoa(mangaID))) response, errRequest := doRequest("GET", apiBaseURL+path.Join("manga", strconv.Itoa(mangaID)))
if errRequest != nil { if errRequest != nil {
logrus.Errorf("Request error: %s", errRequest) logrus.Errorf("Request error: %s", errRequest)
return result, errRequest return result, errRequest

View File

@ -6,7 +6,8 @@ import (
"strconv" "strconv"
) )
type MangaDexResponse struct { // Response handles the response from MangaDex
type Response struct {
// Same as HTTP status code // Same as HTTP status code
Code int `json:"code"` Code int `json:"code"`
// `OK` or `error` // `OK` or `error`
@ -17,11 +18,19 @@ type MangaDexResponse struct {
Data json.RawMessage `json:"data"` Data json.RawMessage `json:"data"`
} }
type MangaDexChaptersResponse struct { // IsOK Checks if the response is correct
func (response Response) IsOK() bool {
return response.Status == "OK"
}
// ChaptersResponse handles the response of the chapters list which returns
// two kinds of objects in the `json:"data"` key.
type ChaptersResponse struct {
Chapters []MangaChapterList `json:"chapters"` Chapters []MangaChapterList `json:"chapters"`
Groups []MangaGroup `json:"groups"` Groups []MangaGroup `json:"groups"`
} }
// MangaRelation relations between mangas
type MangaRelation struct { type MangaRelation struct {
ID int `json:"id"` ID int `json:"id"`
IsHentai bool `json:"isHentai"` IsHentai bool `json:"isHentai"`
@ -29,6 +38,8 @@ type MangaRelation struct {
Type int `json:"type"` Type int `json:"type"`
} }
// MangaPublication stores certain information for the publication of the manga
// Some values are easily guessed, others are not ...
type MangaPublication struct { type MangaPublication struct {
// ??? // ???
Demographic int8 `json:"demographic"` Demographic int8 `json:"demographic"`
@ -37,12 +48,19 @@ type MangaPublication struct {
Language string `json:"language"` Language string `json:"language"`
} }
// IsComplete returns if the manga has finished publishing
func (publication MangaPublication) IsComplete() bool {
return publication.Status == 2
}
// MangaRating the rating for a particular manga
type MangaRating struct { type MangaRating struct {
Bayesian float32 `json:"bayesian"` Bayesian float32 `json:"bayesian"`
Mean float32 `json:"mean"` Mean float32 `json:"mean"`
Users int `json:"users"` Users int `json:"users"`
} }
// MangaLinks contains the relation of the links map with more verbose names
type MangaLinks struct { type MangaLinks struct {
AniList string `json:"al"` AniList string `json:"al"`
AnimePlanet string `json:"ap"` AnimePlanet string `json:"ap"`
@ -56,6 +74,7 @@ type MangaLinks struct {
EnglishRaw string `json:"engtl"` EnglishRaw string `json:"engtl"`
} }
// Manga stores the base manga object and details
type Manga struct { type Manga struct {
ID int `json:"id"` ID int `json:"id"`
AlternativeTitles []string `json:"altTitles"` AlternativeTitles []string `json:"altTitles"`
@ -78,11 +97,13 @@ type Manga struct {
Views int `json:"views"` Views int `json:"views"`
} }
// MangaCover stores the cover object
type MangaCover struct { type MangaCover struct {
URL string `json:"url"` URL string `json:"url"`
Volume string `json:"volume"` Volume string `json:"volume"`
} }
// MangaChapterBase the base attributes for a chapter for both the list and the detail
type MangaChapterBase struct { type MangaChapterBase struct {
ID int `json:"id"` ID int `json:"id"`
Hash string `json:"hash"` Hash string `json:"hash"`
@ -99,11 +120,15 @@ type MangaChapterBase struct {
Views int `json:"views"` Views int `json:"views"`
} }
// MangaChapterList stores the chapter object from the listing, which only uses the
// base details and uses the ID for the groups instead of returning the entire object
type MangaChapterList struct { type MangaChapterList struct {
MangaChapterBase MangaChapterBase
Groups []int `json:"groups"` Groups []int `json:"groups"`
} }
// MangaChapterDetail stores the bases of a chapter plus the required attributes
// to retrieve the page blobs
type MangaChapterDetail struct { type MangaChapterDetail struct {
MangaChapterBase MangaChapterBase
Groups []MangaGroup `json:"groups"` Groups []MangaGroup `json:"groups"`
@ -113,11 +138,13 @@ type MangaChapterDetail struct {
ServerFallback string `json:"serverFallback"` ServerFallback string `json:"serverFallback"`
} }
// MangaGroupMember stores the member of a group
type MangaGroupMember struct { type MangaGroupMember struct {
ID int `json:"id"` ID int `json:"id"`
Name string `json:"name"` Name string `json:"name"`
} }
// MangaGroup stores the group behind releases of a particular manga
type MangaGroup struct { type MangaGroup struct {
ID int `json:"id"` ID int `json:"id"`
Name string `json:"name"` Name string `json:"name"`
@ -145,12 +172,17 @@ type MangaGroup struct {
Banner string `json:"banner"` Banner string `json:"banner"`
} }
// ChaptersParams the request parameters for the chapters listing
type ChaptersParams struct { type ChaptersParams struct {
Limit int `json:"limit"` // How many items per page (max 100)
Page int `json:"p"` Limit int `json:"limit"`
// Page to retrieve (default 0)
Page int `json:"p"`
// Hide groups blocked by the user (auth not implemented)
BlockGroups bool `json:"blockgroups"` BlockGroups bool `json:"blockgroups"`
} }
// NewChaptersParams returns a ChapterParams object with sensible defaults
func NewChaptersParams() ChaptersParams { func NewChaptersParams() ChaptersParams {
return ChaptersParams{ return ChaptersParams{
Limit: 100, Limit: 100,