Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a89ec3652 | ||
|
|
5a14863a8f | ||
|
|
a16c804692 | ||
|
|
d08756c36f | ||
|
|
b42456f255 | ||
|
|
49ac5bef03 | ||
|
|
b9f4c12839 | ||
|
|
54c109ae3b | ||
|
|
d3bc93f6c9 | ||
|
|
0384f570c5 | ||
|
|
6925cd4e08 | ||
|
|
23960e0613 | ||
|
|
669b41c62c | ||
|
|
c54a11e7bd | ||
|
|
b6bdee4df6 | ||
|
|
cbda217d18 | ||
|
|
ceb756a1a8 | ||
|
|
3621b66437 | ||
|
|
7f92b1714e | ||
|
|
cd142f7601 | ||
|
|
fe48e6a8c4 | ||
|
|
24d4d24edf | ||
|
|
b68abfc1c9 | ||
|
|
eadc96fb4f | ||
|
|
6a662cdf6e | ||
|
|
2a9777872f | ||
|
|
b03d4ab7e3 | ||
|
|
ff1b8fb742 | ||
|
|
cbd4c7cd01 | ||
|
|
335d757255 |
+3
-1
@@ -35,4 +35,6 @@ environment.go
|
|||||||
http-client.private.env.json
|
http-client.private.env.json
|
||||||
|
|
||||||
# Build artifacts
|
# Build artifacts
|
||||||
build/*.tar.gz
|
*.tar
|
||||||
|
*.tar.gz
|
||||||
|
/AniTrack
|
||||||
|
|||||||
+604
-39
@@ -77,25 +77,71 @@ func (a *App) GetAniListItem(aniId int, login bool) AniListGetSingleAnime {
|
|||||||
id
|
id
|
||||||
idMal
|
idMal
|
||||||
title {
|
title {
|
||||||
|
userPreferred
|
||||||
romaji
|
romaji
|
||||||
english
|
english
|
||||||
native
|
native
|
||||||
}
|
}
|
||||||
description
|
description
|
||||||
coverImage {
|
coverImage {
|
||||||
|
extraLarge
|
||||||
large
|
large
|
||||||
|
medium
|
||||||
|
color
|
||||||
}
|
}
|
||||||
|
startDate {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
endDate {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
bannerImage
|
||||||
|
format
|
||||||
season
|
season
|
||||||
seasonYear
|
seasonYear
|
||||||
status
|
status
|
||||||
episodes
|
episodes
|
||||||
|
duration
|
||||||
|
countryOfOrigin
|
||||||
|
source
|
||||||
|
synonyms
|
||||||
|
averageScore
|
||||||
|
meanScore
|
||||||
|
popularity
|
||||||
|
trending
|
||||||
|
favourites
|
||||||
|
isFavourite
|
||||||
|
relations {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
title {
|
||||||
|
userPreferred
|
||||||
|
romaji
|
||||||
|
english
|
||||||
|
native
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
nextAiringEpisode {
|
nextAiringEpisode {
|
||||||
airingAt
|
airingAt
|
||||||
timeUntilAiring
|
timeUntilAiring
|
||||||
episode
|
episode
|
||||||
}
|
}
|
||||||
|
airingSchedule {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
airingAt
|
||||||
|
timeUntilAiring
|
||||||
|
episode
|
||||||
|
mediaId
|
||||||
|
}
|
||||||
|
}
|
||||||
genres
|
genres
|
||||||
tags{
|
tags {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
description
|
description
|
||||||
@@ -207,23 +253,69 @@ func (a *App) AniListSearch(query string) (interface{}, error) {
|
|||||||
id
|
id
|
||||||
idMal
|
idMal
|
||||||
title {
|
title {
|
||||||
|
userPreferred
|
||||||
romaji
|
romaji
|
||||||
english
|
english
|
||||||
native
|
native
|
||||||
}
|
}
|
||||||
description
|
description
|
||||||
coverImage {
|
coverImage {
|
||||||
|
extraLarge
|
||||||
large
|
large
|
||||||
|
medium
|
||||||
|
color
|
||||||
}
|
}
|
||||||
|
bannerImage
|
||||||
|
format
|
||||||
season
|
season
|
||||||
seasonYear
|
seasonYear
|
||||||
status
|
status
|
||||||
episodes
|
episodes
|
||||||
|
duration
|
||||||
|
countryOfOrigin
|
||||||
|
source
|
||||||
|
synonyms
|
||||||
|
averageScore
|
||||||
|
meanScore
|
||||||
|
popularity
|
||||||
|
trending
|
||||||
|
favourites
|
||||||
|
isFavourite
|
||||||
|
relations{
|
||||||
|
nodes{
|
||||||
|
id
|
||||||
|
title{
|
||||||
|
userPreferred
|
||||||
|
romaji
|
||||||
|
english
|
||||||
|
native
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startDate{
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
endDate{
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
nextAiringEpisode{
|
nextAiringEpisode{
|
||||||
airingAt
|
airingAt
|
||||||
timeUntilAiring
|
timeUntilAiring
|
||||||
episode
|
episode
|
||||||
}
|
}
|
||||||
|
airingSchedule{
|
||||||
|
nodes{
|
||||||
|
id
|
||||||
|
airingAt
|
||||||
|
timeUntilAiring
|
||||||
|
episode
|
||||||
|
mediaId
|
||||||
|
}
|
||||||
|
}
|
||||||
genres
|
genres
|
||||||
tags{
|
tags{
|
||||||
id
|
id
|
||||||
@@ -235,6 +327,7 @@ func (a *App) AniListSearch(query string) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
isAdult
|
isAdult
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
@@ -271,14 +364,14 @@ func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) (An
|
|||||||
Variables Variables `json:"variables"`
|
Variables Variables `json:"variables"`
|
||||||
}{
|
}{
|
||||||
Query: `
|
Query: `
|
||||||
query(
|
query (
|
||||||
$page: Int
|
$page: Int
|
||||||
$perPage: Int
|
$perPage: Int
|
||||||
$userId: Int
|
$userId: Int
|
||||||
$listType: MediaType
|
$listType: MediaType
|
||||||
$status: MediaListStatus
|
$status: MediaListStatus
|
||||||
$sort:[MediaListSort]
|
$sort: [MediaListSort]
|
||||||
) {
|
) {
|
||||||
Page(page: $page, perPage: $perPage) {
|
Page(page: $page, perPage: $perPage) {
|
||||||
pageInfo {
|
pageInfo {
|
||||||
total
|
total
|
||||||
@@ -295,23 +388,69 @@ func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) (An
|
|||||||
id
|
id
|
||||||
idMal
|
idMal
|
||||||
title {
|
title {
|
||||||
|
userPreferred
|
||||||
romaji
|
romaji
|
||||||
english
|
english
|
||||||
native
|
native
|
||||||
}
|
}
|
||||||
description
|
description
|
||||||
coverImage {
|
coverImage {
|
||||||
|
extraLarge
|
||||||
large
|
large
|
||||||
|
medium
|
||||||
|
color
|
||||||
}
|
}
|
||||||
|
bannerImage
|
||||||
|
format
|
||||||
season
|
season
|
||||||
seasonYear
|
seasonYear
|
||||||
status
|
status
|
||||||
episodes
|
episodes
|
||||||
|
duration
|
||||||
|
countryOfOrigin
|
||||||
|
source
|
||||||
|
synonyms
|
||||||
|
averageScore
|
||||||
|
meanScore
|
||||||
|
popularity
|
||||||
|
trending
|
||||||
|
favourites
|
||||||
|
isFavourite
|
||||||
|
relations{
|
||||||
|
nodes{
|
||||||
|
id
|
||||||
|
title{
|
||||||
|
userPreferred
|
||||||
|
romaji
|
||||||
|
english
|
||||||
|
native
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startDate{
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
endDate{
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
nextAiringEpisode{
|
nextAiringEpisode{
|
||||||
airingAt
|
airingAt
|
||||||
timeUntilAiring
|
timeUntilAiring
|
||||||
episode
|
episode
|
||||||
}
|
}
|
||||||
|
airingSchedule{
|
||||||
|
nodes{
|
||||||
|
id
|
||||||
|
airingAt
|
||||||
|
timeUntilAiring
|
||||||
|
episode
|
||||||
|
mediaId
|
||||||
|
}
|
||||||
|
}
|
||||||
genres
|
genres
|
||||||
tags{
|
tags{
|
||||||
id
|
id
|
||||||
@@ -323,6 +462,7 @@ func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) (An
|
|||||||
}
|
}
|
||||||
isAdult
|
isAdult
|
||||||
}
|
}
|
||||||
|
|
||||||
status
|
status
|
||||||
startedAt {
|
startedAt {
|
||||||
year
|
year
|
||||||
@@ -341,7 +481,7 @@ func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) (An
|
|||||||
user {
|
user {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
avatar{
|
avatar {
|
||||||
large
|
large
|
||||||
medium
|
medium
|
||||||
}
|
}
|
||||||
@@ -357,7 +497,7 @@ func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) (An
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
Variables: Variables{
|
Variables: Variables{
|
||||||
Page: page,
|
Page: page,
|
||||||
@@ -418,32 +558,32 @@ func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) (An
|
|||||||
return post, nil
|
return post, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) AniListGetSingleAnime {
|
func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) (AniListGetSingleAnime, error) {
|
||||||
body := struct {
|
body := struct {
|
||||||
Query string `json:"query"`
|
Query string `json:"query"`
|
||||||
Variables AniListUpdateVariables `json:"variables"`
|
Variables AniListUpdateVariables `json:"variables"`
|
||||||
}{
|
}{
|
||||||
Query: `
|
Query: `
|
||||||
mutation(
|
mutation (
|
||||||
$mediaId:Int,
|
$mediaId: Int
|
||||||
$progress:Int,
|
$progress: Int
|
||||||
$status:MediaListStatus,
|
$status: MediaListStatus
|
||||||
$score:Float,
|
$score: Float
|
||||||
$repeat:Int,
|
$repeat: Int
|
||||||
$notes:String,
|
$notes: String
|
||||||
$startedAt:FuzzyDateInput,
|
$startedAt: FuzzyDateInput
|
||||||
$completedAt:FuzzyDateInput,
|
$completedAt: FuzzyDateInput
|
||||||
){
|
) {
|
||||||
SaveMediaListEntry(
|
SaveMediaListEntry(
|
||||||
mediaId:$mediaId,
|
mediaId: $mediaId
|
||||||
progress:$progress,
|
progress: $progress
|
||||||
status:$status,
|
status: $status
|
||||||
score:$score,
|
score: $score
|
||||||
repeat:$repeat,
|
repeat: $repeat
|
||||||
notes:$notes,
|
notes: $notes
|
||||||
startedAt:$startedAt
|
startedAt: $startedAt
|
||||||
completedAt:$completedAt
|
completedAt: $completedAt
|
||||||
){
|
) {
|
||||||
id
|
id
|
||||||
mediaId
|
mediaId
|
||||||
userId
|
userId
|
||||||
@@ -451,32 +591,87 @@ func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) AniListGetSi
|
|||||||
id
|
id
|
||||||
idMal
|
idMal
|
||||||
title {
|
title {
|
||||||
|
userPreferred
|
||||||
romaji
|
romaji
|
||||||
english
|
english
|
||||||
native
|
native
|
||||||
}
|
}
|
||||||
description
|
description
|
||||||
coverImage {
|
coverImage {
|
||||||
|
extraLarge
|
||||||
large
|
large
|
||||||
|
medium
|
||||||
|
color
|
||||||
}
|
}
|
||||||
|
bannerImage
|
||||||
|
format
|
||||||
season
|
season
|
||||||
seasonYear
|
seasonYear
|
||||||
status
|
status
|
||||||
episodes
|
episodes
|
||||||
|
duration
|
||||||
|
countryOfOrigin
|
||||||
|
source
|
||||||
|
synonyms
|
||||||
|
averageScore
|
||||||
|
meanScore
|
||||||
|
popularity
|
||||||
|
trending
|
||||||
|
favourites
|
||||||
|
isFavourite
|
||||||
|
relations {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
title {
|
||||||
|
userPreferred
|
||||||
|
romaji
|
||||||
|
english
|
||||||
|
native
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startDate {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
endDate {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
nextAiringEpisode {
|
nextAiringEpisode {
|
||||||
airingAt
|
airingAt
|
||||||
timeUntilAiring
|
timeUntilAiring
|
||||||
episode
|
episode
|
||||||
}
|
}
|
||||||
|
airingSchedule {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
airingAt
|
||||||
|
timeUntilAiring
|
||||||
|
episode
|
||||||
|
mediaId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
genres
|
||||||
|
tags {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
description
|
||||||
|
rank
|
||||||
|
isMediaSpoiler
|
||||||
|
isAdult
|
||||||
|
}
|
||||||
isAdult
|
isAdult
|
||||||
}
|
}
|
||||||
status
|
status
|
||||||
startedAt{
|
startedAt {
|
||||||
year
|
year
|
||||||
month
|
month
|
||||||
day
|
day
|
||||||
}
|
}
|
||||||
completedAt{
|
completedAt {
|
||||||
year
|
year
|
||||||
month
|
month
|
||||||
day
|
day
|
||||||
@@ -488,14 +683,14 @@ func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) AniListGetSi
|
|||||||
user {
|
user {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
avatar{
|
avatar {
|
||||||
large
|
large
|
||||||
medium
|
medium
|
||||||
}
|
}
|
||||||
statistics{
|
statistics {
|
||||||
anime{
|
anime {
|
||||||
count
|
count
|
||||||
statuses{
|
statuses {
|
||||||
status
|
status
|
||||||
count
|
count
|
||||||
}
|
}
|
||||||
@@ -503,27 +698,58 @@ func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) AniListGetSi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
Variables: updateBody,
|
Variables: updateBody,
|
||||||
}
|
}
|
||||||
|
|
||||||
returnedBody, _ := AniListQuery(body, true)
|
returnedBody, status := AniListQuery(body, true)
|
||||||
|
|
||||||
|
var badPost struct {
|
||||||
|
Errors []struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
Status int `json:"status"`
|
||||||
|
Locations []struct {
|
||||||
|
Line int `json:"line"`
|
||||||
|
Column int `json:"column"`
|
||||||
|
} `json:"locations"`
|
||||||
|
} `json:"errors"`
|
||||||
|
Data any `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if status == "403 Forbidden" {
|
||||||
|
err := json.Unmarshal(returnedBody, &badPost)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed at unmarshal, %s\n", err)
|
||||||
|
return AniListGetSingleAnime{}, fmt.Errorf("API authentication error")
|
||||||
|
}
|
||||||
|
return AniListGetSingleAnime{}, fmt.Errorf("AniList API error: %s", badPost.Errors[0].Message)
|
||||||
|
}
|
||||||
|
if status != "200 OK" {
|
||||||
|
log.Printf("AniListUpdateEntry failed with status: %s, body: %s\n", status, string(returnedBody))
|
||||||
|
return AniListGetSingleAnime{}, fmt.Errorf("API request failed with status: %s", status)
|
||||||
|
}
|
||||||
|
|
||||||
var returnedJson AniListUpdateReturn
|
var returnedJson AniListUpdateReturn
|
||||||
err := json.Unmarshal(returnedBody, &returnedJson)
|
err := json.Unmarshal(returnedBody, &returnedJson)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at unmarshal, %s\n", err)
|
log.Printf("AniListUpdateEntry failed to unmarshal response: %s, body: %s\n", err, string(returnedBody))
|
||||||
|
return AniListGetSingleAnime{}, fmt.Errorf("failed to parse AniList update response")
|
||||||
|
}
|
||||||
|
|
||||||
|
if returnedJson.Data.SaveMediaListEntry.MediaID == 0 {
|
||||||
|
log.Printf("AniListUpdateEntry returned no saved entry, body: %s\n", string(returnedBody))
|
||||||
|
return AniListGetSingleAnime{}, fmt.Errorf("AniList returned no saved entry")
|
||||||
}
|
}
|
||||||
|
|
||||||
var post AniListGetSingleAnime
|
var post AniListGetSingleAnime
|
||||||
|
|
||||||
post.Data.MediaList = returnedJson.Data.SaveMediaListEntry
|
post.Data.MediaList = returnedJson.Data.SaveMediaListEntry
|
||||||
|
|
||||||
return post
|
return post, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) AniListDeleteEntry(mediaListId int) DeleteAniListReturn {
|
func (a *App) AniListDeleteEntry(mediaListId int) (DeleteAniListReturn, error) {
|
||||||
type Variables = struct {
|
type Variables = struct {
|
||||||
Id int `json:"id"`
|
Id int `json:"id"`
|
||||||
}
|
}
|
||||||
@@ -548,13 +774,352 @@ func (a *App) AniListDeleteEntry(mediaListId int) DeleteAniListReturn {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
returnedBody, _ := AniListQuery(body, true)
|
returnedBody, status := AniListQuery(body, true)
|
||||||
|
|
||||||
|
var badPost struct {
|
||||||
|
Errors []struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
Status int `json:"status"`
|
||||||
|
Locations []struct {
|
||||||
|
Line int `json:"line"`
|
||||||
|
Column int `json:"column"`
|
||||||
|
} `json:"locations"`
|
||||||
|
} `json:"errors"`
|
||||||
|
Data any `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if status == "403 Forbidden" {
|
||||||
|
err := json.Unmarshal(returnedBody, &badPost)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed at unmarshal, %s\n", err)
|
||||||
|
return DeleteAniListReturn{}, fmt.Errorf("API authentication error")
|
||||||
|
}
|
||||||
|
return DeleteAniListReturn{}, fmt.Errorf("AniList API error: %s", badPost.Errors[0].Message)
|
||||||
|
}
|
||||||
|
if status != "200 OK" {
|
||||||
|
log.Printf("AniListDeleteEntry failed with status: %s, body: %s\n", status, string(returnedBody))
|
||||||
|
return DeleteAniListReturn{}, fmt.Errorf("API request failed with status: %s", status)
|
||||||
|
}
|
||||||
|
|
||||||
var post DeleteAniListReturn
|
var post DeleteAniListReturn
|
||||||
err := json.Unmarshal(returnedBody, &post)
|
err := json.Unmarshal(returnedBody, &post)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at unmarshal, %s\n", err)
|
log.Printf("Failed at unmarshal, %s\n", err)
|
||||||
|
return DeleteAniListReturn{}, fmt.Errorf("failed to parse AniList delete response")
|
||||||
}
|
}
|
||||||
|
|
||||||
return post
|
return post, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) AniListBrowse(
|
||||||
|
page int,
|
||||||
|
perPage int,
|
||||||
|
id int,
|
||||||
|
isAdult bool,
|
||||||
|
search string,
|
||||||
|
format []string,
|
||||||
|
status string,
|
||||||
|
countryOfOrigin string,
|
||||||
|
source string,
|
||||||
|
season string,
|
||||||
|
seasonYear int,
|
||||||
|
year string,
|
||||||
|
onList bool,
|
||||||
|
yearLesser int,
|
||||||
|
yearGreater int,
|
||||||
|
episodeLesser int,
|
||||||
|
episodeGreater int,
|
||||||
|
durationLesser int,
|
||||||
|
durationGreater int,
|
||||||
|
chapterLesser int,
|
||||||
|
chapterGreater int,
|
||||||
|
volumeLesser int,
|
||||||
|
volumeGreater int,
|
||||||
|
licensedBy []int,
|
||||||
|
isLicensed bool,
|
||||||
|
genres []string,
|
||||||
|
excludedGenres []string,
|
||||||
|
tags []string,
|
||||||
|
excludedTags []string,
|
||||||
|
minimumTagRank int,
|
||||||
|
sort []string) (AniListCurrentUserWatchList, error) {
|
||||||
|
// user := a.GetAniListLoggedInUser()
|
||||||
|
type Variables struct {
|
||||||
|
Page int `json:"page"`
|
||||||
|
PerPage int `json:"perPage"`
|
||||||
|
Id int `json:"id"`
|
||||||
|
IsAdult bool `json:"isAdult"`
|
||||||
|
Search string `json:"search"`
|
||||||
|
Format []string `json:"format"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
CountryOfOrigin string `json:"countryOfOrigin"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
Season string `json:"season"`
|
||||||
|
SeasonYear int `json:"seasonYear"`
|
||||||
|
Year string `json:"year"`
|
||||||
|
OnList bool `json:"onList"`
|
||||||
|
YearLesser int `json:"yearLesser"`
|
||||||
|
YearGreater int `json:"yearGreater"`
|
||||||
|
EpisodeLesser int `json:"episodeLesser"`
|
||||||
|
EpisodeGreater int `json:"episodeGreater"`
|
||||||
|
DurationLesser int `json:"durationLesser"`
|
||||||
|
DurationGreater int `json:"durationGreater"`
|
||||||
|
ChapterLesser int `json:"chapterLesser"`
|
||||||
|
ChapterGreater int `json:"chapterGreater"`
|
||||||
|
VolumeLesser int `json:"volumeLesser"`
|
||||||
|
VolumeGreater int `json:"volumeGreater"`
|
||||||
|
LicensedBy []int `json:"licensedBy"`
|
||||||
|
IsLicensed bool `json:"isLicensed"`
|
||||||
|
Genres []string `json:"genres"`
|
||||||
|
ExcludedGenres []string `json:"excludedGenres"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
ExcludedTags []string `json:"excludedTags"`
|
||||||
|
MinimumTagRank int `json:"minimumTagRank"`
|
||||||
|
Sort []string `json:"sort"`
|
||||||
|
}
|
||||||
|
body := struct {
|
||||||
|
Query string `json:"query"`
|
||||||
|
Variables Variables `json:"variables"`
|
||||||
|
}{
|
||||||
|
Query: `
|
||||||
|
query (
|
||||||
|
$page: Int = 1
|
||||||
|
$perPage: Int = 20
|
||||||
|
$id: Int
|
||||||
|
$isAdult: Boolean = false
|
||||||
|
$search: String
|
||||||
|
$format: [MediaFormat]
|
||||||
|
$status: MediaStatus
|
||||||
|
$countryOfOrigin: CountryCode
|
||||||
|
$source: MediaSource
|
||||||
|
$season: MediaSeason
|
||||||
|
$seasonYear: Int
|
||||||
|
$year: String
|
||||||
|
$onList: Boolean
|
||||||
|
$yearLesser: FuzzyDateInt
|
||||||
|
$yearGreater: FuzzyDateInt
|
||||||
|
$episodeLesser: Int
|
||||||
|
$episodeGreater: Int
|
||||||
|
$durationLesser: Int
|
||||||
|
$durationGreater: Int
|
||||||
|
$chapterLesser: Int
|
||||||
|
$chapterGreater: Int
|
||||||
|
$volumeLesser: Int
|
||||||
|
$volumeGreater: Int
|
||||||
|
$licensedBy: [Int]
|
||||||
|
$isLicensed: Boolean
|
||||||
|
$genres: [String]
|
||||||
|
$excludedGenres: [String]
|
||||||
|
$tags: [String]
|
||||||
|
$excludedTags: [String]
|
||||||
|
$minimumTagRank: Int
|
||||||
|
$sort: [MediaSort] = [POPULARITY_DESC, SCORE_DESC]
|
||||||
|
) {
|
||||||
|
Page(page: $page, perPage: $perPage) {
|
||||||
|
pageInfo {
|
||||||
|
total
|
||||||
|
perPage
|
||||||
|
currentPage
|
||||||
|
lastPage
|
||||||
|
hasNextPage
|
||||||
|
}
|
||||||
|
media(
|
||||||
|
id: $id
|
||||||
|
type: ANIME
|
||||||
|
season: $season
|
||||||
|
format_in: $format
|
||||||
|
status: $status
|
||||||
|
countryOfOrigin: $countryOfOrigin
|
||||||
|
source: $source
|
||||||
|
search: $search
|
||||||
|
onList: $onList
|
||||||
|
seasonYear: $seasonYear
|
||||||
|
startDate_like: $year
|
||||||
|
startDate_lesser: $yearLesser
|
||||||
|
startDate_greater: $yearGreater
|
||||||
|
episodes_lesser: $episodeLesser
|
||||||
|
episodes_greater: $episodeGreater
|
||||||
|
duration_lesser: $durationLesser
|
||||||
|
duration_greater: $durationGreater
|
||||||
|
chapters_lesser: $chapterLesser
|
||||||
|
chapters_greater: $chapterGreater
|
||||||
|
volumes_lesser: $volumeLesser
|
||||||
|
volumes_greater: $volumeGreater
|
||||||
|
licensedById_in: $licensedBy
|
||||||
|
isLicensed: $isLicensed
|
||||||
|
genre_in: $genres
|
||||||
|
genre_not_in: $excludedGenres
|
||||||
|
tag_in: $tags
|
||||||
|
tag_not_in: $excludedTags
|
||||||
|
minimumTagRank: $minimumTagRank
|
||||||
|
sort: $sort
|
||||||
|
isAdult: $isAdult
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
mediaId
|
||||||
|
userId
|
||||||
|
media {
|
||||||
|
id
|
||||||
|
idMal
|
||||||
|
title {
|
||||||
|
userPreferred
|
||||||
|
romaji
|
||||||
|
english
|
||||||
|
native
|
||||||
|
}
|
||||||
|
description
|
||||||
|
coverImage {
|
||||||
|
extraLarge
|
||||||
|
large
|
||||||
|
medium
|
||||||
|
color
|
||||||
|
}
|
||||||
|
bannerImage
|
||||||
|
format
|
||||||
|
season
|
||||||
|
seasonYear
|
||||||
|
status
|
||||||
|
episodes
|
||||||
|
duration
|
||||||
|
countryOfOrigin
|
||||||
|
source
|
||||||
|
synonyms
|
||||||
|
averageScore
|
||||||
|
meanScore
|
||||||
|
popularity
|
||||||
|
trending
|
||||||
|
favourites
|
||||||
|
isFavourite
|
||||||
|
relations {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
title {
|
||||||
|
userPreferred
|
||||||
|
romaji
|
||||||
|
english
|
||||||
|
native
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startDate {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
endDate {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
nextAiringEpisode {
|
||||||
|
airingAt
|
||||||
|
timeUntilAiring
|
||||||
|
episode
|
||||||
|
}
|
||||||
|
airingSchedule {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
airingAt
|
||||||
|
timeUntilAiring
|
||||||
|
episode
|
||||||
|
mediaId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
genres
|
||||||
|
tags {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
description
|
||||||
|
rank
|
||||||
|
isMediaSpoiler
|
||||||
|
isAdult
|
||||||
|
}
|
||||||
|
isAdult
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
Variables: Variables{
|
||||||
|
Page: page,
|
||||||
|
PerPage: perPage,
|
||||||
|
Id: id,
|
||||||
|
IsAdult: isAdult,
|
||||||
|
Search: search,
|
||||||
|
Format: format,
|
||||||
|
Status: status,
|
||||||
|
CountryOfOrigin: countryOfOrigin,
|
||||||
|
Source: source,
|
||||||
|
Season: season,
|
||||||
|
SeasonYear: seasonYear,
|
||||||
|
Year: year,
|
||||||
|
OnList: onList,
|
||||||
|
YearLesser: yearLesser,
|
||||||
|
YearGreater: yearGreater,
|
||||||
|
EpisodeLesser: episodeLesser,
|
||||||
|
EpisodeGreater: episodeGreater,
|
||||||
|
DurationLesser: durationLesser,
|
||||||
|
DurationGreater: durationGreater,
|
||||||
|
ChapterLesser: chapterLesser,
|
||||||
|
ChapterGreater: chapterGreater,
|
||||||
|
VolumeLesser: volumeLesser,
|
||||||
|
VolumeGreater: volumeGreater,
|
||||||
|
LicensedBy: licensedBy,
|
||||||
|
IsLicensed: isLicensed,
|
||||||
|
Genres: genres,
|
||||||
|
ExcludedGenres: excludedGenres,
|
||||||
|
Tags: tags,
|
||||||
|
ExcludedTags: excludedTags,
|
||||||
|
MinimumTagRank: minimumTagRank,
|
||||||
|
Sort: sort,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
returnedBody, status := AniListQuery(body, true)
|
||||||
|
|
||||||
|
var badPost struct {
|
||||||
|
Errors []struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
Status int `json:"status"`
|
||||||
|
Locations []struct {
|
||||||
|
Line int `json:"line"`
|
||||||
|
Column int `json:"column"`
|
||||||
|
} `json:"locations"`
|
||||||
|
} `json:"errors"`
|
||||||
|
Data any `json:"data"`
|
||||||
|
}
|
||||||
|
var post AniListCurrentUserWatchList
|
||||||
|
if status == "200 OK" {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
if status == "403 Forbidden" {
|
||||||
|
err := json.Unmarshal(returnedBody, &badPost)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed at unmarshal, %s\n", err)
|
||||||
|
return post, fmt.Errorf("API authentication error")
|
||||||
|
}
|
||||||
|
return post, fmt.Errorf("AniList API error: %s", badPost.Errors[0].Message)
|
||||||
|
}
|
||||||
|
if status != "200 OK" {
|
||||||
|
return post, fmt.Errorf("API request failed with status: %s", status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return post, nil
|
||||||
}
|
}
|
||||||
|
|||||||
+76
-11
@@ -37,6 +37,20 @@ type AniListCurrentUserWatchList struct {
|
|||||||
} `json:"data"`
|
} `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AniListBrowseList 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"`
|
||||||
|
Media []Media `json:"mediaList"`
|
||||||
|
} `json:"Page"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
type AniListGetSingleAnime struct {
|
type AniListGetSingleAnime struct {
|
||||||
Data struct {
|
Data struct {
|
||||||
MediaList MediaList `json:"MediaList"`
|
MediaList MediaList `json:"MediaList"`
|
||||||
@@ -49,31 +63,42 @@ type AniListUpdateReturn struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type MediaList struct {
|
type Media struct {
|
||||||
ID int `json:"id"`
|
|
||||||
MediaID int `json:"mediaId"`
|
|
||||||
UserID int `json:"userId"`
|
|
||||||
Media struct {
|
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
IDMal int `json:"idMal"`
|
IDMal int `json:"idMal"`
|
||||||
Title struct {
|
Title MediaTitle `json:"title"`
|
||||||
Romaji string `json:"romaji"`
|
|
||||||
English string `json:"english"`
|
|
||||||
Native string `json:"native"`
|
|
||||||
} `json:"title"`
|
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
CoverImage struct {
|
CoverImage struct {
|
||||||
|
ExtraLarge string
|
||||||
Large string `json:"large"`
|
Large string `json:"large"`
|
||||||
|
Medium string
|
||||||
|
Color string
|
||||||
} `json:"coverImage"`
|
} `json:"coverImage"`
|
||||||
|
BannerImage string
|
||||||
|
Format string
|
||||||
Season string `json:"season"`
|
Season string `json:"season"`
|
||||||
SeasonYear int `json:"seasonYear"`
|
SeasonYear int `json:"seasonYear"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Episodes int `json:"episodes"`
|
Episodes int `json:"episodes"`
|
||||||
|
Duration int
|
||||||
|
CountryOfOrigin string
|
||||||
|
Source string
|
||||||
|
Synonyms []string
|
||||||
|
AverageScore int
|
||||||
|
MeanScore int
|
||||||
|
Popularity int
|
||||||
|
Trending int
|
||||||
|
Favourites int
|
||||||
|
isFavourite bool
|
||||||
|
Relations MediaRelations `json:"relations"`
|
||||||
|
StartDate MediaFuzzyDate `json:"startDate"`
|
||||||
|
EndDate MediaFuzzyDate `json:"endDate"`
|
||||||
NextAiringEpisode struct {
|
NextAiringEpisode struct {
|
||||||
AiringAt int `json:"airingAt"`
|
AiringAt int `json:"airingAt"`
|
||||||
TimeUntilAiring int `json:"timeUntilAiring"`
|
TimeUntilAiring int `json:"timeUntilAiring"`
|
||||||
Episode int `json:"episode"`
|
Episode int `json:"episode"`
|
||||||
} `json:"nextAiringEpisode"`
|
} `json:"nextAiringEpisode"`
|
||||||
|
AiringSchedule MediaAiringSchedule `json:"airingSchedule"`
|
||||||
Genres []string `json:"genres"`
|
Genres []string `json:"genres"`
|
||||||
Tags []struct {
|
Tags []struct {
|
||||||
Id int `json:"id"`
|
Id int `json:"id"`
|
||||||
@@ -84,8 +109,48 @@ type MediaList struct {
|
|||||||
IsAdult bool `json:"isAdult"`
|
IsAdult bool `json:"isAdult"`
|
||||||
} `json:"tags"`
|
} `json:"tags"`
|
||||||
IsAdult bool `json:"isAdult"`
|
IsAdult bool `json:"isAdult"`
|
||||||
} `json:"media"`
|
}
|
||||||
|
|
||||||
|
type MediaTitle struct {
|
||||||
|
UserPreferred string `json:"userPreferred"`
|
||||||
|
Romaji string `json:"romaji"`
|
||||||
|
English string `json:"english"`
|
||||||
|
Native string `json:"native"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaRelations struct {
|
||||||
|
Nodes []MediaRelation `json:"nodes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaRelation struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
Title MediaTitle `json:"title"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaFuzzyDate struct {
|
||||||
|
Year int `json:"year"`
|
||||||
|
Month int `json:"month"`
|
||||||
|
Day int `json:"day"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaAiringSchedule struct {
|
||||||
|
Nodes []AiringScheduleNode `json:"nodes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AiringScheduleNode struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
AiringAt int `json:"airingAt"`
|
||||||
|
TimeUntilAiring int `json:"timeUntilAiring"`
|
||||||
|
Episode int `json:"episode"`
|
||||||
|
MediaId int `json:"mediaId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaList struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
MediaID int `json:"mediaId"`
|
||||||
|
UserID int `json:"userId"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
|
Media Media `json:"media"`
|
||||||
StartedAt struct {
|
StartedAt struct {
|
||||||
Year int `json:"year"`
|
Year int `json:"year"`
|
||||||
Month int `json:"month"`
|
Month int `json:"month"`
|
||||||
|
|||||||
@@ -11,6 +11,21 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Unmarshalling accidental numbers received from MAL to strings
|
||||||
|
func (f *FlexString) UnmarshalJSON(data []byte) error {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(data, &s); err == nil {
|
||||||
|
*f = FlexString(s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var n json.Number
|
||||||
|
if err := json.Unmarshal(data, &n); err == nil {
|
||||||
|
*f = FlexString(string(n))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("FlexString: invalid value")
|
||||||
|
}
|
||||||
|
|
||||||
func MALHelper(method string, malUrl string, body url.Values) (json.RawMessage, string, error) {
|
func MALHelper(method string, malUrl string, body url.Values) (json.RawMessage, string, error) {
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
|
|
||||||
|
|||||||
+10
-6
@@ -1,6 +1,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FlexString string
|
||||||
|
|
||||||
type MyAnimeListJWT struct {
|
type MyAnimeListJWT struct {
|
||||||
TokenType string `json:"token_type"`
|
TokenType string `json:"token_type"`
|
||||||
@@ -129,11 +133,11 @@ type MALAnime struct {
|
|||||||
Statistics struct {
|
Statistics struct {
|
||||||
NumListUsers int `json:"num_list_users" ts_type:"numListUsers"`
|
NumListUsers int `json:"num_list_users" ts_type:"numListUsers"`
|
||||||
Status struct {
|
Status struct {
|
||||||
Watching string `json:"watching" ts_type:"watching"`
|
Watching FlexString `json:"watching" ts_type:"string"`
|
||||||
Completed string `json:"completed" ts_type:"completed"`
|
Completed FlexString `json:"completed" ts_type:"string"`
|
||||||
OnHold string `json:"on_hold" ts_type:"onHold"`
|
OnHold FlexString `json:"on_hold" ts_type:"string"`
|
||||||
Dropped string `json:"dropped" ts_type:"dropped"`
|
Dropped FlexString `json:"dropped" ts_type:"string"`
|
||||||
PlanToWatch string `json:"plan_to_watch" ts_type:"planToWatch"`
|
PlanToWatch FlexString `json:"plan_to_watch" ts_type:"string"`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-12
@@ -235,7 +235,7 @@ func getMyAnimeListAuthorizationToken(content string, verifier *CodeVerifier) My
|
|||||||
return post
|
return post
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshMyAnimeListAuthorizationToken() {
|
func refreshMyAnimeListAuthorizationToken() bool {
|
||||||
dataForURLs := struct {
|
dataForURLs := struct {
|
||||||
GrantType string `json:"grant_type"`
|
GrantType string `json:"grant_type"`
|
||||||
RefreshToken string `json:"refresh_token"`
|
RefreshToken string `json:"refresh_token"`
|
||||||
@@ -260,13 +260,15 @@ func refreshMyAnimeListAuthorizationToken() {
|
|||||||
response, err := http.NewRequest("POST", "https://myanimelist.net/v1/oauth2/token", strings.NewReader(data.Encode()))
|
response, err := http.NewRequest("POST", "https://myanimelist.net/v1/oauth2/token", strings.NewReader(data.Encode()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at response, %s\n", err)
|
log.Printf("Failed at response, %s\n", err)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
response.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
response.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
res, resErr := client.Do(response)
|
res, resErr := client.Do(response)
|
||||||
if resErr != nil {
|
if resErr != nil {
|
||||||
log.Printf("Failed at res, %s\n", err)
|
log.Printf("Failed at res, %s\n", resErr)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
@@ -274,13 +276,22 @@ func refreshMyAnimeListAuthorizationToken() {
|
|||||||
returnedBody, err := io.ReadAll(res.Body)
|
returnedBody, err := io.ReadAll(res.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Could not read returned body, %s\n", err)
|
log.Printf("Could not read returned body, %s\n", err)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.Unmarshal(returnedBody, &myAnimeListJwt)
|
var refreshed MyAnimeListJWT
|
||||||
|
err = json.Unmarshal(returnedBody, &refreshed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at unmarshal, %s\n", err)
|
log.Printf("Failed at unmarshal, %s\n", err)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if refreshed.AccessToken == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
myAnimeListJwt = refreshed
|
||||||
|
|
||||||
_ = myAnimeListRing.Set(keyring.Item{
|
_ = myAnimeListRing.Set(keyring.Item{
|
||||||
Key: "MyAnimeListTokenType",
|
Key: "MyAnimeListTokenType",
|
||||||
Data: []byte(myAnimeListJwt.TokenType),
|
Data: []byte(myAnimeListJwt.TokenType),
|
||||||
@@ -297,26 +308,33 @@ func refreshMyAnimeListAuthorizationToken() {
|
|||||||
Key: "MyAnimeListRefreshToken",
|
Key: "MyAnimeListRefreshToken",
|
||||||
Data: []byte(myAnimeListJwt.RefreshToken),
|
Data: []byte(myAnimeListJwt.RefreshToken),
|
||||||
})
|
})
|
||||||
_, err = runtime.MessageDialog(*wailsContext, runtime.MessageDialogOptions{
|
return true
|
||||||
Title: "MyAnimeList Authorization",
|
|
||||||
Message: "It is now safe to close your browser tab",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) GetMyAnimeListLoggedInUser() MyAnimeListUser {
|
func (a *App) GetMyAnimeListLoggedInUser() MyAnimeListUser {
|
||||||
a.MyAnimeListLogin()
|
a.MyAnimeListLogin()
|
||||||
user := createUser()
|
user := createUser()
|
||||||
if user.Name == "" {
|
if user.Name == "" && !a.refreshMyAnimeListAndGetUser(&user) {
|
||||||
refreshMyAnimeListAuthorizationToken()
|
a.LogoutMyAnimeList()
|
||||||
|
a.MyAnimeListLogin()
|
||||||
user = createUser()
|
user = createUser()
|
||||||
}
|
}
|
||||||
|
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) refreshMyAnimeListAndGetUser(user *MyAnimeListUser) bool {
|
||||||
|
if !refreshMyAnimeListAuthorizationToken() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
freshUser := createUser()
|
||||||
|
if freshUser.Name == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
*user = freshUser
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func createUser() MyAnimeListUser {
|
func createUser() MyAnimeListUser {
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
TAGS := webkit2_41
|
||||||
|
|
||||||
|
.PHONY: dev build clean
|
||||||
|
|
||||||
|
dev:
|
||||||
|
wails dev -tags $(TAGS)
|
||||||
|
|
||||||
|
build:
|
||||||
|
wails build -tags $(TAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf build/bin/*
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
# 🎉 AniTrack v0.7.0 Release Notes
|
|
||||||
|
|
||||||
## 🚨 Major Feature: Comprehensive Error Handling
|
|
||||||
|
|
||||||
This release brings **massive improvements** to application stability and user experience. The app will **no longer crash** when API services are down or experiencing errors!
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✨ What's New
|
|
||||||
|
|
||||||
### 🛡️ Graceful API Error Handling
|
|
||||||
- **Application Stability**: App now remains running even when AniList, MAL, or Simkl APIs are down
|
|
||||||
- **Visual Error Feedback**: Beautiful modal dialogs display error details instead of silent crashes
|
|
||||||
- **Manual Retry**: Users can retry failed API connections with a single button click
|
|
||||||
- **Continue Using App**: Dismiss error messages and continue with limited functionality
|
|
||||||
|
|
||||||
### 🔧 Backend Improvements
|
|
||||||
|
|
||||||
#### AniList Integration
|
|
||||||
- Replaced all `log.Fatal()` calls with proper error returns
|
|
||||||
- Added error handling for API failures, network issues, and authentication errors
|
|
||||||
- `AniListSearch` now returns detailed error messages
|
|
||||||
- `GetAniListUserWatchingList` gracefully handles 403/404 responses
|
|
||||||
|
|
||||||
#### MyAnimeList Integration
|
|
||||||
- Updated `MALHelper` to return structured errors
|
|
||||||
- All MAL API functions now return errors instead of crashing:
|
|
||||||
- `GetMyAnimeList`
|
|
||||||
- `MyAnimeListUpdate`
|
|
||||||
- `GetMyAnimeListAnime`
|
|
||||||
- `DeleteMyAnimeListEntry`
|
|
||||||
- Fixed OAuth callback server to prevent shutdown on errors
|
|
||||||
|
|
||||||
#### Simkl Integration
|
|
||||||
- Updated `SimklHelper` with status code validation (200-299 range)
|
|
||||||
- All Simkl API functions now return errors:
|
|
||||||
- `SimklGetUserWatchlist`
|
|
||||||
- `SimklSyncEpisodes`
|
|
||||||
- `SimklSyncRating`
|
|
||||||
- `SimklSyncStatus`
|
|
||||||
- `SimklSearch`
|
|
||||||
- `SimklSyncRemove`
|
|
||||||
- Replaced `log.Fatal()` with proper error logging
|
|
||||||
|
|
||||||
### 💻 Frontend Improvements
|
|
||||||
|
|
||||||
#### New Error Management System
|
|
||||||
- **Centralized Error State**: New reactive stores for API errors
|
|
||||||
- `apiError` - Tracks current error with service, message, and status
|
|
||||||
- `isApiDown` - Quick flag for API availability
|
|
||||||
- **Error Helper Functions**:
|
|
||||||
- `setApiError()` - Set error state from any component
|
|
||||||
- `clearApiError()` - Reset error state
|
|
||||||
|
|
||||||
#### New Error Modal Component
|
|
||||||
- Auto-displays when API errors occur
|
|
||||||
- Shows service name, error message, and HTTP status code
|
|
||||||
- **"Retry Connection"** button - Attempts to reconnect to the failed service
|
|
||||||
- **"Dismiss"** button - Close modal and continue using the app
|
|
||||||
- Beautiful red-themed styling with Tailwind CSS
|
|
||||||
- Supports all three services (AniList, MAL, Simkl)
|
|
||||||
|
|
||||||
#### Updated User Interface
|
|
||||||
- Home page now displays "API Unavailable" state when services are down
|
|
||||||
- Helpful warning messages guide users during outages
|
|
||||||
- App structure remains visible and functional during errors
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🐛 Bug Fixes
|
|
||||||
|
|
||||||
- **Critical**: Fixed app crashes when AniList API returns 403 Forbidden errors
|
|
||||||
- **Critical**: Fixed app crashes when AniList API is down or unreachable
|
|
||||||
- **Critical**: Fixed MAL OAuth server crashing on connection errors
|
|
||||||
- **Critical**: Fixed Simkl JSON marshaling errors crashing the app
|
|
||||||
- **Improved**: Better error messages for all API failures
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 Technical Details
|
|
||||||
|
|
||||||
### Go Backend Changes
|
|
||||||
- **AniListFunctions.go**: Updated 3 functions with error handling
|
|
||||||
- **MALFunctions.go**: Updated 5 functions with error handling
|
|
||||||
- **MALUserFunctions.go**: Fixed server error handling
|
|
||||||
- **SimklFunctions.go**: Updated 7 functions with error handling
|
|
||||||
- **SimklUserFunctions.go**: Fixed 2 critical error handling issues
|
|
||||||
|
|
||||||
### Frontend Changes
|
|
||||||
- **ErrorModal.svelte**: New component (73 lines)
|
|
||||||
- **GlobalVariablesAndHelperFunctions.svelte**: Added error state system (27 lines)
|
|
||||||
- **App.svelte**: Integrated ErrorModal into main layout
|
|
||||||
- **CheckIfAniListLoggedInAndLoadWatchList.svelte**: Added error handling
|
|
||||||
- **Home.svelte**: Added API down state display
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 Impact
|
|
||||||
|
|
||||||
### Before (v0.6.5)
|
|
||||||
- ❌ App crashes to desktop when AniList API is down
|
|
||||||
- ❌ Users see only console error messages
|
|
||||||
- ❌ Must restart app after API failures
|
|
||||||
- ❌ No way to retry failed connections
|
|
||||||
|
|
||||||
### After (v0.7.0)
|
|
||||||
- ✅ App stays open during any API failures
|
|
||||||
- ✅ Beautiful modal dialogs explain what went wrong
|
|
||||||
- ✅ One-click retry to reconnect services
|
|
||||||
- ✅ Continue using app with limited functionality
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔄 Upgrading
|
|
||||||
|
|
||||||
No migration needed! This release is fully backward compatible.
|
|
||||||
|
|
||||||
Simply run:
|
|
||||||
```bash
|
|
||||||
git pull
|
|
||||||
wails build
|
|
||||||
```
|
|
||||||
|
|
||||||
Or download the latest release from the releases page.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🙏 Acknowledgments
|
|
||||||
|
|
||||||
This release addresses a critical stability issue that was affecting users during API service outages. Special thanks to the community for reporting these crashes and providing detailed error logs.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📝 Full Changelog
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Error modal component with retry/dismiss functionality
|
|
||||||
- Centralized error state management system
|
|
||||||
- API availability status tracking
|
|
||||||
- Visual feedback for all API failures
|
|
||||||
- Manual retry functionality for all services
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- AniList API functions to return errors instead of crashing
|
|
||||||
- MAL API functions to return errors instead of crashing
|
|
||||||
- Simkl API functions to return errors instead of crashing
|
|
||||||
- Error handling from console-only to UI modal dialogs
|
|
||||||
- App behavior from crash-on-error to graceful-degradation
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- App crashes on AniList 403 errors
|
|
||||||
- App crashes on network failures
|
|
||||||
- MAL OAuth server shutdown on errors
|
|
||||||
- Simkl JSON marshaling crashes
|
|
||||||
- All `log.Fatal()` calls that terminated the app
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
- `alert()` calls in favor of modal system
|
|
||||||
- Vite timestamp file (cleanup)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Version**: 0.7.0
|
|
||||||
**Release Date**: March 2026
|
|
||||||
**Upgrade Difficulty**: ⭐ Easy (backward compatible)
|
|
||||||
**Recommended**: ⭐⭐⭐⭐⭐ Highly recommended for all users
|
|
||||||
@@ -16,7 +16,6 @@ headers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body:graphql {
|
body:graphql {
|
||||||
# Write your query or mutation here
|
|
||||||
query (
|
query (
|
||||||
$page: Int
|
$page: Int
|
||||||
$perPage: Int
|
$perPage: Int
|
||||||
@@ -41,23 +40,79 @@ body:graphql {
|
|||||||
id
|
id
|
||||||
idMal
|
idMal
|
||||||
title {
|
title {
|
||||||
|
userPreferred
|
||||||
romaji
|
romaji
|
||||||
english
|
english
|
||||||
native
|
native
|
||||||
}
|
}
|
||||||
description
|
description
|
||||||
coverImage {
|
coverImage {
|
||||||
|
extraLarge
|
||||||
large
|
large
|
||||||
|
medium
|
||||||
|
color
|
||||||
}
|
}
|
||||||
|
bannerImage
|
||||||
|
format
|
||||||
season
|
season
|
||||||
seasonYear
|
seasonYear
|
||||||
status
|
status
|
||||||
episodes
|
episodes
|
||||||
|
duration
|
||||||
|
countryOfOrigin
|
||||||
|
source
|
||||||
|
synonyms
|
||||||
|
averageScore
|
||||||
|
meanScore
|
||||||
|
popularity
|
||||||
|
trending
|
||||||
|
favourites
|
||||||
|
isFavourite
|
||||||
|
relations {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
title {
|
||||||
|
userPreferred
|
||||||
|
romaji
|
||||||
|
english
|
||||||
|
native
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startDate {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
endDate {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
nextAiringEpisode {
|
nextAiringEpisode {
|
||||||
airingAt
|
airingAt
|
||||||
timeUntilAiring
|
timeUntilAiring
|
||||||
episode
|
episode
|
||||||
}
|
}
|
||||||
|
airingSchedule {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
airingAt
|
||||||
|
timeUntilAiring
|
||||||
|
episode
|
||||||
|
mediaId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
genres
|
||||||
|
tags {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
description
|
||||||
|
rank
|
||||||
|
isMediaSpoiler
|
||||||
|
isAdult
|
||||||
|
}
|
||||||
|
isAdult
|
||||||
}
|
}
|
||||||
status
|
status
|
||||||
startedAt {
|
startedAt {
|
||||||
|
|||||||
@@ -17,63 +17,161 @@ headers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body:graphql {
|
body:graphql {
|
||||||
mutation(
|
mutation (
|
||||||
$mediaId:Int,
|
$mediaId: Int
|
||||||
$progress:Int,
|
$progress: Int
|
||||||
$status:MediaListStatus,
|
$status: MediaListStatus
|
||||||
$score:Float,
|
$score: Float
|
||||||
$repeat:Int,
|
$repeat: Int
|
||||||
$notes:String,
|
$notes: String
|
||||||
$startedAt:FuzzyDateInput,
|
$startedAt: FuzzyDateInput
|
||||||
$completedAt:FuzzyDateInput,
|
$completedAt: FuzzyDateInput
|
||||||
){
|
) {
|
||||||
SaveMediaListEntry(
|
SaveMediaListEntry(
|
||||||
mediaId:$mediaId,
|
mediaId: $mediaId
|
||||||
progress:$progress,
|
progress: $progress
|
||||||
status:$status,
|
status: $status
|
||||||
score:$score,
|
score: $score
|
||||||
repeat:$repeat,
|
repeat: $repeat
|
||||||
notes:$notes,
|
notes: $notes
|
||||||
startedAt:$startedAt
|
startedAt: $startedAt
|
||||||
completedAt:$completedAt
|
completedAt: $completedAt
|
||||||
){
|
) {
|
||||||
|
id
|
||||||
mediaId
|
mediaId
|
||||||
progress
|
userId
|
||||||
|
media {
|
||||||
|
id
|
||||||
|
idMal
|
||||||
|
title {
|
||||||
|
userPreferred
|
||||||
|
romaji
|
||||||
|
english
|
||||||
|
native
|
||||||
|
}
|
||||||
|
description
|
||||||
|
coverImage {
|
||||||
|
extraLarge
|
||||||
|
large
|
||||||
|
medium
|
||||||
|
color
|
||||||
|
}
|
||||||
|
bannerImage
|
||||||
|
format
|
||||||
|
season
|
||||||
|
seasonYear
|
||||||
status
|
status
|
||||||
|
episodes
|
||||||
|
duration
|
||||||
|
countryOfOrigin
|
||||||
|
source
|
||||||
|
synonyms
|
||||||
|
averageScore
|
||||||
|
meanScore
|
||||||
|
popularity
|
||||||
|
trending
|
||||||
|
favourites
|
||||||
|
isFavourite
|
||||||
|
relations {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
title {
|
||||||
|
userPreferred
|
||||||
|
romaji
|
||||||
|
english
|
||||||
|
native
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startDate {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
endDate {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
nextAiringEpisode {
|
||||||
|
airingAt
|
||||||
|
timeUntilAiring
|
||||||
|
episode
|
||||||
|
}
|
||||||
|
airingSchedule {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
airingAt
|
||||||
|
timeUntilAiring
|
||||||
|
episode
|
||||||
|
mediaId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
genres
|
||||||
|
tags {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
description
|
||||||
|
rank
|
||||||
|
isMediaSpoiler
|
||||||
|
isAdult
|
||||||
|
}
|
||||||
|
isAdult
|
||||||
|
}
|
||||||
|
status
|
||||||
|
startedAt {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
completedAt {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
notes
|
||||||
|
progress
|
||||||
score
|
score
|
||||||
repeat
|
repeat
|
||||||
notes
|
user {
|
||||||
startedAt{
|
id
|
||||||
year
|
name
|
||||||
month
|
avatar {
|
||||||
day
|
large
|
||||||
|
medium
|
||||||
}
|
}
|
||||||
completedAt{
|
statistics {
|
||||||
year
|
anime {
|
||||||
month
|
count
|
||||||
day
|
statuses {
|
||||||
|
status
|
||||||
|
count
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body:graphql:vars {
|
body:graphql:vars {
|
||||||
{
|
{
|
||||||
"mediaId":170998,
|
"mediaId": 170998,
|
||||||
"progress":5,
|
"progress": 5,
|
||||||
"status":"CURRENT",
|
"status": "CURRENT",
|
||||||
"score":9.0,
|
"score": 9,
|
||||||
"repeat":0,
|
"repeat": 0,
|
||||||
"notes":",malSync::eyJ1IjoiaHR0cHM6Ly93d3cuY3J1bmNoeXJvbGwuY29tL3Nlcmllcy9HVkRIWDg1Wk4vI3NlYXNvbj1HNjNWQzJHUUsiLCJwIjoiIn0=::",
|
"notes": ",malSync::eyJ1IjoiaHR0cHM6Ly93d3cuY3J1bmNoeXJvbGwuY29tL3Nlcmllcy9HVkRIWDg1Wk4vI3NlYXNvbj1HNjNWQzJHUUsiLCJwIjoiIn0=::",
|
||||||
"startedAt":{
|
"startedAt": {
|
||||||
"year":2024,
|
"year": 2024,
|
||||||
"month":7,
|
"month": 7,
|
||||||
"day":10
|
"day": 10
|
||||||
},
|
},
|
||||||
"completedAt":{
|
"completedAt": {
|
||||||
"year": 0,
|
"year": 0,
|
||||||
"month":0,
|
"month": 0,
|
||||||
"day":0
|
"day": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,191 @@
|
|||||||
|
meta {
|
||||||
|
name: SeasonBasedBrowse
|
||||||
|
type: graphql
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: https://graphql.anilist.co
|
||||||
|
body: graphql
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
body:graphql {
|
||||||
|
query (
|
||||||
|
$page: Int = 1
|
||||||
|
$perPage: Int = 20
|
||||||
|
$id: Int
|
||||||
|
$type: MediaType
|
||||||
|
$isAdult: Boolean = false
|
||||||
|
$search: String
|
||||||
|
$format: [MediaFormat]
|
||||||
|
$status: MediaStatus
|
||||||
|
$countryOfOrigin: CountryCode
|
||||||
|
$source: MediaSource
|
||||||
|
$season: MediaSeason
|
||||||
|
$seasonYear: Int
|
||||||
|
$year: String
|
||||||
|
$onList: Boolean
|
||||||
|
$yearLesser: FuzzyDateInt
|
||||||
|
$yearGreater: FuzzyDateInt
|
||||||
|
$episodeLesser: Int
|
||||||
|
$episodeGreater: Int
|
||||||
|
$durationLesser: Int
|
||||||
|
$durationGreater: Int
|
||||||
|
$chapterLesser: Int
|
||||||
|
$chapterGreater: Int
|
||||||
|
$volumeLesser: Int
|
||||||
|
$volumeGreater: Int
|
||||||
|
$licensedBy: [Int]
|
||||||
|
$isLicensed: Boolean
|
||||||
|
$genres: [String]
|
||||||
|
$excludedGenres: [String]
|
||||||
|
$tags: [String]
|
||||||
|
$excludedTags: [String]
|
||||||
|
$minimumTagRank: Int
|
||||||
|
$sort: [MediaSort] = [POPULARITY_DESC, SCORE_DESC]
|
||||||
|
) {
|
||||||
|
Page(page: $page, perPage: $perPage) {
|
||||||
|
pageInfo {
|
||||||
|
total
|
||||||
|
perPage
|
||||||
|
currentPage
|
||||||
|
lastPage
|
||||||
|
hasNextPage
|
||||||
|
}
|
||||||
|
media(
|
||||||
|
id: $id
|
||||||
|
type: $type
|
||||||
|
season: $season
|
||||||
|
format_in: $format
|
||||||
|
status: $status
|
||||||
|
countryOfOrigin: $countryOfOrigin
|
||||||
|
source: $source
|
||||||
|
search: $search
|
||||||
|
onList: $onList
|
||||||
|
seasonYear: $seasonYear
|
||||||
|
startDate_like: $year
|
||||||
|
startDate_lesser: $yearLesser
|
||||||
|
startDate_greater: $yearGreater
|
||||||
|
episodes_lesser: $episodeLesser
|
||||||
|
episodes_greater: $episodeGreater
|
||||||
|
duration_lesser: $durationLesser
|
||||||
|
duration_greater: $durationGreater
|
||||||
|
chapters_lesser: $chapterLesser
|
||||||
|
chapters_greater: $chapterGreater
|
||||||
|
volumes_lesser: $volumeLesser
|
||||||
|
volumes_greater: $volumeGreater
|
||||||
|
licensedById_in: $licensedBy
|
||||||
|
isLicensed: $isLicensed
|
||||||
|
genre_in: $genres
|
||||||
|
genre_not_in: $excludedGenres
|
||||||
|
tag_in: $tags
|
||||||
|
tag_not_in: $excludedTags
|
||||||
|
minimumTagRank: $minimumTagRank
|
||||||
|
sort: $sort
|
||||||
|
isAdult: $isAdult
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
idMal
|
||||||
|
title {
|
||||||
|
userPreferred
|
||||||
|
romaji
|
||||||
|
english
|
||||||
|
native
|
||||||
|
}
|
||||||
|
description
|
||||||
|
coverImage {
|
||||||
|
extraLarge
|
||||||
|
large
|
||||||
|
medium
|
||||||
|
color
|
||||||
|
}
|
||||||
|
startDate {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
endDate {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
bannerImage
|
||||||
|
format
|
||||||
|
season
|
||||||
|
seasonYear
|
||||||
|
status
|
||||||
|
episodes
|
||||||
|
duration
|
||||||
|
countryOfOrigin
|
||||||
|
source
|
||||||
|
synonyms
|
||||||
|
averageScore
|
||||||
|
meanScore
|
||||||
|
popularity
|
||||||
|
trending
|
||||||
|
favourites
|
||||||
|
isFavourite
|
||||||
|
relations {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
title {
|
||||||
|
userPreferred
|
||||||
|
romaji
|
||||||
|
english
|
||||||
|
native
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nextAiringEpisode {
|
||||||
|
airingAt
|
||||||
|
timeUntilAiring
|
||||||
|
episode
|
||||||
|
}
|
||||||
|
airingSchedule {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
airingAt
|
||||||
|
timeUntilAiring
|
||||||
|
episode
|
||||||
|
mediaId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
genres
|
||||||
|
tags {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
description
|
||||||
|
rank
|
||||||
|
isMediaSpoiler
|
||||||
|
isAdult
|
||||||
|
}
|
||||||
|
isAdult
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
body:graphql:vars {
|
||||||
|
{
|
||||||
|
"page": 1,
|
||||||
|
"perPage": 20,
|
||||||
|
"season": "SUMMER",
|
||||||
|
"seasonYear": 2026,
|
||||||
|
"type": "ANIME",
|
||||||
|
"excludedTags": [
|
||||||
|
"Ecchi",
|
||||||
|
"LGBTQ+ Themes",
|
||||||
|
"Yuri"
|
||||||
|
],
|
||||||
|
"excludedGenres": [
|
||||||
|
"Boy's Love"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
settings {
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
meta {
|
||||||
|
name: AniListCalendar
|
||||||
|
seq: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
auth {
|
||||||
|
mode: inherit
|
||||||
|
}
|
||||||
+16
-3
@@ -1,26 +1,39 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# copy desktop file
|
# copy desktop file
|
||||||
if [ -e "~/.local/share/applications/AniTrack.desktop" ]; then
|
if [ ! -f "$HOME/.local/share/applications/AniTrack.desktop" ]; then
|
||||||
if [ -d "~/.local/share/applications/" ]; then
|
if [ -d "~/.local/share/applications/" ]; then
|
||||||
|
echo "Copying desktop file..."
|
||||||
cp ./AniTrack.desktop ~/.local/share/applications/
|
cp ./AniTrack.desktop ~/.local/share/applications/
|
||||||
else
|
else
|
||||||
mkdir -p ~/.local/share/applications/
|
mkdir -p ~/.local/share/applications/
|
||||||
|
echo "Copying desktop file..."
|
||||||
cp ./AniTrack.desktop ~/.local/share/applications/
|
cp ./AniTrack.desktop ~/.local/share/applications/
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
echo "Desktop file already installed..."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# copy icons to xdg folders
|
# copy icons to xdg folders
|
||||||
for size in 32 48 64 128; do
|
for size in 32 48 64 128; do
|
||||||
|
if [ ! -f $HOME/.local/share/icons/hicolor/${size}x${size}/apps/AniTrack.png ]; then
|
||||||
|
echo "Installing ${size} icon size..."
|
||||||
xdg-icon-resource install --novendor --context apps --size $size ./icon/$size/AniTrack.png AniTrack
|
xdg-icon-resource install --novendor --context apps --size $size ./icon/$size/AniTrack.png AniTrack
|
||||||
|
else
|
||||||
|
echo "${size} icon size already exists..."
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# copy AniTrack Binary to $HOME/Applications/
|
# copy AniTrack Binary to $HOME/Applications/
|
||||||
if ! [ -d "~/Applications" ]; then
|
if ! [ -d "$HOME/Applications" ]; then
|
||||||
mkdir -p ~/Applications
|
mkdir -p ~/Applications
|
||||||
|
echo "Installing app to ~/Applications..."
|
||||||
cp ./bin/AniTrack ~/Applications/
|
cp ./bin/AniTrack ~/Applications/
|
||||||
elif ! [[ -e ~/Applications/AniTrack ]]; then
|
elif ! [[ -e $HOME/Applications/AniTrack ]]; then
|
||||||
|
echo "Installing app to ~/Applications"
|
||||||
cp ./bin/AniTrack ~/Applications/
|
cp ./bin/AniTrack ~/Applications/
|
||||||
|
else
|
||||||
|
echo "AniTrack already in Applications..."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "AniTrack has been successfully installed."
|
echo "AniTrack has been successfully installed."
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
simklPrimary,
|
simklPrimary,
|
||||||
malWatchList,
|
malWatchList,
|
||||||
simklWatchList,
|
simklWatchList,
|
||||||
|
serviceLoggingIn,
|
||||||
} from "./helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
} from "./helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import Router from "svelte-spa-router";
|
import Router from "svelte-spa-router";
|
||||||
@@ -35,6 +36,8 @@
|
|||||||
malLoggedIn.subscribe((value) => (isMALLoggedIn = value));
|
malLoggedIn.subscribe((value) => (isMALLoggedIn = value));
|
||||||
simklLoggedIn.subscribe((value) => (isSimklLoggedIn = value));
|
simklLoggedIn.subscribe((value) => (isSimklLoggedIn = value));
|
||||||
|
|
||||||
|
serviceLoggingIn.set(["anilist", "mal", "simkl"]);
|
||||||
|
|
||||||
!isAniListLoggedIn && (await CheckIfAniListLoggedInAndLoadWatchList());
|
!isAniListLoggedIn && (await CheckIfAniListLoggedInAndLoadWatchList());
|
||||||
!isMALLoggedIn && (await CheckIfMALLoggedInAndSetUser());
|
!isMALLoggedIn && (await CheckIfMALLoggedInAndSetUser());
|
||||||
!isSimklLoggedIn && (await CheckIfSimklLoggedInAndSetUser());
|
!isSimklLoggedIn && (await CheckIfSimklLoggedInAndSetUser());
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
aniListLoggedIn,
|
aniListLoggedIn,
|
||||||
malAnime,
|
malAnime,
|
||||||
malLoggedIn,
|
malLoggedIn,
|
||||||
|
setApiError,
|
||||||
simklAnime,
|
simklAnime,
|
||||||
simklLoggedIn,
|
simklLoggedIn,
|
||||||
watchlistNeedsRefresh,
|
watchlistNeedsRefresh,
|
||||||
@@ -207,10 +208,15 @@
|
|||||||
completedAt: convertDateToAniList(completedAtDate),
|
completedAt: convertDateToAniList(completedAtDate),
|
||||||
};
|
};
|
||||||
await AniListUpdateEntry(body).then((value: AniListGetSingleAnime) => {
|
await AniListUpdateEntry(body).then((value: AniListGetSingleAnime) => {
|
||||||
value.data.MediaList.media.tags =
|
if (!value.data?.MediaList || value.data.MediaList.mediaId === 0) {
|
||||||
currentAniListAnime.data.MediaList.media.tags;
|
setApiError(
|
||||||
value.data.MediaList.media.genres =
|
"anilist",
|
||||||
currentAniListAnime.data.MediaList.media.genres;
|
"AniList update failed: no saved entry was returned",
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
aniListAnime.update((newValue) => {
|
aniListAnime.update((newValue) => {
|
||||||
newValue = value;
|
newValue = value;
|
||||||
return newValue;
|
return newValue;
|
||||||
@@ -233,7 +239,18 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||||
|
console.error("Error submitting AniList changes:", error);
|
||||||
|
setApiError(
|
||||||
|
"anilist",
|
||||||
|
`Failed to sync AniList: ${errorMsg}`,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
if (malLoggedIn && currentMalAnime.id !== 0) {
|
if (malLoggedIn && currentMalAnime.id !== 0) {
|
||||||
let body: MALUploadStatus = {
|
let body: MALUploadStatus = {
|
||||||
status: submitData.status.mal,
|
status: submitData.status.mal,
|
||||||
@@ -286,7 +303,18 @@
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||||
|
console.error("Error submitting MyAnimeList changes:", error);
|
||||||
|
setApiError(
|
||||||
|
"mal",
|
||||||
|
`Failed to sync MyAnimeList: ${errorMsg}`,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
if (simklLoggedIn && currentSimklAnime.show.ids.simkl !== 0) {
|
if (simklLoggedIn && currentSimklAnime.show.ids.simkl !== 0) {
|
||||||
if (currentSimklAnime.watched_episodes_count !== submitData.episodes) {
|
if (currentSimklAnime.watched_episodes_count !== submitData.episodes) {
|
||||||
await SimklSyncEpisodes(currentSimklAnime, submitData.episodes).then(
|
await SimklSyncEpisodes(currentSimklAnime, submitData.episodes).then(
|
||||||
@@ -359,13 +387,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error submitting changes:", error);
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||||
} finally {
|
console.error("Error submitting Simkl changes:", error);
|
||||||
|
setApiError(
|
||||||
|
"simkl",
|
||||||
|
`Failed to sync Simkl: ${errorMsg}`,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
submitting.set(false);
|
submitting.set(false);
|
||||||
submitSuccess.set(true);
|
submitSuccess.set(true);
|
||||||
watchlistNeedsRefresh.set(true);
|
watchlistNeedsRefresh.set(true);
|
||||||
setTimeout(() => submitSuccess.set(false), 2000);
|
setTimeout(() => submitSuccess.set(false), 2000);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteEntries = async () => {
|
const deleteEntries = async () => {
|
||||||
@@ -389,6 +423,18 @@
|
|||||||
notes: "",
|
notes: "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||||
|
console.error("Error deleting AniList entry:", error);
|
||||||
|
setApiError(
|
||||||
|
"anilist",
|
||||||
|
`Failed to delete AniList entry: ${errorMsg}`,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
if (malLoggedIn && currentMalAnime.id !== 0) {
|
if (malLoggedIn && currentMalAnime.id !== 0) {
|
||||||
await DeleteMyAnimeListEntry(currentMalAnime.id);
|
await DeleteMyAnimeListEntry(currentMalAnime.id);
|
||||||
AddAnimeServiceToTable({
|
AddAnimeServiceToTable({
|
||||||
@@ -404,6 +450,18 @@
|
|||||||
notes: "",
|
notes: "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||||
|
console.error("Error deleting MyAnimeList entry:", error);
|
||||||
|
setApiError(
|
||||||
|
"mal",
|
||||||
|
`Failed to delete MyAnimeList entry: ${errorMsg}`,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
if (simklLoggedIn && currentSimklAnime.show.ids.simkl !== 0) {
|
if (simklLoggedIn && currentSimklAnime.show.ids.simkl !== 0) {
|
||||||
await SimklSyncRemove(currentSimklAnime);
|
await SimklSyncRemove(currentSimklAnime);
|
||||||
AddAnimeServiceToTable({
|
AddAnimeServiceToTable({
|
||||||
@@ -420,13 +478,19 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error deleting entries:", error);
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||||
} finally {
|
console.error("Error deleting Simkl entry:", error);
|
||||||
|
setApiError(
|
||||||
|
"simkl",
|
||||||
|
`Failed to delete Simkl entry: ${errorMsg}`,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
submitting.set(false);
|
submitting.set(false);
|
||||||
submitSuccess.set(true);
|
submitSuccess.set(true);
|
||||||
watchlistNeedsRefresh.set(true);
|
watchlistNeedsRefresh.set(true);
|
||||||
setTimeout(() => submitSuccess.set(false), 2000);
|
setTimeout(() => submitSuccess.set(false), 2000);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let max = 999;
|
let max = 999;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
logoutOfAniList,
|
logoutOfAniList,
|
||||||
logoutOfMAL,
|
logoutOfMAL,
|
||||||
logoutOfSimkl,
|
logoutOfSimkl,
|
||||||
|
serviceLoggingIn,
|
||||||
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
||||||
import * as runtime from "../../wailsjs/runtime";
|
import * as runtime from "../../wailsjs/runtime";
|
||||||
import type { MyAnimeListUser } from "../mal/types/MALTypes";
|
import type { MyAnimeListUser } from "../mal/types/MALTypes";
|
||||||
@@ -26,6 +27,7 @@
|
|||||||
let isAniListLoggedIn: boolean;
|
let isAniListLoggedIn: boolean;
|
||||||
let isSimklLoggedIn: boolean;
|
let isSimklLoggedIn: boolean;
|
||||||
let isMALLoggedIn: boolean;
|
let isMALLoggedIn: boolean;
|
||||||
|
let loggingIn: string[] = [];
|
||||||
|
|
||||||
aniListUser.subscribe((value) => (currentAniListUser = value));
|
aniListUser.subscribe((value) => (currentAniListUser = value));
|
||||||
malUser.subscribe((value) => (currentMALUser = value));
|
malUser.subscribe((value) => (currentMALUser = value));
|
||||||
@@ -33,6 +35,7 @@
|
|||||||
aniListLoggedIn.subscribe((value) => (isAniListLoggedIn = value));
|
aniListLoggedIn.subscribe((value) => (isAniListLoggedIn = value));
|
||||||
simklLoggedIn.subscribe((value) => (isSimklLoggedIn = value));
|
simklLoggedIn.subscribe((value) => (isSimklLoggedIn = value));
|
||||||
malLoggedIn.subscribe((value) => (isMALLoggedIn = value));
|
malLoggedIn.subscribe((value) => (isMALLoggedIn = value));
|
||||||
|
serviceLoggingIn.subscribe((value) => (loggingIn = value));
|
||||||
|
|
||||||
function dropdownUser(): void {
|
function dropdownUser(): void {
|
||||||
let dropdown = document.querySelector("#userDropdown");
|
let dropdown = document.querySelector("#userDropdown");
|
||||||
@@ -98,6 +101,14 @@
|
|||||||
</li>
|
</li>
|
||||||
{:else}
|
{:else}
|
||||||
<li>
|
<li>
|
||||||
|
{#if loggingIn.includes("anilist")}
|
||||||
|
<span class="flex items-center px-4 py-2 w-full truncate">
|
||||||
|
<span
|
||||||
|
class="inline-block w-4 h-4 mr-4 border-2 border-gray-300 border-t-transparent rounded-full animate-spin"
|
||||||
|
></span>
|
||||||
|
<span class="maple-font text-lg mr-4">A</span>Checking AniList
|
||||||
|
</span>
|
||||||
|
{:else}
|
||||||
<button
|
<button
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
dropdownUser();
|
dropdownUser();
|
||||||
@@ -107,6 +118,7 @@
|
|||||||
>
|
>
|
||||||
<span class="maple-font text-lg mr-4">A</span>Login to AniList
|
<span class="maple-font text-lg mr-4">A</span>Login to AniList
|
||||||
</button>
|
</button>
|
||||||
|
{/if}
|
||||||
</li>
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
{#if isMALLoggedIn}
|
{#if isMALLoggedIn}
|
||||||
@@ -120,6 +132,14 @@
|
|||||||
</li>
|
</li>
|
||||||
{:else}
|
{:else}
|
||||||
<li>
|
<li>
|
||||||
|
{#if loggingIn.includes("mal")}
|
||||||
|
<span class="flex items-center px-4 py-2 w-full truncate">
|
||||||
|
<span
|
||||||
|
class="inline-block w-4 h-4 mr-4 border-2 border-gray-300 border-t-transparent rounded-full animate-spin"
|
||||||
|
></span>
|
||||||
|
<span class="maple-font text-lg mr-4">M</span>Checking MyAnimeList
|
||||||
|
</span>
|
||||||
|
{:else}
|
||||||
<button
|
<button
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
dropdownUser();
|
dropdownUser();
|
||||||
@@ -129,6 +149,7 @@
|
|||||||
>
|
>
|
||||||
<span class="maple-font text-lg mr-4">M</span>Login to MyAnimeList
|
<span class="maple-font text-lg mr-4">M</span>Login to MyAnimeList
|
||||||
</button>
|
</button>
|
||||||
|
{/if}
|
||||||
</li>
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
{#if isSimklLoggedIn}
|
{#if isSimklLoggedIn}
|
||||||
@@ -143,6 +164,14 @@
|
|||||||
</li>
|
</li>
|
||||||
{:else}
|
{:else}
|
||||||
<li>
|
<li>
|
||||||
|
{#if loggingIn.includes("simkl")}
|
||||||
|
<span class="flex items-center px-4 py-2 w-full truncate">
|
||||||
|
<span
|
||||||
|
class="inline-block w-4 h-4 mr-4 border-2 border-gray-300 border-t-transparent rounded-full animate-spin"
|
||||||
|
></span>
|
||||||
|
<span class="maple-font text-lg mr-4">S</span>Checking Simkl
|
||||||
|
</span>
|
||||||
|
{:else}
|
||||||
<button
|
<button
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
dropdownUser();
|
dropdownUser();
|
||||||
@@ -152,6 +181,7 @@
|
|||||||
>
|
>
|
||||||
<span class="maple-font text-lg mr-4">S</span>Login to Simkl
|
<span class="maple-font text-lg mr-4">S</span>Login to Simkl
|
||||||
</button>
|
</button>
|
||||||
|
{/if}
|
||||||
</li>
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
</ul>
|
</ul>
|
||||||
@@ -174,4 +204,3 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
loginToSimkl,
|
loginToSimkl,
|
||||||
malLoggedIn,
|
malLoggedIn,
|
||||||
simklLoggedIn,
|
simklLoggedIn,
|
||||||
|
serviceLoggingIn,
|
||||||
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
||||||
import AvatarMenu from "./AvatarMenu.svelte";
|
import AvatarMenu from "./AvatarMenu.svelte";
|
||||||
import logo from "../assets/images/AniTrackLogo.svg";
|
import logo from "../assets/images/AniTrackLogo.svg";
|
||||||
@@ -15,10 +16,12 @@
|
|||||||
let isAniListLoggedIn: boolean;
|
let isAniListLoggedIn: boolean;
|
||||||
let isSimklLoggedIn: boolean;
|
let isSimklLoggedIn: boolean;
|
||||||
let isMALLoggedIn: boolean;
|
let isMALLoggedIn: boolean;
|
||||||
|
let loggingIn: string[] = [];
|
||||||
|
|
||||||
aniListLoggedIn.subscribe((value) => (isAniListLoggedIn = value));
|
aniListLoggedIn.subscribe((value) => (isAniListLoggedIn = value));
|
||||||
simklLoggedIn.subscribe((value) => (isSimklLoggedIn = value));
|
simklLoggedIn.subscribe((value) => (isSimklLoggedIn = value));
|
||||||
malLoggedIn.subscribe((value) => (isMALLoggedIn = value));
|
malLoggedIn.subscribe((value) => (isMALLoggedIn = value));
|
||||||
|
serviceLoggingIn.subscribe((value) => (loggingIn = value));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav class="border-gray-200 bg-gray-900">
|
<nav class="border-gray-200 bg-gray-900">
|
||||||
@@ -74,23 +77,50 @@
|
|||||||
>
|
>
|
||||||
<li>
|
<li>
|
||||||
{#if !isAniListLoggedIn}
|
{#if !isAniListLoggedIn}
|
||||||
<button on:click={loginToAniList}>
|
<button
|
||||||
<!-- class="block py-2 px-3 w-full min-[950px]:w-auto rounded text-gray-300 min-[950px]:hover:text-blue-500 hover:bg-gray-700 hover:text-white min-[950px]:hover:bg-transparent border-gray-700">-->
|
disabled={loggingIn.includes("anilist")}
|
||||||
|
on:click={loginToAniList}
|
||||||
|
>
|
||||||
|
{#if loggingIn.includes("anilist")}
|
||||||
|
<span
|
||||||
|
class="inline-block w-4 h-4 mr-2 border-2 border-gray-300 border-t-transparent rounded-full animate-spin align-middle"
|
||||||
|
></span
|
||||||
|
>Checking AniList
|
||||||
|
{:else}
|
||||||
AniList Login
|
AniList Login
|
||||||
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
{#if !isMALLoggedIn}
|
{#if !isMALLoggedIn}
|
||||||
<button on:click={loginToMAL}>
|
<button
|
||||||
<!-- class="block py-2 px-3 w-full min-[950px]:w-auto rounded min-[950px]:p-0 text-gray-300 min-[950px]:hover:text-blue-500 hover:bg-gray-700 hover:text-white min-[950px]:hover:bg-transparent border-gray-700">-->
|
disabled={loggingIn.includes("mal")}
|
||||||
|
on:click={loginToMAL}
|
||||||
|
>
|
||||||
|
{#if loggingIn.includes("mal")}
|
||||||
|
<span
|
||||||
|
class="inline-block w-4 h-4 mr-2 border-2 border-gray-300 border-t-transparent rounded-full animate-spin align-middle"
|
||||||
|
></span
|
||||||
|
>Checking MAL
|
||||||
|
{:else}
|
||||||
MyAnimeList Login
|
MyAnimeList Login
|
||||||
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
{#if !isSimklLoggedIn}
|
{#if !isSimklLoggedIn}
|
||||||
<button on:click={loginToSimkl}>
|
<button
|
||||||
<!-- class="block py-2 px-3 w-full min-[950px]:w-auto rounded min-[950px]:p-0 text-gray-300 min-[950px]:hover:text-blue-500 hover:bg-gray-700 hover:text-white min-[950px]:hover:bg-transparent border-gray-700">-->
|
disabled={loggingIn.includes("simkl")}
|
||||||
|
on:click={loginToSimkl}
|
||||||
|
>
|
||||||
|
{#if loggingIn.includes("simkl")}
|
||||||
|
<span
|
||||||
|
class="inline-block w-4 h-4 mr-2 border-2 border-gray-300 border-t-transparent rounded-full animate-spin align-middle"
|
||||||
|
></span
|
||||||
|
>Checking Simkl
|
||||||
|
{:else}
|
||||||
Simkl Login
|
Simkl Login
|
||||||
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -118,7 +118,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="flex mt-5">
|
<div class="flex">
|
||||||
<div class="w-20 mx-auto">
|
<div class="w-20 mx-auto">
|
||||||
<select
|
<select
|
||||||
bind:value={perPage}
|
bind:value={perPage}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { CheckIfAniListLoggedInAndLoadWatchList } from "../helperModules/CheckIfAniListLoggedInAndLoadWatchList.svelte";
|
||||||
|
import { loading } from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="py-2 px-4 mt-4 mr-4 bg-gray-700 rounded-lg"
|
||||||
|
on:click={async () => {
|
||||||
|
loading.set(true);
|
||||||
|
await CheckIfAniListLoggedInAndLoadWatchList();
|
||||||
|
loading.set(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Refresh WatchList
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
aniListSort,
|
||||||
|
watchListPage,
|
||||||
|
animePerPage,
|
||||||
|
aniListWatchlist,
|
||||||
|
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
||||||
|
import { MediaListSort } from "../anilist/types/AniListTypes";
|
||||||
|
import { GetAniListUserWatchingList } from "../../wailsjs/go/main/App";
|
||||||
|
|
||||||
|
const sortTypes = [
|
||||||
|
{ value: MediaListSort.MediaId, name: "Media Id Asc" },
|
||||||
|
{ value: MediaListSort.MediaIdDesc, name: "Media Id Desc" },
|
||||||
|
{ value: MediaListSort.Score, name: "Score Asc" },
|
||||||
|
{ value: MediaListSort.ScoreDesc, name: "Score Desc" },
|
||||||
|
{ value: MediaListSort.Status, name: "Status Asc" },
|
||||||
|
{ value: MediaListSort.StatusDesc, name: "Status Desc" },
|
||||||
|
{ value: MediaListSort.Progress, name: "Progress Asc" },
|
||||||
|
{ value: MediaListSort.ProgressDesc, name: "Progress Desc" },
|
||||||
|
{ value: MediaListSort.ProgressVolumes, name: "Progress Valumes Asc" },
|
||||||
|
{ value: MediaListSort.ProgressVolumesDesc, name: "Progress Valumes Desc" },
|
||||||
|
{ value: MediaListSort.Repeat, name: "Repeat Asc" },
|
||||||
|
{ value: MediaListSort.RepeatDesc, name: "Repeat Desc" },
|
||||||
|
{ value: MediaListSort.Priority, name: "Priority Asc" },
|
||||||
|
{ value: MediaListSort.PriorityDesc, name: "Priority Desc" },
|
||||||
|
{ value: MediaListSort.StartedOn, name: "Started On Asc" },
|
||||||
|
{ value: MediaListSort.StartedOnDesc, name: "Started On Desc" },
|
||||||
|
{ value: MediaListSort.FinishedOn, name: "Finished On Asc" },
|
||||||
|
{ value: MediaListSort.FinishedOnDesc, name: "Finished On Desc" },
|
||||||
|
{ value: MediaListSort.AddedTime, name: "Added Time Asc" },
|
||||||
|
{ value: MediaListSort.AddedTimeDesc, name: "Added Time Desc" },
|
||||||
|
{ value: MediaListSort.UpdatedTime, name: "Updated Time Asc" },
|
||||||
|
{ value: MediaListSort.UpdatedTimeDesc, name: "Updated Time Desc" },
|
||||||
|
{ value: MediaListSort.MediaTitleRomaji, name: "Media Title Romaji Asc" },
|
||||||
|
{
|
||||||
|
value: MediaListSort.MediaTitleRomajiDesc,
|
||||||
|
name: "Media Title Romaji Desc",
|
||||||
|
},
|
||||||
|
{ value: MediaListSort.MediaTitleEnglish, name: "Media Title English Asc" },
|
||||||
|
{
|
||||||
|
value: MediaListSort.MediaTitleEnglishDesc,
|
||||||
|
name: "Media Title English Desc",
|
||||||
|
},
|
||||||
|
{ value: MediaListSort.MediaTitleNative, name: "Media Title Native Asc" },
|
||||||
|
{
|
||||||
|
value: MediaListSort.MediaTitleNativeDesc,
|
||||||
|
name: "Media Title Native Desc",
|
||||||
|
},
|
||||||
|
{ value: MediaListSort.MediaPopularity, name: "Media Popularity Asc" },
|
||||||
|
{ value: MediaListSort.MediaPopularityDesc, name: "Media Popularity Desc" },
|
||||||
|
];
|
||||||
|
|
||||||
|
let sort: string;
|
||||||
|
aniListSort.subscribe((value) => (sort = value));
|
||||||
|
console.log(sort);
|
||||||
|
|
||||||
|
async function changeWatchListSort() {
|
||||||
|
const result = await GetAniListUserWatchingList(
|
||||||
|
$watchListPage,
|
||||||
|
$animePerPage,
|
||||||
|
$aniListSort,
|
||||||
|
);
|
||||||
|
aniListWatchlist.set(result);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<select
|
||||||
|
id="sort"
|
||||||
|
class="border rounded-lg block p-1.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500"
|
||||||
|
bind:value={$aniListSort}
|
||||||
|
on:change={() => changeWatchListSort()}
|
||||||
|
>
|
||||||
|
<option value="" disabled>Sort WatchList</option>
|
||||||
|
{#each sortTypes as sort}
|
||||||
|
<option value={sort.value}>{sort.name}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
import { Rating } from "flowbite-svelte";
|
import { Rating } from "flowbite-svelte";
|
||||||
import loader from "../helperFunctions/loader";
|
import loader from "../helperFunctions/loader";
|
||||||
import { CheckIfAniListLoggedInAndLoadWatchList } from "../helperModules/CheckIfAniListLoggedInAndLoadWatchList.svelte";
|
import { CheckIfAniListLoggedInAndLoadWatchList } from "../helperModules/CheckIfAniListLoggedInAndLoadWatchList.svelte";
|
||||||
|
import Sort from "../helperComponents/Sort.svelte";
|
||||||
|
|
||||||
let isAniListLoggedIn: boolean;
|
let isAniListLoggedIn: boolean;
|
||||||
let aniListWatchListLoaded: AniListCurrentUserWatchList;
|
let aniListWatchListLoaded: AniListCurrentUserWatchList;
|
||||||
@@ -25,17 +26,7 @@
|
|||||||
>
|
>
|
||||||
<div class="flex justify-between items-center mb-4">
|
<div class="flex justify-between items-center mb-4">
|
||||||
<h1 class="text-left text-xl font-bold">Your AniList WatchList</h1>
|
<h1 class="text-left text-xl font-bold">Your AniList WatchList</h1>
|
||||||
<button
|
<Sort />
|
||||||
type="button"
|
|
||||||
class="py-2 px-4 bg-gray-700 rounded-lg"
|
|
||||||
on:click={async () => {
|
|
||||||
loading.set(true);
|
|
||||||
await CheckIfAniListLoggedInAndLoadWatchList();
|
|
||||||
loading.set(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Refresh WatchList
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
aniListSort,
|
aniListSort,
|
||||||
clearApiError,
|
clearApiError,
|
||||||
setApiError,
|
setApiError,
|
||||||
|
serviceLoggingIn,
|
||||||
} from "./GlobalVariablesAndHelperFunctions.svelte";
|
} from "./GlobalVariablesAndHelperFunctions.svelte";
|
||||||
|
|
||||||
let isAniListPrimary: boolean;
|
let isAniListPrimary: boolean;
|
||||||
@@ -60,6 +61,7 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
export const CheckIfAniListLoggedInAndLoadWatchList = async () => {
|
export const CheckIfAniListLoggedInAndLoadWatchList = async () => {
|
||||||
|
serviceLoggingIn.update((s) => [...s, "anilist"]);
|
||||||
try {
|
try {
|
||||||
const loggedIn = await CheckIfAniListLoggedIn();
|
const loggedIn = await CheckIfAniListLoggedIn();
|
||||||
if (loggedIn) {
|
if (loggedIn) {
|
||||||
@@ -76,6 +78,8 @@
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
aniListLoggedIn.set(false);
|
aniListLoggedIn.set(false);
|
||||||
|
} finally {
|
||||||
|
serviceLoggingIn.update((s) => s.filter((item) => item !== "anilist"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
<script lang="ts" context="module">
|
<script lang="ts" context="module">
|
||||||
import {CheckIfMyAnimeListLoggedIn, GetMyAnimeList, GetMyAnimeListLoggedInUser} from "../../wailsjs/go/main/App";
|
import {CheckIfMyAnimeListLoggedIn, GetMyAnimeList, GetMyAnimeListLoggedInUser} from "../../wailsjs/go/main/App";
|
||||||
import {malUser, malPrimary, malWatchList, malLoggedIn} from "./GlobalVariablesAndHelperFunctions.svelte"
|
import {malUser, malPrimary, malWatchList, malLoggedIn, serviceLoggingIn} from "./GlobalVariablesAndHelperFunctions.svelte"
|
||||||
|
import type { MyAnimeListUser } from "../mal/types/MALTypes";
|
||||||
|
|
||||||
let isMalPrimary: boolean
|
let isMalPrimary: boolean
|
||||||
malPrimary.subscribe(value => isMalPrimary = value)
|
malPrimary.subscribe(value => isMalPrimary = value)
|
||||||
|
|
||||||
export const CheckIfMALLoggedInAndSetUser = async () => {
|
export const CheckIfMALLoggedInAndSetUser = async () => {
|
||||||
|
serviceLoggingIn.update((s) => [...s, "mal"])
|
||||||
await CheckIfMyAnimeListLoggedIn().then(loggedIn => {
|
await CheckIfMyAnimeListLoggedIn().then(loggedIn => {
|
||||||
if (loggedIn) {
|
if (loggedIn) {
|
||||||
GetMyAnimeListLoggedInUser().then(user => {
|
GetMyAnimeListLoggedInUser().then(user => {
|
||||||
|
if (!user.name) {
|
||||||
|
malUser.set({} as MyAnimeListUser)
|
||||||
|
malLoggedIn.set(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
malUser.set(user)
|
malUser.set(user)
|
||||||
if (isMalPrimary) {
|
if (isMalPrimary) {
|
||||||
GetMyAnimeList(1000).then(watchList => {
|
GetMyAnimeList(1000).then(watchList => {
|
||||||
@@ -20,6 +27,8 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
serviceLoggingIn.update((s) => s.filter(item => item !== "mal"))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
<script lang="ts" context="module">
|
<script lang="ts" context="module">
|
||||||
import {CheckIfSimklLoggedIn, GetSimklLoggedInUser, SimklGetUserWatchlist} from "../../wailsjs/go/main/App";
|
import {CheckIfSimklLoggedIn, GetSimklLoggedInUser, SimklGetUserWatchlist} from "../../wailsjs/go/main/App";
|
||||||
import { simklLoggedIn, simklUser, simklPrimary, simklWatchList } from "./GlobalVariablesAndHelperFunctions.svelte";
|
import { simklLoggedIn, simklUser, simklPrimary, simklWatchList, serviceLoggingIn } from "./GlobalVariablesAndHelperFunctions.svelte";
|
||||||
|
|
||||||
let isSimklPrimary: boolean
|
let isSimklPrimary: boolean
|
||||||
simklPrimary.subscribe(value => isSimklPrimary = value)
|
simklPrimary.subscribe(value => isSimklPrimary = value)
|
||||||
|
|
||||||
export const CheckIfSimklLoggedInAndSetUser = async () => {
|
export const CheckIfSimklLoggedInAndSetUser = async () => {
|
||||||
|
serviceLoggingIn.update((s) => [...s, "simkl"])
|
||||||
await CheckIfSimklLoggedIn().then(loggedIn => {
|
await CheckIfSimklLoggedIn().then(loggedIn => {
|
||||||
if (loggedIn) {
|
if (loggedIn) {
|
||||||
GetSimklLoggedInUser().then(user => {
|
GetSimklLoggedInUser().then(user => {
|
||||||
@@ -24,6 +25,8 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
serviceLoggingIn.update((s) => s.filter(item => item !== "simkl"))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -39,6 +39,7 @@
|
|||||||
export let aniListLoggedIn = writable(false);
|
export let aniListLoggedIn = writable(false);
|
||||||
export let simklLoggedIn = writable(false);
|
export let simklLoggedIn = writable(false);
|
||||||
export let malLoggedIn = writable(false);
|
export let malLoggedIn = writable(false);
|
||||||
|
export const serviceLoggingIn = writable([] as string[]);
|
||||||
export let simklWatchList = writable({} as SimklWatchList);
|
export let simklWatchList = writable({} as SimklWatchList);
|
||||||
export let aniListPrimary = writable(true);
|
export let aniListPrimary = writable(true);
|
||||||
export let simklPrimary = writable(false);
|
export let simklPrimary = writable(false);
|
||||||
@@ -148,8 +149,27 @@
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setServiceLoggingIn(service: string, isLoggingIn: boolean): void {
|
||||||
|
serviceLoggingIn.update((services) => {
|
||||||
|
if (isLoggingIn) {
|
||||||
|
return services.includes(service) ? services : [...services, service];
|
||||||
|
}
|
||||||
|
return services.filter((s) => s !== service);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isServiceLoggingIn(service: string): boolean {
|
||||||
|
let loggingIn = false;
|
||||||
|
serviceLoggingIn.subscribe((services) => {
|
||||||
|
loggingIn = services.includes(service);
|
||||||
|
})();
|
||||||
|
return loggingIn;
|
||||||
|
}
|
||||||
|
|
||||||
export function loginToSimkl(): void {
|
export function loginToSimkl(): void {
|
||||||
GetSimklLoggedInUser().then((user) => {
|
setServiceLoggingIn("simkl", true);
|
||||||
|
GetSimklLoggedInUser()
|
||||||
|
.then((user) => {
|
||||||
if (Object.keys(user).length === 0) {
|
if (Object.keys(user).length === 0) {
|
||||||
simklLoggedIn.set(false);
|
simklLoggedIn.set(false);
|
||||||
} else {
|
} else {
|
||||||
@@ -159,11 +179,14 @@
|
|||||||
simklLoggedIn.set(true);
|
simklLoggedIn.set(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.finally(() => setServiceLoggingIn("simkl", false));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loginToAniList(): void {
|
export function loginToAniList(): void {
|
||||||
GetAniListLoggedInUser().then((result) => {
|
setServiceLoggingIn("anilist", true);
|
||||||
|
GetAniListLoggedInUser()
|
||||||
|
.then((result) => {
|
||||||
aniListUser.set(result);
|
aniListUser.set(result);
|
||||||
if (isAniListPrimary) {
|
if (isAniListPrimary) {
|
||||||
GetAniListUserWatchingList(page, perPage, sort).then((result) => {
|
GetAniListUserWatchingList(page, perPage, sort).then((result) => {
|
||||||
@@ -173,14 +196,23 @@
|
|||||||
} else {
|
} else {
|
||||||
aniListLoggedIn.set(true);
|
aniListLoggedIn.set(true);
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.finally(() => setServiceLoggingIn("anilist", false));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loginToMAL(): void {
|
export function loginToMAL(): void {
|
||||||
GetMyAnimeListLoggedInUser().then((result) => {
|
setServiceLoggingIn("mal", true);
|
||||||
|
GetMyAnimeListLoggedInUser()
|
||||||
|
.then((result) => {
|
||||||
|
if (!result.name) {
|
||||||
|
malUser.set({} as MyAnimeListUser);
|
||||||
|
malLoggedIn.set(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
malUser.set(result);
|
malUser.set(result);
|
||||||
malLoggedIn.set(true);
|
malLoggedIn.set(true);
|
||||||
});
|
})
|
||||||
|
.finally(() => setServiceLoggingIn("mal", false));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function logoutOfAniList(): void {
|
export function logoutOfAniList(): void {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Pagination from "../helperComponents/Pagination.svelte";
|
import Pagination from "../helperComponents/Pagination.svelte";
|
||||||
import WatchList from "../helperComponents/WatchList.svelte";
|
import WatchList from "../helperComponents/WatchList.svelte";
|
||||||
|
import RefreshWatchListButton from "../helperComponents/RefreshWatchListButton.svelte";
|
||||||
import {
|
import {
|
||||||
aniListLoggedIn,
|
aniListLoggedIn,
|
||||||
aniListPrimary,
|
aniListPrimary,
|
||||||
@@ -45,6 +46,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else if isAniListLoggedIn && isAniListPrimary}
|
{:else if isAniListLoggedIn && isAniListPrimary}
|
||||||
|
<RefreshWatchListButton />
|
||||||
<div class="container py-10">
|
<div class="container py-10">
|
||||||
<Pagination />
|
<Pagination />
|
||||||
<WatchList />
|
<WatchList />
|
||||||
|
|||||||
Vendored
+2
@@ -2,6 +2,8 @@
|
|||||||
// This file is automatically generated. DO NOT EDIT
|
// This file is automatically generated. DO NOT EDIT
|
||||||
import {main} from '../models';
|
import {main} from '../models';
|
||||||
|
|
||||||
|
export function AniListBrowse(arg1:number,arg2:number,arg3:number,arg4:boolean,arg5:string,arg6:Array<string>,arg7:string,arg8:string,arg9:string,arg10:string,arg11:number,arg12:string,arg13:boolean,arg14:number,arg15:number,arg16:number,arg17:number,arg18:number,arg19:number,arg20:number,arg21:number,arg22:number,arg23:number,arg24:Array<number>,arg25:boolean,arg26:Array<string>,arg27:Array<string>,arg28:Array<string>,arg29:Array<string>,arg30:number,arg31:Array<string>):Promise<main.AniListCurrentUserWatchList>;
|
||||||
|
|
||||||
export function AniListDeleteEntry(arg1:number):Promise<main.DeleteAniListReturn>;
|
export function AniListDeleteEntry(arg1:number):Promise<main.DeleteAniListReturn>;
|
||||||
|
|
||||||
export function AniListLogin():Promise<void>;
|
export function AniListLogin():Promise<void>;
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
// This file is automatically generated. DO NOT EDIT
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
export function AniListBrowse(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22, arg23, arg24, arg25, arg26, arg27, arg28, arg29, arg30, arg31) {
|
||||||
|
return window['go']['main']['App']['AniListBrowse'](arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22, arg23, arg24, arg25, arg26, arg27, arg28, arg29, arg30, arg31);
|
||||||
|
}
|
||||||
|
|
||||||
export function AniListDeleteEntry(arg1) {
|
export function AniListDeleteEntry(arg1) {
|
||||||
return window['go']['main']['App']['AniListDeleteEntry'](arg1);
|
return window['go']['main']['App']['AniListDeleteEntry'](arg1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,25 @@
|
|||||||
export namespace main {
|
export namespace main {
|
||||||
|
|
||||||
|
export class AiringScheduleNode {
|
||||||
|
id: number;
|
||||||
|
airingAt: number;
|
||||||
|
timeUntilAiring: number;
|
||||||
|
episode: number;
|
||||||
|
mediaId: number;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new AiringScheduleNode(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.id = source["id"];
|
||||||
|
this.airingAt = source["airingAt"];
|
||||||
|
this.timeUntilAiring = source["timeUntilAiring"];
|
||||||
|
this.episode = source["episode"];
|
||||||
|
this.mediaId = source["mediaId"];
|
||||||
|
}
|
||||||
|
}
|
||||||
export class AniListCurrentUserWatchList {
|
export class AniListCurrentUserWatchList {
|
||||||
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 []main.;
|
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 []main.;
|
||||||
|
|
||||||
@@ -230,8 +250,7 @@ export namespace main {
|
|||||||
background: background;
|
background: background;
|
||||||
related_anime: relatedAnime;
|
related_anime: relatedAnime;
|
||||||
recommendations: recommendations;
|
recommendations: recommendations;
|
||||||
// Go type: struct { NumListUsers int "json:\"num_list_users\" ts_type:\"numListUsers\""; Status struct { Watching string "json:\"watching\" ts_type:\"watching\""; Completed string "json:\"completed\" ts_type:\"completed\""; OnHold string "json:\"on_hold\" ts_type:\"onHold\""; Dropped string "json:\"dropped\" ts_type:\"dropped\""; PlanToWatch string "json:\"plan_to_watch\" ts_type:\"planToWatch\"" } }
|
Statistics: struct { NumListUsers int "json:\"num_list_users\" ts_type:\"numListUsers\""; Status struct { Watching main.;
|
||||||
Statistics: any;
|
|
||||||
|
|
||||||
static createFrom(source: any = {}) {
|
static createFrom(source: any = {}) {
|
||||||
return new MALAnime(source);
|
return new MALAnime(source);
|
||||||
@@ -360,13 +379,248 @@ export namespace main {
|
|||||||
this.updated_at = source["updated_at"];
|
this.updated_at = source["updated_at"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export class {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
rank: number;
|
||||||
|
isMediaSpoiler: boolean;
|
||||||
|
isAdult: boolean;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new (source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.id = source["id"];
|
||||||
|
this.name = source["name"];
|
||||||
|
this.description = source["description"];
|
||||||
|
this.rank = source["rank"];
|
||||||
|
this.isMediaSpoiler = source["isMediaSpoiler"];
|
||||||
|
this.isAdult = source["isAdult"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class MediaAiringSchedule {
|
||||||
|
nodes: AiringScheduleNode[];
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new MediaAiringSchedule(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.nodes = this.convertValues(source["nodes"], AiringScheduleNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 MediaFuzzyDate {
|
||||||
|
year: number;
|
||||||
|
month: number;
|
||||||
|
day: number;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new MediaFuzzyDate(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.year = source["year"];
|
||||||
|
this.month = source["month"];
|
||||||
|
this.day = source["day"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class MediaRelation {
|
||||||
|
id: number;
|
||||||
|
title: MediaTitle;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new MediaRelation(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.id = source["id"];
|
||||||
|
this.title = this.convertValues(source["title"], MediaTitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 MediaRelations {
|
||||||
|
nodes: MediaRelation[];
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new MediaRelations(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.nodes = this.convertValues(source["nodes"], MediaRelation);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 MediaTitle {
|
||||||
|
userPreferred: string;
|
||||||
|
romaji: string;
|
||||||
|
english: string;
|
||||||
|
native: string;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new MediaTitle(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.userPreferred = source["userPreferred"];
|
||||||
|
this.romaji = source["romaji"];
|
||||||
|
this.english = source["english"];
|
||||||
|
this.native = source["native"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class Media {
|
||||||
|
id: number;
|
||||||
|
idMal: number;
|
||||||
|
title: MediaTitle;
|
||||||
|
description: string;
|
||||||
|
// Go type: struct { ExtraLarge string; Large string "json:\"large\""; Medium string; Color string }
|
||||||
|
coverImage: any;
|
||||||
|
BannerImage: string;
|
||||||
|
Format: string;
|
||||||
|
season: string;
|
||||||
|
seasonYear: number;
|
||||||
|
status: string;
|
||||||
|
episodes: number;
|
||||||
|
Duration: number;
|
||||||
|
CountryOfOrigin: string;
|
||||||
|
Source: string;
|
||||||
|
Synonyms: string[];
|
||||||
|
AverageScore: number;
|
||||||
|
MeanScore: number;
|
||||||
|
Popularity: number;
|
||||||
|
Trending: number;
|
||||||
|
Favourites: number;
|
||||||
|
relations: MediaRelations;
|
||||||
|
startDate: MediaFuzzyDate;
|
||||||
|
endDate: MediaFuzzyDate;
|
||||||
|
// Go type: struct { AiringAt int "json:\"airingAt\""; TimeUntilAiring int "json:\"timeUntilAiring\""; Episode int "json:\"episode\"" }
|
||||||
|
nextAiringEpisode: any;
|
||||||
|
airingSchedule: MediaAiringSchedule;
|
||||||
|
genres: string[];
|
||||||
|
tags: [];
|
||||||
|
isAdult: boolean;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new Media(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.id = source["id"];
|
||||||
|
this.idMal = source["idMal"];
|
||||||
|
this.title = this.convertValues(source["title"], MediaTitle);
|
||||||
|
this.description = source["description"];
|
||||||
|
this.coverImage = this.convertValues(source["coverImage"], Object);
|
||||||
|
this.BannerImage = source["BannerImage"];
|
||||||
|
this.Format = source["Format"];
|
||||||
|
this.season = source["season"];
|
||||||
|
this.seasonYear = source["seasonYear"];
|
||||||
|
this.status = source["status"];
|
||||||
|
this.episodes = source["episodes"];
|
||||||
|
this.Duration = source["Duration"];
|
||||||
|
this.CountryOfOrigin = source["CountryOfOrigin"];
|
||||||
|
this.Source = source["Source"];
|
||||||
|
this.Synonyms = source["Synonyms"];
|
||||||
|
this.AverageScore = source["AverageScore"];
|
||||||
|
this.MeanScore = source["MeanScore"];
|
||||||
|
this.Popularity = source["Popularity"];
|
||||||
|
this.Trending = source["Trending"];
|
||||||
|
this.Favourites = source["Favourites"];
|
||||||
|
this.relations = this.convertValues(source["relations"], MediaRelations);
|
||||||
|
this.startDate = this.convertValues(source["startDate"], MediaFuzzyDate);
|
||||||
|
this.endDate = this.convertValues(source["endDate"], MediaFuzzyDate);
|
||||||
|
this.nextAiringEpisode = this.convertValues(source["nextAiringEpisode"], Object);
|
||||||
|
this.airingSchedule = this.convertValues(source["airingSchedule"], MediaAiringSchedule);
|
||||||
|
this.genres = source["genres"];
|
||||||
|
this.tags = this.convertValues(source["tags"], );
|
||||||
|
this.isAdult = source["isAdult"];
|
||||||
|
}
|
||||||
|
|
||||||
|
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 MediaList {
|
export class MediaList {
|
||||||
id: number;
|
id: number;
|
||||||
mediaId: number;
|
mediaId: number;
|
||||||
userId: number;
|
userId: number;
|
||||||
// Go type: 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\""; Genres []string "json:\"genres\""; Tags []struct { Id int "json:\"id\""; Name string "json:\"name\""; Description string "json:\"description\""; Rank int "json:\"rank\""; IsMediaSpoiler bool "json:\"isMediaSpoiler\""; IsAdult bool "json:\"isAdult\"" } "json:\"tags\""; IsAdult bool "json:\"isAdult\"" }
|
|
||||||
media: any;
|
|
||||||
status: string;
|
status: string;
|
||||||
|
media: Media;
|
||||||
// Go type: struct { Year int "json:\"year\""; Month int "json:\"month\""; Day int "json:\"day\"" }
|
// Go type: struct { Year int "json:\"year\""; Month int "json:\"month\""; Day int "json:\"day\"" }
|
||||||
startedAt: any;
|
startedAt: any;
|
||||||
// Go type: struct { Year int "json:\"year\""; Month int "json:\"month\""; Day int "json:\"day\"" }
|
// Go type: struct { Year int "json:\"year\""; Month int "json:\"month\""; Day int "json:\"day\"" }
|
||||||
@@ -387,8 +641,8 @@ export namespace main {
|
|||||||
this.id = source["id"];
|
this.id = source["id"];
|
||||||
this.mediaId = source["mediaId"];
|
this.mediaId = source["mediaId"];
|
||||||
this.userId = source["userId"];
|
this.userId = source["userId"];
|
||||||
this.media = this.convertValues(source["media"], Object);
|
|
||||||
this.status = source["status"];
|
this.status = source["status"];
|
||||||
|
this.media = this.convertValues(source["media"], Media);
|
||||||
this.startedAt = this.convertValues(source["startedAt"], Object);
|
this.startedAt = this.convertValues(source["startedAt"], Object);
|
||||||
this.completedAt = this.convertValues(source["completedAt"], Object);
|
this.completedAt = this.convertValues(source["completedAt"], Object);
|
||||||
this.notes = source["notes"];
|
this.notes = source["notes"];
|
||||||
@@ -416,6 +670,9 @@ export namespace main {
|
|||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export class MyAnimeListUser {
|
export class MyAnimeListUser {
|
||||||
id: id;
|
id: id;
|
||||||
name: name;
|
name: name;
|
||||||
@@ -624,6 +881,43 @@ export namespace struct { Node struct { Id int "json:\"id\" ts_type:\"id\""; Tit
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export namespace struct { NumListUsers int "json:\"num_list_users\" ts_type:\"numListUsers\""; Status struct { Watching main {
|
||||||
|
|
||||||
|
export class {
|
||||||
|
num_list_users: numListUsers;
|
||||||
|
Status: struct { Watching main.;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new (source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.num_list_users = source["num_list_users"];
|
||||||
|
this.Status = this.convertValues(source["Status"], 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 namespace struct { Status string "json:\"status\" ts_type:\"status\""; Score int "json:\"score\" ts_type:\"score\""; NumEpisodesWatched int "json:\"num_episodes_watched\" ts_type:\"numEpisodesWatched\""; IsRewatching bool "json:\"is_rewatching\" ts_type:\"isRewatching\""; UpdatedAt time {
|
export namespace struct { Status string "json:\"status\" ts_type:\"status\""; Score int "json:\"score\" ts_type:\"score\""; NumEpisodesWatched int "json:\"num_episodes_watched\" ts_type:\"numEpisodesWatched\""; IsRewatching bool "json:\"is_rewatching\" ts_type:\"isRewatching\""; UpdatedAt time {
|
||||||
|
|
||||||
export class {
|
export class {
|
||||||
@@ -653,3 +947,28 @@ export namespace struct { Status string "json:\"status\" ts_type:\"status\""; Sc
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export namespace struct { Watching main {
|
||||||
|
|
||||||
|
export class {
|
||||||
|
watching: string;
|
||||||
|
completed: string;
|
||||||
|
on_hold: string;
|
||||||
|
dropped: string;
|
||||||
|
plan_to_watch: string;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new (source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.watching = source["watching"];
|
||||||
|
this.completed = source["completed"];
|
||||||
|
this.on_hold = source["on_hold"];
|
||||||
|
this.dropped = source["dropped"];
|
||||||
|
this.plan_to_watch = source["plan_to_watch"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
+81
@@ -247,3 +247,84 @@ export function CanResolveFilePaths(): boolean;
|
|||||||
|
|
||||||
// Resolves file paths for an array of files
|
// Resolves file paths for an array of files
|
||||||
export function ResolveFilePaths(files: File[]): void
|
export function ResolveFilePaths(files: File[]): void
|
||||||
|
|
||||||
|
// Notification types
|
||||||
|
export interface NotificationOptions {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
subtitle?: string; // macOS and Linux only
|
||||||
|
body?: string;
|
||||||
|
categoryId?: string;
|
||||||
|
data?: { [key: string]: any };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NotificationAction {
|
||||||
|
id?: string;
|
||||||
|
title?: string;
|
||||||
|
destructive?: boolean; // macOS-specific
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NotificationCategory {
|
||||||
|
id?: string;
|
||||||
|
actions?: NotificationAction[];
|
||||||
|
hasReplyField?: boolean;
|
||||||
|
replyPlaceholder?: string;
|
||||||
|
replyButtonTitle?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [InitializeNotifications](https://wails.io/docs/reference/runtime/notification#initializenotifications)
|
||||||
|
// Initializes the notification service for the application.
|
||||||
|
// This must be called before sending any notifications.
|
||||||
|
export function InitializeNotifications(): Promise<void>;
|
||||||
|
|
||||||
|
// [CleanupNotifications](https://wails.io/docs/reference/runtime/notification#cleanupnotifications)
|
||||||
|
// Cleans up notification resources and releases any held connections.
|
||||||
|
export function CleanupNotifications(): Promise<void>;
|
||||||
|
|
||||||
|
// [IsNotificationAvailable](https://wails.io/docs/reference/runtime/notification#isnotificationavailable)
|
||||||
|
// Checks if notifications are available on the current platform.
|
||||||
|
export function IsNotificationAvailable(): Promise<boolean>;
|
||||||
|
|
||||||
|
// [RequestNotificationAuthorization](https://wails.io/docs/reference/runtime/notification#requestnotificationauthorization)
|
||||||
|
// Requests notification authorization from the user (macOS only).
|
||||||
|
export function RequestNotificationAuthorization(): Promise<boolean>;
|
||||||
|
|
||||||
|
// [CheckNotificationAuthorization](https://wails.io/docs/reference/runtime/notification#checknotificationauthorization)
|
||||||
|
// Checks the current notification authorization status (macOS only).
|
||||||
|
export function CheckNotificationAuthorization(): Promise<boolean>;
|
||||||
|
|
||||||
|
// [SendNotification](https://wails.io/docs/reference/runtime/notification#sendnotification)
|
||||||
|
// Sends a basic notification with the given options.
|
||||||
|
export function SendNotification(options: NotificationOptions): Promise<void>;
|
||||||
|
|
||||||
|
// [SendNotificationWithActions](https://wails.io/docs/reference/runtime/notification#sendnotificationwithactions)
|
||||||
|
// Sends a notification with action buttons. Requires a registered category.
|
||||||
|
export function SendNotificationWithActions(options: NotificationOptions): Promise<void>;
|
||||||
|
|
||||||
|
// [RegisterNotificationCategory](https://wails.io/docs/reference/runtime/notification#registernotificationcategory)
|
||||||
|
// Registers a notification category that can be used with SendNotificationWithActions.
|
||||||
|
export function RegisterNotificationCategory(category: NotificationCategory): Promise<void>;
|
||||||
|
|
||||||
|
// [RemoveNotificationCategory](https://wails.io/docs/reference/runtime/notification#removenotificationcategory)
|
||||||
|
// Removes a previously registered notification category.
|
||||||
|
export function RemoveNotificationCategory(categoryId: string): Promise<void>;
|
||||||
|
|
||||||
|
// [RemoveAllPendingNotifications](https://wails.io/docs/reference/runtime/notification#removeallpendingnotifications)
|
||||||
|
// Removes all pending notifications from the notification center.
|
||||||
|
export function RemoveAllPendingNotifications(): Promise<void>;
|
||||||
|
|
||||||
|
// [RemovePendingNotification](https://wails.io/docs/reference/runtime/notification#removependingnotification)
|
||||||
|
// Removes a specific pending notification by its identifier.
|
||||||
|
export function RemovePendingNotification(identifier: string): Promise<void>;
|
||||||
|
|
||||||
|
// [RemoveAllDeliveredNotifications](https://wails.io/docs/reference/runtime/notification#removealldeliverednotifications)
|
||||||
|
// Removes all delivered notifications from the notification center.
|
||||||
|
export function RemoveAllDeliveredNotifications(): Promise<void>;
|
||||||
|
|
||||||
|
// [RemoveDeliveredNotification](https://wails.io/docs/reference/runtime/notification#removedeliverednotification)
|
||||||
|
// Removes a specific delivered notification by its identifier.
|
||||||
|
export function RemoveDeliveredNotification(identifier: string): Promise<void>;
|
||||||
|
|
||||||
|
// [RemoveNotification](https://wails.io/docs/reference/runtime/notification#removenotification)
|
||||||
|
// Removes a notification by its identifier (cross-platform convenience function).
|
||||||
|
export function RemoveNotification(identifier: string): Promise<void>;
|
||||||
@@ -48,6 +48,10 @@ export function EventsOff(eventName, ...additionalEventNames) {
|
|||||||
return window.runtime.EventsOff(eventName, ...additionalEventNames);
|
return window.runtime.EventsOff(eventName, ...additionalEventNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function EventsOffAll() {
|
||||||
|
return window.runtime.EventsOffAll();
|
||||||
|
}
|
||||||
|
|
||||||
export function EventsOnce(eventName, callback) {
|
export function EventsOnce(eventName, callback) {
|
||||||
return EventsOnMultiple(eventName, callback, 1);
|
return EventsOnMultiple(eventName, callback, 1);
|
||||||
}
|
}
|
||||||
@@ -236,3 +240,59 @@ export function CanResolveFilePaths() {
|
|||||||
export function ResolveFilePaths(files) {
|
export function ResolveFilePaths(files) {
|
||||||
return window.runtime.ResolveFilePaths(files);
|
return window.runtime.ResolveFilePaths(files);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function InitializeNotifications() {
|
||||||
|
return window.runtime.InitializeNotifications();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CleanupNotifications() {
|
||||||
|
return window.runtime.CleanupNotifications();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IsNotificationAvailable() {
|
||||||
|
return window.runtime.IsNotificationAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RequestNotificationAuthorization() {
|
||||||
|
return window.runtime.RequestNotificationAuthorization();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CheckNotificationAuthorization() {
|
||||||
|
return window.runtime.CheckNotificationAuthorization();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SendNotification(options) {
|
||||||
|
return window.runtime.SendNotification(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SendNotificationWithActions(options) {
|
||||||
|
return window.runtime.SendNotificationWithActions(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RegisterNotificationCategory(category) {
|
||||||
|
return window.runtime.RegisterNotificationCategory(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RemoveNotificationCategory(categoryId) {
|
||||||
|
return window.runtime.RemoveNotificationCategory(categoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RemoveAllPendingNotifications() {
|
||||||
|
return window.runtime.RemoveAllPendingNotifications();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RemovePendingNotification(identifier) {
|
||||||
|
return window.runtime.RemovePendingNotification(identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RemoveAllDeliveredNotifications() {
|
||||||
|
return window.runtime.RemoveAllDeliveredNotifications();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RemoveDeliveredNotification(identifier) {
|
||||||
|
return window.runtime.RemoveDeliveredNotification(identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RemoveNotification(identifier) {
|
||||||
|
return window.runtime.RemoveNotification(identifier);
|
||||||
|
}
|
||||||
@@ -1,49 +1,51 @@
|
|||||||
module AniTrack
|
module AniTrack
|
||||||
|
|
||||||
go 1.24.0
|
go 1.25.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/99designs/keyring v1.2.2
|
github.com/99designs/keyring v1.2.2
|
||||||
github.com/tidwall/gjson v1.18.0
|
github.com/tidwall/gjson v1.19.0
|
||||||
github.com/wailsapp/wails/v2 v2.10.1
|
github.com/wailsapp/wails/v2 v2.15.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 // indirect
|
||||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
|
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
|
||||||
github.com/bep/debounce v1.2.1 // indirect
|
github.com/bep/debounce v1.2.1 // indirect
|
||||||
github.com/danieljoos/wincred v1.2.2 // indirect
|
github.com/danieljoos/wincred v1.2.3 // indirect
|
||||||
github.com/dvsekhvalnov/jose2go v1.8.0 // indirect
|
github.com/dvsekhvalnov/jose2go v1.8.0 // indirect
|
||||||
github.com/go-ole/go-ole v1.3.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 v0.0.0-20190726142602-4481cbc300e2 // indirect
|
||||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
github.com/godbus/dbus/v5 v5.2.2 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // 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/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 // indirect
|
||||||
github.com/labstack/echo/v4 v4.13.3 // indirect
|
github.com/labstack/echo/v4 v4.15.2 // indirect
|
||||||
github.com/labstack/gommon v0.4.2 // indirect
|
github.com/labstack/gommon v0.5.0 // indirect
|
||||||
github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
|
github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
|
||||||
github.com/leaanthony/gosod v1.0.4 // indirect
|
github.com/leaanthony/gosod v1.0.4 // indirect
|
||||||
github.com/leaanthony/slicer v1.6.0 // indirect
|
github.com/leaanthony/slicer v1.6.0 // indirect
|
||||||
github.com/leaanthony/u v1.1.1 // indirect
|
github.com/leaanthony/u v1.1.1 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.22 // indirect
|
||||||
github.com/mtibben/percent v0.2.1 // indirect
|
github.com/mtibben/percent v0.2.1 // indirect
|
||||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/samber/lo v1.49.1 // indirect
|
github.com/samber/lo v1.53.0 // indirect
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
github.com/tidwall/match v1.2.0 // indirect
|
||||||
github.com/tidwall/pretty v1.2.1 // indirect
|
github.com/tidwall/pretty v1.2.1 // indirect
|
||||||
github.com/tkrajina/go-reflector v0.5.8 // indirect
|
github.com/tkrajina/go-reflector v0.5.8 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||||
github.com/wailsapp/go-webview2 v1.0.19 // indirect
|
github.com/wailsapp/go-webview2 v1.0.23 // indirect
|
||||||
github.com/wailsapp/mimetype v1.4.1 // indirect
|
github.com/wailsapp/mimetype v1.4.1 // indirect
|
||||||
golang.org/x/crypto v0.45.0 // indirect
|
golang.org/x/crypto v0.53.0 // indirect
|
||||||
golang.org/x/net v0.47.0 // indirect
|
golang.org/x/net v0.56.0 // indirect
|
||||||
golang.org/x/sys v0.38.0 // indirect
|
golang.org/x/sys v0.46.0 // indirect
|
||||||
golang.org/x/term v0.37.0 // indirect
|
golang.org/x/term v0.44.0 // indirect
|
||||||
golang.org/x/text v0.31.0 // indirect
|
golang.org/x/text v0.39.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
// replace github.com/wailsapp/wails/v2 v2.9.1 => /home/nymusicman/go/pkg/mod
|
// replace github.com/wailsapp/wails/v2 v2.9.1 => /home/nymusicman/go/pkg/mod
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
|
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 h1:N3IGoHHp9pb6mj1cbXbuaSXV/UMKwmbKLf53nQmtqMA=
|
||||||
|
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3/go.mod h1:QtOLZGz8olr4qH2vWK0QH0w0O4T9fEIjMuWpKUsH7nc=
|
||||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
|
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/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 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0=
|
||||||
github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk=
|
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 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
|
||||||
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
||||||
github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0=
|
github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ=
|
||||||
github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8=
|
github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dvsekhvalnov/jose2go v1.8.0 h1:LqkkVKAlHFfH9LOEl5fe4p/zL02OhWE7pCufMBG2jLA=
|
github.com/dvsekhvalnov/jose2go v1.8.0 h1:LqkkVKAlHFfH9LOEl5fe4p/zL02OhWE7pCufMBG2jLA=
|
||||||
@@ -14,22 +16,23 @@ 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/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 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 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.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ=
|
||||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
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/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU=
|
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/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-20250406163304-c1995be93bd1 h1:njuLRcjAuMKr7kI3D85AXWkw6/+v9PwtV6M6o11sWHQ=
|
||||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
|
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
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/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/labstack/echo/v4 v4.15.2 h1:nnh2sCzGCVYnU+wCisMPiYapEg/QVo/gcI9ePKg5/T4=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/labstack/echo/v4 v4.15.2/go.mod h1:Xzp1Ns1RA2c9fY7nSgUJkpkUZGNbEIVHZbtbOMPktBI=
|
||||||
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
|
github.com/labstack/gommon v0.5.0 h1:6VSQ2NOzsnEJ5W6+84E0RbcaDDmgB6NIAzWCczTEe6c=
|
||||||
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
|
github.com/labstack/gommon v0.5.0/go.mod h1:Rzlg7HHy1maLfzBYGg9NZcVuz1sA68HHhLjhcEllYE0=
|
||||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
|
||||||
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
|
||||||
github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc=
|
github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc=
|
||||||
github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
|
github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
|
||||||
github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A=
|
github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A=
|
||||||
@@ -45,8 +48,8 @@ github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
|
|||||||
github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
||||||
github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs=
|
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/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 h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
@@ -60,17 +63,16 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
|
github.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM=
|
||||||
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
|
github.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU=
|
||||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
github.com/tidwall/gjson v1.19.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc=
|
||||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM=
|
||||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
|
||||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
github.com/tkrajina/go-reflector v0.5.8 h1:yPADHrwmUbMq4RGEyaOUpz2H90sRsETNVpjzo3DLVQQ=
|
github.com/tkrajina/go-reflector v0.5.8 h1:yPADHrwmUbMq4RGEyaOUpz2H90sRsETNVpjzo3DLVQQ=
|
||||||
@@ -79,30 +81,29 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw
|
|||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||||
github.com/wailsapp/go-webview2 v1.0.19 h1:7U3QcDj1PrBPaxJNCui2k1SkWml+Q5kvFUFyTImA6NU=
|
github.com/wailsapp/go-webview2 v1.0.23 h1:jmv8qhz1lHibCc79bMM/a/FqOnnzOGEisLav+a0b9P0=
|
||||||
github.com/wailsapp/go-webview2 v1.0.19/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
|
github.com/wailsapp/go-webview2 v1.0.23/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
|
||||||
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
|
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
|
||||||
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
|
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
|
||||||
github.com/wailsapp/wails/v2 v2.10.1 h1:QWHvWMXII2nI/nXz77gpPG8P3ehl6zKe+u4su5BWIns=
|
github.com/wailsapp/wails/v2 v2.15.0 h1:u7cHK+UesZOlYxyJxfYLteaCPhws6UsZoDdqUejuX6Q=
|
||||||
github.com/wailsapp/wails/v2 v2.10.1/go.mod h1:zrebnFV6MQf9kx8HI4iAv63vsR5v67oS7GTEZ7Pz1TY=
|
github.com/wailsapp/wails/v2 v2.15.0/go.mod h1:scxrgwfsv6yR6fE6cCF+Flfl+JeU+SR87T9x4kILJ6M=
|
||||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||||
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||||
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.1.0/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.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus=
|
||||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
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/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
+2
-2
@@ -8,10 +8,10 @@
|
|||||||
"frontend:dev:serverUrl": "auto",
|
"frontend:dev:serverUrl": "auto",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "John O'Keefe",
|
"name": "John O'Keefe",
|
||||||
"email": "jokeefe@fastmail.com"
|
"email": "admin@linuxhg.com"
|
||||||
},
|
},
|
||||||
"info": {
|
"info": {
|
||||||
"productName": "AniTrack",
|
"productName": "AniTrack",
|
||||||
"productVersion": "0.7.0"
|
"productVersion": "1.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user