diff --git a/AniListFunctions.go b/AniListFunctions.go index 6c0cb1b..06469dc 100644 --- a/AniListFunctions.go +++ b/AniListFunctions.go @@ -489,3 +489,39 @@ func (a *App) AniListUpdateEntry(updateBody AniListUpdateVariables) AniListGetSi return post } + +func (a *App) AniListDeleteEntry(mediaListId int) DeleteAniListReturn { + type Variables = struct { + Id int `json:"id"` + } + + body := struct { + Query string `json:"query"` + Variables Variables `json:"variables"` + }{ + Query: ` + mutation( + $id:Int, + ){ + DeleteMediaListEntry( + id:$id, + ){ + deleted + } + } + `, + Variables: Variables{ + Id: mediaListId, + }, + } + + returnedBody, _ := AniListQuery(body, true) + + var post DeleteAniListReturn + err := json.Unmarshal(returnedBody, &post) + if err != nil { + log.Printf("Failed at unmarshal, %s\n", err) + } + + return post +} diff --git a/AniListTypes.go b/AniListTypes.go index 6cad8e6..862628b 100644 --- a/AniListTypes.go +++ b/AniListTypes.go @@ -139,6 +139,14 @@ type AniListUpdateVariables struct { CompletedAt CompletedAt `json:"completedAt"` } +type DeleteAniListReturn struct { + Data struct { + DeleteMediaListEntry struct { + Deleted bool `json:"deleted"` + } `json:"DeleteMediaListEntry"` + } `json:"data"` +} + //var MediaListSort = struct { // MediaId string // MediaIdDesc string diff --git a/MALFunctions.go b/MALFunctions.go index 6758c0b..818aabe 100644 --- a/MALFunctions.go +++ b/MALFunctions.go @@ -11,13 +11,14 @@ import ( "strings" ) -func MALHelper(method string, malUrl string, body url.Values) json.RawMessage { +func MALHelper(method string, malUrl string, body url.Values) (json.RawMessage, string) { client := &http.Client{} req, _ := http.NewRequest(method, malUrl, strings.NewReader(body.Encode())) req.Header.Add("Content-Type", "application/x-www-form-urlencoded") req.Header.Add("Authorization", "Bearer "+myAnimeListJwt.AccessToken) + fmt.Println(myAnimeListJwt.AccessToken) resp, err := client.Do(req) @@ -29,13 +30,18 @@ func MALHelper(method string, malUrl string, body url.Values) json.RawMessage { Message: "Errored when sending request to the server" + err.Error(), }) - return message + return message, resp.Status } defer resp.Body.Close() respBody, _ := io.ReadAll(resp.Body) - return respBody + if resp.Status == "401 Unauthorized" { + refreshMyAnimeListAuthorizationToken() + MALHelper(method, malUrl, body) + } + + return respBody, resp.Status } func (a *App) GetMyAnimeList(count int) MALWatchlist { @@ -45,11 +51,13 @@ func (a *App) GetMyAnimeList(count int) MALWatchlist { var malList MALWatchlist - respBody := MALHelper("GET", malUrl, nil) + respBody, resStatus := MALHelper("GET", malUrl, nil) - err := json.Unmarshal(respBody, &malList) - if err != nil { - log.Printf("Failed to unmarshal json response, %s\n", err) + if resStatus == "200 OK" { + err := json.Unmarshal(respBody, &malList) + if err != nil { + log.Printf("Failed to unmarshal json response, %s\n", err) + } } return malList @@ -71,23 +79,40 @@ func (a *App) MyAnimeListUpdate(anime MALAnime, update MALUploadStatus) MalListS malUrl := "https://api.myanimelist.net/v2/anime/" + strconv.Itoa(anime.Id) + "/my_list_status" var status MalListStatus - respBody := MALHelper("PATCH", malUrl, body) - err := json.Unmarshal(respBody, &status) - if err != nil { - log.Printf("Failed to unmarshal json response, %s\n", err) + respBody, respStatus := MALHelper("PATCH", malUrl, body) + + if respStatus == "200 OK" { + err := json.Unmarshal(respBody, &status) + if err != nil { + log.Printf("Failed to unmarshal json response, %s\n", err) + } } return status } func (a *App) GetMyAnimeListAnime(id int) MALAnime { + fmt.Println(id) 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 := MALHelper("GET", malUrl, nil) + respBody, respStatus := MALHelper("GET", malUrl, nil) var malAnime MALAnime - err := json.Unmarshal(respBody, &malAnime) - if err != nil { - log.Printf("Failed to unmarshal json response, %s\n", err) + + if respStatus == "200 OK" { + err := json.Unmarshal(respBody, &malAnime) + if err != nil { + log.Printf("Failed to unmarshal json response, %s\n", err) + } } return malAnime } + +func (a *App) DeleteMyAnimeListEntry(id int) bool { + malUrl := "https://api.myanimelist.net/v2/anime/" + strconv.Itoa(id) + "/my_list_status" + _, respStatus := MALHelper("DELETE", malUrl, nil) + if respStatus == "200 OK" { + return true + } else { + return false + } +} diff --git a/MALUserFunctions.go b/MALUserFunctions.go index 16a560c..ef77a72 100644 --- a/MALUserFunctions.go +++ b/MALUserFunctions.go @@ -224,6 +224,77 @@ func getMyAnimeListAuthorizationToken(content string, verifier *CodeVerifier) My return post } +func refreshMyAnimeListAuthorizationToken() { + dataForURLs := struct { + GrantType string `json:"grant_type"` + RefreshToken string `json:"refresh_token"` + ClientID string `json:"client_id"` + ClientSecret string `json:"client_secret"` + RedirectURI string `json:"redirect_uri"` + }{ + GrantType: "refresh_token", + RefreshToken: myAnimeListJwt.RefreshToken, + ClientID: Environment.MAL_CLIENT_ID, + ClientSecret: Environment.MAL_CLIENT_SECRET, + RedirectURI: Environment.MAL_CALLBACK_URI, + } + + data := url.Values{} + data.Set("grant_type", dataForURLs.GrantType) + data.Set("refresh_token", dataForURLs.RefreshToken) + data.Set("client_id", dataForURLs.ClientID) + data.Set("client_secret", dataForURLs.ClientSecret) + data.Set("redirect_uri", dataForURLs.RedirectURI) + + response, err := http.NewRequest("POST", "https://myanimelist.net/v1/oauth2/token", strings.NewReader(data.Encode())) + if err != nil { + log.Printf("Failed at response, %s\n", err) + } + response.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + client := &http.Client{} + res, reserr := client.Do(response) + if reserr != nil { + log.Printf("Failed at res, %s\n", err) + } + + defer res.Body.Close() + + returnedBody, err := io.ReadAll(res.Body) + + err = json.Unmarshal(returnedBody, &myAnimeListJwt) + if err != nil { + log.Printf("Failed at unmarshal, %s\n", err) + } + + _ = myAnimeListRing.Set(keyring.Item{ + Key: "MyAnimeListTokenType", + Data: []byte(myAnimeListJwt.TokenType), + }) + _ = myAnimeListRing.Set(keyring.Item{ + Key: "MyAnimeListExpiresIn", + Data: []byte(strconv.Itoa(myAnimeListJwt.ExpiresIn)), + }) + _ = myAnimeListRing.Set(keyring.Item{ + Key: "MyAnimeListAccessToken", + Data: []byte(myAnimeListJwt.AccessToken), + }) + _ = myAnimeListRing.Set(keyring.Item{ + Key: "MyAnimeListRefreshToken", + Data: []byte(myAnimeListJwt.RefreshToken), + }) + _, err = runtime.MessageDialog(*wailsContext, runtime.MessageDialogOptions{ + Title: "MyAnimeList Authorization", + Message: "It is now safe to close your browser tab", + }) + + if err != nil { + fmt.Println(err) + } + + return +} + func (a *App) GetMyAnimeListLoggedInUser() MyAnimeListUser { a.MyAnimeListLogin() diff --git a/SimklFunctions.go b/SimklFunctions.go index 824817a..d25358d 100644 --- a/SimklFunctions.go +++ b/SimklFunctions.go @@ -289,3 +289,30 @@ func (a *App) SimklSearch(aniListAnime MediaList) SimklAnime { return result } + +func (a *App) SimklSyncRemove(anime SimklAnime) bool { + url := "https://api.simkl.com/sync/history/remove" + var show = SimklShowStatus{ + Title: anime.Show.Title, + Ids: Ids{ + Simkl: anime.Show.Ids.Simkl, + Mal: anime.Show.Ids.Mal, + Anilist: anime.Show.Ids.AniList, + }, + } + + respBody := SimklHelper("POST", url, show) + + var success SimklDeleteType + + err := json.Unmarshal(respBody, &success) + if err != nil { + log.Printf("Failed at unmarshal, %s\n", err) + } + + if success.Deleted.Shows >= 1 { + return true + } else { + return false + } +} diff --git a/SimklTypes.go b/SimklTypes.go index 0834f6c..12afbfa 100644 --- a/SimklTypes.go +++ b/SimklTypes.go @@ -119,3 +119,15 @@ type SimklSearchType []struct { TotalEpisodes int `json:"total_episodes"` AnimeType string `json:"anime_type"` } + +type SimklDeleteType struct { + Deleted struct { + Movies int `json:"movies"` + Shows int `json:"shows"` + Episodes int `json:"episodes"` + } `json:"deleted"` + NotFound struct { + Movies []interface{} `json:"movies"` + Shows []interface{} `json:"shows"` + } `json:"not_found"` +} diff --git a/bruno/AniTrack/AniList/Get Items/AniList Item.bru b/bruno/AniTrack/AniList/Get Items/AniList Item.bru index 9953d23..46d101c 100644 --- a/bruno/AniTrack/AniList/Get Items/AniList Item.bru +++ b/bruno/AniTrack/AniList/Get Items/AniList Item.bru @@ -93,7 +93,7 @@ body:graphql { body:graphql:vars { { "userId": 413504, - "mediaId": 168345, + "mediaId": 170998, "listType": "ANIME" } } diff --git a/bruno/AniTrack/AniList/Get Items/AniList Search.bru b/bruno/AniTrack/AniList/Get Items/AniList Search.bru index 6a0cfe2..6446a50 100644 --- a/bruno/AniTrack/AniList/Get Items/AniList Search.bru +++ b/bruno/AniTrack/AniList/Get Items/AniList Search.bru @@ -53,7 +53,7 @@ body:graphql { body:graphql:vars { { - "search": "dungeon people", + "search": "dan-da-dan", "listType": "ANIME" } } diff --git a/bruno/AniTrack/MAL/Get Single Anime.bru b/bruno/AniTrack/MAL/Get Single Anime.bru index 7ec6495..3556f16 100644 --- a/bruno/AniTrack/MAL/Get Single Anime.bru +++ b/bruno/AniTrack/MAL/Get Single Anime.bru @@ -5,7 +5,7 @@ meta { } get { - url: https://api.myanimelist.net/v2/anime/53580?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 + url: https://api.myanimelist.net/v2/anime/57380?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 body: none auth: bearer } diff --git a/bruno/AniTrack/environments/Dev.bru b/bruno/AniTrack/environments/Dev.bru index 94141dc..7da3f20 100644 --- a/bruno/AniTrack/environments/Dev.bru +++ b/bruno/AniTrack/environments/Dev.bru @@ -8,11 +8,11 @@ vars { MAL_CALLBACK_URI: {{process.env.MAL_CALLBACK_URI}} } vars:secret [ + ANILIST_ACCESS_TOKEN, code, SIMKL_AUTH_TOKEN, - ANILIST_ACCESS_TOKEN, MAL_CODE, MAL_VERIFIER, MAL_USER, - MAL_ACCESS_TOKEN + ~MAL_ACCESS_TOKEN ] diff --git a/frontend/src/helperComponents/Anime.svelte b/frontend/src/helperComponents/Anime.svelte index bb05634..d1d81dd 100644 --- a/frontend/src/helperComponents/Anime.svelte +++ b/frontend/src/helperComponents/Anime.svelte @@ -20,10 +20,11 @@ import type {AniListUpdateVariables} from "../anilist/types/AniListTypes"; import convertDateStringToAniList from "../helperFunctions/convertDateStringToAniList"; import { - AniListUpdateEntry, + AniListDeleteEntry, + AniListUpdateEntry, DeleteMyAnimeListEntry, MyAnimeListUpdate, SimklSyncEpisodes, - SimklSyncRating, + SimklSyncRating, SimklSyncRemove, SimklSyncStatus } from "../../wailsjs/go/main/App"; import {AddAnimeServiceToTable} from "../helperModules/AddAnimeServiceToTable.svelte"; @@ -286,6 +287,16 @@ submitSuccess.set(true) setTimeout(() => submitSuccess.set(false), 2000) } + + const deleteEntries = async () => { + submitting.set(true) + if (isAniListLoggedIn && currentAniListAnime.data.MediaList.mediaId !== 0) await AniListDeleteEntry(currentAniListAnime.data.MediaList.id) + if (malLoggedIn && currentMalAnime.id !== 0) await DeleteMyAnimeListEntry(currentMalAnime.id) + if (simklLoggedIn && currentSimklAnime.show.ids.simkl !== 0) await SimklSyncRemove(currentSimklAnime) + submitting.set(false) + submitSuccess.set(true) + setTimeout(() => submitSuccess.set(false), 2000) + }
@@ -420,7 +431,27 @@ -
+
+
+ +