7 Commits
main ... dev

Author SHA1 Message Date
3bfb31f8bf docs: update AniList search query in Bruno collection
Add the genre field to the AniList Search GraphQL query in the Bruno API
collection. This keeps the API documentation collection in sync with the
application's query structure, allowing for testing and verification of
genre data retrieval from the AniList API.
2026-03-19 20:55:48 -04:00
4739fb4344 build: update Wails generated models for genres support
Regenerate the Wails TypeScript models to include the new Genres field
in the MediaList type definition. This is an auto-generated file that
reflects the updated Go backend type structure with the genres []string
field added to the media object.
2026-03-19 20:55:48 -04:00
f4382304df feat(frontend): add genre display UI and enhance link component
- Anime.svelte: Add genre display section with clickable badges that link
  to AniList search results for each genre. Genres are now displayed above
  the existing tags section with consistent styling.

- WebsiteLink.svelte: Enhance component to support custom URLs via the `url`
  export parameter. Previously, the component only generated URLs based on
  service prefixes (a-, m-, s-). Now it accepts a direct URL parameter for
  flexible linking to AniList searches and other external resources.

These changes provide users with an improved browsing experience by making
genres interactive and easily searchable.
2026-03-19 20:55:21 -04:00
4400dfd637 feat(frontend): update TypeScript types for AniList genres support
Update the AniListCurrentUserWatchListType TypeScript interface to include
the genres field as a string array, matching the updated backend Go type
definition. This ensures type safety and proper IDE autocomplete when
working with genre data in the frontend.
2026-03-19 20:54:39 -04:00
b90d8eb2d3 feat(backend): add genres support to AniList integration
Add the `genres` field to AniList GraphQL queries and type definitions:
- Add genres field to GetAniListItem query for fetching single anime details
- Add genres field to AniListSearch query for search results
- Add genres field to GetAniListUserWatchingList query for user's watch list
- Update MediaList type definition to include Genres []string field

This enhancement allows the application to retrieve and display anime genre
information from the AniList API, providing users with better categorization
and discovery capabilities.
2026-03-19 20:54:39 -04:00
1f796189b4 moved user information into a userstore and updated code that touches it 2025-12-24 11:49:09 -05:00
18daf41bf9 moved user information into a userstore and updated code that touches it 2025-12-24 11:45:59 -05:00
31 changed files with 484 additions and 407 deletions

View File

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

View File

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

View File

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

View File

