Compare commits

...

6 Commits

Author SHA1 Message Date
daru
8df72d6f18 Hotfix 2022-07-01 19:05:31 +02:00
daru
d87b658837 LastSeason 2022-07-01 18:43:30 +02:00
daru
be04fb92e5 Doc bump 2022-07-01 18:28:17 +02:00
daru
f968c1b98e NextNextSeason 2022-07-01 18:20:17 +02:00
daru
fd84193d9d the big talk 2022-07-01 17:47:08 +02:00
daru
f078c93b9e We fast now 2022-07-01 17:08:10 +02:00
9 changed files with 156 additions and 116 deletions

View File

@@ -43,6 +43,8 @@ _X-HUSO-AUTH_ -> _secret_ des users
| - | - | - | - | - | - | - |
| GET | /api/season | Aktuelle Season holen | []Anime JSON | | | |
| GET | /api/nextseason | Nächste Season holen | []Anime JSON | | | |
| GET | /api/nextnextseason | Übernächste Season holen | []Anime JSON | | | |
| GET | /api/lastseason | Letzte Season holen | []Anime JSON | | | |
| GET | /api/anime/{id} | Einzelnen Anime holen | Anime JSON | {id} = MAL Anime id | | |
| GET | /api/animesearch | Anime auf MAL suchen | []Anime JSON | Query parameter {q} = Suchtext | | |

View File

