Compare commits
5 Commits
d7233a52ba
...
4e11b218be
Author | SHA1 | Date | |
---|---|---|---|
4e11b218be | |||
b1880690dc | |||
59fe3d32ff | |||
64def1a763 | |||
dcf7322b0c |
@ -2,38 +2,22 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type JWT struct {
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
var jwt JWT
|
||||
|
||||
func AniListQuery(body interface{}, login bool) interface{} {
|
||||
func AniListQuery(body interface{}, login bool) (json.RawMessage, string) {
|
||||
reader, _ := json.Marshal(body)
|
||||
response, err := http.NewRequest("POST", "https://graphql.anilist.co", bytes.NewBuffer(reader))
|
||||
if err != nil {
|
||||
log.Printf("Failed at response, %s\n", err)
|
||||
}
|
||||
if login && (JWT{}) != jwt {
|
||||
if login && (AniListJWT{}) != jwt {
|
||||
response.Header.Add("Authorization", "Bearer "+jwt.AccessToken)
|
||||
} else if login {
|
||||
return "Please login to AniList to make this request"
|
||||
return nil, "Please login to anilist to make this request"
|
||||
}
|
||||
response.Header.Add("Content-Type", "application/json")
|
||||
response.Header.Add("Accept", "application/json")
|
||||
@ -48,13 +32,7 @@ func AniListQuery(body interface{}, login bool) interface{} {
|
||||
|
||||
returnedBody, err := io.ReadAll(res.Body)
|
||||
|
||||
var post interface{}
|
||||
err = json.Unmarshal(returnedBody, &post)
|
||||
if err != nil {
|
||||
log.Printf("Failed at unmarshal, %s\n", err)
|
||||
}
|
||||
|
||||
return post
|
||||
return returnedBody, ""
|
||||
}
|
||||
|
||||
func (a *App) GetAniListItem(aniId int) any {
|
||||
@ -101,7 +79,15 @@ func (a *App) GetAniListItem(aniId int) any {
|
||||
},
|
||||
}
|
||||
|
||||
return AniListQuery(body, false)
|
||||
returnedBody, _ := AniListQuery(body, false)
|
||||
|
||||
var post interface{}
|
||||
err := json.Unmarshal(returnedBody, &post)
|
||||
if err != nil {
|
||||
log.Printf("Failed at unmarshal, %s\n", err)
|
||||
}
|
||||
|
||||
return post
|
||||
}
|
||||
|
||||
func (a *App) AniListSearch(query string) any {
|
||||
@ -143,95 +129,131 @@ func (a *App) AniListSearch(query string) any {
|
||||
ListType: "ANIME",
|
||||
},
|
||||
}
|
||||
return AniListQuery(body, false)
|
||||
}
|
||||
returnedBody, _ := AniListQuery(body, false)
|
||||
|
||||
var ctxShutdown, cancel = context.WithCancel(context.Background())
|
||||
|
||||
func (a *App) AniListLogin() {
|
||||
getAniListCodeUrl := "https://anilist.co/api/v2/oauth/authorize?client_id=" + os.Getenv("ANILIST_APP_ID") + "&redirect_uri=" + os.Getenv("ANILIST_CALLBACK_URI") + "&response_type=code"
|
||||
runtime.BrowserOpenURL(a.ctx, getAniListCodeUrl)
|
||||
|
||||
serverDone := &sync.WaitGroup{}
|
||||
serverDone.Add(1)
|
||||
handleAniListCallback(serverDone)
|
||||
serverDone.Wait()
|
||||
}
|
||||
|
||||
func handleAniListCallback(wg *sync.WaitGroup) {
|
||||
srv := &http.Server{Addr: ":6734"}
|
||||
http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
|
||||
select {
|
||||
case <-ctxShutdown.Done():
|
||||
fmt.Println("Shutting down...")
|
||||
return
|
||||
default:
|
||||
}
|
||||
content := r.FormValue("code")
|
||||
|
||||
if content != "" {
|
||||
jwt = getAniListAuthorizationToken(content)
|
||||
fmt.Println("Shutting down...")
|
||||
cancel()
|
||||
err := srv.Shutdown(context.Background())
|
||||
if err != nil {
|
||||
log.Println("server.Shutdown:", err)
|
||||
}
|
||||
} else {
|
||||
_, err := fmt.Fprintf(w, "Getting code failed.")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("listen: %s\n", err)
|
||||
}
|
||||
fmt.Println("Shutting down...")
|
||||
}()
|
||||
}
|
||||
|
||||
func getAniListAuthorizationToken(content string) JWT {
|
||||
apiUrl := "https://anilist.co/api/v2/oauth/token"
|
||||
resource := "/api/v2/oauth/token"
|
||||
data := url.Values{}
|
||||
data.Set("grant_type", "authorization_code")
|
||||
data.Set("client_id", os.Getenv("ANILIST_APP_ID"))
|
||||
data.Set("client_secret", os.Getenv("ANILIST_SECRET_TOKEN"))
|
||||
data.Set("redirect_uri", os.Getenv("ANILIST_CALLBACK_URI"))
|
||||
data.Set("code", content)
|
||||
|
||||
u, _ := url.ParseRequestURI(apiUrl)
|
||||
u.Path = resource
|
||||
urlStr := u.String()
|
||||
|
||||
response, err := http.NewRequest("POST", urlStr, strings.NewReader(data.Encode()))
|
||||
if err != nil {
|
||||
log.Printf("Failed at response, %s\n", err)
|
||||
}
|
||||
response.Header.Add("content-type", "application/x-www-form-urlencoded")
|
||||
response.Header.Add("Content-Type", "application/json")
|
||||
response.Header.Add("Accept", "application/json")
|
||||
|
||||
client := &http.Client{}
|
||||
res, reserr := client.Do(response)
|
||||
if reserr != nil {
|
||||
log.Printf("Failed at res, %s\n", err)
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
returnedBody, err := io.ReadAll(res.Body)
|
||||
|
||||
var post JWT
|
||||
err = json.Unmarshal(returnedBody, &post)
|
||||
var post interface{}
|
||||
err := json.Unmarshal(returnedBody, &post)
|
||||
if err != nil {
|
||||
log.Printf("Failed at unmarshal, %s\n", err)
|
||||
}
|
||||
|
||||
return post
|
||||
}
|
||||
|
||||
func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) AniListCurrentUserWatchList {
|
||||
var user = a.GetAniListLoggedInUserId()
|
||||
type Variables struct {
|
||||
Page int `json:"page"`
|
||||
PerPage int `json:"perPage"`
|
||||
UserId int `json:"userId"`
|
||||
ListType string `json:"listType"`
|
||||
Status string `json:"status"`
|
||||
Sort string `json:"sort"`
|
||||
}
|
||||
body := struct {
|
||||
Query string `json:"query"`
|
||||
Variables Variables `json:"variables"`
|
||||
}{
|
||||
Query: `
|
||||
query(
|
||||
$page: Int
|
||||
$perPage: Int
|
||||
$userId: Int
|
||||
$listType: MediaType
|
||||
$status: MediaListStatus
|
||||
$sort:[MediaListSort]
|
||||
) {
|
||||
Page(page: $page, perPage: $perPage) {
|
||||
pageInfo {
|
||||
total
|
||||
perPage
|
||||
currentPage
|
||||
lastPage
|
||||
hasNextPage
|
||||
}
|
||||
mediaList(userId: $userId, type: $listType, status: $status, sort: $sort) {
|
||||
id
|
||||
mediaId
|
||||
userId
|
||||
media {
|
||||
id
|
||||
idMal
|
||||
title {
|
||||
romaji
|
||||
english
|
||||
native
|
||||
}
|
||||
description
|
||||
coverImage {
|
||||
large
|
||||
}
|
||||
season
|
||||
seasonYear
|
||||
status
|
||||
episodes
|
||||
nextAiringEpisode{
|
||||
airingAt
|
||||
timeUntilAiring
|
||||
episode
|
||||
}
|
||||
}
|
||||
status
|
||||
notes
|
||||
progress
|
||||
score
|
||||
repeat
|
||||
user {
|
||||
id
|
||||
name
|
||||
avatar{
|
||||
large
|
||||
medium
|
||||
}
|
||||
statistics {
|
||||
anime {
|
||||
count
|
||||
statuses {
|
||||
status
|
||||
count
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
Variables: Variables{
|
||||
Page: page,
|
||||
PerPage: perPage,
|
||||
UserId: user.Data.Viewer.ID,
|
||||
ListType: "ANIME",
|
||||
Status: "CURRENT",
|
||||
Sort: sort,
|
||||
},
|
||||
}
|
||||
|
||||
returnedBody, _ := AniListQuery(body, true)
|
||||
|
||||
var post AniListCurrentUserWatchList
|
||||
err := json.Unmarshal(returnedBody, &post)
|
||||
if err != nil {
|
||||
log.Printf("Failed at unmarshal, %s\n", err)
|
||||
}
|
||||
|
||||
// Getting the real total, finding the real last page and storing that in the Page info
|
||||
statuses := post.Data.Page.MediaList[0].User.Statistics.Anime.Statuses
|
||||
var total int
|
||||
for _, status := range statuses {
|
||||
if status.Status == "CURRENT" {
|
||||
total = status.Count
|
||||
}
|
||||
}
|
||||
|
||||
lastPage := total / perPage
|
||||
|
||||
post.Data.Page.PageInfo.Total = total
|
||||
post.Data.Page.PageInfo.LastPage = lastPage
|
||||
|
||||
return post
|
||||
}
|
||||
|
144
AniListTypes.go
Normal file
144
AniListTypes.go
Normal file
@ -0,0 +1,144 @@
|
||||
package main
|
||||
|
||||
type AniListJWT struct {
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
type AniListUser struct {
|
||||
Data struct {
|
||||
Viewer struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
} `json:"Viewer"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type AniListCurrentUserWatchList struct {
|
||||
Data struct {
|
||||
Page struct {
|
||||
PageInfo struct {
|
||||
Total int `json:"total"`
|
||||
PerPage int `json:"perPage"`
|
||||
CurrentPage int `json:"currentPage"`
|
||||
LastPage int `json:"lastPage"`
|
||||
HasNextPage bool `json:"hasNextPage"`
|
||||
} `json:"pageInfo"`
|
||||
MediaList []struct {
|
||||
ID int `json:"id"`
|
||||
MediaID int `json:"mediaId"`
|
||||
UserID int `json:"userId"`
|
||||
Media struct {
|
||||
ID int `json:"id"`
|
||||
IDMal int `json:"idMal"`
|
||||
Title struct {
|
||||
Romaji string `json:"romaji"`
|
||||
English string `json:"english"`
|
||||
Native string `json:"native"`
|
||||
} `json:"title"`
|
||||
Description string `json:"description"`
|
||||
CoverImage struct {
|
||||
Large string `json:"large"`
|
||||
} `json:"coverImage"`
|
||||
Season string `json:"season"`
|
||||
SeasonYear int `json:"seasonYear"`
|
||||
Status string `json:"status"`
|
||||
Episodes int `json:"episodes"`
|
||||
NextAiringEpisode struct {
|
||||
AiringAt int `json:"airingAt"`
|
||||
TimeUntilAiring int `json:"timeUntilAiring"`
|
||||
Episode int `json:"episode"`
|
||||
} `json:"nextAiringEpisode"`
|
||||
} `json:"media"`
|
||||
Status string `json:"status"`
|
||||
Notes string `json:"notes"`
|
||||
Progress int `json:"progress"`
|
||||
Score int `json:"score"`
|
||||
Repeat int `json:"repeat"`
|
||||
User struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Avatar struct {
|
||||
Large string `json:"large"`
|
||||
Medium string `json:"medium"`
|
||||
} `json:"avatar"`
|
||||
Statistics struct {
|
||||
Anime struct {
|
||||
Count int `json:"count"`
|
||||
Statuses []struct {
|
||||
Status string `json:"status"`
|
||||
Count int `json:"count"`
|
||||
} `json:"statuses"`
|
||||
} `json:"anime"`
|
||||
} `json:"statistics"`
|
||||
} `json:"user"`
|
||||
} `json:"mediaList"`
|
||||
} `json:"Page"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
var MediaListSort = struct {
|
||||
MediaId string
|
||||
MediaIdDesc string
|
||||
Score string
|
||||
ScoreDesc string
|
||||
Status string
|
||||
StatusDesc string
|
||||
Progress string
|
||||
ProgressDesc string
|
||||
ProgressVolumes string
|
||||
ProgressVolumesDesc string
|
||||
Repeat string
|
||||
RepeatDesc string
|
||||
Priority string
|
||||
PriorityDesc string
|
||||
StartedOn string
|
||||
StartedOnDesc string
|
||||
FinishedOn string
|
||||
FinishedOnDesc string
|
||||
AddedTime string
|
||||
AddedTimeDesc string
|
||||
UpdatedTime string
|
||||
UpdatedTimeDesc string
|
||||
MediaTitleRomaji string
|
||||
MediaTitleRomajiDesc string
|
||||
MediaTitleEnglish string
|
||||
MediaTitleEnglishDesc string
|
||||
MediaTitleNative string
|
||||
MediaTitleNativeDesc string
|
||||
MediaPopularity string
|
||||
MediaPopularityDesc string
|
||||
}{
|
||||
MediaId: "MEDIA_ID",
|
||||
MediaIdDesc: "MEDIA_ID_DESC",
|
||||
Score: "SCORE",
|
||||
ScoreDesc: "SCORE_DESC",
|
||||
Status: "STATUS",
|
||||
StatusDesc: "STATUS_DESC",
|
||||
Progress: "PROGRESS",
|
||||
ProgressDesc: "PROGRESS_DESC",
|
||||
ProgressVolumes: "PROGRESS_VOLUMES",
|
||||
ProgressVolumesDesc: "PROGRESS_VOLUMES_DESC",
|
||||
Repeat: "REPEAT",
|
||||
RepeatDesc: "REPEAT_DESC",
|
||||
Priority: "PRIORITY",
|
||||
PriorityDesc: "PRIORITY_DESC",
|
||||
StartedOn: "STARTED_ON",
|
||||
StartedOnDesc: "STARTED_ON_DESC",
|
||||
FinishedOn: "FINISHED_ON",
|
||||
FinishedOnDesc: "FINISHED_ON_DESC",
|
||||
AddedTime: "ADDED_TIME",
|
||||
AddedTimeDesc: "ADDED_TIME_DESC",
|
||||
UpdatedTime: "UPDATED_TIME",
|
||||
UpdatedTimeDesc: "UPDATED_TIME_DESC",
|
||||
MediaTitleRomaji: "MEDIA_TITLE_ROMAJI",
|
||||
MediaTitleRomajiDesc: "MEDIA_TITLE_ROMAJI_DESC",
|
||||
MediaTitleEnglish: "MEDIA_TITLE_ENGLISH",
|
||||
MediaTitleEnglishDesc: "MEDIA_TITLE_ENGLISH_DESC",
|
||||
MediaTitleNative: "MEDIA_TITLE_NATIVE",
|
||||
MediaTitleNativeDesc: "MEDIA_TITLE_NATIVE_DESC",
|
||||
MediaPopularity: "MEDIA_POPULARITY",
|
||||
MediaPopularityDesc: "MEDIA_POPULARITY_DESC",
|
||||
}
|
170
AniListUserFunctions.go
Normal file
170
AniListUserFunctions.go
Normal file
@ -0,0 +1,170 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/99designs/keyring"
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var jwt AniListJWT
|
||||
|
||||
var ring, _ = keyring.Open(keyring.Config{
|
||||
ServiceName: "AniTrack",
|
||||
})
|
||||
|
||||
var ctxShutdown, cancel = context.WithCancel(context.Background())
|
||||
|
||||
func (a *App) AniListLogin() {
|
||||
if (AniListJWT{}) == jwt {
|
||||
tokenType, err := ring.Get("anilistTokenType")
|
||||
expiresIn, err := ring.Get("anilistTokenExpiresIn")
|
||||
accessToken, err := ring.Get("anilistAccessToken")
|
||||
refreshToken, err := ring.Get("anilistRefreshToken")
|
||||
if err != nil {
|
||||
getAniListCodeUrl := "https://anilist.co/api/v2/oauth/authorize?client_id=" + os.Getenv("ANILIST_APP_ID") + "&redirect_uri=" + os.Getenv("ANILIST_CALLBACK_URI") + "&response_type=code"
|
||||
runtime.BrowserOpenURL(a.ctx, getAniListCodeUrl)
|
||||
|
||||
serverDone := &sync.WaitGroup{}
|
||||
serverDone.Add(1)
|
||||
handleAniListCallback(serverDone)
|
||||
serverDone.Wait()
|
||||
} else {
|
||||
jwt.TokenType = string(tokenType.Data)
|
||||
jwt.AccessToken = string(accessToken.Data)
|
||||
jwt.RefreshToken = string(refreshToken.Data)
|
||||
expiresInString := string(expiresIn.Data)
|
||||
jwt.ExpiresIn, _ = strconv.Atoi(expiresInString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleAniListCallback(wg *sync.WaitGroup) {
|
||||
srv := &http.Server{Addr: ":6734"}
|
||||
http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
|
||||
select {
|
||||
case <-ctxShutdown.Done():
|
||||
fmt.Println("Shutting down...")
|
||||
return
|
||||
default:
|
||||
}
|
||||
content := r.FormValue("code")
|
||||
|
||||
if content != "" {
|
||||
jwt = getAniListAuthorizationToken(content)
|
||||
_ = ring.Set(keyring.Item{
|
||||
Key: "anilistTokenType",
|
||||
Data: []byte(jwt.TokenType),
|
||||
})
|
||||
_ = ring.Set(keyring.Item{
|
||||
Key: "anilistTokenExpiresIn",
|
||||
Data: []byte(string(jwt.ExpiresIn)),
|
||||
})
|
||||
_ = ring.Set(keyring.Item{
|
||||
Key: "anilistAccessToken",
|
||||
Data: []byte(jwt.AccessToken),
|
||||
})
|
||||
_ = ring.Set(keyring.Item{
|
||||
Key: "anilistRefreshToken",
|
||||
Data: []byte(jwt.RefreshToken),
|
||||
})
|
||||
fmt.Println("Shutting down...")
|
||||
cancel()
|
||||
err := srv.Shutdown(context.Background())
|
||||
if err != nil {
|
||||
log.Println("server.Shutdown:", err)
|
||||
}
|
||||
} else {
|
||||
_, err := fmt.Fprintf(w, "Getting code failed.")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("listen: %s\n", err)
|
||||
}
|
||||
fmt.Println("Shutting down...")
|
||||
}()
|
||||
}
|
||||
|
||||
func getAniListAuthorizationToken(content string) AniListJWT {
|
||||
apiUrl := "https://anilist.co/api/v2/oauth/token"
|
||||
resource := "/api/v2/oauth/token"
|
||||
data := url.Values{}
|
||||
data.Set("grant_type", "authorization_code")
|
||||
data.Set("client_id", os.Getenv("ANILIST_APP_ID"))
|
||||
data.Set("client_secret", os.Getenv("ANILIST_SECRET_TOKEN"))
|
||||
data.Set("redirect_uri", os.Getenv("ANILIST_CALLBACK_URI"))
|
||||
data.Set("code", content)
|
||||
|
||||
u, _ := url.ParseRequestURI(apiUrl)
|
||||
u.Path = resource
|
||||
urlStr := u.String()
|
||||
|
||||
response, err := http.NewRequest("POST", urlStr, strings.NewReader(data.Encode()))
|
||||
if err != nil {
|
||||
log.Printf("Failed at response, %s\n", err)
|
||||
}
|
||||
response.Header.Add("content-type", "application/x-www-form-urlencoded")
|
||||
response.Header.Add("Content-Type", "application/json")
|
||||
response.Header.Add("Accept", "application/json")
|
||||
|
||||
client := &http.Client{}
|
||||
res, reserr := client.Do(response)
|
||||
if reserr != nil {
|
||||
log.Printf("Failed at res, %s\n", err)
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
returnedBody, err := io.ReadAll(res.Body)
|
||||
|
||||
var post AniListJWT
|
||||
err = json.Unmarshal(returnedBody, &post)
|
||||
if err != nil {
|
||||
log.Printf("Failed at unmarshal, %s\n", err)
|
||||
}
|
||||
|
||||
return post
|
||||
}
|
||||
|
||||
func (a *App) GetAniListLoggedInUserId() AniListUser {
|
||||
a.AniListLogin()
|
||||
body := struct {
|
||||
Query string `json:"query"`
|
||||
}{
|
||||
Query: `
|
||||
query {
|
||||
Viewer {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`,
|
||||
}
|
||||
|
||||
user, _ := AniListQuery(body, true)
|
||||
|
||||
var post AniListUser
|
||||
err := json.Unmarshal(user, &post)
|
||||
if err != nil {
|
||||
log.Printf("Failed at unmarshal, %s\n", err)
|
||||
}
|
||||
|
||||
return post
|
||||
|
||||
}
|
@ -29,16 +29,6 @@ body:graphql {
|
||||
coverImage {
|
||||
large
|
||||
}
|
||||
tags {
|
||||
id
|
||||
name
|
||||
description
|
||||
category
|
||||
rank
|
||||
isGeneralSpoiler
|
||||
isMediaSpoiler
|
||||
isAdult
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,3 +40,19 @@ body:graphql:vars {
|
||||
"listType": "ANIME"
|
||||
}
|
||||
}
|
||||
|
||||
docs {
|
||||
Title
|
||||
|
||||
Image
|
||||
|
||||
Description
|
||||
|
||||
Episodes
|
||||
|
||||
Status
|
||||
|
||||
Season
|
||||
|
||||
External & Streaming Links
|
||||
}
|
||||
|
103
bruno/AniTrack/AniList MediaList User Query.bru
Normal file
103
bruno/AniTrack/AniList MediaList User Query.bru
Normal file
@ -0,0 +1,103 @@
|
||||
meta {
|
||||
name: AniList MediaList User Query
|
||||
type: graphql
|
||||
seq: 6
|
||||
}
|
||||
|
||||
post {
|
||||
url: https://graphql.anilist.co
|
||||
body: graphql
|
||||
auth: none
|
||||
}
|
||||
|
||||
headers {
|
||||
Content-Type: "application/json"
|
||||
Accept: "application/json"
|
||||
}
|
||||
|
||||
body:graphql {
|
||||
# Write your query or mutation here
|
||||
query(
|
||||
$page: Int
|
||||
$perPage: Int
|
||||
$userId: Int
|
||||
$listType: MediaType
|
||||
$status: MediaListStatus
|
||||
) {
|
||||
Page(page: $page, perPage: $perPage) {
|
||||
pageInfo {
|
||||
total
|
||||
perPage
|
||||
currentPage
|
||||
lastPage
|
||||
hasNextPage
|
||||
}
|
||||
mediaList(userId: $userId, type: $listType, status: $status) {
|
||||
id
|
||||
mediaId
|
||||
userId
|
||||
media {
|
||||
id
|
||||
idMal
|
||||
title {
|
||||
romaji
|
||||
english
|
||||
native
|
||||
}
|
||||
description
|
||||
coverImage {
|
||||
large
|
||||
}
|
||||
season
|
||||
seasonYear
|
||||
episodes
|
||||
}
|
||||
status
|
||||
notes
|
||||
progress
|
||||
score
|
||||
repeat
|
||||
user {
|
||||
id
|
||||
statistics {
|
||||
anime {
|
||||
count
|
||||
statuses {
|
||||
status
|
||||
count
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
body:graphql:vars {
|
||||
{
|
||||
"page": 1,
|
||||
"perPage": 20,
|
||||
"userId": 413504,
|
||||
"listType": "ANIME",
|
||||
"status": "CURRENT"
|
||||
}
|
||||
}
|
||||
|
||||
docs {
|
||||
Title
|
||||
Image
|
||||
Description
|
||||
Episodes
|
||||
Status
|
||||
Season
|
||||
External & Streaming Links
|
||||
|
||||
User Stuff Per Item
|
||||
Status
|
||||
Score
|
||||
Episode Progress
|
||||
Total Rewatches
|
||||
Notes
|
||||
}
|
28
bruno/AniTrack/AniList MediaList User.bru
Normal file
28
bruno/AniTrack/AniList MediaList User.bru
Normal file
@ -0,0 +1,28 @@
|
||||
meta {
|
||||
name: AniList MediaList User
|
||||
type: http
|
||||
seq: 5
|
||||
}
|
||||
|
||||
post {
|
||||
url: https://graphql.anilist.co
|
||||
body: none
|
||||
auth: none
|
||||
}
|
||||
|
||||
docs {
|
||||
Title
|
||||
Image
|
||||
Description
|
||||
Episodes
|
||||
Status
|
||||
Season
|
||||
External & Streaming Links
|
||||
|
||||
User Stuff Per Item
|
||||
Status
|
||||
Score
|
||||
Episode Progress
|
||||
Total Rewatches
|
||||
Notes
|
||||
}
|
@ -12,11 +12,17 @@
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^1.0.1",
|
||||
"@tsconfig/svelte": "^3.0.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"postcss": "^8.4.39",
|
||||
"svelte": "^3.49.0",
|
||||
"svelte-check": "^2.8.0",
|
||||
"svelte-preprocess": "^4.10.7",
|
||||
"tailwindcss": "^3.4.6",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "^4.6.4",
|
||||
"vite": "^3.0.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/aspect-ratio": "^0.4.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
6
frontend/postcss.config.js
Normal file
6
frontend/postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
@ -1,125 +1,134 @@
|
||||
<script lang="ts">
|
||||
import logo from './assets/images/logo-universal.png'
|
||||
import {Greet} from '../wailsjs/go/main/App.js'
|
||||
import {AniListLogin, AniListSearch, GetAniListItem} from "../wailsjs/go/main/App";
|
||||
import type {AniListItem, AniSearchList} from "./AniListTypes";
|
||||
import {
|
||||
AniListSearch,
|
||||
GetAniListItem,
|
||||
GetAniListLoggedInUserId,
|
||||
GetAniListUserWatchingList
|
||||
} from "../wailsjs/go/main/App";
|
||||
import {type AniListItem, type AniSearchList, MediaListSort} from "./anilist/types/AniListTypes";
|
||||
import type {AniListCurrentUserWatchList} from "./anilist/types/AniListCurrentUserWatchListType"
|
||||
|
||||
let resultText: string = "Please enter your name below 👇"
|
||||
let name: string
|
||||
let aniId = "157371"
|
||||
let aniSearch = ""
|
||||
let aniListItem: AniListItem
|
||||
let aniListSearch: AniSearchList
|
||||
let aniListLoggedIn = false
|
||||
let aniId = "157371"
|
||||
let aniSearch = ""
|
||||
let aniListItem: AniListItem
|
||||
let aniListSearch: AniSearchList
|
||||
let aniListLoggedIn = false
|
||||
let aniListWatchlist: AniListCurrentUserWatchList
|
||||
let page = 1
|
||||
let perPage = 20
|
||||
|
||||
function greet(): void {
|
||||
Greet(name).then(result => resultText = result)
|
||||
}
|
||||
function getAniListitem(): void {
|
||||
GetAniListItem(Number(aniId)).then(result => aniListItem = result)
|
||||
}
|
||||
|
||||
function getAniListitem(): void {
|
||||
GetAniListItem(Number(aniId)).then(result => aniListItem = result)
|
||||
}
|
||||
function runAniListSearch(): void {
|
||||
AniListSearch(aniSearch).then(result => aniListSearch = result)
|
||||
}
|
||||
|
||||
function runAniListSearch(): void {
|
||||
AniListSearch(aniSearch).then(result => aniListSearch = result)
|
||||
}
|
||||
|
||||
function loginToAniList(): void {
|
||||
AniListLogin().then(() => aniListLoggedIn = true)
|
||||
}
|
||||
function anilistGetUserWatchlist(): void {
|
||||
GetAniListUserWatchingList(page, perPage, MediaListSort.UpdatedTimeDesc).then((result) => {
|
||||
aniListWatchlist = result
|
||||
aniListLoggedIn = true
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<img alt="Wails logo" id="logo" src="{aniListItem === undefined ? logo : aniListItem.data.Media.coverImage.extraLarge}">
|
||||
<!-- <div class="result" id="result">{resultText}</div>-->
|
||||
<!-- <div class="input-box" id="input">-->
|
||||
<!-- <input autocomplete="off" bind:value={name} class="input" id="name" type="text"/>-->
|
||||
<!-- <button class="btn" on:click={greet}>Greet</button>-->
|
||||
<!-- </div>-->
|
||||
<div class="input-box" id="aniSearch">
|
||||
<input autocomplete="off" bind:value={aniSearch} class="input" id="aniSearchInput" type="text"/>
|
||||
<button class="btn" on:click={runAniListSearch}>Search AniList</button>
|
||||
</div>
|
||||
|
||||
<div class="result" id="AniTest">{aniListItem !== undefined ? aniListItem.data.Media.title.english : ""}</div>
|
||||
<div class="input-box" id="aniButton">
|
||||
<input autocomplete="off" bind:value={aniId} class="input" id="aniId" type="text"/>
|
||||
<button class="btn" on:click={getAniListitem}>Get AniList Item</button>
|
||||
</div>
|
||||
<button class="btn" on:click={anilistGetUserWatchlist}>Login to AniList</button>
|
||||
{#if aniListLoggedIn}
|
||||
<div>You are logged in {aniListWatchlist.data.Page.mediaList[0].user.name}!</div>
|
||||
{/if}
|
||||
|
||||
<button class="btn" on:click={loginToAniList}>Login to AniList</button>
|
||||
{#if aniListLoggedIn}
|
||||
<div>You are logged in!</div>
|
||||
{/if}
|
||||
{#if aniListLoggedIn}
|
||||
<div class="mx-auto max-w-2xl px-4 py-16 sm:px-6 sm:py-24 lg:max-w-7xl lg:px-8">
|
||||
<h1>Your Watching List</h1>
|
||||
|
||||
<div class="input-box" id="aniSearch">
|
||||
<input autocomplete="off" bind:value={aniSearch} class="input" id="aniSearchInput" type="text"/>
|
||||
<button class="btn" on:click={runAniListSearch}>Search AniList</button>
|
||||
</div>
|
||||
{#if aniListSearch !== undefined}
|
||||
<ul>
|
||||
{#each aniListSearch.data.Page.media as media, index (media.id)}
|
||||
<li>
|
||||
<div>{media.title.english !== null ? media.title.english : media.title.romaji}</div>
|
||||
</li>
|
||||
{:else}
|
||||
<div>No Results Yet...</div>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
<div class="grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 xl:gap-x-8">
|
||||
{#each aniListWatchlist.data.Page.mediaList as media}
|
||||
<a href="#" class="group">
|
||||
<!-- <div class="aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-lg bg-gray-200 xl:aspect-h-8 xl:aspect-w-7">-->
|
||||
<div class="justify-center rounded-lg bg-gray-200">
|
||||
<img src={media.media.coverImage.large} alt="anime cover"/>
|
||||
</div>
|
||||
<h3 class="mt-4 text-sm text-white-700">{
|
||||
media.media.title.english === "" ?
|
||||
media.media.title.romaji :
|
||||
media.media.title.english
|
||||
}</h3>
|
||||
<p class="mt-1 text-lg font-medium text-white-900">{media.progress}
|
||||
/ {media.media.nextAiringEpisode.episode !== 0 ?
|
||||
media.media.nextAiringEpisode.episode - 1 : media.media.episodes}</p>
|
||||
{#if media.media.episodes > 0}
|
||||
<p class="mt-1 text-lg font-medium text-white-900">Total
|
||||
Episodes: {media.media.episodes}</p>
|
||||
{/if}
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</main>
|
||||
|
||||
<style>
|
||||
|
||||
#logo {
|
||||
display: block;
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
margin: auto;
|
||||
padding: 10% 0 0;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
background-origin: content-box;
|
||||
}
|
||||
#logo {
|
||||
display: block;
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
margin: auto;
|
||||
padding: 10% 0 0;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
background-origin: content-box;
|
||||
}
|
||||
|
||||
.result {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
margin: 1.5rem auto;
|
||||
}
|
||||
.result {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
margin: 1.5rem auto;
|
||||
}
|
||||
|
||||
.input-box .btn {
|
||||
/*width: 60px;*/
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
margin: 0 0 0 20px;
|
||||
padding: 0 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.input-box .btn {
|
||||
/*width: 60px;*/
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
margin: 0 0 0 20px;
|
||||
padding: 0 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.input-box .btn:hover {
|
||||
background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%);
|
||||
color: #333333;
|
||||
}
|
||||
.input-box .btn:hover {
|
||||
background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%);
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.input-box .input {
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
outline: none;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 0 10px;
|
||||
background-color: rgba(240, 240, 240, 1);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
.input-box .input {
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
outline: none;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 0 10px;
|
||||
background-color: rgba(240, 240, 240, 1);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.input-box .input:hover {
|
||||
border: none;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
.input-box .input:hover {
|
||||
border: none;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.input-box .input:focus {
|
||||
border: none;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
.input-box .input:focus {
|
||||
border: none;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -0,0 +1,88 @@
|
||||
export interface AniListCurrentUserWatchList {
|
||||
data: Data
|
||||
}
|
||||
|
||||
export interface Data {
|
||||
Page: Page
|
||||
}
|
||||
|
||||
export interface Page {
|
||||
pageInfo: PageInfo
|
||||
mediaList: MediaList[]
|
||||
}
|
||||
|
||||
export interface PageInfo {
|
||||
total: number
|
||||
perPage: number
|
||||
currentPage: number
|
||||
lastPage: number
|
||||
hasNextPage: boolean
|
||||
}
|
||||
|
||||
export interface MediaList {
|
||||
id: number
|
||||
mediaId: number
|
||||
userId: number
|
||||
media: Media
|
||||
status: string
|
||||
notes?: string
|
||||
progress: number
|
||||
score: number
|
||||
repeat: number
|
||||
user: User
|
||||
}
|
||||
|
||||
export interface Media {
|
||||
id: number
|
||||
idMal: number
|
||||
title: Title
|
||||
description: string
|
||||
coverImage: CoverImage
|
||||
season: string
|
||||
seasonYear: number
|
||||
status: string
|
||||
episodes?: number
|
||||
nextAiringEpisode?: NextAiringEpisode
|
||||
}
|
||||
|
||||
export interface Title {
|
||||
romaji: string
|
||||
english?: string
|
||||
native: string
|
||||
}
|
||||
|
||||
export interface CoverImage {
|
||||
large: string
|
||||
}
|
||||
|
||||
export interface NextAiringEpisode {
|
||||
airingAt: number
|
||||
timeUntilAiring: number
|
||||
episode: number
|
||||
}
|
||||
|
||||
export interface User {
|
||||
id: number
|
||||
name: string
|
||||
avatar: Avatar
|
||||
statistics: Statistics
|
||||
}
|
||||
|
||||
export interface Avatar {
|
||||
large: string
|
||||
medium: string
|
||||
}
|
||||
|
||||
export interface Statistics {
|
||||
anime: Anime
|
||||
}
|
||||
|
||||
export interface Anime {
|
||||
count: number
|
||||
statuses: Status[]
|
||||
}
|
||||
|
||||
export interface Status {
|
||||
status: string
|
||||
count: number
|
||||
}
|
@ -44,6 +44,7 @@ export interface AniSearchList {
|
||||
title: {
|
||||
romaji: string,
|
||||
english: string,
|
||||
native: string,
|
||||
},
|
||||
coverImage: {
|
||||
extraLarge: string,
|
||||
@ -52,4 +53,46 @@ export interface AniSearchList {
|
||||
}],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export interface AniListUser {
|
||||
"data": {
|
||||
"Viewer": {
|
||||
id: number,
|
||||
name: string,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export enum MediaListSort {
|
||||
MediaId = "MEDIA_ID",
|
||||
MediaIdDesc = "MEDIA_ID_DESC",
|
||||
Score = "SCORE",
|
||||
ScoreDesc = "SCORE_DESC",
|
||||
Status = "STATUS",
|
||||
StatusDesc = "STATUS_DESC",
|
||||
Progress = "PROGRESS",
|
||||
ProgressDesc = "PROGRESS_DESC",
|
||||
ProgressVolumes = "PROGRESS_VOLUMES",
|
||||
ProgressVolumesDesc = "PROGRESS_VOLUMES_DESC",
|
||||
Repeat = "REPEAT",
|
||||
RepeatDesc = "REPEAT_DESC",
|
||||
Priority = "PRIORITY",
|
||||
PriorityDesc = "PRIORITY_DESC",
|
||||
StartedOn = "STARTED_ON",
|
||||
StartedOnDesc = "STARTED_ON_DESC",
|
||||
FinishedOn = "FINISHED_ON",
|
||||
FinishedOnDesc = "FINISHED_ON_DESC",
|
||||
AddedTime = "ADDED_TIME",
|
||||
AddedTimeDesc = "ADDED_TIME_DESC",
|
||||
UpdatedTime = "UPDATED_TIME",
|
||||
UpdatedTimeDesc = "UPDATED_TIME_DESC",
|
||||
MediaTitleRomaji = "MEDIA_TITLE_ROMAJI",
|
||||
MediaTitleRomajiDesc = "MEDIA_TITLE_ROMAJI_DESC",
|
||||
MediaTitleEnglish = "MEDIA_TITLE_ENGLISH",
|
||||
MediaTitleEnglishDesc = "MEDIA_TITLE_ENGLISH_DESC",
|
||||
MediaTitleNative = "MEDIA_TITLE_NATIVE",
|
||||
MediaTitleNativeDesc = "MEDIA_TITLE_NATIVE_DESC",
|
||||
MediaPopularity = "MEDIA_POPULARITY",
|
||||
MediaPopularityDesc = "MEDIA_POPULARITY_DESC"
|
||||
}
|
@ -1,3 +1,7 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
html {
|
||||
background-color: rgba(27, 38, 54, 1);
|
||||
text-align: center;
|
||||
|
14
frontend/tailwind.config.js
Normal file
14
frontend/tailwind.config.js
Normal file
@ -0,0 +1,14 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{svelte,js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [
|
||||
require('@tailwindcss/aspect-ratio'),
|
||||
],
|
||||
}
|
||||
|
5
frontend/wailsjs/go/main/App.d.ts
vendored
5
frontend/wailsjs/go/main/App.d.ts
vendored
@ -1,5 +1,6 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
import {main} from '../models';
|
||||
|
||||
export function AniListLogin():Promise<void>;
|
||||
|
||||
@ -7,4 +8,8 @@ export function AniListSearch(arg1:string):Promise<any>;
|
||||
|
||||
export function GetAniListItem(arg1:number):Promise<any>;
|
||||
|
||||
export function GetAniListLoggedInUserId():Promise<main.AniListUser>;
|
||||
|
||||
export function GetAniListUserWatchingList(arg1:number,arg2:number,arg3:string):Promise<main.AniListCurrentUserWatchList>;
|
||||
|
||||
export function Greet(arg1:string):Promise<string>;
|
||||
|
@ -14,6 +14,14 @@ export function GetAniListItem(arg1) {
|
||||
return window['go']['main']['App']['GetAniListItem'](arg1);
|
||||
}
|
||||
|
||||
export function GetAniListLoggedInUserId() {
|
||||
return window['go']['main']['App']['GetAniListLoggedInUserId']();
|
||||
}
|
||||
|
||||
export function GetAniListUserWatchingList(arg1, arg2, arg3) {
|
||||
return window['go']['main']['App']['GetAniListUserWatchingList'](arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
export function Greet(arg1) {
|
||||
return window['go']['main']['App']['Greet'](arg1);
|
||||
}
|
||||
|
67
frontend/wailsjs/go/models.ts
Executable file
67
frontend/wailsjs/go/models.ts
Executable file
@ -0,0 +1,67 @@
|
||||
export namespace main {
|
||||
|
||||
export class AniListCurrentUserWatchList {
|
||||
// Go type: struct { Page struct { PageInfo struct { Total int "json:\"total\""; PerPage int "json:\"perPage\""; CurrentPage int "json:\"currentPage\""; LastPage int "json:\"lastPage\""; HasNextPage bool "json:\"hasNextPage\"" } "json:\"pageInfo\""; MediaList []struct { ID int "json:\"id\""; MediaID int "json:\"mediaId\""; UserID int "json:\"userId\""; Media struct { ID int "json:\"id\""; IDMal int "json:\"idMal\""; Title struct { Romaji string "json:\"romaji\""; English string "json:\"english\""; Native string "json:\"native\"" } "json:\"title\""; Description string "json:\"description\""; CoverImage struct { Large string "json:\"large\"" } "json:\"coverImage\""; Season string "json:\"season\""; SeasonYear int "json:\"seasonYear\""; Status string "json:\"status\""; Episodes int "json:\"episodes\""; NextAiringEpisode struct { AiringAt int "json:\"airingAt\""; TimeUntilAiring int "json:\"timeUntilAiring\""; Episode int "json:\"episode\"" } "json:\"nextAiringEpisode\"" } "json:\"media\""; Status string "json:\"status\""; Notes string "json:\"notes\""; Progress int "json:\"progress\""; Score int "json:\"score\""; Repeat int "json:\"repeat\""; User struct { ID int "json:\"id\""; Name string "json:\"name\""; Avatar struct { Large string "json:\"large\""; Medium string "json:\"medium\"" } "json:\"avatar\""; Statistics struct { Anime struct { Count int "json:\"count\""; Statuses []struct { Status string "json:\"status\""; Count int "json:\"count\"" } "json:\"statuses\"" } "json:\"anime\"" } "json:\"statistics\"" } "json:\"user\"" } "json:\"mediaList\"" } "json:\"Page\"" }
|
||||
data: any;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new AniListCurrentUserWatchList(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.data = this.convertValues(source["data"], Object);
|
||||
}
|
||||
|
||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||
if (!a) {
|
||||
return a;
|
||||
}
|
||||
if (a.slice && a.map) {
|
||||
return (a as any[]).map(elem => this.convertValues(elem, classs));
|
||||
} else if ("object" === typeof a) {
|
||||
if (asMap) {
|
||||
for (const key of Object.keys(a)) {
|
||||
a[key] = new classs(a[key]);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
return new classs(a);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
export class AniListUser {
|
||||
// Go type: struct { Viewer struct { ID int "json:\"id\""; Name string "json:\"name\"" } "json:\"Viewer\"" }
|
||||
data: any;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new AniListUser(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.data = this.convertValues(source["data"], Object);
|
||||
}
|
||||
|
||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||
if (!a) {
|
||||
return a;
|
||||
}
|
||||
if (a.slice && a.map) {
|
||||
return (a as any[]).map(elem => this.convertValues(elem, classs));
|
||||
} else if ("object" === typeof a) {
|
||||
if (asMap) {
|
||||
for (const key of Object.keys(a)) {
|
||||
a[key] = new classs(a[key]);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
return new classs(a);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
8
go.mod
8
go.mod
@ -5,15 +5,21 @@ go 1.21
|
||||
toolchain go1.21.11
|
||||
|
||||
require (
|
||||
github.com/99designs/keyring v1.2.2
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/wailsapp/wails/v2 v2.9.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
|
||||
github.com/bep/debounce v1.2.1 // indirect
|
||||
github.com/danieljoos/wincred v1.1.2 // indirect
|
||||
github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
|
||||
github.com/labstack/echo/v4 v4.12.0 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
@ -23,6 +29,7 @@ require (
|
||||
github.com/leaanthony/u v1.1.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mtibben/percent v0.2.1 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
@ -35,6 +42,7 @@ require (
|
||||
golang.org/x/crypto v0.25.0 // indirect
|
||||
golang.org/x/net v0.27.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/term v0.22.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
)
|
||||
|
||||
|
31
go.sum
31
go.sum
@ -1,17 +1,33 @@
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=
|
||||
github.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0=
|
||||
github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk=
|
||||
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
|
||||
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
||||
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
|
||||
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM=
|
||||
github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0=
|
||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
|
||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
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/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU=
|
||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0=
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0=
|
||||
github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
|
||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||
@ -34,6 +50,10 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs=
|
||||
github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@ -45,6 +65,10 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/samber/lo v1.46.0 h1:w8G+oaCPgz1PoCJztqymCFaKwXt+5cCXn51uPxExFfQ=
|
||||
github.com/samber/lo v1.46.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As=
|
||||
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/tkrajina/go-reflector v0.5.6 h1:hKQ0gyocG7vgMD2M3dRlYN6WBBOmdoOzJ6njQSepKdE=
|
||||
@ -68,15 +92,22 @@ golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
||||
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
Loading…
Reference in New Issue
Block a user