2 Commits

56 changed files with 1936 additions and 1216 deletions

3
.gitignore vendored
View File

@@ -33,6 +33,3 @@ environment.go
# REST (http files) # REST (http files)
http-client.private.env.json http-client.private.env.json
# Build artifacts
build/*.tar.gz

View File

@@ -93,7 +93,6 @@ func (a *App) GetAniListItem(aniId int, login bool) AniListGetSingleAnime {
timeUntilAiring timeUntilAiring
episode episode
} }
genres
tags{ tags{
id id
name name
@@ -223,7 +222,6 @@ func (a *App) AniListSearch(query string) any {
timeUntilAiring timeUntilAiring
episode episode
} }
genres
tags{ tags{
id id
name name
@@ -309,7 +307,6 @@ func (a *App) GetAniListUserWatchingList(page int, perPage int, sort string) Ani
timeUntilAiring timeUntilAiring
episode episode
} }
genres
tags{ tags{
id id
name name

View File

@@ -74,8 +74,7 @@ type MediaList struct {
TimeUntilAiring int `json:"timeUntilAiring"` TimeUntilAiring int `json:"timeUntilAiring"`
Episode int `json:"episode"` Episode int `json:"episode"`
} `json:"nextAiringEpisode"` } `json:"nextAiringEpisode"`
Genres []string `json:"genres"` Tags []struct {
Tags []struct {
Id int `json:"id"` Id int `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Description string `json:"description"` Description string `json:"description"`

View File

@@ -33,7 +33,6 @@ body:graphql {
english english
native native
} }
genre
description description
coverImage { coverImage {
large large

View File

@@ -1,42 +1,27 @@
<script lang="ts"> <script lang="ts">
import {
aniListLoggedIn,
malLoggedIn,
simklLoggedIn,
} from "./helperModules/GlobalVariablesAndHelperFunctions.svelte";
import {onMount} from "svelte"; import {onMount} from "svelte";
import {userStore} from "./helperFunctions/userStore"
import Router from "svelte-spa-router" import Router from "svelte-spa-router"
import Home from "./routes/Home.svelte"; import Home from "./routes/Home.svelte";
import {wrap} from "svelte-spa-router/wrap"; import {wrap} from "svelte-spa-router/wrap";
import Spinner from "./helperComponents/Spinner.svelte"; import Spinner from "./helperComponents/Spinner.svelte";
import Header from "./helperComponents/Header.svelte"; import Header from "./helperComponents/Header.svelte";
import {CheckIfAniListLoggedInAndLoadWatchList} from "./helperModules/CheckIfAniListLoggedInAndLoadWatchList.svelte";
import { CheckIfMALLoggedInAndSetUser } from "./helperModules/CheckIfMyAnimeListLoggedIn.svelte";
import {CheckIfSimklLoggedInAndSetUser} from "./helperModules/CheckIsSimklLoggedIn.svelte"
import {CheckIfAniListLoggedIn} from "../wailsjs/go/main/App";
onMount(async () => { onMount(async () => {
let isAniListLoggedIn: boolean await userStore.checkProvider('anilist')
let isMALLoggedIn: boolean await userStore.checkProvider('mal')
let isSimklLoggedIn: boolean await userStore.checkProvider('simkl')
aniListLoggedIn.subscribe((value) => isAniListLoggedIn = value)
malLoggedIn.subscribe((value) => isMALLoggedIn = value)
simklLoggedIn.subscribe((value) => isSimklLoggedIn = value)
console.log(isAniListLoggedIn)
!isAniListLoggedIn && await CheckIfAniListLoggedInAndLoadWatchList()
!isMALLoggedIn && await CheckIfMALLoggedInAndSetUser()
!isSimklLoggedIn && await CheckIfSimklLoggedInAndSetUser()
}) })
</script> </script>
<Header /> {#if $userStore.anilist.isLoggedIn}
<Router routes={{ <Header />
'/': Home, <Router routes={{
'/anime/:id': wrap({ '/': Home,
asyncComponent: () => import('./routes/AnimeRoutePage.svelte'), '/anime/:id': wrap({
conditions: [async () => await CheckIfAniListLoggedIn()], asyncComponent: () => import('./routes/AnimeRoutePage.svelte'),
loadingComponent: Spinner loadingComponent: Spinner
}), }),
// '*': "Not Found" // '*': "Not Found"
}} /> }} />
{/if}

View File

@@ -1,6 +1,6 @@
import type {AniListGetSingleAnime} from "../anilist/types/AniListCurrentUserWatchListType"; import type {AniListGetSingleAnime} from "../types/AniListCurrentUserWatchListType";
export const AniListGetSingleAnimeDefaultData: AniListGetSingleAnime = { export const AniListGetSingleAnimeDefaultData: AniListGetSingleAnime = {
data: { data: {
MediaList: { MediaList: {
id: 0, id: 0,
@@ -26,7 +26,18 @@ export const AniListGetSingleAnimeDefaultData: AniListGetSingleAnime = {
airingAt: 0, airingAt: 0,
timeUntilAiring: 0, timeUntilAiring: 0,
episode: 0, episode: 0,
} },
tags: [
{
id: 0,
name: "",
description: "",
rank: 0,
isMediaSpoiler: false,
isAdult: false
}
],
isAdult: false
}, },
status: "", status: "",
startedAt: { startedAt: {

View File

@@ -0,0 +1,89 @@
import type {AniListCurrentUserWatchList} from "../types/AniListCurrentUserWatchListType"
export const AniListWatchListDefaultData: AniListCurrentUserWatchList = {
data: {
Page: {
pageInfo: {
total: 0,
perPage: 0,
currentPage: 0,
lastPage: 0,
hasNextPage: false
},
mediaList: [
{
id: 0,
mediaId: 0,
userId: 0,
media: {
id: 0,
idMal: 0,
title: {
romaji: "",
english: "",
native: "",
},
description: "",
coverImage: {
large: "",
},
season: "",
seasonYear: 0,
status: "",
episodes: 0,
nextAiringEpisode: {
airingAt: 0,
timeUntilAiring: 0,
episode: 0,
},
tags: [
{
id: 0,
name: "",
description: "",
rank: 0,
isMediaSpoiler: false,
isAdult: false,
},
],
isAdult: false,
},
status: "",
startedAt: {
year: 0,
month: 0,
day: 0,
},
completedAt: {
year: 0,
month: 0,
day: 0,
},
notes: "",
progress: 0,
score: 0,
repeat: 0,
user: {
id: 0,
name: "",
avatar: {
large: "",
medium: "",
},
statistics: {
anime: {
count: 0,
statuses: [
{
status: "",
count: 0,
}
]
}
}
}
}
]
}
}
}

View File

@@ -0,0 +1,67 @@
import type {AniListUser} from "../types/AniListTypes";
import type {MyAnimeListUser} from "../types/MALTypes";
import type {SimklUser} from "../types/simklTypes";
export const AniListUserDefaultData: AniListUser = {
"data": {
"Viewer": {
id: 0,
name: "",
avatar: {
large: "",
medium: "",
},
bannerImage: "",
siteUrl: ""
}
}
}
export const MALUserDefaultData: MyAnimeListUser = {
id: 0,
name: "",
picture: "",
gender: "",
birthday: "",
location: "",
joinedAt: "",
AnimeStatistics: {
numItemsWatching: 0,
numItemsCompleted: 0,
numItemsOnHold: 0,
numItemsDropped: 0,
numItemsPlanToWatch: 0,
numItems: 0,
numDaysWatched: 0,
numDaysWatching: 0,
numDaysCompleted: 0,
numDaysOnHold: 0,
numDaysDropped: 0,
numDays: 0,
numEpisodes: 0,
numTimesRewatched: 0,
meanScore: 0
},
timeZone: "",
isSupporter: false
}
export const SimklUserDefaultData: SimklUser = {
user: {
name: "",
joined_at: "",
gender: "",
avatar: "",
bio: "",
loc: "",
age: "",
},
account: {
id: 0,
timezone: "",
type: "",
},
connections: {
facebook: false
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,39 +1,9 @@
<script lang="ts"> <script lang="ts">
import { Avatar } from "flowbite-svelte"; import { Avatar } from "flowbite-svelte";
import type { AniListUser } from "../anilist/types/AniListTypes"; import {userStore} from "../helperFunctions/userStore"
import {
aniListLoggedIn,
aniListUser,
malUser,
simklUser,
malLoggedIn,
simklLoggedIn,
loginToAniList,
loginToMAL,
loginToSimkl,
logoutOfAniList,
logoutOfMAL,
logoutOfSimkl
} 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 {SimklUser} from "../simkl/types/simklTypes";
import { ShowVersion } from "../../wailsjs/go/main/App"; import { ShowVersion } from "../../wailsjs/go/main/App";
let currentAniListUser: AniListUser;
let currentMALUser: MyAnimeListUser;
let currentSimklUser: SimklUser;
let isAniListLoggedIn: boolean;
let isSimklLoggedIn: boolean;
let isMALLoggedIn: boolean;
aniListUser.subscribe((value) => (currentAniListUser = value));
malUser.subscribe((value) => (currentMALUser = value))
simklUser.subscribe(value => currentSimklUser = value)
aniListLoggedIn.subscribe((value) => (isAniListLoggedIn = value));
simklLoggedIn.subscribe((value) => (isSimklLoggedIn = value));
malLoggedIn.subscribe((value) => (isMALLoggedIn = value));
function dropdownUser(): void { function dropdownUser(): void {
let dropdown = document.querySelector("#userDropdown"); let dropdown = document.querySelector("#userDropdown");
dropdown.classList.toggle("hidden"); dropdown.classList.toggle("hidden");
@@ -56,9 +26,9 @@
<div class="relative"> <div class="relative">
<button id="userDropdownButton" on:click={dropdownUser}> <button id="userDropdownButton" on:click={dropdownUser}>
{#if isAniListLoggedIn} {#if $userStore.anilist.isLoggedIn}
<Avatar <Avatar
src={currentAniListUser.data.Viewer.avatar.medium} src={$userStore.anilist.user.data.Viewer.avatar.medium}
class="cursor-pointer" class="cursor-pointer"
dot={{ color: "green" }} dot={{ color: "green" }}
/> />
@@ -71,8 +41,8 @@
class="absolute hidden right-0 2xl:left-1/2 2xl:-translate-x-1/2 z-10 divide-y rounded-lg shadow w-44 bg-gray-700 divide-gray-600" class="absolute hidden right-0 2xl:left-1/2 2xl:-translate-x-1/2 z-10 divide-y rounded-lg shadow w-44 bg-gray-700 divide-gray-600"
> >
<div class="px-4 py-3 text-sm text-white"> <div class="px-4 py-3 text-sm text-white">
{#if isAniListLoggedIn} {#if $userStore.anilist.isLoggedIn}
<div>{currentAniListUser.data.Viewer.name}</div> <div>{$userStore.anilist.user.data.Viewer.name}</div>
{:else} {:else}
<div>You are not logged into AniList</div> <div>You are not logged into AniList</div>
{/if} {/if}
@@ -81,60 +51,60 @@
class="py-2 text-sm text-gray-200" class="py-2 text-sm text-gray-200"
aria-labelledby="dropdownUserAvatarButton" aria-labelledby="dropdownUserAvatarButton"
> >
{#if isAniListLoggedIn} {#if $userStore.anilist.isLoggedIn}
<li> <li>
<button <button
on:click={logoutOfAniList} on:click={() => userStore.logout("anilist")}
class="block px-4 py-2 w-full hover:bg-gray-600 truncate bg-green-800 hover:text-white" class="block px-4 py-2 w-full hover:bg-gray-600 truncate bg-green-800 hover:text-white"
> >
<span class="maple-font text-lg text-green-200 mr-4">A</span>Logout {currentAniListUser.data.Viewer.name} <span class="maple-font text-lg text-green-200 mr-4">A</span>Logout {$userStore.anilist.user.data.Viewer.name}
</button> </button>
</li> </li>
{:else} {:else}
<li> <li>
<button on:click={() => { <button on:click={() => {
dropdownUser() dropdownUser()
loginToAniList() userStore.checkProvider("anilist")
}} }}
class="block px-4 py-2 w-full hover:bg-gray-600 truncate hover:text-white"> class="block px-4 py-2 w-full hover:bg-gray-600 truncate hover:text-white">
<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>
</li> </li>
{/if} {/if}
{#if isMALLoggedIn} {#if $userStore.mal.isLoggedIn}
<li> <li>
<button <button
on:click={logoutOfMAL} on:click={() => userStore.logout("mal")}
class="block px-4 py-2 w-full hover:bg-gray-600 truncate bg-blue-800 hover:text-white" class="block px-4 py-2 w-full hover:bg-gray-600 truncate bg-blue-800 hover:text-white"
> >
<span class="maple-font text-lg text-blue-200 mr-4">M</span>Logout {currentMALUser.name} <span class="maple-font text-lg text-blue-200 mr-4">M</span>Logout {$userStore.mal.user.name}
</button> </button>
</li> </li>
{:else} {:else}
<li> <li>
<button on:click={() => { <button on:click={() => {
dropdownUser() dropdownUser()
loginToMAL() userStore.checkProvider("mal")
}} }}
class="block px-4 py-2 w-full hover:bg-gray-600 truncate hover:text-white"> class="block px-4 py-2 w-full hover:bg-gray-600 truncate hover:text-white">
<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>
</li> </li>
{/if} {/if}
{#if isSimklLoggedIn} {#if $userStore.simkl.isLoggedIn}
<li> <li>
<button <button
on:click={logoutOfSimkl} on:click={() => userStore.logout("simkl")}
class="block px-4 py-2 w-full hover:bg-gray-600 truncate bg-indigo-800 hover:text-white" class="block px-4 py-2 w-full hover:bg-gray-600 truncate bg-indigo-800 hover:text-white"
> >
<span class="maple-font text-lg text-indigo-200 mr-4">S</span>Logout {currentSimklUser.user.name} <span class="maple-font text-lg text-indigo-200 mr-4">S</span>Logout {$userStore.simkl.user.user.name}
</button> </button>
</li> </li>
{:else} {:else}
<li> <li>
<button on:click={() => { <button on:click={() => {
dropdownUser() dropdownUser()
loginToSimkl() userStore.checkProvider("simkl")
}} }}
class="block px-4 py-2 w-full hover:bg-gray-600 truncate hover:text-white"> class="block px-4 py-2 w-full hover:bg-gray-600 truncate hover:text-white">
<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

View File

@@ -1,102 +1,63 @@
<script lang="ts"> <script lang="ts">
import Search from "./Search.svelte"; import Search from "./Search.svelte"
import {
aniListLoggedIn,
loginToAniList,
loginToMAL,
loginToSimkl,
malLoggedIn,
simklLoggedIn,
} 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"
import { link } from "svelte-spa-router"; import {userStore} from "../helperFunctions/userStore"
let isAniListLoggedIn: boolean;
let isSimklLoggedIn: boolean;
let isMALLoggedIn: boolean;
aniListLoggedIn.subscribe((value) => (isAniListLoggedIn = value));
simklLoggedIn.subscribe((value) => (isSimklLoggedIn = value));
malLoggedIn.subscribe((value) => (isMALLoggedIn = value));
</script> </script>
<nav class="border-gray-200 bg-gray-900"> <nav class="border-gray-200 bg-gray-900">
<div <div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4"
>
<div class="flex items-center space-x-3 rtl:space-x-reverse"> <div class="flex items-center space-x-3 rtl:space-x-reverse">
<a href="/" use:link <a href="/"><img src={logo} class="h-8" alt="AniTrack Logo"/></a>
><img src={logo} class="h-8" alt="AniTrack Logo" /></a
>
</div> </div>
<div <div class="flex items-center min-[950px]:order-2 space-x-3 min-[950px]:space-x-0 rtl:space-x-reverse">
class="flex items-center min-[950px]:order-2 space-x-3 min-[950px]:space-x-0 rtl:space-x-reverse"
>
<div class="min-[950px]:block min-[950px]:mr-4"> <div class="min-[950px]:block min-[950px]:mr-4">
<Search /> <Search />
</div> </div>
<AvatarMenu /> <AvatarMenu/>
<button <button on:click={() => {
on:click={() => { let menu = document.querySelector("#navbar-user")
let menu = document.querySelector("#navbar-user"); menu.classList.toggle("hidden")
menu.classList.toggle("hidden"); }} type="button"
}} class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm rounded-lg min-[950px]:hidden focus:outline-none focus:ring-2 text-gray-400 hover:bg-gray-700 focus:ring-gray-600"
type="button" aria-controls="navbar-user" aria-expanded="false">
class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm rounded-lg min-[950px]:hidden focus:outline-none focus:ring-2 text-gray-400 hover:bg-gray-700 focus:ring-gray-600"
aria-controls="navbar-user"
aria-expanded="false"
>
<span class="sr-only">Open main menu</span> <span class="sr-only">Open main menu</span>
<svg <svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
class="w-5 h-5" viewBox="0 0 17 14">
aria-hidden="true" <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
xmlns="http://www.w3.org/2000/svg" d="M1 1h15M1 7h15M1 13h15"/>
fill="none"
viewBox="0 0 17 14"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M1 1h15M1 7h15M1 13h15"
/>
</svg> </svg>
</button> </button>
</div> </div>
<div <div class="hidden items-center justify-between w-full pb-4 min-[950px]:pb-0 min-[950px]:flex min-[950px]:w-auto min-[950px]:order-1 border border-gray-700 min-[950px]:border-0 bg-gray-800 min-[950px]:bg-transparent rounded-lg" id="navbar-user">
class="hidden items-center justify-between w-full pb-4 min-[950px]:pb-0 min-[950px]:flex min-[950px]:w-auto min-[950px]:order-1 border border-gray-700 min-[950px]:border-0 bg-gray-800 min-[950px]:bg-transparent rounded-lg" <ul class="flex flex-col font-medium pb-6 min-[950px]:p-0 mt-4 min-[950px]:space-x-8 rtl:space-x-reverse min-[950px]:flex-row min-[950px]:mt-0">
id="navbar-user"
>
<ul
class="flex flex-col font-medium pb-6 min-[950px]:p-0 mt-4 min-[950px]:space-x-8 rtl:space-x-reverse min-[950px]:flex-row min-[950px]:mt-0"
>
<li> <li>
{#if !isAniListLoggedIn} {#if !$userStore.anilist.isLoggedIn}
<button on:click={loginToAniList}> <button on:click={() => userStore.checkProvider("anilist")}>
<!-- 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">--> <!-- 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">-->
AniList Login AniList Login
</button> </button>
{/if} {/if}
{#if !isMALLoggedIn} {#if !$userStore.mal.isLoggedIn}
<button on:click={loginToMAL}> <button on:click={() => userStore.checkProvider("mal")}>
<!-- 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">--> <!-- 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">-->
MyAnimeList Login MyAnimeList Login
</button> </button>
{/if} {/if}
</li> </li>
<li> <li>
{#if !isSimklLoggedIn} {#if !$userStore.simkl.isLoggedIn}
<button on:click={loginToSimkl}> <button on:click={() => userStore.checkProvider("simkl")}>
<!-- 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">--> <!-- 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">-->
Simkl Login Simkl Login
</button> </button>
{/if} {/if}
</li> </li>
</ul> </ul>
<div class="flex justify-center min-[950px]:hidden"> <div class="flex justify-center min-[950px]:hidden">
<Search /> <Search/>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -6,9 +6,9 @@
watchListPage, watchListPage,
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte"; } from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
import type {AniListCurrentUserWatchList} from "../anilist/types/AniListCurrentUserWatchListType" import type {AniListCurrentUserWatchList} from "../types/AniListCurrentUserWatchListType"
import {GetAniListUserWatchingList} from "../../wailsjs/go/main/App"; import {GetAniListUserWatchingList} from "../../wailsjs/go/main/App";
import {MediaListSort} from "../anilist/types/AniListTypes"; import {MediaListSort} from "../types/AniListTypes";
let aniListWatchListLoaded: AniListCurrentUserWatchList let aniListWatchListLoaded: AniListCurrentUserWatchList
let page: number let page: number

View File

@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import {AniListSearch} from "../../wailsjs/go/main/App"; import {AniListSearch} from "../../wailsjs/go/main/App";
import type {AniSearchList} from "../anilist/types/AniListTypes"; import type {AniSearchList} from "../types/AniListTypes";
import {push} from "svelte-spa-router"; import {push} from "svelte-spa-router";
let aniSearch = "" let aniSearch = ""

View File

@@ -1,68 +1,59 @@
<script lang="ts"> <script lang="ts">
import { import {
aniListLoggedIn,
aniListWatchlist, aniListWatchlist,
GetAnimeSingleItem, GetAnimeSingleItem,
loading, loading,
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte"; } from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
import {push} from "svelte-spa-router"; import {push} from "svelte-spa-router";
import type {AniListCurrentUserWatchList} from "../anilist/types/AniListCurrentUserWatchListType" import type {AniListCurrentUserWatchList} from "../types/AniListCurrentUserWatchListType"
import {Rating} from "flowbite-svelte"; import {Rating} from "flowbite-svelte";
import loader from '../helperFunctions/loader' import loader from '../helperFunctions/loader'
let isAniListLoggedIn: boolean
let aniListWatchListLoaded: AniListCurrentUserWatchList let aniListWatchListLoaded: AniListCurrentUserWatchList
aniListLoggedIn.subscribe((value) => isAniListLoggedIn = value)
aniListWatchlist.subscribe((value) => aniListWatchListLoaded = value) aniListWatchlist.subscribe((value) => aniListWatchListLoaded = value)
</script> </script>
<div> <div>
{#if isAniListLoggedIn} <div class="mx-auto max-w-2xl p-4 sm:p-6 lg:max-w-7xl lg:px-8 relative items-center">
<div class="mx-auto max-w-2xl p-4 sm:p-6 lg:max-w-7xl lg:px-8 relative items-center"> <h1 class="text-left text-xl font-bold mb-4">Your AniList WatchList</h1>
<h1 class="text-left text-xl font-bold mb-4">Your AniList WatchList</h1>
<div class="grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 xl:gap-x-8"> <div class="grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 xl:gap-x-8">
{#each aniListWatchListLoaded.data.Page.mediaList as media} {#each aniListWatchListLoaded.data.Page.mediaList as media}
<div use:loader={loading} class="aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-lg xl:aspect-h-8 xl:aspect-w-7"> <div use:loader={loading}
<div class="flex flex-col items-center group"> class="aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-lg xl:aspect-h-8 xl:aspect-w-7">
<button on:click={() => { <div class="flex flex-col items-center group">
<button on:click={() => {
push(`#/anime/${media.media.id}`) push(`#/anime/${media.media.id}`)
// loading.set(true)
// GetAniListSingleItem(media.media.id, true).then(() => {
// loading.set(false)
//
// })
}} }}
> >
<img class="rounded-lg" src={media.media.coverImage.large} alt={ <img class="rounded-lg" src={media.media.coverImage.large} alt={
media.media.title.english === "" ? media.media.title.english === "" ?
media.media.title.romaji : media.media.title.romaji :
media.media.title.english media.media.title.english
}/> }/>
</button> </button>
<Rating id="anime-rating" total={5} size={35} rating={media.score/2.0}/> <Rating id="anime-rating" total={5} size={35} rating={media.score/2.0}/>
<button class="mt-4 text-md font-semibold text-white-700" <button class="mt-4 text-md font-semibold text-white-700"
on:click={() => GetAnimeSingleItem(media.media.id, true)}> on:click={() => GetAnimeSingleItem(media.media.id, true)}>
{ {
media.media.title.english === "" ? media.media.title.english === "" ?
media.media.title.romaji : media.media.title.romaji :
media.media.title.english media.media.title.english
} }
</button> </button>
<p class="mt-1 text-lg font-medium text-white-900">{media.progress} <p class="mt-1 text-lg font-medium text-white-900">{media.progress}
/ {media.media.nextAiringEpisode.episode !== 0 ? / {media.media.nextAiringEpisode.episode !== 0 ?
media.media.nextAiringEpisode.episode - 1 : media.media.episodes}</p> media.media.nextAiringEpisode.episode - 1 : media.media.episodes}</p>
{#if media.media.episodes > 0} {#if media.media.episodes > 0}
<p class="mt-1 text-lg font-medium text-white-900">Total <p class="mt-1 text-lg font-medium text-white-900">Total
Episodes: {media.media.episodes}</p> Episodes: {media.media.episodes}</p>
{/if} {/if}
</div>
</div> </div>
{/each} </div>
</div> {/each}
</div> </div>
{/if} </div>
</div> </div>

View File

@@ -1,32 +1,28 @@
<script lang="ts"> <script lang="ts">
import { BrowserOpenURL } from "../../wailsjs/runtime"; import {BrowserOpenURL} from "../../wailsjs/runtime"
export let id: string; export let id: string
export let url = ""; let url = ""
let isAniList = false; let isAniList = false
let isMAL = false; let isMAL = false
let isSimkl = false; let isSimkl = false
let newId = id; let newId = id
let re = /[ams]?-?(.*)/; let re = /[ams]?-?(.*)/
if (id !== undefined && id.length > 0) { if (id !== undefined && id.length > 0) {
isAniList = id.includes("a-"); isAniList = id.includes("a-")
isMAL = id.includes("m-"); isMAL = id.includes("m-")
isSimkl = id.includes("s-"); isSimkl = id.includes("s-")
if (isAniList || isMAL || isSimkl) newId = id.match(re)[1]; newId = id.match(re)[1]
else newId = id; }
}
if (isAniList) url = `https://anilist.co/anime/${newId}`;
if (isMAL) url = `https://myanimelist.net/anime/${newId}`; if (isAniList) url = `https://anilist.co/anime/${newId}`
if (isSimkl) url = `https://simkl.com/anime/${newId}`; if (isMAL) url = `https://myanimelist.net/anime/${newId}`
if (isSimkl) url = `https://simkl.com/anime/${newId}`
</script> </script>
{#if url.length > 0} {#if url.length > 0}
<button <button class="underline underline-offset-2 px-4 py-1" on:click={() => BrowserOpenURL(url)}>{newId}</button>
type="button"
class="underline underline-offset-2 px-4 py-1"
on:click={() => BrowserOpenURL(url)}>{newId}</button
>
{:else} {:else}
{id} {id}
{/if} {/if}

View File

@@ -0,0 +1,121 @@
// stores/user.ts
import {get, writable} from 'svelte/store';
import type {SimklUser, SimklWatchList} from "../types/simklTypes";
import type {AniListUser} from "../types/AniListTypes";
import type {MALWatchlist, MyAnimeListUser} from "../types/MALTypes";
import {
GetAniListLoggedInUser,
GetMyAnimeList,
GetMyAnimeListLoggedInUser,
GetSimklLoggedInUser,
LogoutAniList,
LogoutMyAnimeList,
LogoutSimkl,
SimklGetUserWatchlist
} from "../../wailsjs/go/main/App";
import {LoadAniListWatchList} from "../helperModules/LoadAniListWatchList.svelte";
import {aniListWatchlist, malWatchList, simklWatchList} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte"
import type {AniListCurrentUserWatchList} from "../types/AniListCurrentUserWatchListType";
import {AniListUserDefaultData, MALUserDefaultData, SimklUserDefaultData} from "../defaults/UserTypes";
let aniWatchlist: AniListCurrentUserWatchList
aniListWatchlist.subscribe(value => aniWatchlist = value)
interface UserState {
simkl: { user: SimklUser; isLoggedIn: boolean; isPrimary: boolean };
anilist: { user: AniListUser; isLoggedIn: boolean; isPrimary: boolean };
mal: { user: MyAnimeListUser; isLoggedIn: boolean; isPrimary: boolean };
}
const createUserStore = () => {
const {subscribe, update} = writable<UserState>({
anilist: {user: AniListUserDefaultData, isLoggedIn: false, isPrimary: true},
mal: {user: MALUserDefaultData, isLoggedIn: false, isPrimary: false},
simkl: {user: SimklUserDefaultData, isLoggedIn: false, isPrimary: false}
});
return {
subscribe,
setAniListUser: (user: AniListUser, isPrimary = true) =>
update(s => ({
...s,
anilist: {user, isLoggedIn: true, isPrimary}
})),
setMalUser: (user: MyAnimeListUser, isPrimary = false) =>
update(s => ({
...s,
mal: {user, isLoggedIn: true, isPrimary}
})),
setSimklUser: (user: SimklUser, isPrimary = false) =>
update(s => ({
...s,
simkl: {user, isLoggedIn: true, isPrimary}
})),
setPrimary: (provider: 'simkl' | 'anilist' | 'mal') =>
update(s => ({
...s,
simkl: {...s.simkl, isPrimary: provider === 'simkl'},
anilist: {...s.anilist, isPrimary: provider === 'anilist'},
mal: {...s.mal, isPrimary: provider === 'mal'}
})),
checkProvider: async (provider: 'simkl' | 'anilist' | 'mal') => {
const state = get(userStore);
if (state[provider].isLoggedIn) return;
if (provider === 'anilist') {
const user: AniListUser = await GetAniListLoggedInUser();
console.log(user)
userStore.setAniListUser(user, state.anilist.isPrimary);
console.log(state.anilist.isPrimary)
if (state.anilist.isPrimary) await LoadAniListWatchList();
} else if (provider === 'mal') {
const user: MyAnimeListUser = await GetMyAnimeListLoggedInUser();
userStore.setMalUser(user, state.mal.isPrimary);
if (state.mal.isPrimary) {
const watchList = await GetMyAnimeList(1000);
malWatchList.set(watchList);
}
} else if (provider === 'simkl') {
const user: SimklUser = await GetSimklLoggedInUser();
userStore.setSimklUser(user, state.simkl.isPrimary);
if (state.simkl.isPrimary) {
const watchList = await SimklGetUserWatchlist();
simklWatchList.set(watchList)
}
}
},
logout: async (provider: 'simkl' | 'anilist' | 'mal') => {
update(s => {
s[provider].user = {} as SimklUser | AniListUser | MyAnimeListUser
s[provider].isLoggedIn = false;
if (s[provider].isPrimary) {
s[provider].isPrimary = false;
const others = ['simkl', 'anilist', 'mal'] as const;
const newPrimary = others.find(p => p !== provider && s[p].isLoggedIn);
if (newPrimary) s[newPrimary].isPrimary = true;
}
return s;
});
// Clear provider-specific watchlist
if (provider === 'anilist') {
if (Object.keys(aniWatchlist).length !== 0) {
aniListWatchlist.set({} as AniListCurrentUserWatchList)
}
await LogoutAniList();
} else if (provider === 'mal') {
if (Object.keys(aniWatchlist).length !== 0) {
malWatchList.set({} as MALWatchlist);
}
await LogoutMyAnimeList();
} else if (provider === 'simkl') {
if (Object.keys(aniWatchlist).length !== 0) {
simklWatchList.set({} as SimklWatchList);
}
await LogoutSimkl();
}
}
}
};
export const userStore = createUserStore();

View File

@@ -1,5 +1,5 @@
<script lang="ts" context="module"> <script lang="ts" context="module">
import type {TableItem} from "../helperTypes/TableTypes"; import type {TableItem} from "../types/TableTypes";
import { tableItems } from "./GlobalVariablesAndHelperFunctions.svelte" import { tableItems } from "./GlobalVariablesAndHelperFunctions.svelte"
export function AddAnimeServiceToTable(animeItem: TableItem) { export function AddAnimeServiceToTable(animeItem: TableItem) {

View File

@@ -1,34 +0,0 @@
<script lang="ts" context="module">
import {CheckIfAniListLoggedIn, GetAniListLoggedInUser, GetAniListUserWatchingList} from "../../wailsjs/go/main/App";
import {MediaListSort} from "../anilist/types/AniListTypes";
import { aniListUser, watchListPage, animePerPage, aniListPrimary, aniListLoggedIn, aniListWatchlist } from "./GlobalVariablesAndHelperFunctions.svelte"
let isAniListPrimary: boolean
let page: number
let perPage: number
aniListPrimary.subscribe(value => isAniListPrimary = value)
watchListPage.subscribe(value => page = value)
animePerPage.subscribe(value => perPage = value)
export const LoadAniListUser = async () => {
await GetAniListLoggedInUser().then(user => {
aniListUser.set(user)
})
}
export const LoadAniListWatchList = async () => {
await GetAniListUserWatchingList(page, perPage, MediaListSort.UpdatedTimeDesc).then((watchList) => {
aniListWatchlist.set(watchList)
})
}
export const CheckIfAniListLoggedInAndLoadWatchList = async () => {
const loggedIn = await CheckIfAniListLoggedIn()
if (loggedIn) {
await LoadAniListUser()
if (isAniListPrimary) await LoadAniListWatchList()
}
aniListLoggedIn.set(loggedIn)
}
</script>

View File

@@ -1,25 +0,0 @@
<script lang="ts" context="module">
import {CheckIfMyAnimeListLoggedIn, GetMyAnimeList, GetMyAnimeListLoggedInUser} from "../../wailsjs/go/main/App";
import {malUser, malPrimary, malWatchList, malLoggedIn} from "./GlobalVariablesAndHelperFunctions.svelte"
let isMalPrimary: boolean
malPrimary.subscribe(value => isMalPrimary = value)
export const CheckIfMALLoggedInAndSetUser = async () => {
await CheckIfMyAnimeListLoggedIn().then(loggedIn => {
if (loggedIn) {
GetMyAnimeListLoggedInUser().then(user => {
malUser.set(user)
if (isMalPrimary) {
GetMyAnimeList(1000).then(watchList => {
malWatchList.set(watchList)
malLoggedIn.set(loggedIn)
})
} else {
malLoggedIn.set(loggedIn)
}
})
}
})
}
</script>

View File

@@ -1,29 +0,0 @@
<script lang="ts" context="module">
import {CheckIfSimklLoggedIn, GetSimklLoggedInUser, SimklGetUserWatchlist} from "../../wailsjs/go/main/App";
import { simklLoggedIn, simklUser, simklPrimary, simklWatchList } from "./GlobalVariablesAndHelperFunctions.svelte";
let isSimklPrimary: boolean
simklPrimary.subscribe(value => isSimklPrimary = value)
export const CheckIfSimklLoggedInAndSetUser = async () => {
await CheckIfSimklLoggedIn().then(loggedIn => {
if (loggedIn) {
GetSimklLoggedInUser().then(user => {
if (Object.keys(user).length === 0) {
simklLoggedIn.set(false)
} else {
simklUser.set(user)
if (isSimklPrimary) {
SimklGetUserWatchlist().then(result => {
simklWatchList.set(result)
simklLoggedIn.set(loggedIn)
})
} else {
simklLoggedIn.set(loggedIn)
}
}
})
}
})
}
</script>

View File

@@ -1,41 +1,25 @@
<script lang="ts" context="module"> <script lang="ts" context="module">
import { import {
GetAniListItem, GetAniListItem,
GetAniListLoggedInUser,
GetAniListUserWatchingList,
GetMyAnimeListAnime, GetMyAnimeListAnime,
GetMyAnimeListLoggedInUser,
GetSimklLoggedInUser,
LogoutAniList,
LogoutMyAnimeList,
LogoutSimkl,
SimklGetUserWatchlist,
SimklSearch SimklSearch
} from "../../wailsjs/go/main/App"; } from "../../wailsjs/go/main/App";
import {userStore} from "../helperFunctions/userStore";
import type { import type {
AniListCurrentUserWatchList,
AniListGetSingleAnime AniListGetSingleAnime
} from "../anilist/types/AniListCurrentUserWatchListType.js"; } from "../types/AniListCurrentUserWatchListType.js";
import {writable} from 'svelte/store' import {get, writable} from 'svelte/store'
import type {SimklAnime, SimklUser, SimklWatchList} from "../simkl/types/simklTypes"; import type {SimklAnime, SimklWatchList} from "../types/simklTypes";
import {type AniListUser, MediaListSort} from "../anilist/types/AniListTypes"; import type {MALAnime, MALWatchlist} from "../types/MALTypes";
import type {MALAnime, MALWatchlist, MyAnimeListUser} from "../mal/types/MALTypes"; import type {TableItems} from "../types/TableTypes";
import type {TableItems} from "../helperTypes/TableTypes"; import {AniListGetSingleAnimeDefaultData} from "../defaults/AniListGetSingleAnime";
import {AniListGetSingleAnimeDefaultData} from "../helperDefaults/AniListGetSingleAnime"; import {AniListWatchListDefaultData} from "../defaults/AniListWatchListDefaultData";
export let aniListAnime = writable(AniListGetSingleAnimeDefaultData) export let aniListAnime = writable(AniListGetSingleAnimeDefaultData)
export let title = writable("") export let title = writable("")
export let aniListLoggedIn = writable(false) export let aniListLoggedIn = writable(false)
export let simklLoggedIn = writable(false)
export let malLoggedIn = writable(false)
export let simklWatchList = writable({} as SimklWatchList) export let simklWatchList = writable({} as SimklWatchList)
export let aniListPrimary = writable(true) export let aniListWatchlist = writable(AniListWatchListDefaultData)
export let simklPrimary = writable(false)
export let malPrimary = writable(false)
export let simklUser = writable({} as SimklUser)
export let aniListUser = writable({} as AniListUser)
export let malUser = writable({} as MyAnimeListUser)
export let aniListWatchlist = writable({} as AniListCurrentUserWatchList)
export let malWatchList = writable({} as MALWatchlist) export let malWatchList = writable({} as MALWatchlist)
export let malAnime = writable({} as MALAnime) export let malAnime = writable({} as MALAnime)
export let simklAnime = writable({} as SimklAnime) export let simklAnime = writable({} as SimklAnime)
@@ -45,25 +29,14 @@
export let watchListPage = writable(1) export let watchListPage = writable(1)
export let animePerPage = writable(20) export let animePerPage = writable(20)
let isAniListPrimary: boolean
let page: number
let perPage: number
let aniWatchlist: AniListCurrentUserWatchList
let currentAniListAnime: AniListGetSingleAnime let currentAniListAnime: AniListGetSingleAnime
let isMalLoggedIn: boolean
let isSimklLoggedIn: boolean
aniListPrimary.subscribe(value => isAniListPrimary = value)
watchListPage.subscribe(value => page = value)
animePerPage.subscribe(value => perPage = value)
aniListWatchlist.subscribe(value => aniWatchlist = value)
malLoggedIn.subscribe(value => isMalLoggedIn = value)
simklLoggedIn.subscribe(value => isSimklLoggedIn = value)
aniListAnime.subscribe(value => currentAniListAnime = value) aniListAnime.subscribe(value => currentAniListAnime = value)
export async function GetAnimeSingleItem(aniId: number, login: boolean): Promise<""> { export async function GetAnimeSingleItem(aniId: number, login: boolean): Promise<""> {
const store = get(userStore)
if (store.anilist.isLoggedIn)
await GetAniListItem(aniId, login).then(aniListResult => { await GetAniListItem(aniId, login).then(aniListResult => {
let finalResult: AniListGetSingleAnime let finalResult: AniListGetSingleAnime
finalResult = aniListResult finalResult = aniListResult
@@ -85,78 +58,16 @@
currentAniListAnime.data.MediaList.media.title.romaji : currentAniListAnime.data.MediaList.media.title.romaji :
currentAniListAnime.data.MediaList.media.title.english) currentAniListAnime.data.MediaList.media.title.english)
}) })
if (isMalLoggedIn) { if (store.mal.isLoggedIn) {
await GetMyAnimeListAnime(currentAniListAnime.data.MediaList.media.idMal).then(malResult => { await GetMyAnimeListAnime(currentAniListAnime.data.MediaList.media.idMal).then(malResult => {
malAnime.set(malResult) malAnime.set(malResult)
}) })
} }
if (isSimklLoggedIn) { if (store.simkl.isLoggedIn) {
await SimklSearch(currentAniListAnime.data.MediaList).then((value: SimklAnime) => { await SimklSearch(currentAniListAnime.data.MediaList).then((value: SimklAnime) => {
simklAnime.set(value) simklAnime.set(value)
}) })
} }
return "" return ""
} }
export function loginToSimkl(): void {
GetSimklLoggedInUser().then(user => {
if (Object.keys(user).length === 0) {
simklLoggedIn.set(false)
} else {
simklUser.set(user)
SimklGetUserWatchlist().then(result => {
simklWatchList.set(result)
simklLoggedIn.set(true)
})
}
})
}
export function loginToAniList(): void {
GetAniListLoggedInUser().then(result => {
aniListUser.set(result)
if (isAniListPrimary) {
GetAniListUserWatchingList(page, perPage, MediaListSort.UpdatedTimeDesc).then((result) => {
aniListWatchlist.set(result)
aniListLoggedIn.set(true)
})
} else {
aniListLoggedIn.set(true)
}
})
}
export function loginToMAL(): void {
GetMyAnimeListLoggedInUser().then(result => {
malUser.set(result)
malLoggedIn.set(true)
})
}
export function logoutOfAniList(): void {
LogoutAniList().then(result => {
console.log(result)
if (Object.keys(aniWatchlist).length !== 0) {
aniListWatchlist.set({} as AniListCurrentUserWatchList)
}
aniListUser.set({} as AniListUser)
aniListLoggedIn.set(false)
})
}
export function logoutOfMAL(): void {
LogoutMyAnimeList().then(result => {
console.log(result)
malUser.set({} as MyAnimeListUser)
malLoggedIn.set(false)
})
}
export function logoutOfSimkl(): void {
LogoutSimkl().then(result => {
console.log(result)
simklUser.set({} as SimklUser)
simklLoggedIn.set(false)
})
}
</script> </script>

View File

@@ -0,0 +1,17 @@
<script lang="ts" context="module">
import {GetAniListUserWatchingList} from "../../wailsjs/go/main/App";
import {MediaListSort} from "../types/AniListTypes";
import { watchListPage, animePerPage, aniListWatchlist } from "./GlobalVariablesAndHelperFunctions.svelte"
let page: number
let perPage: number
watchListPage.subscribe(value => page = value)
animePerPage.subscribe(value => perPage = value)
export const LoadAniListWatchList = async () => {
await GetAniListUserWatchingList(page, perPage, MediaListSort.UpdatedTimeDesc).then((watchList) => {
aniListWatchlist.set(watchList)
})
}
</script>

View File

@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { aniListAnime, GetAnimeSingleItem } from "../helperModules/GlobalVariablesAndHelperFunctions.svelte"; import { aniListAnime, GetAnimeSingleItem } from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
import Anime from "../helperComponents/Anime.svelte" import Anime from "../helperComponents/Anime.svelte"
import { AniListGetSingleAnimeDefaultData } from "../helperDefaults/AniListGetSingleAnime"; import { AniListGetSingleAnimeDefaultData } from "../defaults/AniListGetSingleAnime";
import Spinner from "../helperComponents/Spinner.svelte"; import Spinner from "../helperComponents/Spinner.svelte";
export let params: Record<string, string> export let params: Record<string, string>

View File

@@ -2,19 +2,13 @@
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,
aniListPrimary,
loading, loading,
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte"; } from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
import loader from '../helperFunctions/loader' import loader from '../helperFunctions/loader'
import {userStore} from "../helperFunctions/userStore";
let isAniListPrimary: boolean
let isAniListLoggedIn: boolean
aniListPrimary.subscribe((value) => isAniListPrimary = value)
aniListLoggedIn.subscribe((value) => isAniListLoggedIn = value)
</script> </script>
{#if isAniListLoggedIn && isAniListPrimary} {#if $userStore.anilist.isLoggedIn && $userStore.anilist.isPrimary}
<div class="container py-10"> <div class="container py-10">
<Pagination /> <Pagination />
<WatchList /> <WatchList />

View File

@@ -44,7 +44,6 @@ export interface MediaList {
timeUntilAiring: number; timeUntilAiring: number;
episode: number; episode: number;
}; };
genres: string[];
tags: [ tags: [
{ {
id: number; id: number;

View File

@@ -364,7 +364,7 @@ export namespace main {
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\"" } // 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\""; 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; media: any;
status: string; status: string;
// 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\"" }

View File

@@ -0,0 +1,79 @@
# @name AniChart
POST https://graphql.anilist.co
Accept: applicaton/json
X-REQUEST-TYPE: Graphql
query ($page: Int, $perPage: Int, $airingAt_greater:Int) {
Page(page: $page, perPage: $perPage) {
pageInfo {
total
perPage
currentPage
lastPage
hasNextPage
}
airingSchedules(airingAt_greater:$airingAt_greater){
id
airingAt
timeUntilAiring
episode
mediaId
media{
id
title{
english
romaji
native
}
type
format
status
startDate{
year
month
day
}
endDate{
year
month
day
}
season
seasonYear
episodes
duration
coverImage{
medium
large
color
extraLarge
}
bannerImage
genres
averageScore
meanScore
popularity
trending
favourites
tags{
id
name
description
category
rank
isGeneralSpoiler
isMediaSpoiler
isAdult
}
isAdult
}
}
}
}
{
"page": 50,
"perPage": 20,
"airingAt_greater": 1730260800
}

View File

@@ -0,0 +1,83 @@
# @name AniList Item
POST https://graphql.anilist.co
Accept: applicaton/json
X-REQUEST-TYPE: Graphql
Authorization: Bearer {{ANILIST_ACCESS_TOKEN}}
query ($userId: Int, $mediaId: Int, $listType: MediaType) {
MediaList(mediaId: $mediaId, userId: $userId, type: $listType) {
id
mediaId
userId
media {
id
idMal
tags {
id
name
description
rank
isMediaSpoiler
isAdult
}
title {
romaji
english
native
}
description
coverImage {
large
}
season
seasonYear
status
episodes
nextAiringEpisode {
airingAt
timeUntilAiring
episode
}
isAdult
}
status
startedAt {
year
month
day
}
completedAt {
year
month
day
}
notes
progress
score
repeat
user {
id
name
avatar {
large
medium
}
statistics {
anime {
count
statuses {
status
count
}
}
}
}
}
}
{
"userId": 413504,
"mediaId": 170998,
"listType": "ANIME"
}

View File

@@ -0,0 +1,70 @@
# @name AniList MediaList User Query
POST https://graphql.anilist.co
Accept: applicaton/json
X-REQUEST-TYPE: Graphql
query(
$page: Int
$perPage: Int
$userId: Int
$listType: MediaType
$status: MediaListStatus
) {
Page(page: $page, perPage: $perPage) {
pageInfo {
total
perPage
currentPage
lastPage
hasNextPage
}
mediaList(userId: $userId, type: $listType, status: $status) {
id
mediaId
userId
media {
id
idMal
title {
romaji
english
native
}
description
coverImage {
large
}
season
seasonYear
episodes
}
status
notes
progress
score
repeat
user {
id
statistics {
anime {
count
statuses {
status
count
}
}
}
}
}
}
}
{
"page": 1,
"perPage": 20,
"userId": 413504,
"listType": "ANIME",
"status": "CURRENT"
}

View File

@@ -0,0 +1,44 @@
# @name AniList Search
POST https://graphql.anilist.co
Accept: applicaton/json
X-REQUEST-TYPE: Graphql
query ($search: String!, $listType: MediaType) {
Page (page: 1, perPage: 100) {
pageInfo {
total
currentPage
lastPage
hasNextPage
perPage
}
media (search: $search, type: $listType) {
id
idMal
title {
romaji
english
native
}
description
coverImage {
large
}
season
seasonYear
status
episodes
nextAiringEpisode{
airingAt
timeUntilAiring
episode
}
}
}
}
{
"search": "dan-da-dan",
"listType": "ANIME"
}

View File

@@ -0,0 +1,93 @@
# @name GetAniListUserWatchList
POST https://graphql.anilist.co
Accept: applicaton/json
X-REQUEST-TYPE: Graphql
query (
$page: Int
$perPage: Int
$userId: Int
$listType: MediaType
$status: MediaListStatus
$sort: [MediaListSort]
) {
Page(page: $page, perPage: $perPage) {
pageInfo {
total
perPage
currentPage
lastPage
hasNextPage
}
mediaList(userId: $userId, type: $listType, status: $status, sort: $sort) {
id
mediaId
userId
media {
id
idMal
title {
romaji
english
native
}
description
coverImage {
large
}
season
seasonYear
status
episodes
nextAiringEpisode {
airingAt
timeUntilAiring
episode
}
}
status
startedAt {
year
month
day
}
completedAt {
year
month
day
}
notes
progress
score
repeat
user {
id
name
avatar {
large
medium
}
statistics {
anime {
count
statuses {
status
count
}
}
}
}
}
}
}
{
"page": 1,
"perPage": 20,
"userId": 413504,
"listType": "ANIME",
"status": "CURRENT",
"sort": "UPDATED_TIME_DESC"
}

View File

@@ -0,0 +1,3 @@
# @name GetAuthorizationToken
GET https://anilist.co/api/v2/oauth/authorize?client_id={{ANILIST_APP_ID}}&redirect_uri=http://localhost:6734/callback&response_type=code

View File

@@ -0,0 +1,11 @@
# @name Load AniList Oauth Token
POST https://anilist.co/api/v2/oauth/token
Content-Type: application/x-www-form-urlencoded
Accept: application/json
grant_type=authorization_code
client_id={{ANILIST_APP_ID}}
client_secret={{ANILIST_SECRET_ID}}
redirect_uri=http://localhost:6734/callback
code={{ANILIST_CODE}}

View File

@@ -0,0 +1,76 @@
# @name AniList Change Episode Watched
POST https://graphql.anilist.co
Content-Type: applicaton/json
Accept: applicaton/json
X-REQUEST-TYPE: Graphql
Authorization: Bearer {{ANILIST_ACCESS_TOKEN}}
mutation($mediaId:Int, $progress:Int, $status:MediaListStatus){
SaveMediaListEntry(mediaId:$mediaId, progress:$progress, status:$status){
id
mediaId
userId
media {
id
idMal
title {
romaji
english
native
}
description
coverImage {
large
}
season
seasonYear
status
episodes
nextAiringEpisode {
airingAt
timeUntilAiring
episode
}
isAdult
}
status
startedAt{
year
month
day
}
completedAt{
year
month
day
}
notes
progress
score
repeat
user {
id
name
avatar{
large
medium
}
statistics{
anime{
count
statuses{
status
count
}
}
}
}
}
}
{
"mediaId": 169417,
"progress": 12,
"status":"COMPLETED"
}

View File

@@ -0,0 +1,19 @@
# @name AniList Change Status
POST https://graphql.anilist.co
Content-Type: applicaton/json
Accept: applicaton/json
X-REQUEST-TYPE: Graphql
Authorization: Bearer {{ANILIST_ACCESS_TOKEN}}
mutation($mediaId:Int, $status:MediaListStatus){
SaveMediaListEntry(mediaId:$mediaId, status:$status){
id
status
}
}
{
"mediaId": 1,
"status": "CURRENT"
}

View File

@@ -0,0 +1,65 @@
# @name AniList Change Count
POST https://graphql.anilist.co
Content-Type: applicaton/json
Accept: applicaton/json
X-REQUEST-TYPE: Graphql
Authorization: Bearer {{ANILIST_ACCESS_TOKEN}}
mutation (
$mediaId: Int
$progress: Int
$status: MediaListStatus
$score: Float
$repeat: Int
$notes: String
$startedAt: FuzzyDateInput
$completedAt: FuzzyDateInput
) {
SaveMediaListEntry(
mediaId: $mediaId
progress: $progress
status: $status
score: $score
repeat: $repeat
notes: $notes
startedAt: $startedAt
completedAt: $completedAt
) {
mediaId
progress
status
score
repeat
notes
startedAt {
year
month
day
}
completedAt {
year
month
day
}
}
}
{
"mediaId": 170998,
"progress": 5,
"status": "CURRENT",
"score": 9.0,
"repeat": 0,
"notes": ",malSync::eyJ1IjoiaHR0cHM6Ly93d3cuY3J1bmNoeXJvbGwuY29tL3Nlcmllcy9HVkRIWDg1Wk4vI3NlYXNvbj1HNjNWQzJHUUsiLCJwIjoiIn0=::",
"startedAt": {
"year": 2024,
"month": 7,
"day": 10
},
"completedAt": {
"year": 0,
"month": 0,
"day": 0
}
}

View File

@@ -0,0 +1,17 @@
# @name AniList Delete Media
POST https://graphql.anilist.co
Content-Type: applicaton/json
Accept: applicaton/json
X-REQUEST-TYPE: Graphql
Authorization: Bearer {{ANILIST_ACCESS_TOKEN}}
mutation ($id: Int) {
DeleteMediaListEntry(id: $id) {
deleted
}
}
{
"id": 430978266
}

View File

@@ -0,0 +1,6 @@
# @name Get AnimeList
GET https://api.myanimelist.net/v2/users/{{MAL_USER}}/animelist?fields=list_status&status=watching&limit=1000
Content-Type application/x-www-form-urlencoded
Accept: application/json
Authorization: Bearer {{MAL_ACCESS_TOKEN}}

View File

@@ -0,0 +1,12 @@
# @name Get Authorization
POST https://myanimelist.net/v1/oauth2/token
Content-Type: application/x-www-form-urlencoded
Accept: application/json
grant_type=authorization_code&
client_id={{MAL_CLIENT_ID}}&
client_secret={{MAL_CLIENT_SECRET}}&
redirect_uri=http://localhost:6734/callback&
code={{MAL_CODE}}&
code_verifier={{MAL_VERIFIER}}

View File

@@ -0,0 +1,5 @@
# @name Get Single Anime
GET 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
Accept: application/json
Authorization: Bearer {{MAL_ACCESS_TOKEN}}

View File

@@ -0,0 +1,5 @@
# @name MAL Oauth Page
GET https://myanimelist.net/v1/oauth2/authorize?response_type=code&client_id={{MAL_CLIENT_ID}}&redirect_uri={{MAL_CALLBACK_URI}}
cookie MALSESSIONID=5ad688aafb78239bfd84752752ce193f; MALHLOGSESSID=632f67c3955267b4e57fc3d74b373ebb
Accept: application/json

View File

@@ -0,0 +1,8 @@
# @name Update Anime Status
PATCH https://api.myanimelist.net/v2/anime/50205/my_list_status
Content-Type: application/x-www-form-urlencoded
Accept: application/json
Authorization: Bearer {{MAL_ACCESS_TOKEN}}
num_watched_episodes=3

3
rest/Simkl/Get Code.http Normal file
View File

@@ -0,0 +1,3 @@
# @name Get Code
GET https://simkl.com/oauth/authorize?response_type=code&client_id={{SIMKL_CLIENT_ID}}&redirect_uri=http://localhost:6734/callback

View File

@@ -0,0 +1,5 @@
# @name Get Anime Full Info
GET https://api.simkl.com/anime/40084?extended=full
Accept application/json
simkl-api-key {{SIMKL_CLIENT_ID}}

View File

@@ -0,0 +1,7 @@
# @name GetUser WatchList
GET https://api.simkl.com/sync/all-items/anime/
Content-Type application/json
Accept application/json
simkl-api-key {{SIMKL_CLIENT_ID}}
Authorization Bearer {{SIMKL_AUTH_TOKEN}}

View File

@@ -0,0 +1,5 @@
# @name Search By MALID to Get Simkl ID
GET https://api.simkl.com/search/id?anilist=174576
Accept application/json
simkl-api-key {{SIMKL_CLIENT_ID}}

View File

@@ -0,0 +1,17 @@
# @name Delete Entry
GET https://api.simkl.com/sync/history/remove
Content-Type application/json
Accept application/json
simkl-api-key {{SIMKL_CLIENT_ID}}
Authorization Bearer {{SIMKL_AUTH_TOKEN}}
{
"shows": [
{
"ids": {
"simkl": 909121
}
}
]
}

View File

@@ -0,0 +1,40 @@
# @name Update Episode
GET https://api.simkl.com/sync/history
Content-Type application/json
Accept application/json
simkl-api-key {{SIMKL_CLIENT_ID}}
Authorization Bearer {{SIMKL_AUTH_TOKEN}}
{
"shows": [
{
"title": "Ramen Aka Neko",
"ids": {
"simkl": 2307708,
"mal": "57325",
"anilist": "170998"
},
"episodes": [
{
"number": 1
},
{
"number": 2
},
{
"number": 3
},
{
"number": 4
},
{
"number": 5
},
{
"number": 6
}
]
}
]
}

View File

@@ -0,0 +1,12 @@
# @name SimklGetAuthorizationToken
POST https://api.simkl.com/oauth/token
Content-Type application/json
{
"grant_type": "authorization_code",
"client_id": "{{SIMKL_CLIENT_ID}}",
"client_secret": "{{SIMKL_CLIENT_SECRET}}",
"redirect_uri": "http://localhost:6734/callback",
"code": {{SIMKL_CODE}}
}

19
rest/http-client.env.json Normal file
View File

@@ -0,0 +1,19 @@
{
"$schema": "https://raw.githubusercontent.com/mistweaverco/kulala.nvim/main/schemas/http-client.env.schema.json",
"dev": {
"ANILIST_ACCESS_TOKEN": "",
"ANILIST_APP_ID": "",
"ANILIST_SECRET": "",
"ANILSIT_CODE": "",
"MAL_ACCESS_TOKEN": "",
"MAL_CLIENT_ID": "",
"MAL_CLIENT_SECRET": "",
"MAL_CODE": "",
"MAL_USER": "",
"MAL_VERIFIER": "",
"SIMKL_AUTH_TOKEN": "",
"SIMKL_CODE": "",
"SIMKL_CLIENT_ID": "",
"SIMKL_CLIENT_SECRET": ""
}
}

View File

@@ -12,6 +12,6 @@
}, },
"info": { "info": {
"productName": "AniTrack", "productName": "AniTrack",
"productVersion": "0.5.3" "productVersion": "0.6.0"
} }
} }