added getting MAL watchlist

This commit is contained in:
John O'Keefe 2024-08-15 21:27:31 -04:00
parent f9c6f4b827
commit 22ff290a81
9 changed files with 252 additions and 22 deletions

56
MALFunctions.go Normal file
View File

@ -0,0 +1,56 @@
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"strconv"
"strings"
)
func MALHelper(method string, malUrl string, body url.Values) json.RawMessage {
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)
resp, err := client.Do(req)
if err != nil {
fmt.Println("Errored when sending request to the server")
message, _ := json.Marshal(struct {
Message string `json:"message" ts_type:"message"`
}{
Message: "Errored when sending request to the server" + err.Error(),
})
return message
}
defer resp.Body.Close()
respBody, _ := io.ReadAll(resp.Body)
return respBody
}
func (a *App) GetMyAnimeList(count int) MALWatchlist {
limit := strconv.Itoa(count)
user := a.GetMyAnimeListLoggedInUser()
malUrl := "https://api.myanimelist.net/v2/users/" + user.Name + "/animelist?fields=list_status&status=watching&limit=" + limit
var malList MALWatchlist
respBody := MALHelper("GET", malUrl, nil)
err := json.Unmarshal(respBody, &malList)
if err != nil {
log.Printf("Failed to unmarshal json response, %s\n", err)
}
return malList
}

View File

@ -1,5 +1,7 @@
package main package main
import "time"
type MyAnimeListJWT struct { type MyAnimeListJWT struct {
TokenType string `json:"token_type"` TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"` ExpiresIn int `json:"expires_in"`
@ -37,3 +39,29 @@ type AnimeStatistics struct {
NumTimesRewatched int `json:"num_times_rewatched" ts_type:"numTimesRewatched"` NumTimesRewatched int `json:"num_times_rewatched" ts_type:"numTimesRewatched"`
MeanScore float64 `json:"mean_score" ts_type:"meanScore"` MeanScore float64 `json:"mean_score" ts_type:"meanScore"`
} }
type MALWatchlist struct {
Data []struct {
Node struct {
Id int `json:"id" ts_type:"id"`
Title string `json:"title" ts_type:"title"`
MainPicture struct {
Medium string `json:"medium" json:"medium"`
Large string `json:"large" json:"large"`
} `json:"main_picture" json:"mainPicture"`
} `json:"node" json:"node"`
ListStatus 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.Time `json:"updated_at" ts_type:"updatedAt"`
StartDate string `json:"start_date" ts_type:"startDate"`
FinishDate string `json:"finish_date" ts_type:"finishDate"`
} `json:"list_status" ts_type:"listStatus"`
} `json:"data" json:"data"`
Paging struct {
Previous string `json:"previous" ts_type:"previous"`
Next string `json:"next" ts_type:"next"`
} `json:"paging" ts_type:"paging"`
}

View File

@ -12,5 +12,7 @@ vars:secret [
SIMKL_AUTH_TOKEN, SIMKL_AUTH_TOKEN,
ANILIST_ACCESS_TOKEN, ANILIST_ACCESS_TOKEN,
MAL_CODE, MAL_CODE,
MAL_VERIFIER MAL_VERIFIER,
MAL_USER,
MAL_ACCESS_TOKEN
] ]

View File

