Compare commits
11 Commits
1090f112f3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 335d757255 | |||
| a3b4857175 | |||
| 6e4b9c041a | |||
| ae54fd20dd | |||
| 2c7e2d0eff | |||
| 9a9f055c38 | |||
| bc497521e7 | |||
| 48a6005725 | |||
| 93a94d0797 | |||
| a2576b044c | |||
| 2ee2d85e9e |
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -183,7 +184,7 @@ func (a *App) GetAniListItem(aniId int, login bool) AniListGetSingleAnime {
|
|||||||
return post
|
return post
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) AniListSearch(query string) any {
|
func (a *App) AniListSearch(query string) (interface{}, error) {
|
||||||
type Variables struct {
|
type Variables struct {
|
||||||
Search string `json:"search"`
|
Search string `json:"search"`
|
||||||
ListType string `json:"listType"`
|
ListType string `json:"listType"`
|
||||||
@@ -242,18 +243,20 @@ func (a *App) AniListSearch(query string) any {
|
|||||||
ListType: "ANIME",
|
ListType: "ANIME",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
returnedBody, _ := AniListQuery(body, false)
|
returnedBody, status := AniListQuery(body, false)
|
||||||
|
if status != "200 OK" {
|
||||||
|
return nil, fmt.Errorf("API search failed with status: %s", status)
|
||||||
|
}
|
||||||
var post interface{}
|
var post interface{}
|
||||||
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 nil, fmt.Errorf("Failed to parse search results")
|
||||||
}
|
}
|
||||||
|
return post, nil
|
||||||
return post
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) AniListCurrentUserWatchList {
|
func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) (AniListCurrentUserWatchList, error) {
|
||||||
user := a.GetAniListLoggedInUser()
|
user := a.GetAniListLoggedInUser()
|
||||||
type Variables struct {
|
type Variables struct {
|
||||||
Page int `json:"page"`
|
Page int `json:"page"`
|
||||||
@@ -404,11 +407,15 @@ func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) Ani
|
|||||||
err := json.Unmarshal(returnedBody, &badPost)
|
err := json.Unmarshal(returnedBody, &badPost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at unmarshal, %s\n", err)
|
log.Printf("Failed at unmarshal, %s\n", err)
|
||||||
|
return post, fmt.Errorf("API authentication error")
|
||||||
}
|
}
|
||||||
log.Fatal(badPost.Errors[0].Message)
|
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
|
return post, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) AniListGetSingleAnime {
|
func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) AniListGetSingleAnime {
|
||||||
|
|||||||
@@ -11,10 +11,19 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MALHelper(method string, malUrl string, body url.Values) (json.RawMessage, string) {
|
func MALHelper(method string, malUrl string, body url.Values) (json.RawMessage, string, error) {
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
|
|
||||||
req, _ := http.NewRequest(method, malUrl, strings.NewReader(body.Encode()))
|
req, err := http.NewRequest(method, malUrl, strings.NewReader(body.Encode()))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
message, _ := json.Marshal(struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
}{
|
||||||
|
Message: "Failed to create request: " + err.Error(),
|
||||||
|
})
|
||||||
|
return message, "", fmt.Errorf("failed to create request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
req.Header.Add("Authorization", "Bearer "+myAnimeListJwt.AccessToken)
|
req.Header.Add("Authorization", "Bearer "+myAnimeListJwt.AccessToken)
|
||||||
@@ -22,47 +31,57 @@ func MALHelper(method string, malUrl string, body url.Values) (json.RawMessage,
|
|||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Errored when sending request to the server")
|
|
||||||
message, _ := json.Marshal(struct {
|
message, _ := json.Marshal(struct {
|
||||||
Message string `json:"message" ts_type:"message"`
|
Message string `json:"message"`
|
||||||
}{
|
}{
|
||||||
Message: "Errored when sending request to the server" + err.Error(),
|
Message: "Network error: " + err.Error(),
|
||||||
})
|
})
|
||||||
|
return message, "", fmt.Errorf("network error: %w", err)
|
||||||
return message, resp.Status
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
respBody, _ := io.ReadAll(resp.Body)
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp.Status, fmt.Errorf("failed to read response: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
if resp.Status == "401 Unauthorized" {
|
if resp.Status == "401 Unauthorized" {
|
||||||
refreshMyAnimeListAuthorizationToken()
|
refreshMyAnimeListAuthorizationToken()
|
||||||
MALHelper(method, malUrl, body)
|
return MALHelper(method, malUrl, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
return respBody, resp.Status
|
if resp.Status != "200 OK" && resp.Status != "201 Created" && resp.Status != "202 Accepted" {
|
||||||
|
return respBody, resp.Status, fmt.Errorf("API returned status: %s", resp.Status)
|
||||||
|
}
|
||||||
|
return respBody, resp.Status, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) GetMyAnimeList(count int) MALWatchlist {
|
func (a *App) GetMyAnimeList(count int) (MALWatchlist, error) {
|
||||||
limit := strconv.Itoa(count)
|
limit := strconv.Itoa(count)
|
||||||
user := a.GetMyAnimeListLoggedInUser()
|
user := a.GetMyAnimeListLoggedInUser()
|
||||||
malUrl := "https://api.myanimelist.net/v2/users/" + user.Name + "/animelist?fields=list_status&status=watching&limit=" + limit
|
malUrl := "https://api.myanimelist.net/v2/users/" + user.Name + "/animelist?fields=list_status&status=watching&limit=" + limit
|
||||||
|
|
||||||
var malList MALWatchlist
|
var malList MALWatchlist
|
||||||
|
|
||||||
respBody, resStatus := MALHelper("GET", malUrl, nil)
|
respBody, resStatus, err := MALHelper("GET", malUrl, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return malList, fmt.Errorf("failed to get MAL watchlist: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
if resStatus == "200 OK" {
|
if resStatus == "200 OK" {
|
||||||
err := json.Unmarshal(respBody, &malList)
|
err := json.Unmarshal(respBody, &malList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to unmarshal json response, %s\n", err)
|
log.Printf("Failed to unmarshal json response, %s\n", err)
|
||||||
|
return malList, fmt.Errorf("failed to parse response: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return malList
|
return malList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) MyAnimeListUpdate(anime MALAnime, update MALUploadStatus) MalListStatus {
|
func (a *App) MyAnimeListUpdate(anime MALAnime, update MALUploadStatus) (MalListStatus, error) {
|
||||||
if update.NumTimesRewatched >= 1 {
|
if update.NumTimesRewatched >= 1 {
|
||||||
update.IsRewatching = true
|
update.IsRewatching = true
|
||||||
} else {
|
} else {
|
||||||
@@ -78,39 +97,52 @@ func (a *App) MyAnimeListUpdate(anime MALAnime, update MALUploadStatus) MalListS
|
|||||||
|
|
||||||
malUrl := "https://api.myanimelist.net/v2/anime/" + strconv.Itoa(anime.Id) + "/my_list_status"
|
malUrl := "https://api.myanimelist.net/v2/anime/" + strconv.Itoa(anime.Id) + "/my_list_status"
|
||||||
var status MalListStatus
|
var status MalListStatus
|
||||||
respBody, respStatus := MALHelper("PATCH", malUrl, body)
|
respBody, respStatus, err := MALHelper("PATCH", malUrl, body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return status, fmt.Errorf("failed to update MAL entry: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
if respStatus == "200 OK" {
|
if respStatus == "200 OK" {
|
||||||
err := json.Unmarshal(respBody, &status)
|
err := json.Unmarshal(respBody, &status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to unmarshal json response, %s\n", err)
|
log.Printf("Failed to unmarshal json response, %s\n", err)
|
||||||
|
return status, fmt.Errorf("failed to parse response: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return status, nil
|
||||||
return status
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) GetMyAnimeListAnime(id int) MALAnime {
|
func (a *App) GetMyAnimeListAnime(id int) (MALAnime, error) {
|
||||||
malUrl := "https://api.myanimelist.net/v2/anime/" + strconv.Itoa(id) + "?fields=id,title,main_picture,alternative_titles,start_date,end_date,synopsis,mean,rank,popularity,num_list_users,num_scoring_users,nsfw,genres,created_at,updated_at,media_type,status,my_list_status,num_episodes,start_season,broadcast,source,average_episode_duration,rating,pictures,background,related_anime,recommendations,studios,statistics"
|
malUrl := "https://api.myanimelist.net/v2/anime/" + strconv.Itoa(id) + "?fields=id,title,main_picture,alternative_titles,start_date,end_date,synopsis,mean,rank,popularity,num_list_users,num_scoring_users,nsfw,genres,created_at,updated_at,media_type,status,my_list_status,num_episodes,start_season,broadcast,source,average_episode_duration,rating,pictures,background,related_anime,recommendations,studios,statistics"
|
||||||
respBody, respStatus := MALHelper("GET", malUrl, nil)
|
|
||||||
var malAnime MALAnime
|
var malAnime MALAnime
|
||||||
|
respBody, respStatus, err := MALHelper("GET", malUrl, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return malAnime, fmt.Errorf("failed to get MAL anime: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
if respStatus == "200 OK" {
|
if respStatus == "200 OK" {
|
||||||
err := json.Unmarshal(respBody, &malAnime)
|
err := json.Unmarshal(respBody, &malAnime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to unmarshal json response, %s\n", err)
|
log.Printf("Failed to unmarshal json response, %s\n", err)
|
||||||
|
return malAnime, fmt.Errorf("failed to parse response: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return malAnime, nil
|
||||||
return malAnime
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) DeleteMyAnimeListEntry(id int) bool {
|
func (a *App) DeleteMyAnimeListEntry(id int) (bool, error) {
|
||||||
malUrl := "https://api.myanimelist.net/v2/anime/" + strconv.Itoa(id) + "/my_list_status"
|
malUrl := "https://api.myanimelist.net/v2/anime/" + strconv.Itoa(id) + "/my_list_status"
|
||||||
_, respStatus := MALHelper("DELETE", malUrl, nil)
|
_, respStatus, err := MALHelper("DELETE", malUrl, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("failed to delete MAL entry: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
if respStatus == "200 OK" {
|
if respStatus == "200 OK" {
|
||||||
return true
|
return true, nil
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false, fmt.Errorf("delete failed with status: %s", respStatus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ func (a *App) handleMyAnimeListCallback(wg *sync.WaitGroup, verifier *CodeVerifi
|
|||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
log.Fatalf("listen: %s\n", err)
|
log.Printf("Server error: %s\n", err)
|
||||||
}
|
}
|
||||||
fmt.Println("Shutting down...")
|
fmt.Println("Shutting down...")
|
||||||
}()
|
}()
|
||||||
|
|||||||
@@ -14,16 +14,24 @@ import (
|
|||||||
|
|
||||||
var SimklWatchList SimklWatchListType
|
var SimklWatchList SimklWatchListType
|
||||||
|
|
||||||
func SimklHelper(method string, url string, body interface{}) json.RawMessage {
|
func SimklHelper(method string, url string, body interface{}) (json.RawMessage, error) {
|
||||||
reader, _ := json.Marshal(body)
|
reader, err := json.Marshal(body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to marshal body: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
var req *http.Request
|
var req *http.Request
|
||||||
|
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
|
|
||||||
if body != nil {
|
if body != nil {
|
||||||
req, _ = http.NewRequest(method, url, bytes.NewBuffer(reader))
|
req, err = http.NewRequest(method, url, bytes.NewBuffer(reader))
|
||||||
} else {
|
} else {
|
||||||
req, _ = http.NewRequest(method, url, nil)
|
req, err = http.NewRequest(method, url, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
req.Header.Add("Content-Type", "application/json")
|
||||||
@@ -32,41 +40,45 @@ func SimklHelper(method string, url string, body interface{}) json.RawMessage {
|
|||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Errored when sending request to the server")
|
return nil, fmt.Errorf("network error: %w", err)
|
||||||
message, _ := json.Marshal(struct {
|
|
||||||
Message string `json:"message"`
|
|
||||||
}{
|
|
||||||
Message: "Errored when sending request to the server" + err.Error(),
|
|
||||||
})
|
|
||||||
|
|
||||||
return message
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
respBody, _ := io.ReadAll(resp.Body)
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
return respBody
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read response: %w", err)
|
||||||
|
}
|
||||||
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||||
|
return respBody, fmt.Errorf("API returned status: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
return respBody, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SimklGetUserWatchlist() SimklWatchListType {
|
func (a *App) SimklGetUserWatchlist() (SimklWatchListType, error) {
|
||||||
method := "GET"
|
method := "GET"
|
||||||
url := "https://api.simkl.com/sync/all-items/anime"
|
url := "https://api.simkl.com/sync/all-items/anime"
|
||||||
|
|
||||||
respBody := SimklHelper(method, url, nil)
|
respBody, err := SimklHelper(method, url, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return SimklWatchListType{}, fmt.Errorf("failed to get Simkl watchlist: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
var errCheck struct {
|
var errCheck struct {
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
}
|
}
|
||||||
|
|
||||||
err := json.Unmarshal(respBody, &errCheck)
|
err = json.Unmarshal(respBody, &errCheck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at unmarshal, %s\n", err)
|
log.Printf("Failed at unmarshal, %s\n", err)
|
||||||
|
return SimklWatchListType{}, fmt.Errorf("failed to parse error response: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if errCheck.Error != "" {
|
if errCheck.Error != "" {
|
||||||
a.LogoutSimkl()
|
a.LogoutSimkl()
|
||||||
return SimklWatchListType{}
|
return SimklWatchListType{}, fmt.Errorf("Simkl API error: %s", errCheck.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
var watchlist SimklWatchListType
|
var watchlist SimklWatchListType
|
||||||
@@ -74,14 +86,15 @@ func (a *App) SimklGetUserWatchlist() SimklWatchListType {
|
|||||||
err = json.Unmarshal(respBody, &watchlist)
|
err = json.Unmarshal(respBody, &watchlist)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at unmarshal, %s\n", err)
|
log.Printf("Failed at unmarshal, %s\n", err)
|
||||||
|
return SimklWatchListType{}, fmt.Errorf("failed to parse watchlist: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
SimklWatchList = watchlist
|
SimklWatchList = watchlist
|
||||||
|
|
||||||
return watchlist
|
return watchlist, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SimklSyncEpisodes(anime SimklAnime, progress int) SimklAnime {
|
func (a *App) SimklSyncEpisodes(anime SimklAnime, progress int) (SimklAnime, error) {
|
||||||
var episodes []Episode
|
var episodes []Episode
|
||||||
var url string
|
var url string
|
||||||
var shows []SimklPostShow
|
var shows []SimklPostShow
|
||||||
@@ -112,13 +125,19 @@ func (a *App) SimklSyncEpisodes(anime SimklAnime, progress int) SimklAnime {
|
|||||||
|
|
||||||
simklSync := SimklSyncHistoryType{shows}
|
simklSync := SimklSyncHistoryType{shows}
|
||||||
|
|
||||||
respBody := SimklHelper("POST", url, simklSync)
|
respBody, err := SimklHelper("POST", url, simklSync)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to sync episodes: %s\n", err)
|
||||||
|
return anime, fmt.Errorf("failed to sync episodes: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
var success interface{}
|
var success interface{}
|
||||||
|
|
||||||
err := json.Unmarshal(respBody, &success)
|
err = json.Unmarshal(respBody, &success)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at unmarshal, %s\n", err)
|
log.Printf("Failed at unmarshal, %s\n", err)
|
||||||
|
return anime, fmt.Errorf("failed to parse response: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, simklAnime := range SimklWatchList.Anime {
|
for i, simklAnime := range SimklWatchList.Anime {
|
||||||
@@ -131,10 +150,10 @@ func (a *App) SimklSyncEpisodes(anime SimklAnime, progress int) SimklAnime {
|
|||||||
|
|
||||||
WatchListUpdate(anime)
|
WatchListUpdate(anime)
|
||||||
|
|
||||||
return anime
|
return anime, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SimklSyncRating(anime SimklAnime, rating int) SimklAnime {
|
func (a *App) SimklSyncRating(anime SimklAnime, rating int) (SimklAnime, error) {
|
||||||
var url string
|
var url string
|
||||||
showWithRating := ShowWithRating{
|
showWithRating := ShowWithRating{
|
||||||
Title: anime.Show.Title,
|
Title: anime.Show.Title,
|
||||||
@@ -169,13 +188,17 @@ func (a *App) SimklSyncRating(anime SimklAnime, rating int) SimklAnime {
|
|||||||
Shows []interface{} `json:"shows" ts_type:"shows"`
|
Shows []interface{} `json:"shows" ts_type:"shows"`
|
||||||
}{shows}
|
}{shows}
|
||||||
|
|
||||||
respBody := SimklHelper("POST", url, simklSync)
|
respBody, err := SimklHelper("POST", url, simklSync)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to sync rating: %s\n", err)
|
||||||
|
return anime, fmt.Errorf("failed to sync rating: %w", err)
|
||||||
|
}
|
||||||
var success interface{}
|
var success interface{}
|
||||||
|
|
||||||
err := json.Unmarshal(respBody, &success)
|
err = json.Unmarshal(respBody, &success)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at unmarshal, %s\n", err)
|
log.Printf("Failed at unmarshal, %s\n", err)
|
||||||
|
return anime, fmt.Errorf("failed to parse response: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, simklAnime := range SimklWatchList.Anime {
|
for i, simklAnime := range SimklWatchList.Anime {
|
||||||
@@ -188,10 +211,10 @@ func (a *App) SimklSyncRating(anime SimklAnime, rating int) SimklAnime {
|
|||||||
|
|
||||||
WatchListUpdate(anime)
|
WatchListUpdate(anime)
|
||||||
|
|
||||||
return anime
|
return anime, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SimklSyncStatus(anime SimklAnime, status string) SimklAnime {
|
func (a *App) SimklSyncStatus(anime SimklAnime, status string) (SimklAnime, error) {
|
||||||
url := "https://api.simkl.com/sync/add-to-list"
|
url := "https://api.simkl.com/sync/add-to-list"
|
||||||
show := SimklShowStatus{
|
show := SimklShowStatus{
|
||||||
Title: anime.Show.Title,
|
Title: anime.Show.Title,
|
||||||
@@ -211,13 +234,17 @@ func (a *App) SimklSyncStatus(anime SimklAnime, status string) SimklAnime {
|
|||||||
Shows []SimklShowStatus `json:"shows" ts_type:"shows"`
|
Shows []SimklShowStatus `json:"shows" ts_type:"shows"`
|
||||||
}{shows}
|
}{shows}
|
||||||
|
|
||||||
respBody := SimklHelper("POST", url, simklSync)
|
respBody, err := SimklHelper("POST", url, simklSync)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to sync status: %s\n", err)
|
||||||
|
return anime, fmt.Errorf("failed to sync status: %w", err)
|
||||||
|
}
|
||||||
var success interface{}
|
var success interface{}
|
||||||
|
|
||||||
err := json.Unmarshal(respBody, &success)
|
err = json.Unmarshal(respBody, &success)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at unmarshal, %s\n", err)
|
log.Printf("Failed at unmarshal, %s\n", err)
|
||||||
|
return anime, fmt.Errorf("failed to parse response: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, simklAnime := range SimklWatchList.Anime {
|
for i, simklAnime := range SimklWatchList.Anime {
|
||||||
@@ -230,15 +257,20 @@ func (a *App) SimklSyncStatus(anime SimklAnime, status string) SimklAnime {
|
|||||||
|
|
||||||
WatchListUpdate(anime)
|
WatchListUpdate(anime)
|
||||||
|
|
||||||
return anime
|
return anime, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SimklSearch(aniListAnime MediaList) SimklAnime {
|
func (a *App) SimklSearch(aniListAnime MediaList) (SimklAnime, error) {
|
||||||
var result SimklAnime
|
var result SimklAnime
|
||||||
|
|
||||||
if reflect.DeepEqual(SimklWatchList, SimklWatchListType{}) {
|
if reflect.DeepEqual(SimklWatchList, SimklWatchListType{}) {
|
||||||
fmt.Println("Watchlist empty. Calling...")
|
fmt.Println("Watchlist empty. Calling...")
|
||||||
SimklWatchList = a.SimklGetUserWatchlist()
|
watchlist, err := a.SimklGetUserWatchlist()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to get watchlist: %s\n", err)
|
||||||
|
return result, fmt.Errorf("failed to load watchlist for search: %w", err)
|
||||||
|
}
|
||||||
|
SimklWatchList = watchlist
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, anime := range SimklWatchList.Anime {
|
for _, anime := range SimklWatchList.Anime {
|
||||||
@@ -255,22 +287,30 @@ func (a *App) SimklSearch(aniListAnime MediaList) SimklAnime {
|
|||||||
var anime SimklSearchType
|
var anime SimklSearchType
|
||||||
url := "https://api.simkl.com/search/id?anilist=" + strconv.Itoa(aniListAnime.Media.ID)
|
url := "https://api.simkl.com/search/id?anilist=" + strconv.Itoa(aniListAnime.Media.ID)
|
||||||
|
|
||||||
respBody := SimklHelper("GET", url, nil)
|
respBody, err := SimklHelper("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
err := json.Unmarshal(respBody, &anime)
|
log.Printf("Failed to search Simkl: %s\n", err)
|
||||||
|
return result, fmt.Errorf("failed to search Simkl by AniList ID: %w", err)
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(respBody, &anime)
|
||||||
|
|
||||||
if len(anime) == 0 {
|
if len(anime) == 0 {
|
||||||
url = "https://api.simkl.com/search/id?mal=" + strconv.Itoa(aniListAnime.Media.IDMal)
|
url = "https://api.simkl.com/search/id?mal=" + strconv.Itoa(aniListAnime.Media.IDMal)
|
||||||
respBody = SimklHelper("GET", url, nil)
|
respBody, err = SimklHelper("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to search Simkl by MAL ID: %s\n", err)
|
||||||
|
return result, fmt.Errorf("failed to search by MAL ID: %w", err)
|
||||||
|
}
|
||||||
err = json.Unmarshal(respBody, &anime)
|
err = json.Unmarshal(respBody, &anime)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at unmarshal, %s\n", err)
|
log.Printf("Failed at unmarshal, %s\n", err)
|
||||||
|
return result, fmt.Errorf("failed to parse search results: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(anime) == 0 {
|
if len(anime) == 0 {
|
||||||
return result
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, watchListAnime := range SimklWatchList.Anime {
|
for _, watchListAnime := range SimklWatchList.Anime {
|
||||||
@@ -288,10 +328,10 @@ func (a *App) SimklSearch(aniListAnime MediaList) SimklAnime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SimklSyncRemove(anime SimklAnime) bool {
|
func (a *App) SimklSyncRemove(anime SimklAnime) (bool, error) {
|
||||||
url := "https://api.simkl.com/sync/history/remove"
|
url := "https://api.simkl.com/sync/history/remove"
|
||||||
var showArray []SimklShowStatus
|
var showArray []SimklShowStatus
|
||||||
|
|
||||||
@@ -312,25 +352,28 @@ func (a *App) SimklSyncRemove(anime SimklAnime) bool {
|
|||||||
Shows: showArray,
|
Shows: showArray,
|
||||||
}
|
}
|
||||||
|
|
||||||
respBody := SimklHelper("POST", url, show)
|
respBody, err := SimklHelper("POST", url, show)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to sync remove: %s\n", err)
|
||||||
|
return false, fmt.Errorf("failed to sync remove: %w", err)
|
||||||
|
}
|
||||||
var success SimklDeleteType
|
var success SimklDeleteType
|
||||||
|
err = json.Unmarshal(respBody, &success)
|
||||||
err := json.Unmarshal(respBody, &success)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed at unmarshal, %s\n", err)
|
log.Printf("Failed at unmarshal, %s\n", err)
|
||||||
|
return false, fmt.Errorf("failed to parse response: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if success.Deleted.Shows >= 1 {
|
if success.Deleted.Shows >= 1 {
|
||||||
for i, simklAnime := range SimklWatchList.Anime {
|
for i, simklAnime := range SimklWatchList.Anime {
|
||||||
if simklAnime.Show.Ids.Simkl == anime.Show.Ids.Simkl {
|
if simklAnime.Show.Ids.Simkl == anime.Show.Ids.Simkl {
|
||||||
SimklWatchList.Anime = slices.Delete(SimklWatchList.Anime, i, i+1)
|
SimklWatchList.Anime = slices.Delete(SimklWatchList.Anime, i, i+1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true, nil
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false, fmt.Errorf("no shows were deleted")
|
||||||
}
|
}
|
||||||
|
|
||||||
func WatchListUpdate(anime SimklAnime) {
|
func WatchListUpdate(anime SimklAnime) {
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ func (a *App) handleSimklCallback(wg *sync.WaitGroup) {
|
|||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
log.Fatalf("listen: %s\n", err)
|
log.Printf("Server error: %s\n", err)
|
||||||
}
|
}
|
||||||
fmt.Println("Shutting down...")
|
fmt.Println("Shutting down...")
|
||||||
}()
|
}()
|
||||||
@@ -138,7 +138,8 @@ func getSimklAuthorizationToken(content string) SimklJWT {
|
|||||||
}
|
}
|
||||||
jsonData, err := json.Marshal(data)
|
jsonData, err := json.Marshal(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Printf("Failed to marshal data: %s\n", err)
|
||||||
|
return SimklJWT{}
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := http.NewRequest("POST", "https://api.simkl.com/oauth/token", bytes.NewBuffer(jsonData))
|
response, err := http.NewRequest("POST", "https://api.simkl.com/oauth/token", bytes.NewBuffer(jsonData))
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
SimklGetUserWatchlist,
|
SimklGetUserWatchlist,
|
||||||
} from "../wailsjs/go/main/App";
|
} from "../wailsjs/go/main/App";
|
||||||
import { loc } from "svelte-spa-router";
|
import { loc } from "svelte-spa-router";
|
||||||
|
import ErrorModal from "./helperComponents/ErrorModal.svelte";
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
let isAniListLoggedIn: boolean;
|
let isAniListLoggedIn: boolean;
|
||||||
@@ -57,6 +58,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Header />
|
<Header />
|
||||||
|
<ErrorModal />
|
||||||
<Router
|
<Router
|
||||||
routes={{
|
routes={{
|
||||||
"/": Home,
|
"/": Home,
|
||||||
|
|||||||
73
frontend/src/helperComponents/ErrorModal.svelte
Normal file
73
frontend/src/helperComponents/ErrorModal.svelte
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
apiError,
|
||||||
|
isApiDown,
|
||||||
|
clearApiError,
|
||||||
|
setApiError,
|
||||||
|
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
||||||
|
import { CheckIfAniListLoggedInAndLoadWatchList } from "../helperModules/CheckIfAniListLoggedInAndLoadWatchList.svelte";
|
||||||
|
import { CheckIfMALLoggedInAndSetUser } from "../helperModules/CheckIfMyAnimeListLoggedIn.svelte";
|
||||||
|
import { CheckIfSimklLoggedInAndSetUser } from "../helperModules/CheckIsSimklLoggedIn.svelte";
|
||||||
|
import { Modal, Button } from "flowbite-svelte";
|
||||||
|
let showModal = false;
|
||||||
|
$: if ($apiError) {
|
||||||
|
showModal = true;
|
||||||
|
}
|
||||||
|
async function handleRetry() {
|
||||||
|
const service = $apiError?.service;
|
||||||
|
clearApiError();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (service === "anilist") {
|
||||||
|
await CheckIfAniListLoggedInAndLoadWatchList();
|
||||||
|
} else if (service === "mal") {
|
||||||
|
await CheckIfMALLoggedInAndSetUser();
|
||||||
|
} else if (service === "simkl") {
|
||||||
|
await CheckIfSimklLoggedInAndSetUser();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
||||||
|
setApiError(
|
||||||
|
service || "unknown",
|
||||||
|
`Retry failed: ${errorMsg}`,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handleDismiss() {
|
||||||
|
clearApiError();
|
||||||
|
showModal = false;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if showModal && $apiError}
|
||||||
|
<Modal
|
||||||
|
open={showModal}
|
||||||
|
title="{$apiError.service.toUpperCase()} API Error"
|
||||||
|
size="md"
|
||||||
|
>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div class="p-4 bg-red-50 border border-red-200 rounded-lg">
|
||||||
|
<p class="text-red-800 font-medium">{$apiError.message}</p>
|
||||||
|
{#if $apiError.statusCode}
|
||||||
|
<p class="text-red-600 text-sm mt-2">
|
||||||
|
Status: {$apiError.statusCode}
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<p class="text-gray-600">
|
||||||
|
The application will remain open. You can retry the connection or
|
||||||
|
dismiss this message to continue with limited functionality.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div slot="footer" class="flex gap-3 justify-end">
|
||||||
|
{#if $apiError.canRetry}
|
||||||
|
<Button on:click={handleRetry} class="bg-blue-600 hover:bg-blue-700">
|
||||||
|
Retry Connection
|
||||||
|
</Button>
|
||||||
|
{/if}
|
||||||
|
<Button on:click={handleDismiss} color="alternative">Dismiss</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
{/if}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {
|
import {
|
||||||
aniListLoggedIn,
|
aniListLoggedIn,
|
||||||
|
aniListSort,
|
||||||
aniListWatchlist,
|
aniListWatchlist,
|
||||||
animePerPage,
|
animePerPage,
|
||||||
watchListPage,
|
watchListPage,
|
||||||
@@ -8,24 +9,21 @@
|
|||||||
|
|
||||||
import type { AniListCurrentUserWatchList } from "../anilist/types/AniListCurrentUserWatchListType";
|
import type { AniListCurrentUserWatchList } from "../anilist/types/AniListCurrentUserWatchListType";
|
||||||
import { GetAniListUserWatchingList } from "../../wailsjs/go/main/App";
|
import { GetAniListUserWatchingList } from "../../wailsjs/go/main/App";
|
||||||
import { MediaListSort } from "../anilist/types/AniListTypes";
|
|
||||||
|
|
||||||
let aniListWatchListLoaded: AniListCurrentUserWatchList;
|
let aniListWatchListLoaded: AniListCurrentUserWatchList;
|
||||||
let page: number;
|
let page: number;
|
||||||
let perPage: number;
|
let perPage: number;
|
||||||
|
let sort: string;
|
||||||
|
|
||||||
watchListPage.subscribe((value) => (page = value));
|
watchListPage.subscribe((value) => (page = value));
|
||||||
animePerPage.subscribe((value) => (perPage = value));
|
animePerPage.subscribe((value) => (perPage = value));
|
||||||
aniListWatchlist.subscribe((value) => (aniListWatchListLoaded = value));
|
aniListWatchlist.subscribe((value) => (aniListWatchListLoaded = value));
|
||||||
|
aniListSort.subscribe((value) => (sort = value));
|
||||||
|
|
||||||
const perPageOptions = [10, 20, 50];
|
const perPageOptions = [10, 20, 50];
|
||||||
|
|
||||||
function ChangeWatchListPage(newPage: number) {
|
function ChangeWatchListPage(newPage: number) {
|
||||||
GetAniListUserWatchingList(
|
GetAniListUserWatchingList(newPage, perPage, sort).then((result) => {
|
||||||
newPage,
|
|
||||||
perPage,
|
|
||||||
MediaListSort.UpdatedTimeDesc,
|
|
||||||
).then((result) => {
|
|
||||||
watchListPage.set(newPage);
|
watchListPage.set(newPage);
|
||||||
aniListWatchlist.set(result);
|
aniListWatchlist.set(result);
|
||||||
aniListLoggedIn.set(true);
|
aniListLoggedIn.set(true);
|
||||||
@@ -45,16 +43,14 @@
|
|||||||
function changeCountPerPage(
|
function changeCountPerPage(
|
||||||
e: Event & { currentTarget: HTMLSelectElement },
|
e: Event & { currentTarget: HTMLSelectElement },
|
||||||
): void {
|
): void {
|
||||||
GetAniListUserWatchingList(
|
GetAniListUserWatchingList(1, Number(e.currentTarget.value), sort).then(
|
||||||
1,
|
(result) => {
|
||||||
Number(e.currentTarget.value),
|
|
||||||
MediaListSort.UpdatedTimeDesc,
|
|
||||||
).then((result) => {
|
|
||||||
animePerPage.set(Number(e.currentTarget.value));
|
animePerPage.set(Number(e.currentTarget.value));
|
||||||
watchListPage.set(1);
|
watchListPage.set(1);
|
||||||
aniListWatchlist.set(result);
|
aniListWatchlist.set(result);
|
||||||
aniListLoggedIn.set(true);
|
aniListLoggedIn.set(true);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
0
frontend/src/helperComponents/Sort.svelte
Normal file
0
frontend/src/helperComponents/Sort.svelte
Normal file
@@ -1,34 +1,81 @@
|
|||||||
<script lang="ts" context="module">
|
<script lang="ts" context="module">
|
||||||
import {CheckIfAniListLoggedIn, GetAniListLoggedInUser, GetAniListUserWatchingList} from "../../wailsjs/go/main/App";
|
import {
|
||||||
import {MediaListSort} from "../anilist/types/AniListTypes";
|
CheckIfAniListLoggedIn,
|
||||||
import { aniListUser, watchListPage, animePerPage, aniListPrimary, aniListLoggedIn, aniListWatchlist } from "./GlobalVariablesAndHelperFunctions.svelte"
|
GetAniListLoggedInUser,
|
||||||
|
GetAniListUserWatchingList,
|
||||||
|
} from "../../wailsjs/go/main/App";
|
||||||
|
import {
|
||||||
|
aniListUser,
|
||||||
|
watchListPage,
|
||||||
|
animePerPage,
|
||||||
|
aniListPrimary,
|
||||||
|
aniListLoggedIn,
|
||||||
|
aniListWatchlist,
|
||||||
|
aniListSort,
|
||||||
|
clearApiError,
|
||||||
|
setApiError,
|
||||||
|
} from "./GlobalVariablesAndHelperFunctions.svelte";
|
||||||
|
|
||||||
let isAniListPrimary: boolean
|
let isAniListPrimary: boolean;
|
||||||
let page: number
|
let page: number;
|
||||||
let perPage: number
|
let perPage: number;
|
||||||
|
let sort: string;
|
||||||
|
|
||||||
aniListPrimary.subscribe(value => isAniListPrimary = value)
|
aniListPrimary.subscribe((value) => (isAniListPrimary = value));
|
||||||
watchListPage.subscribe(value => page = value)
|
watchListPage.subscribe((value) => (page = value));
|
||||||
animePerPage.subscribe(value => perPage = value)
|
animePerPage.subscribe((value) => (perPage = value));
|
||||||
|
aniListSort.subscribe((value) => (sort = value));
|
||||||
|
|
||||||
export const LoadAniListUser = async () => {
|
export const LoadAniListUser = async () => {
|
||||||
await GetAniListLoggedInUser().then(user => {
|
try {
|
||||||
aniListUser.set(user)
|
await GetAniListLoggedInUser().then((user) => {
|
||||||
})
|
aniListUser.set(user);
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
||||||
|
setApiError(
|
||||||
|
"anilist",
|
||||||
|
`Failed to load user: ${errorMsg}`,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const LoadAniListWatchList = async () => {
|
export const LoadAniListWatchList = async () => {
|
||||||
await GetAniListUserWatchingList(page, perPage, MediaListSort.UpdatedTimeDesc).then((watchList) => {
|
try {
|
||||||
aniListWatchlist.set(watchList)
|
const watchList = await GetAniListUserWatchingList(page, perPage, sort);
|
||||||
})
|
aniListWatchlist.set(watchList);
|
||||||
|
clearApiError();
|
||||||
|
} catch (err) {
|
||||||
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
||||||
|
setApiError(
|
||||||
|
"anilist",
|
||||||
|
`Failed to load watch list: ${errorMsg}`,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
export const CheckIfAniListLoggedInAndLoadWatchList = async () => {
|
export const CheckIfAniListLoggedInAndLoadWatchList = async () => {
|
||||||
const loggedIn = await CheckIfAniListLoggedIn()
|
try {
|
||||||
|
const loggedIn = await CheckIfAniListLoggedIn();
|
||||||
if (loggedIn) {
|
if (loggedIn) {
|
||||||
await LoadAniListUser()
|
await LoadAniListUser();
|
||||||
if (isAniListPrimary) await LoadAniListWatchList()
|
if (isAniListPrimary) await LoadAniListWatchList();
|
||||||
}
|
}
|
||||||
aniListLoggedIn.set(loggedIn)
|
aniListLoggedIn.set(loggedIn);
|
||||||
|
} catch (err) {
|
||||||
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
||||||
|
setApiError(
|
||||||
|
"anilist",
|
||||||
|
`Authentication failed: ${errorMsg}`,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
aniListLoggedIn.set(false);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -53,6 +53,7 @@
|
|||||||
export let loading = writable(false);
|
export let loading = writable(false);
|
||||||
export let tableItems = writable([] as TableItems);
|
export let tableItems = writable([] as TableItems);
|
||||||
export let watchlistNeedsRefresh = writable(false);
|
export let watchlistNeedsRefresh = writable(false);
|
||||||
|
export let aniListSort = writable(MediaListSort.UpdatedTimeDesc);
|
||||||
|
|
||||||
export let watchListPage = writable(1);
|
export let watchListPage = writable(1);
|
||||||
export let animePerPage = writable(20);
|
export let animePerPage = writable(20);
|
||||||
@@ -60,6 +61,7 @@
|
|||||||
let isAniListPrimary: boolean;
|
let isAniListPrimary: boolean;
|
||||||
let page: number;
|
let page: number;
|
||||||
let perPage: number;
|
let perPage: number;
|
||||||
|
let sort: string;
|
||||||
let aniWatchlist: AniListCurrentUserWatchList;
|
let aniWatchlist: AniListCurrentUserWatchList;
|
||||||
let currentAniListAnime: AniListGetSingleAnime;
|
let currentAniListAnime: AniListGetSingleAnime;
|
||||||
|
|
||||||
@@ -73,6 +75,34 @@
|
|||||||
malLoggedIn.subscribe((value) => (isMalLoggedIn = value));
|
malLoggedIn.subscribe((value) => (isMalLoggedIn = value));
|
||||||
simklLoggedIn.subscribe((value) => (isSimklLoggedIn = value));
|
simklLoggedIn.subscribe((value) => (isSimklLoggedIn = value));
|
||||||
aniListAnime.subscribe((value) => (currentAniListAnime = value));
|
aniListAnime.subscribe((value) => (currentAniListAnime = value));
|
||||||
|
aniListSort.subscribe((value) => (sort = value));
|
||||||
|
|
||||||
|
export interface ApiError {
|
||||||
|
service: string;
|
||||||
|
message: string;
|
||||||
|
statusCode?: string;
|
||||||
|
canRetry: boolean;
|
||||||
|
}
|
||||||
|
export const apiError = writable<ApiError | null>(null);
|
||||||
|
export const isApiDown = writable(false);
|
||||||
|
export function setApiError(
|
||||||
|
service: string,
|
||||||
|
message: string,
|
||||||
|
statusCode?: string,
|
||||||
|
canRetry: boolean = true,
|
||||||
|
) {
|
||||||
|
apiError.set({
|
||||||
|
service,
|
||||||
|
message,
|
||||||
|
statusCode,
|
||||||
|
canRetry,
|
||||||
|
});
|
||||||
|
isApiDown.set(true);
|
||||||
|
}
|
||||||
|
export function clearApiError() {
|
||||||
|
apiError.set(null);
|
||||||
|
isApiDown.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
export async function GetAnimeSingleItem(
|
export async function GetAnimeSingleItem(
|
||||||
aniId: number,
|
aniId: number,
|
||||||
@@ -136,11 +166,7 @@
|
|||||||
GetAniListLoggedInUser().then((result) => {
|
GetAniListLoggedInUser().then((result) => {
|
||||||
aniListUser.set(result);
|
aniListUser.set(result);
|
||||||
if (isAniListPrimary) {
|
if (isAniListPrimary) {
|
||||||
GetAniListUserWatchingList(
|
GetAniListUserWatchingList(page, perPage, sort).then((result) => {
|
||||||
page,
|
|
||||||
perPage,
|
|
||||||
MediaListSort.UpdatedTimeDesc,
|
|
||||||
).then((result) => {
|
|
||||||
aniListWatchlist.set(result);
|
aniListWatchlist.set(result);
|
||||||
aniListLoggedIn.set(true);
|
aniListLoggedIn.set(true);
|
||||||
});
|
});
|
||||||
@@ -184,4 +210,3 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,55 @@
|
|||||||
<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 {
|
import {
|
||||||
aniListLoggedIn,
|
aniListLoggedIn,
|
||||||
aniListPrimary,
|
aniListPrimary,
|
||||||
loading,
|
loading,
|
||||||
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
isApiDown,
|
||||||
import loader from '../helperFunctions/loader'
|
apiError,
|
||||||
|
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
||||||
|
import loader from "../helperFunctions/loader";
|
||||||
|
|
||||||
let isAniListPrimary: boolean
|
let isAniListPrimary: boolean;
|
||||||
let isAniListLoggedIn: boolean
|
let isAniListLoggedIn: boolean;
|
||||||
|
|
||||||
aniListPrimary.subscribe((value) => isAniListPrimary = value)
|
aniListPrimary.subscribe((value) => (isAniListPrimary = value));
|
||||||
aniListLoggedIn.subscribe((value) => isAniListLoggedIn = value)
|
aniListLoggedIn.subscribe((value) => (isAniListLoggedIn = value));
|
||||||
</script>
|
</script>
|
||||||
{#if isAniListLoggedIn && isAniListPrimary}
|
|
||||||
<div class="container py-10">
|
{#if $isApiDown}
|
||||||
|
<div class="container py-10">
|
||||||
|
<div
|
||||||
|
class="bg-yellow-50 border border-yellow-200 rounded-lg p-6 text-center"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="mx-auto h-12 w-12 text-yellow-600 mb-4"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<h2 class="text-xl font-semibold text-yellow-900 mb-2">
|
||||||
|
API Unavailable
|
||||||
|
</h2>
|
||||||
|
<p class="text-yellow-700 mb-4">
|
||||||
|
The {$apiError?.service || "service"} is currently unavailable. The app will
|
||||||
|
remain open, and you can retry when the service is back online.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else if isAniListLoggedIn && isAniListPrimary}
|
||||||
|
<div class="container py-10">
|
||||||
<Pagination />
|
<Pagination />
|
||||||
<WatchList />
|
<WatchList />
|
||||||
<Pagination />
|
<Pagination />
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div use:loader={loading}></div>
|
<div use:loader={loading}></div>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
// vite.config.ts
|
|
||||||
import { defineConfig } from "file:///home/nymusicman/Code/AniTrack/frontend/node_modules/vite/dist/node/index.js";
|
|
||||||
import { svelte } from "file:///home/nymusicman/Code/AniTrack/frontend/node_modules/@sveltejs/vite-plugin-svelte/src/index.js";
|
|
||||||
var vite_config_default = defineConfig({
|
|
||||||
plugins: [svelte()]
|
|
||||||
});
|
|
||||||
export {
|
|
||||||
vite_config_default as default
|
|
||||||
};
|
|
||||||
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvaG9tZS9ueW11c2ljbWFuL0NvZGUvQW5pVHJhY2svZnJvbnRlbmRcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIi9ob21lL255bXVzaWNtYW4vQ29kZS9BbmlUcmFjay9mcm9udGVuZC92aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vaG9tZS9ueW11c2ljbWFuL0NvZGUvQW5pVHJhY2svZnJvbnRlbmQvdml0ZS5jb25maWcudHNcIjtpbXBvcnQge2RlZmluZUNvbmZpZ30gZnJvbSAndml0ZSdcbmltcG9ydCB7c3ZlbHRlfSBmcm9tICdAc3ZlbHRlanMvdml0ZS1wbHVnaW4tc3ZlbHRlJ1xuXG4vLyBodHRwczovL3ZpdGVqcy5kZXYvY29uZmlnL1xuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcbiAgcGx1Z2luczogW3N2ZWx0ZSgpXVxufSlcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBdVMsU0FBUSxvQkFBbUI7QUFDbFUsU0FBUSxjQUFhO0FBR3JCLElBQU8sc0JBQVEsYUFBYTtBQUFBLEVBQzFCLFNBQVMsQ0FBQyxPQUFPLENBQUM7QUFDcEIsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K
|
|
||||||
@@ -12,6 +12,6 @@
|
|||||||
},
|
},
|
||||||
"info": {
|
"info": {
|
||||||
"productName": "AniTrack",
|
"productName": "AniTrack",
|
||||||
"productVersion": "0.6.5"
|
"productVersion": "0.7.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user