@@ -1,42 +1,27 @@
<script lang="ts">
import {
aniListLoggedIn,
malLoggedIn,
simklLoggedIn,
} from "./helperModules/GlobalVariablesAndHelperFunctions.svelte";
import {onMount} from "svelte";
import {userStore} from "./helperFunctions/userStore"
import Router from "svelte-spa-router"
import Home from "./routes/Home.svelte";
import {wrap} from "svelte-spa-router/wrap";
import Spinner from "./helperComponents/Spinner.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 () => {
let isAniListLoggedIn: boolean
let isMALLoggedIn: boolean
let isSimklLoggedIn: boolean
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()
await userStore.checkProvider('anilist')
await userStore.checkProvider('mal')
await userStore.checkProvider('simkl')
})
</script>
{#if $userStore.anilist.isLoggedIn}
<Header />
<Router routes={{
'/': Home,
'/anime/:id': wrap({
asyncComponent: () => import('./routes/AnimeRoutePage.svelte'),
conditions: [async () => await CheckIfAniListLoggedIn()],
loadingComponent: Spinner
}),
// '*': "Not Found"
}} />
{/if}

View File

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

View File

@@ -1,14 +1,17 @@
<script lang="ts">
import {
aniListAnime,
aniListLoggedIn,
malAnime,
malLoggedIn,
simklAnime,
simklLoggedIn,
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
import {userStore} from "../helperFunctions/userStore"
import { push } from "svelte-spa-router";
<<<<<<< HEAD
import type { AniListGetSingleAnime } from "../types/AniListCurrentUserWatchListType";
=======
import WebsiteLink from "./WebsiteLink.svelte";
import type { AniListGetSingleAnime } from "../anilist/types/AniListCurrentUserWatchListType";
>>>>>>> a222c6b (feat(frontend): add genre display UI and enhance link component)
import Rating from "./Rating.svelte";
import {
convertAniListDateToString,
@@ -19,14 +22,14 @@
MALAnime,
MalListStatus,
MALUploadStatus,
} from "../mal/types/MALTypes";
import type { SimklAnime } from "../simkl/types/simklTypes";
} from "../types/MALTypes";
import type { SimklAnime } from "../types/simklTypes";
import { writable } from "svelte/store";
import type {
StatusOption,
StatusOptions,
} from "../helperTypes/StatusTypes";
import type { AniListUpdateVariables } from "../anilist/types/AniListTypes";
} from "../types/StatusTypes";
import type { AniListUpdateVariables } from "../types/AniListTypes";
import { convertDateToAniList } from "../helperFunctions/convertDateToAniList";
import {
AniListDeleteEntry,
@@ -39,14 +42,10 @@
SimklSyncStatus,
} from "../../wailsjs/go/main/App";
import { AddAnimeServiceToTable } from "../helperModules/AddAnimeServiceToTable.svelte";
import { CheckIfAniListLoggedInAndLoadWatchList } from "../helperModules/CheckIfAniListLoggedInAndLoadWatchList.svelte";
import Datepicker from "./Datepicker.svelte";
import { Badge, Tooltip } from "flowbite-svelte";
const re = /^([0-9]{4})-([0-9]{2})-([0-9]{2})/;
let isAniListLoggedIn: boolean;
let isMalLoggedIn: boolean;
let isSimklLoggedIn: boolean;
let currentAniListAnime: AniListGetSingleAnime;
let currentMalAnime: MALAnime;
let currentSimklAnime: SimklAnime;
@@ -54,9 +53,6 @@
let isSubmitting: boolean;
let submitSuccess = writable(false);
aniListLoggedIn.subscribe((value) => (isAniListLoggedIn = value));
malLoggedIn.subscribe((value) => (isMalLoggedIn = value));
simklLoggedIn.subscribe((value) => (isSimklLoggedIn = value));
aniListAnime.subscribe((value) => (currentAniListAnime = value));
malAnime.subscribe((value) => (currentMalAnime = value));
simklAnime.subscribe((value) => (currentSimklAnime = value));
@@ -90,7 +86,7 @@
currentAniListAnime.data.MediaList.completedAt,
);
if (isAniListLoggedIn)
if ($userStore.anilist.isLoggedIn)
AddAnimeServiceToTable({
id: `a-${currentAniListAnime.data.MediaList.mediaId}`,
title,
@@ -108,7 +104,7 @@
notes: currentAniListAnime.data.MediaList.notes,
});
if (isMalLoggedIn) {
if ($userStore.mal.isLoggedIn) {
let startDate = "";
let finishDate = "";
if (currentMalAnime.my_list_status.start_date !== "") {
@@ -137,7 +133,7 @@
});
}
if (isSimklLoggedIn && Object.keys(currentSimklAnime).length > 0)
if ($userStore.simkl.isLoggedIn && Object.keys(currentSimklAnime).length > 0)
AddAnimeServiceToTable({
id: `s-${currentSimklAnime.show.ids.simkl}`,
title: currentSimklAnime.show.title,
@@ -198,7 +194,7 @@
}
if (
isAniListLoggedIn &&
$userStore.anilist.isLoggedIn &&
currentAniListAnime.data.MediaList.mediaId !== 0
) {
let body: AniListUpdateVariables = {
@@ -239,7 +235,7 @@
);
}
if (malLoggedIn && currentMalAnime.id !== 0) {
if ($userStore.mal.isLoggedIn && currentMalAnime.id !== 0) {
let body: MALUploadStatus = {
status: submitData.status.mal,
is_rewatching: submitData.repeat > 0,
@@ -295,7 +291,7 @@
);
}
if (simklLoggedIn && currentSimklAnime.show.ids.simkl !== 0) {
if ($userStore.simkl.isLoggedIn && currentSimklAnime.show.ids.simkl !== 0) {
if (
currentSimklAnime.watched_episodes_count !== submitData.episodes
) {
@@ -379,7 +375,7 @@
const deleteEntries = async () => {
submitting.set(true);
if (
isAniListLoggedIn &&
$userStore.anilist.isLoggedIn &&
currentAniListAnime.data.MediaList.mediaId !== 0
) {
await AniListDeleteEntry(currentAniListAnime.data.MediaList.id);
@@ -396,7 +392,7 @@
notes: "",
});
}
if (malLoggedIn && currentMalAnime.id !== 0) {
if ($userStore.mal.isLoggedIn && currentMalAnime.id !== 0) {
await DeleteMyAnimeListEntry(currentMalAnime.id);
AddAnimeServiceToTable({
id: `m-${currentMalAnime.id}`,
@@ -411,7 +407,7 @@
notes: "",
});
}
if (simklLoggedIn && currentSimklAnime.show.ids.simkl !== 0) {
if ($userStore.simkl.isLoggedIn && currentSimklAnime.show.ids.simkl !== 0) {
await SimklSyncRemove(currentSimklAnime);
AddAnimeServiceToTable({
id: `s-${currentSimklAnime.show.ids.simkl}`,
@@ -444,6 +440,7 @@
currentAniListAnime.data.MediaList.media.nextAiringEpisode.episode -
1;
}
</script>
<form on:submit|preventDefault={handleSubmit} class="container pt-3 pb-10">
@@ -530,7 +527,7 @@
.nextAiringEpisode.episode -
1)
? 'border-red-500 border-[2px] text-rose-300 focus:ring-red-500 focus:border-red-500'
: 'bg-gray-700 hover:bg-gray-600 border-gray-600 text-white focus:ring-blue-500 focus:border-blue-500'} w-24"
: 'bg-gray-700 hover:bg-gray-600 border-gray-600 text-white focus:ring-blue-500 focus:border-blue-500'}"
bind:value={
currentAniListAnime.data.MediaList.progress
}
@@ -667,7 +664,7 @@
class="border {currentAniListAnime.data.MediaList
.repeat < 0
? 'border-red-500 border-[2px] text-rose-300 focus:ring-red-500 focus:border-red-500'
: 'border-gray-500 text-white focus:ring-blue-500 focus:border-blue-500'} text-sm rounded-lg block w-24 p-2.5 bg-gray-600 placeholder-gray-400 text-white"
: 'border-gray-500 focus:ring-blue-500 focus:border-blue-500'} text-sm rounded-lg block w-24 p-2.5 bg-gray-600 placeholder-gray-400 text-white"
bind:value={currentAniListAnime.data.MediaList.repeat}
required
/>
@@ -701,9 +698,9 @@
<button
disabled={isSubmitting}
id="sync-button"
class="text-white {$submitSuccess
? 'bg-green-600 hover:bg-green-700 focus:ring-4 focus:ring-green-800'
: 'bg-blue-600 hover:bg-blue-700 focus:ring-4 focus:ring-blue-800'} font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 focus:outline-none"
class="text-white focus:ring-4 {$submitSuccess
? 'bg-green-600 hover:bg-green-700 focus:ring-green-800'
: 'bg-blue-600 hover:bg-blue-700 focus:ring-blue-800'} font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 focus:outline-none"
type="submit"
>
<svg
@@ -733,7 +730,6 @@
focus:ring-gray-700 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2
hover:border-gray-600"
on:click={async () => {
await CheckIfAniListLoggedInAndLoadWatchList();
return push("/");
}}
>
@@ -750,9 +746,9 @@
<button
disabled={isSubmitting}
id="delete-button"
class="text-white {$submitSuccess
? 'bg-green-600 hover:bg-green-700 focus:ring-4 focus:ring-green-800'
: 'bg-red-600 hover:bg-red-700 focus:ring-4 focus:ring-red-800'} font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 focus:outline-none"
class="text-white focus:ring-4 {$submitSuccess
? 'bg-green-600 hover:bg-green-700 focus:ring-green-800'
: 'bg-red-600 hover:bg-red-700 focus:ring-red-800'} font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 focus:outline-none"
on:click={deleteEntries}
>
<svg
@@ -784,9 +780,9 @@
<button
disabled={isSubmitting}
id="sync-button"
class="text-white {$submitSuccess
? 'bg-green-600 hover:bg-green-700 focus:ring-4 focus:ring-green-800'
: 'bg-blue-600 hover:bg-blue-700 focus:ring-4 focus:ring-blue-800'} font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 focus:outline-none"
class="text-white focus:ring-4 {$submitSuccess
? 'bg-green-600 hover:bg-green-700 focus:ring-green-800'
: 'bg-blue-600 hover:bg-blue-700 focus:ring-blue-800'} font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 focus:outline-none"
type="submit"
>
<svg
@@ -816,7 +812,6 @@
focus:ring-gray-700 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2
hover:border-gray-600"
on:click={async () => {
await CheckIfAniListLoggedInAndLoadWatchList();
return push("/");
}}
>
@@ -827,14 +822,31 @@
<div class="flex m-5">
<div>
<h3 class="text-2xl">Genres</h3>
{#each currentAniListAnime.data.MediaList.media.genres as genre}
<div>
<Badge large border color="blue" class="m-1 w-52">
<div>
<WebsiteLink
id={genre}
url="https://anilist.co/search/anime/{genre}"
/>
</div>
</Badge>
<Tooltip>{genre}</Tooltip>
</div>
{/each}
<h3 class="text-2xl">Tags</h3>
<div class="mt-2">
{#each currentAniListAnime.data.MediaList.media.tags as tag}
<div>
<Badge large border color="blue" class="m-1 w-52">
<div>
{tag.name} -
<span class="text-xs">{tag.rank}%</span>
<WebsiteLink
id={tag.name}
url="https://anilist.co/search/anime/?genres={tag.name}"
/>
<span class="text-xs">({tag.rank}%)</span>
</div>
</Badge>
<Tooltip>{tag.description}</Tooltip>

View File

@@ -1,39 +1,9 @@
<script lang="ts">
import { Avatar } from "flowbite-svelte";
import type { AniListUser } from "../anilist/types/AniListTypes";
import {
aniListLoggedIn,
aniListUser,
malUser,
simklUser,
malLoggedIn,
simklLoggedIn,
loginToAniList,
loginToMAL,
loginToSimkl,
logoutOfAniList,
logoutOfMAL,
logoutOfSimkl
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
import {userStore} from "../helperFunctions/userStore"
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";
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 {
let dropdown = document.querySelector("#userDropdown");
dropdown.classList.toggle("hidden");
@@ -56,9 +26,9 @@
<div class="relative">
<button id="userDropdownButton" on:click={dropdownUser}>
{#if isAniListLoggedIn}
{#if $userStore.anilist.isLoggedIn}
<Avatar
src={currentAniListUser.data.Viewer.avatar.medium}
src={$userStore.anilist.user.data.Viewer.avatar.medium}
class="cursor-pointer"
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"
>
<div class="px-4 py-3 text-sm text-white">
{#if isAniListLoggedIn}
<div>{currentAniListUser.data.Viewer.name}</div>
{#if $userStore.anilist.isLoggedIn}
<div>{$userStore.anilist.user.data.Viewer.name}</div>
{:else}
<div>You are not logged into AniList</div>
{/if}
@@ -81,60 +51,60 @@
class="py-2 text-sm text-gray-200"
aria-labelledby="dropdownUserAvatarButton"
>
{#if isAniListLoggedIn}
{#if $userStore.anilist.isLoggedIn}
<li>
<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"
>
<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>
</li>
{:else}
<li>
<button on:click={() => {
dropdownUser()
loginToAniList()
userStore.checkProvider("anilist")
}}
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
</button>
</li>
{/if}
{#if isMALLoggedIn}
{#if $userStore.mal.isLoggedIn}
<li>
<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"
>
<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>
</li>
{:else}
<li>
<button on:click={() => {
dropdownUser()
loginToMAL()
userStore.checkProvider("mal")
}}
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
</button>
</li>
{/if}
{#if isSimklLoggedIn}
{#if $userStore.simkl.isLoggedIn}
<li>
<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"
>
<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>
</li>
{:else}
<li>
<button on:click={() => {
dropdownUser()
loginToSimkl()
userStore.checkProvider("simkl")
}}
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

View File

@@ -1,23 +1,10 @@
<script lang="ts">
import Search from "./Search.svelte"
import {
aniListLoggedIn,
loginToAniList,
loginToMAL,
loginToSimkl,
malLoggedIn,
simklLoggedIn,
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte"
import AvatarMenu from "./AvatarMenu.svelte";
import logo from "../assets/images/AniTrackLogo.svg"
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>
<nav class="border-gray-200 bg-gray-900">
@@ -47,22 +34,22 @@
<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">
<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>
{#if !isAniListLoggedIn}
<button on:click={loginToAniList}>
{#if !$userStore.anilist.isLoggedIn}
<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">-->
AniList Login
</button>
{/if}
{#if !isMALLoggedIn}
<button on:click={loginToMAL}>
{#if !$userStore.mal.isLoggedIn}
<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">-->
MyAnimeList Login
</button>
{/if}
</li>
<li>
{#if !isSimklLoggedIn}
<button on:click={loginToSimkl}>
{#if !$userStore.simkl.isLoggedIn}
<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">-->
Simkl Login
</button>

View File

@@ -6,9 +6,9 @@
watchListPage,
} 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 {MediaListSort} from "../anilist/types/AniListTypes";
import {MediaListSort} from "../types/AniListTypes";
let aniListWatchListLoaded: AniListCurrentUserWatchList
let page: number

View File

@@ -1,7 +1,7 @@
<script lang="ts">
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";
let aniSearch = ""

View File

@@ -1,40 +1,32 @@
<script lang="ts">
import {
aniListLoggedIn,
aniListWatchlist,
GetAnimeSingleItem,
loading,
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
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 loader from '../helperFunctions/loader'
let isAniListLoggedIn: boolean
let aniListWatchListLoaded: AniListCurrentUserWatchList
aniListLoggedIn.subscribe((value) => isAniListLoggedIn = value)
aniListWatchlist.subscribe((value) => aniListWatchListLoaded = value)
</script>
<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">
<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">
{#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}
class="aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-lg xl:aspect-h-8 xl:aspect-w-7">
<div class="flex flex-col items-center group">
<button on:click={() => {
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={
@@ -64,5 +56,4 @@
{/each}
</div>
</div>
{/if}
</div>

View File

@@ -1,28 +1,32 @@
<script lang="ts">
import {BrowserOpenURL} from "../../wailsjs/runtime"
import { BrowserOpenURL } from "../../wailsjs/runtime";
export let id: string
let url = ""
let isAniList = false
let isMAL = false
let isSimkl = false
let newId = id
let re = /[ams]?-?(.*)/
export let id: string;
export let url = "";
let isAniList = false;
let isMAL = false;
let isSimkl = false;
let newId = id;
let re = /[ams]?-?(.*)/;
if (id !== undefined && id.length > 0) {
isAniList = id.includes("a-")
isMAL = id.includes("m-")
isSimkl = id.includes("s-")
newId = id.match(re)[1]
isAniList = id.includes("a-");
isMAL = id.includes("m-");
isSimkl = id.includes("s-");
if (isAniList || isMAL || isSimkl) 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 (isSimkl) url = `https://simkl.com/anime/${newId}`
if (isAniList) url = `https://anilist.co/anime/${newId}`;
if (isMAL) url = `https://myanimelist.net/anime/${newId}`;
if (isSimkl) url = `https://simkl.com/anime/${newId}`;
</script>
{#if url.length > 0}
<button class="underline underline-offset-2 px-4 py-1" on:click={() => BrowserOpenURL(url)}>{newId}</button>
<button
type="button"
class="underline underline-offset-2 px-4 py-1"
on:click={() => BrowserOpenURL(url)}>{newId}</button
>
{:else}
{id}
{/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">
import type {TableItem} from "../helperTypes/TableTypes";
import type {TableItem} from "../types/TableTypes";
import { tableItems } from "./GlobalVariablesAndHelperFunctions.svelte"
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">
import {
GetAniListItem,
GetAniListLoggedInUser,
GetAniListUserWatchingList,
GetMyAnimeListAnime,
GetMyAnimeListLoggedInUser,
GetSimklLoggedInUser,
LogoutAniList,
LogoutMyAnimeList,
LogoutSimkl,
SimklGetUserWatchlist,
SimklSearch
} from "../../wailsjs/go/main/App";
import {userStore} from "../helperFunctions/userStore";
import type {
AniListCurrentUserWatchList,
AniListGetSingleAnime
} from "../anilist/types/AniListCurrentUserWatchListType.js";
import {writable} from 'svelte/store'
import type {SimklAnime, SimklUser, SimklWatchList} from "../simkl/types/simklTypes";
import {type AniListUser, MediaListSort} from "../anilist/types/AniListTypes";
import type {MALAnime, MALWatchlist, MyAnimeListUser} from "../mal/types/MALTypes";
import type {TableItems} from "../helperTypes/TableTypes";
import {AniListGetSingleAnimeDefaultData} from "../helperDefaults/AniListGetSingleAnime";
} from "../types/AniListCurrentUserWatchListType.js";
import {get, writable} from 'svelte/store'
import type {SimklAnime, SimklWatchList} from "../types/simklTypes";
import type {MALAnime, MALWatchlist} from "../types/MALTypes";
import type {TableItems} from "../types/TableTypes";
import {AniListGetSingleAnimeDefaultData} from "../defaults/AniListGetSingleAnime";
import {AniListWatchListDefaultData} from "../defaults/AniListWatchListDefaultData";
export let aniListAnime = writable(AniListGetSingleAnimeDefaultData)
export let title = writable("")
export let aniListLoggedIn = writable(false)
export let simklLoggedIn = writable(false)
export let malLoggedIn = writable(false)
export let simklWatchList = writable({} as SimklWatchList)
export let aniListPrimary = writable(true)
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 aniListWatchlist = writable(AniListWatchListDefaultData)
export let malWatchList = writable({} as MALWatchlist)
export let malAnime = writable({} as MALAnime)
export let simklAnime = writable({} as SimklAnime)
@@ -45,25 +29,14 @@
export let watchListPage = writable(1)
export let animePerPage = writable(20)
let isAniListPrimary: boolean
let page: number
let perPage: number
let aniWatchlist: AniListCurrentUserWatchList
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)
export async function GetAnimeSingleItem(aniId: number, login: boolean): Promise<""> {
const store = get(userStore)
if (store.anilist.isLoggedIn)
await GetAniListItem(aniId, login).then(aniListResult => {
let finalResult: AniListGetSingleAnime
finalResult = aniListResult
@@ -85,78 +58,16 @@
currentAniListAnime.data.MediaList.media.title.romaji :
currentAniListAnime.data.MediaList.media.title.english)
})
if (isMalLoggedIn) {
if (store.mal.isLoggedIn) {
await GetMyAnimeListAnime(currentAniListAnime.data.MediaList.media.idMal).then(malResult => {
malAnime.set(malResult)
})
}
if (isSimklLoggedIn) {
if (store.simkl.isLoggedIn) {
await SimklSearch(currentAniListAnime.data.MediaList).then((value: SimklAnime) => {
simklAnime.set(value)
})
}
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>

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">
import { aniListAnime, GetAnimeSingleItem } from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
import Anime from "../helperComponents/Anime.svelte"
import { AniListGetSingleAnimeDefaultData } from "../helperDefaults/AniListGetSingleAnime";
import { AniListGetSingleAnimeDefaultData } from "../defaults/AniListGetSingleAnime";
import Spinner from "../helperComponents/Spinner.svelte";
export let params: Record<string, string>

View File

@@ -2,19 +2,13 @@
import Pagination from "../helperComponents/Pagination.svelte";
import WatchList from "../helperComponents/WatchList.svelte";
import {
aniListLoggedIn,
aniListPrimary,
loading,
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
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>
{#if isAniListLoggedIn && isAniListPrimary}
{#if $userStore.anilist.isLoggedIn && $userStore.anilist.isPrimary}
<div class="container py-10">
<Pagination />
<WatchList />

View File

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

View File

@@ -364,7 +364,7 @@ export namespace main {
id: number;
mediaId: 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\""; 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\""; Genres []string "json:\"genres\""; Tags []struct { Id int "json:\"id\""; Name string "json:\"name\""; Description string "json:\"description\""; Rank int "json:\"rank\""; IsMediaSpoiler bool "json:\"isMediaSpoiler\""; IsAdult bool "json:\"isAdult\"" } "json:\"tags\""; IsAdult bool "json:\"isAdult\"" }
media: any;
status: string;
// Go type: struct { Year int "json:\"year\""; Month int "json:\"month\""; Day int "json:\"day\"" }

View File

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