@ -14,6 +14,7 @@
title, title,
watchListPage, watchListPage,
animePerPage, animePerPage,
malWatchList
} from "./GlobalVariablesAndHelperFunctions.svelte"; } from "./GlobalVariablesAndHelperFunctions.svelte";
import { import {
CheckIfAniListLoggedIn, CheckIfAniListLoggedIn,
@ -23,7 +24,7 @@
GetAniListUserWatchingList, GetAniListUserWatchingList,
GetSimklLoggedInUser, GetSimklLoggedInUser,
SimklGetUserWatchlist, SimklGetUserWatchlist,
GetMyAnimeListLoggedInUser, GetMyAnimeListLoggedInUser, GetMyAnimeList,
} from "../wailsjs/go/main/App"; } from "../wailsjs/go/main/App";
import {MediaListSort} from "./anilist/types/AniListTypes"; import {MediaListSort} from "./anilist/types/AniListTypes";
import type {AniListCurrentUserWatchList} from "./anilist/types/AniListCurrentUserWatchListType" import type {AniListCurrentUserWatchList} from "./anilist/types/AniListCurrentUserWatchListType"
@ -51,38 +52,41 @@
const size = "xl" const size = "xl"
onMount(async () => { onMount(async () => {
await CheckIfAniListLoggedIn().then(result => { await CheckIfAniListLoggedIn().then(loggedIn => {
if (result) { if (loggedIn) {
GetAniListLoggedInUser().then(result => { GetAniListLoggedInUser().then(user => {
aniListUser.set(result) aniListUser.set(user)
if (isAniListPrimary) { if (isAniListPrimary) {
GetAniListUserWatchingList(page, perPage, MediaListSort.UpdatedTimeDesc).then((result) => { GetAniListUserWatchingList(page, perPage, MediaListSort.UpdatedTimeDesc).then((watchList) => {
aniListWatchlist.set(result) aniListWatchlist.set(watchList)
aniListLoggedIn.set(true) aniListLoggedIn.set(loggedIn)
}) })
} else { } else {
aniListLoggedIn.set(result) aniListLoggedIn.set(loggedIn)
} }
}) })
} }
}) })
await CheckIfMyAnimeListLoggedIn().then(result => { await CheckIfMyAnimeListLoggedIn().then(loggedIn => {
if (result) { if (loggedIn) {
GetMyAnimeListLoggedInUser().then(result => { GetMyAnimeListLoggedInUser().then(user => {
malUser.set(result) malUser.set(user)
malLoggedIn.set(result) GetMyAnimeList(1000).then(watchList => {
malWatchList.set(watchList)
malLoggedIn.set(loggedIn)
})
}) })
} }
}) })
await CheckIfSimklLoggedIn().then(result => { await CheckIfSimklLoggedIn().then(loggedIn => {
if (result) { if (loggedIn) {
GetSimklLoggedInUser().then(result => { GetSimklLoggedInUser().then(user => {
simklUser.set(result) simklUser.set(user)
SimklGetUserWatchlist().then(result => { SimklGetUserWatchlist().then(result => {
simklWatchList.set(result) simklWatchList.set(result)
simklLoggedIn.set(result) simklLoggedIn.set(loggedIn)
}) })
}) })
} }

View File

@ -11,7 +11,7 @@
import {writable} from 'svelte/store' import {writable} from 'svelte/store'
import type {SimklUser, SimklWatchList} from "./simkl/types/simklTypes"; import type {SimklUser, SimklWatchList} from "./simkl/types/simklTypes";
import {type AniListUser, MediaListSort} from "./anilist/types/AniListTypes"; import {type AniListUser, MediaListSort} from "./anilist/types/AniListTypes";
import type {MyAnimeListUser} from "./mal/types/MALTypes"; import type {MALWatchlist, MyAnimeListUser} from "./mal/types/MALTypes";
export let aniListAnime: AniListGetSingleAnime export let aniListAnime: AniListGetSingleAnime
export let title = writable("") export let title = writable("")
@ -25,6 +25,7 @@
export let aniListUser = writable({} as AniListUser) export let aniListUser = writable({} as AniListUser)
export let malUser = writable({} as MyAnimeListUser) export let malUser = writable({} as MyAnimeListUser)
export let aniListWatchlist = writable({} as AniListCurrentUserWatchList) export let aniListWatchlist = writable({} as AniListCurrentUserWatchList)
export let malWatchList = writable({} as MALWatchlist)
export let watchListPage = writable(1) export let watchListPage = writable(1)
export let animePerPage = writable(20) export let animePerPage = writable(20)

View File

@ -29,3 +29,37 @@ export interface AnimeStatistics {
numTimesRewatched: number numTimesRewatched: number
meanScore: number meanScore: number
} }
export interface MALWatchlist {
data: {
node: Node
listStatus: ListStatus
}[]
paging: Paging
}
export interface Node {
id: number
title: string
mainPicture: MainPicture
}
export interface MainPicture {
medium: string
large: string
}
export interface ListStatus {
status: string
score: number
numEpisodesWatched: number
isRewatching: boolean
updated_at: string
startDate: string
finishDate: string
}
export interface Paging {
previous: string
next: string
}

View File

@ -20,6 +20,8 @@ export function GetAniListLoggedInUser():Promise<main.AniListUser>;
export function GetAniListUserWatchingList(arg1:number,arg2:number,arg3:string):Promise<main.AniListCurrentUserWatchList>; export function GetAniListUserWatchingList(arg1:number,arg2:number,arg3:string):Promise<main.AniListCurrentUserWatchList>;
export function GetMyAnimeList(arg1:number):Promise<main.MALWatchlist>;
export function GetMyAnimeListLoggedInUser():Promise<main.MyAnimeListUser>; export function GetMyAnimeListLoggedInUser():Promise<main.MyAnimeListUser>;
export function GetSimklLoggedInUser():Promise<main.SimklUser>; export function GetSimklLoggedInUser():Promise<main.SimklUser>;

View File

@ -38,6 +38,10 @@ export function GetAniListUserWatchingList(arg1, arg2, arg3) {
return window['go']['main']['App']['GetAniListUserWatchingList'](arg1, arg2, arg3); return window['go']['main']['App']['GetAniListUserWatchingList'](arg1, arg2, arg3);
} }
export function GetMyAnimeList(arg1) {
return window['go']['main']['App']['GetMyAnimeList'](arg1);
}
export function GetMyAnimeListLoggedInUser() { export function GetMyAnimeListLoggedInUser() {
return window['go']['main']['App']['GetMyAnimeListLoggedInUser'](); return window['go']['main']['App']['GetMyAnimeListLoggedInUser']();
} }

View File

@ -121,6 +121,38 @@ export namespace main {
this.anime_type = source["anime_type"]; this.anime_type = source["anime_type"];
} }
} }
export class MALWatchlist {
data: struct { Node struct { Id int "json:\"id\" ts_type:\"id\""; Title string "json:\"title\" ts_type:\"title\""; MainPicture struct { Medium string "json:\"medium\" json:\"medium\""; Large string "json:\"large\" json:\"large\"" } "json:\"main_picture\" json:\"mainPicture\"" } "json:\"node\" json:\"node\""; ListStatus 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.Time "json:\"updated_at\" ts_type:\"updatedAt\""; StartDate string "json:\"start_date\" ts_type:\"startDate\""; FinishDate string "json:\"finish_date\" ts_type:\"finishDate\"" } "json:\"list_status\" ts_type:\"listStatus\"" }[];
paging: paging;
static createFrom(source: any = {}) {
return new MALWatchlist(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.data = this.convertValues(source["data"], struct { Node struct { Id int "json:\"id\" ts_type:\"id\""; Title string "json:\"title\" ts_type:\"title\""; MainPicture struct { Medium string "json:\"medium\" json:\"medium\""; Large string "json:\"large\" json:\"large\"" } "json:\"main_picture\" json:\"mainPicture\"" } "json:\"node\" json:\"node\""; ListStatus 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.Time "json:\"updated_at\" ts_type:\"updatedAt\""; StartDate string "json:\"start_date\" ts_type:\"startDate\""; FinishDate string "json:\"finish_date\" ts_type:\"finishDate\"" } "json:\"list_status\" ts_type:\"listStatus\"" });
this.paging = source["paging"];
}
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;
@ -301,3 +333,70 @@ export namespace struct { MediaList main {
} }
export namespace struct { Node struct { Id int "json:\"id\" ts_type:\"id\""; Title string "json:\"title\" ts_type:\"title\""; MainPicture struct { Medium string "json:\"medium\" json:\"medium\""; Large string "json:\"large\" json:\"large\"" } "json:\"main_picture\" json:\"mainPicture\"" } "json:\"node\" json:\"node\""; ListStatus 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 {
// Go type: struct { Id int "json:\"id\" ts_type:\"id\""; Title string "json:\"title\" ts_type:\"title\""; MainPicture struct { Medium string "json:\"medium\" json:\"medium\""; Large string "json:\"large\" json:\"large\"" } "json:\"main_picture\" json:\"mainPicture\"" }
node: any;
list_status: listStatus;
static createFrom(source: any = {}) {
return new (source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.node = this.convertValues(source["node"], Object);
this.list_status = source["list_status"];
}
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 class {
status: status;
score: score;
num_episodes_watched: numEpisodesWatched;
is_rewatching: isRewatching;
updated_at: updatedAt;
start_date: startDate;
finish_date: finishDate;
static createFrom(source: any = {}) {
return new (source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.status = source["status"];
this.score = source["score"];
this.num_episodes_watched = source["num_episodes_watched"];
this.is_rewatching = source["is_rewatching"];
this.updated_at = source["updated_at"];
this.start_date = source["start_date"];
this.finish_date = source["finish_date"];
}
}
}