-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
156 changed files
with
9,437 additions
and
1,645 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
changelog-temp.md | ||
mangal | ||
|
||
###### | ||
# Go # | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,94 +1,98 @@ | ||
package anilist | ||
|
||
import ( | ||
"encoding/json" | ||
"github.com/metafates/mangal/filesystem" | ||
"github.com/metafates/mangal/log" | ||
"github.com/metafates/mangal/util" | ||
"github.com/metafates/mangal/cache" | ||
"github.com/metafates/mangal/where" | ||
"io" | ||
"os" | ||
"github.com/samber/mo" | ||
"path/filepath" | ||
"time" | ||
) | ||
|
||
var cache = anilistCache{ | ||
data: &anilistCacheData{Mangas: make(map[string]*Manga)}, | ||
type cacheData[K comparable, T any] struct { | ||
Mangas map[K]T `json:"mangas"` | ||
} | ||
|
||
type anilistCacheData struct { | ||
Mangas map[string]*Manga `json:"mangas"` | ||
type cacher[K comparable, T any] struct { | ||
internal *cache.Cache[*cacheData[K, T]] | ||
keyWrapper func(K) K | ||
} | ||
|
||
type anilistCache struct { | ||
data *anilistCacheData | ||
path string | ||
initialized bool | ||
} | ||
|
||
func (a *anilistCache) Init() error { | ||
if a.initialized { | ||
return nil | ||
func (c *cacher[K, T]) Get(key K) mo.Option[T] { | ||
data := c.internal.Get() | ||
if data.IsPresent() { | ||
mangas, ok := data.MustGet().Mangas[c.keyWrapper(key)] | ||
if ok { | ||
return mo.Some(mangas) | ||
} | ||
} | ||
|
||
log.Debug("Initializing anilist cacher") | ||
|
||
path := filepath.Join(where.Cache(), "anilist_cache.json") | ||
a.path = path | ||
log.Debugf("Opening anilist cache file at %s", path) | ||
file, err := filesystem.Api().OpenFile(path, os.O_RDONLY|os.O_CREATE, os.ModePerm) | ||
|
||
if err != nil { | ||
log.Warn(err) | ||
return err | ||
} | ||
|
||
defer util.Ignore(file.Close) | ||
|
||
contents, err := io.ReadAll(file) | ||
if err != nil { | ||
log.Warn(err) | ||
return err | ||
} | ||
return mo.None[T]() | ||
} | ||
|
||
if len(contents) == 0 { | ||
log.Debug("Anilist cache file is empty, skipping unmarshal") | ||
return nil | ||
func (c *cacher[K, T]) Set(key K, t T) error { | ||
data := c.internal.Get() | ||
if data.IsPresent() { | ||
internal := data.MustGet() | ||
internal.Mangas[c.keyWrapper(key)] = t | ||
return c.internal.Set(internal) | ||
} else { | ||
internal := &cacheData[K, T]{Mangas: make(map[K]T)} | ||
internal.Mangas[c.keyWrapper(key)] = t | ||
return c.internal.Set(internal) | ||
} | ||
} | ||
|
||
err = json.Unmarshal(contents, a.data) | ||
if err != nil { | ||
log.Warn(err) | ||
return err | ||
func (c *cacher[K, T]) Delete(key K) error { | ||
data := c.internal.Get() | ||
if data.IsPresent() { | ||
internal := data.MustGet() | ||
delete(internal.Mangas, c.keyWrapper(key)) | ||
return c.internal.Set(internal) | ||
} | ||
|
||
log.Debugf("Anilist cache file unmarshalled successfully, len is %d", len(a.data.Mangas)) | ||
return nil | ||
} | ||
|
||
func (a *anilistCache) Get(name string) (*Manga, bool) { | ||
_ = a.Init() | ||
|
||
mangas, ok := a.data.Mangas[normalizeName(name)] | ||
return mangas, ok | ||
var relationCacher = &cacher[string, int]{ | ||
internal: cache.New[*cacheData[string, int]]( | ||
where.AnilistBinds(), | ||
&cache.Options{ | ||
// never expire | ||
ExpireEvery: mo.None[time.Duration](), | ||
}, | ||
), | ||
keyWrapper: normalizedName, | ||
} | ||
|
||
func (a *anilistCache) Set(name string, manga *Manga) error { | ||
_ = a.Init() | ||
|
||
log.Debug("Setting anilist cacher entry") | ||
a.data.Mangas[normalizeName(name)] = manga | ||
marshalled, err := json.Marshal(a.data) | ||
if err != nil { | ||
log.Warn(err) | ||
return err | ||
} | ||
|
||
file, err := filesystem.Api().OpenFile(a.path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm) | ||
var searchCacher = &cacher[string, []int]{ | ||
internal: cache.New[*cacheData[string, []int]]( | ||
filepath.Join(where.Cache(), "anilist_search_cache.json"), | ||
&cache.Options{ | ||
// update ids every 10 days, since new manga are not added that often | ||
ExpireEvery: mo.Some(time.Hour * 24 * 10), | ||
}, | ||
), | ||
keyWrapper: normalizedName, | ||
} | ||
|
||
_, err = file.Write(marshalled) | ||
if err != nil { | ||
log.Warn(err) | ||
} | ||
var idCacher = &cacher[int, *Manga]{ | ||
internal: cache.New[*cacheData[int, *Manga]]( | ||
filepath.Join(where.Cache(), "anilist_id_cache.json"), | ||
&cache.Options{ | ||
// update manga data every 2 days since it can change often | ||
ExpireEvery: mo.Some(time.Hour * 24 * 2), | ||
}, | ||
), | ||
keyWrapper: func(id int) int { return id }, | ||
} | ||
|
||
return err | ||
var failCacher = &cacher[string, bool]{ | ||
internal: cache.New[*cacheData[string, bool]]( | ||
filepath.Join(where.Cache(), "anilist_fail_cache.json"), | ||
&cache.Options{ | ||
// expire every minute | ||
ExpireEvery: mo.Some(time.Minute), | ||
}, | ||
), | ||
keyWrapper: normalizedName, | ||
} |
Oops, something went wrong.