@@ -21,7 +21,6 @@ import (
const (
husoVersion = "1.3"
registerSecret = "綾波レイ"
seasonApiJikan = "seasons/now"
seasonStrJikan = "seasons/"
userApiJikan = "users/"
userApiMal = "users/"
@@ -74,7 +73,7 @@ func main() {
color.Notice.Printf("huso %s built on %s with %s\n", husoVersion, buildTime, runtime.Version())
logOut.WriteLine(fmt.Sprintf("🎉 huso %s built on %s with %s", husoVersion, buildTime, runtime.Version()))
jikanLimiter = rate.NewLimiter(rate.Every(time.Second), 1)
jikanLimiter = rate.NewLimiter(rate.Every(time.Second+(time.Millisecond*420)), 1)
// cache init
var err error

View File

@@ -320,7 +320,8 @@ type MmOracle struct {
type MovieChart struct {
MmId int `json:"mmId"`
Anime int64 `json:"anime"`
Title string `json:"title"`
AvgScore float64 `json:"avgScore"`
UserCount int `json:"userCount"`
Data Anime `json:"data"`
}

View File

@@ -75,7 +75,9 @@ func GetUserAnimeListData(username, status string) (*AnimeListMal, []byte, error
var list AnimeListMal
data, err := animeListCache.Get(username + status)
if err != nil {
data, err = GetDataMal(userApiMal + username + "/animelist?limit=1000&status=" + status + "&fields=list_status")
color.Infoln(username + "'s " + status + " abfragen...")
logOut.WriteLine("📄 " + username + "'s " + status + " abfragen...")
data, err = GetDataMal(userApiMal + username + "/animelist?limit=1000&status=" + status + "&fields=list_status&nsfw=true")
if err != nil {
return nil, nil, err
}
@@ -133,45 +135,9 @@ func GetUserData(username string) (*User, []byte, error) {
return &user, data, err
}
func GetSeasonDataAll() ([]Anime, []byte, error) {
seasonStr := GetCurrentSeasonString()
color.Infoln("Aktuelle Season (" + seasonStr + ") abfragen...")
logOut.WriteLine("📺 Aktuelle Season (" + seasonStr + ") abfragen...")
data, _, err := GetSeasonData(seasonApiJikan, 1)
if err != nil {
return nil, nil, err
}
color.Infof("%d Anime auf %d Seiten\n", data.Pagination.Items.Total, data.Pagination.LastVisiblePage)
logOut.WriteLine(fmt.Sprintf("📺 %d Anime auf %d Seiten", data.Pagination.Items.Total, data.Pagination.LastVisiblePage))
animes := make([]Anime, 0)
// convert to anime
for _, a := range data.Data {
animes = append(animes, JikanConvert(&a))
}
for i := 2; data.Pagination.HasNextPage; i++ {
color.Infof("Seite %d abfragen...\n", i)
logOut.WriteLine(fmt.Sprintf("📺 Seite %d abfragen...", i))
newData, _, err := GetSeasonData(seasonApiJikan, i)
if err != nil {
return nil, nil, err
}
data.Pagination.CurrentPage = newData.Pagination.CurrentPage
data.Pagination.HasNextPage = newData.Pagination.HasNextPage
data.Pagination.Items.Count += newData.Pagination.Items.Count
// convert to anime
for _, a := range newData.Data {
animes = append(animes, JikanConvert(&a))
}
}
color.Infof("%d Anime bekommen\n", len(animes))
logOut.WriteLine(fmt.Sprintf("📺 %d Anime bekommen", len(animes)))
bytes, err := json.Marshal(animes)
return animes, bytes, err
}
func GetNextSeasonDataAll(season string) ([]Anime, []byte, error) {
color.Infoln("Nächste Season (" + season + ") abfragen...")
logOut.WriteLine("📺 Nächste Season (" + season + ") abfragen...")
func GetSeasonDataAll(season string) ([]Anime, []byte, error) {
color.Infoln("Season " + season + " abfragen...")
logOut.WriteLine("📺 Season (" + season + ") abfragen...")
data, _, err := GetSeasonData(seasonStrJikan+season, 1)
if err != nil {
return nil, nil, err
@@ -292,3 +258,35 @@ func GetNextSeasonString() string {
return fmt.Sprintf("%04d", now.Year())
}
}
func GetNextNextSeasonString() string {
var now = time.Now()
switch now.Month() {
case time.January, time.February, time.March:
return fmt.Sprintf("%04d/summer", now.Year())
case time.April, time.May, time.June:
return fmt.Sprintf("%04d/fall", now.Year())
case time.July, time.August, time.September:
return fmt.Sprintf("%04d/winter", now.Year()+1)
case time.October, time.November, time.December:
return fmt.Sprintf("%04d/spring", now.Year()+1)
default:
return fmt.Sprintf("%04d", now.Year())
}
}
func GetLastSeasonString() string {
var now = time.Now()
switch now.Month() {
case time.January, time.February, time.March:
return fmt.Sprintf("%04d/fall", now.Year()-1)
case time.April, time.May, time.June:
return fmt.Sprintf("%04d/winter", now.Year())
case time.July, time.August, time.September:
return fmt.Sprintf("%04d/spring", now.Year())
case time.October, time.November, time.December:
return fmt.Sprintf("%04d/summer", now.Year())
default:
return fmt.Sprintf("%04d", now.Year())
}
}

40
ober.go
View File

@@ -19,6 +19,8 @@ func RunWebserv() {
r.GET("/", Headers(Start))
r.GET("/api/season", Headers(Season))
r.GET("/api/nextseason", Headers(SeasonNext))
r.GET("/api/nextnextseason", Headers(SeasonNextNext))
r.GET("/api/lastseason", Headers(SeasonLast))
r.GET("/api/auth/{user}", Headers(AuthTest))
r.GET("/api/anime/{id}", Headers(AnimeGet))
r.GET("/api/animesearch", Headers(AnimeSearchGet))
@@ -39,7 +41,7 @@ func RunWebserv() {
}
func Start(ctx *fasthttp.RequestCtx) {
season, err := GetSeasonCache(seasonApiJikan)
season, err := GetSeasonCache(GetCurrentSeasonString())
if err != nil {
addErrorToCtx(ctx, err)
return
@@ -77,7 +79,7 @@ func AuthTest(ctx *fasthttp.RequestCtx) {
}
func Season(ctx *fasthttp.RequestCtx) {
data, err := seasoncache.Get(seasonApiJikan)
data, err := seasoncache.Get(GetCurrentSeasonString())
if err != nil {
addErrorToCtx(ctx, err)
return
@@ -110,6 +112,40 @@ func SeasonNext(ctx *fasthttp.RequestCtx) {
ctx.SetStatusCode(fasthttp.StatusOK)
}
func SeasonNextNext(ctx *fasthttp.RequestCtx) {
data, err := seasoncache.Get(GetNextNextSeasonString())
if err != nil {
addErrorToCtx(ctx, err)
return
}
err = writeResponseBody(ctx, data)
if err != nil {
addErrorToCtx(ctx, err)
return
}
ctx.SetContentType("application/json; charset=utf-8")
ctx.SetStatusCode(fasthttp.StatusOK)
}
func SeasonLast(ctx *fasthttp.RequestCtx) {
data, err := seasoncache.Get(GetLastSeasonString())
if err != nil {
addErrorToCtx(ctx, err)
return
}
err = writeResponseBody(ctx, data)
if err != nil {
addErrorToCtx(ctx, err)
return
}
ctx.SetContentType("application/json; charset=utf-8")
ctx.SetStatusCode(fasthttp.StatusOK)
}
func AnimeGet(ctx *fasthttp.RequestCtx) {
idVal := ctx.UserValue("id")
if idVal == nil {

View File

@@ -55,6 +55,9 @@ func Arbeit() {
}
return
}
color.Infoln("Animelisten abfragen...")
logOut.WriteLine("📜 Animelisten abfragen...")
count := 0
// iterate anime
for _, a := range animesUsers {
// iterate users
@@ -68,40 +71,45 @@ func Arbeit() {
// check if user set anime as completed
if listState == malApiStatusC {
color.Infof("%s finished %d scored %d\n", u.Username, a.Anime, score)
logOut.WriteLine(fmt.Sprintf("📜 %s finished %d scored %d !", u.Username, a.Anime, score))
logOut.WriteLine(fmt.Sprintf("📝 %s finished %d scored %d !", u.Username, a.Anime, score))
// delete user from anime
_, err = DeleteUserFromAnime(u.Username, u.MalID, a.Anime)
if err != nil {
color.Errorln(err.Error())
logOut.WriteError(err)
}
count++
continue
}
// check if user set anime as dropped
if listState == malApiStatusD {
color.Infof("%s dropped %d\n", u.Username, a.Anime)
logOut.WriteLine(fmt.Sprintf("📜 %s dropped %d !", u.Username, a.Anime))
logOut.WriteLine(fmt.Sprintf("📝 %s dropped %d !", u.Username, a.Anime))
// delete user from anime
_, err = DeleteUserFromAnime(u.Username, u.MalID, a.Anime)
if err != nil {
color.Errorln(err.Error())
logOut.WriteError(err)
}
count++
continue
}
if newProgress == u.Progress {
continue
}
color.Infof("%s progress von %d: %d -> %d\n", u.Username, a.Anime, u.Progress, newProgress)
logOut.WriteLine(fmt.Sprintf("📜 %s progress von %d: %d -> %d", u.Username, a.Anime, u.Progress, newProgress))
logOut.WriteLine(fmt.Sprintf("📝 %s progress von %d: %d -> %d", u.Username, a.Anime, u.Progress, newProgress))
// update db
err = UpdateUserAnimeProgress(a.Anime, u.MalID, newProgress, updated)
if err != nil {
color.Errorln(err.Error())
logOut.WriteError(err)
}
count++
}
}
color.Infof("%d Sachen aktualisiert\n", count)
logOut.WriteLine(fmt.Sprintf("📜 %d Sachen aktualisiert", count))
}
func BissleArbeiten() {
@@ -143,30 +151,31 @@ func LangeArbeiten() {
func LangeArbeit() {
// season data
_, bytes, err := GetSeasonDataAll()
err := refreshSeason(GetCurrentSeasonString())
if err != nil {
color.Errorln(err.Error())
logOut.WriteError(err)
} else {
err = seasoncache.Set(seasonApiJikan, bytes)
if err != nil {
color.Errorln(err.Error())
logOut.WriteError(err)
}
}
// next season data
nextSeason := GetNextSeasonString()
_, bytes, err = GetNextSeasonDataAll(nextSeason)
err = refreshSeason(GetNextSeasonString())
if err != nil {
color.Errorln(err.Error())
logOut.WriteError(err)
}
// next next season data
err = refreshSeason(GetNextNextSeasonString())
if err != nil {
color.Errorln(err.Error())
logOut.WriteError(err)
}
// last season data
err = refreshSeason(GetLastSeasonString())
if err != nil {
color.Errorln(err.Error())
logOut.WriteError(err)
} else {
err = seasoncache.Set(nextSeason, bytes)
if err != nil {
color.Errorln(err.Error())
logOut.WriteError(err)
}
}
// refresh anime cache with watched
@@ -179,8 +188,8 @@ func LangeArbeit() {
}
} else {
for _, a := range animesUsers {
// search season first
_, err = SearchSeason(a.Anime)
// search seasons first
_, err = SearchSeasons(a.Anime)
if err == nil {
continue
}
@@ -194,27 +203,6 @@ func LangeArbeit() {
}
}
charts, err := BuildMovieCharts()
if err != nil {
color.Errorln(err.Error())
logOut.WriteError(err)
} else {
for _, c := range charts {
// search season first
_, err = SearchSeason(c.Data.Anime)
if err == nil {
continue
}
err = refreshAnime(c.Data.Anime)
if err != nil {
color.Errorln(err.Error())
logOut.WriteError(err)
continue
}
count++
}
}
color.Infof("%d Anime aktualisiert\n", count)
logOut.WriteLine(fmt.Sprintf("🔃 %d Anime aktualisiert", count))
}
@@ -249,3 +237,11 @@ func refreshAnime(animeId int64) error {
animeCache.Set(key, data)
return nil
}
func refreshSeason(season string) error {
_, bytes, err := GetSeasonDataAll(season)
if err == nil {
err = seasoncache.Set(season, bytes)
}
return err
}

View File

@@ -123,14 +123,11 @@ func GetSeasonCache(key string) ([]Anime, error) {
return seasonData, err
}
func SearchSeason(animeId int64) (*Anime, error) {
season, err := GetSeasonCache(seasonApiJikan)
func SearchSeasons(animeId int64) (*Anime, error) {
season, err := GetSeasonCache(GetCurrentSeasonString())
if err != nil {
return nil, err
}
if len(season) == 0 {
return nil, errors.New("no seasonal anime")
}
for _, a := range season {
if a.Anime == animeId {
return &a, err
@@ -140,8 +137,23 @@ func SearchSeason(animeId int64) (*Anime, error) {
if err != nil {
return nil, err
}
if len(season) == 0 {
return nil, errors.New("no seasonal anime")
for _, a := range season {
if a.Anime == animeId {
return &a, err
}
}
season, err = GetSeasonCache(GetNextNextSeasonString())
if err != nil {
return nil, err
}
for _, a := range season {
if a.Anime == animeId {
return &a, err
}
}
season, err = GetSeasonCache(GetLastSeasonString())
if err != nil {
return nil, err
}
for _, a := range season {
if a.Anime == animeId {
@@ -153,7 +165,7 @@ func SearchSeason(animeId int64) (*Anime, error) {
func SearchAnime(animeId int64) (*Anime, error) {
// search season first
anime, err := SearchSeason(animeId)
anime, err := SearchSeasons(animeId)
if err != nil {
// get from MAL
anime, _, err = GetAnimeDetailData(animeId)
@@ -191,14 +203,6 @@ func FetchProgress(animeId int64, username string) (int, time.Time, int, string,
if newProgress != -1 {
return newProgress, updated, score, malApiStatusW, err
}
// check on hold
newProgress, updated, score, err = FetchProgressOnState(animeId, username, malApiStatusH)
if err != nil {
return newProgress, updated, score, "", err
}
if newProgress != -1 {
return newProgress, updated, score, malApiStatusH, err
}
// check completed
newProgress, updated, score, err = FetchProgressOnState(animeId, username, malApiStatusC)
if err != nil {
@@ -207,6 +211,14 @@ func FetchProgress(animeId int64, username string) (int, time.Time, int, string,
if newProgress != -1 {
return newProgress, updated, score, malApiStatusC, err
}
// check on hold
newProgress, updated, score, err = FetchProgressOnState(animeId, username, malApiStatusH)
if err != nil {
return newProgress, updated, score, "", err
}
if newProgress != -1 {
return newProgress, updated, score, malApiStatusH, err
}
// check dropped
newProgress, updated, score, err = FetchProgressOnState(animeId, username, malApiStatusD)
if err != nil {
@@ -295,18 +307,14 @@ func BuildMovieCharts() ([]MovieChart, error) {
charts = make([]MovieChart, 0)
for _, m := range movieList {
c := MovieChart{
MmId: m.Id,
}
anime, err := SearchAnime(m.Anime)
if err != nil {
color.Errorln(err.Error())
continue
MmId: m.Id,
Anime: m.Anime,
Title: m.Title,
}
c.Data = *anime
scoreSum := 0
for _, u := range users {
progress, _, score, err := FetchProgressOnState(anime.Anime, u.Username, malApiStatusC)
progress, _, score, err := FetchProgressOnState(c.Anime, u.Username, malApiStatusC)
if err != nil {
color.Errorln(err.Error())
continue

View File

@@ -55,10 +55,10 @@ body { background-color: #1a1a1a; color: #fff; }
{% for _, chart := range charts %}
<tr>
<td><a href="https://movies.hanami.family/movie/{%d chart.MmId %}" target="_blank" rel="noopener noreferrer">{%d chart.MmId %}</a></td>
<td><a href="{%s chart.Data.URL %}" target="_blank" rel="noopener noreferrer">{%dl chart.Data.Anime %}</a></td>
<td><a href="https://myanimelist.net/anime/{%dl chart.Anime %}" target="_blank" rel="noopener noreferrer">{%dl chart.Anime %}</a></td>
<td>{%d chart.UserCount %}</td>
<td><strong>{%f.2 chart.AvgScore %}</strong></td>
<td><strong>{%s chart.Data.Title %}</strong></td>
<td><strong>{%s chart.Title %}</strong></td>
</tr>
{% endfor %}
</table>

View File

@@ -67,13 +67,13 @@ func StreamIndex(qw422016 *qt422016.Writer, animes []Anime, oracles []MmOracle,
//line season.qtpl:57
qw422016.N().D(chart.MmId)
//line season.qtpl:57
qw422016.N().S(`</a></td> <td><a href="`)
qw422016.N().S(`</a></td> <td><a href="https://myanimelist.net/anime/`)
//line season.qtpl:58
qw422016.E().S(chart.Data.URL)
qw422016.N().DL(chart.Anime)
//line season.qtpl:58
qw422016.N().S(`" target="_blank" rel="noopener noreferrer">`)
//line season.qtpl:58
qw422016.N().DL(chart.Data.Anime)
qw422016.N().DL(chart.Anime)
//line season.qtpl:58
qw422016.N().S(`</a></td> <td>`)
//line season.qtpl:59
@@ -85,7 +85,7 @@ func StreamIndex(qw422016 *qt422016.Writer, animes []Anime, oracles []MmOracle,
//line season.qtpl:60
qw422016.N().S(`</strong></td> <td><strong>`)
//line season.qtpl:61
qw422016.E().S(chart.Data.Title)
qw422016.E().S(chart.Title)
//line season.qtpl:61
qw422016.N().S(`</strong></td> </tr> `)
//line season.qtpl:63