11 Commits

10 changed files with 760 additions and 731 deletions

View File

@@ -12,7 +12,7 @@ post {
headers { headers {
Accept: application/json Accept: application/json
Content-Type: application/json Content-Type: application/x-www-form-urlencoded
} }
body:form-urlencoded { body:form-urlencoded {
@@ -20,7 +20,7 @@ body:form-urlencoded {
client_id: {{ANILIST_APP_ID}} client_id: {{ANILIST_APP_ID}}
client_secret: {{ANILIST_SECRET_TOKEN}} client_secret: {{ANILIST_SECRET_TOKEN}}
redirect_uri: http://localhost:6734/callback redirect_uri: http://localhost:6734/callback
code: {{code}} code: {{ANILIST_CODE}}
} }
body:multipart-form { body:multipart-form {

View File

@@ -9,7 +9,7 @@ vars {
} }
vars:secret [ vars:secret [
ANILIST_ACCESS_TOKEN, ANILIST_ACCESS_TOKEN,
code, ANILIST_CODE,
SIMKL_AUTH_TOKEN, SIMKL_AUTH_TOKEN,
MAL_CODE, MAL_CODE,
MAL_VERIFIER, MAL_VERIFIER,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

BIN
build/icon/128/AniTrack.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

BIN
build/icon/32/AniTrack.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
build/icon/48/AniTrack.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
build/icon/64/AniTrack.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

26
build/install_linux.sh Executable file
View File

@@ -0,0 +1,26 @@
#!/bin/bash
# copy desktop file
if [ -e "~/.local/share/applications/AniTrack.desktop" ]; then
if [ -d "~/.local/share/applications/" ]; then
cp ./AniTrack.desktop ~/.local/share/applications/
else
mkdir -p ~/.local/share/applications/
cp ./AniTrack.desktop ~/.local/share/applications/
fi
fi
# copy icons to xdg folders
for size in 32 48 64 128; do
xdg-icon-resource install --novendor --context apps --size $size ./icon/$size/AniTrack.png AniTrack
done
# copy AniTrack Binary to $HOME/Applications/
if ! [ -d "~/Applications" ]; then
mkdir -p ~/Applications
cp ./bin/AniTrack ~/Applications/
elif ! [[ -e ~/Applications/AniTrack ]]; then
cp ./bin/AniTrack ~/Applications/
fi
echo "AniTrack has been successfully installed."

View File

@@ -8,7 +8,6 @@
simklLoggedIn, simklLoggedIn,
} from "../helperModules/GlobalVariablesAndHelperFunctions.svelte"; } from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
import { push } from "svelte-spa-router"; import { push } from "svelte-spa-router";
import { Button } from "flowbite-svelte";
import type { AniListGetSingleAnime } from "../anilist/types/AniListCurrentUserWatchListType"; import type { AniListGetSingleAnime } from "../anilist/types/AniListCurrentUserWatchListType";
import Rating from "./Rating.svelte"; import Rating from "./Rating.svelte";
import { import {
@@ -23,15 +22,9 @@
} from "../mal/types/MALTypes"; } from "../mal/types/MALTypes";
import type { SimklAnime } from "../simkl/types/simklTypes"; import type { SimklAnime } from "../simkl/types/simklTypes";
import { writable } from "svelte/store"; import { writable } from "svelte/store";
import type { import type { StatusOption, StatusOptions } from "../helperTypes/StatusTypes";
StatusOption,
StatusOptions,
} from "../helperTypes/StatusTypes";
import type { AniListUpdateVariables } from "../anilist/types/AniListTypes"; import type { AniListUpdateVariables } from "../anilist/types/AniListTypes";
import { import { convertDateToAniList } from "../helperFunctions/convertDateToAniList";
convertDateStringToAniList,
convertDateToAniList,
} from "../helperFunctions/convertDateToAniList";
import { import {
AniListDeleteEntry, AniListDeleteEntry,
AniListUpdateEntry, AniListUpdateEntry,
@@ -83,8 +76,7 @@
{ id: 5, aniList: "REPEATING", mal: "rewatching", simkl: "watching" }, { id: 5, aniList: "REPEATING", mal: "rewatching", simkl: "watching" },
]; ];
let startingAnilistStatusOption: StatusOption = statusOptions.filter( let startingAnilistStatusOption: StatusOption = statusOptions.filter(
(option) => (option) => currentAniListAnime.data.MediaList.status === option.aniList,
currentAniListAnime.data.MediaList.status === option.aniList,
)[0]; )[0];
let startedAtDate: Date | null = convertAniListDateToDate( let startedAtDate: Date | null = convertAniListDateToDate(
currentAniListAnime.data.MediaList.startedAt, currentAniListAnime.data.MediaList.startedAt,
@@ -115,15 +107,11 @@
let startDate = ""; let startDate = "";
let finishDate = ""; let finishDate = "";
if (currentMalAnime.my_list_status.start_date !== "") { if (currentMalAnime.my_list_status.start_date !== "") {
const startArray = re.exec( const startArray = re.exec(currentMalAnime.my_list_status.start_date);
currentMalAnime.my_list_status.start_date,
);
startDate = `${startArray[2]}-${startArray[3]}-${startArray[1]}`; startDate = `${startArray[2]}-${startArray[3]}-${startArray[1]}`;
} }
if (currentMalAnime.my_list_status.finish_date !== "") { if (currentMalAnime.my_list_status.finish_date !== "") {
const finishArray = re.exec( const finishArray = re.exec(currentMalAnime.my_list_status.finish_date);
currentMalAnime.my_list_status.finish_date,
);
finishDate = `${finishArray[2]}-${finishArray[3]}-${finishArray[1]}`; finishDate = `${finishArray[2]}-${finishArray[3]}-${finishArray[1]}`;
} }
AddAnimeServiceToTable({ AddAnimeServiceToTable({
@@ -200,10 +188,7 @@
submitData[key] = value; submitData[key] = value;
} }
if ( if (isAniListLoggedIn && currentAniListAnime.data.MediaList.mediaId !== 0) {
isAniListLoggedIn &&
currentAniListAnime.data.MediaList.mediaId !== 0
) {
let body: AniListUpdateVariables = { let body: AniListUpdateVariables = {
mediaId: currentAniListAnime.data.MediaList.mediaId, mediaId: currentAniListAnime.data.MediaList.mediaId,
progress: submitData.episodes, progress: submitData.episodes,
@@ -214,8 +199,7 @@
startedAt: convertDateToAniList(startedAtDate), startedAt: convertDateToAniList(startedAtDate),
completedAt: convertDateToAniList(completedAtDate), completedAt: convertDateToAniList(completedAtDate),
}; };
await AniListUpdateEntry(body).then( await AniListUpdateEntry(body).then((value: AniListGetSingleAnime) => {
(value: AniListGetSingleAnime) => {
/* TODO in future when you inevitably add tags to typescript, until Anilist fixes the api bug /* TODO in future when you inevitably add tags to typescript, until Anilist fixes the api bug
where tags break the SaveMediaListEntry return, you'll want to use this delete line where tags break the SaveMediaListEntry return, you'll want to use this delete line
delete value.data.MediaList.media.tags */ delete value.data.MediaList.media.tags */
@@ -239,8 +223,7 @@
repeat: currentAniListAnime.data.MediaList.repeat, repeat: currentAniListAnime.data.MediaList.repeat,
notes: currentAniListAnime.data.MediaList.notes, notes: currentAniListAnime.data.MediaList.notes,
}); });
}, });
);
} }
if (malLoggedIn && currentMalAnime.id !== 0) { if (malLoggedIn && currentMalAnime.id !== 0) {
@@ -257,8 +240,7 @@
(malAnimeReturn: MalListStatus) => { (malAnimeReturn: MalListStatus) => {
malAnime.update((value) => { malAnime.update((value) => {
value.my_list_status.status = malAnimeReturn.status; value.my_list_status.status = malAnimeReturn.status;
value.my_list_status.is_rewatching = value.my_list_status.is_rewatching = malAnimeReturn.is_rewatching;
malAnimeReturn.is_rewatching;
value.my_list_status.score = malAnimeReturn.score; value.my_list_status.score = malAnimeReturn.score;
value.my_list_status.num_episodes_watched = value.my_list_status.num_episodes_watched =
malAnimeReturn.num_episodes_watched; malAnimeReturn.num_episodes_watched;
@@ -285,14 +267,12 @@
id: `m-${currentMalAnime.id}`, id: `m-${currentMalAnime.id}`,
title: currentMalAnime.title, title: currentMalAnime.title,
service: "MyAnimeList", service: "MyAnimeList",
progress: progress: currentMalAnime.my_list_status.num_episodes_watched,
currentMalAnime.my_list_status.num_episodes_watched,
status: currentMalAnime.my_list_status.status, status: currentMalAnime.my_list_status.status,
startedAt: startDate, startedAt: startDate,
completedAt: finishDate, completedAt: finishDate,
score: currentMalAnime.my_list_status.score, score: currentMalAnime.my_list_status.score,
repeat: currentMalAnime.my_list_status repeat: currentMalAnime.my_list_status.num_times_rewatched,
.num_times_rewatched,
notes: currentMalAnime.my_list_status.comments, notes: currentMalAnime.my_list_status.comments,
}); });
}, },
@@ -300,13 +280,9 @@
} }
if (simklLoggedIn && currentSimklAnime.show.ids.simkl !== 0) { if (simklLoggedIn && currentSimklAnime.show.ids.simkl !== 0) {
if ( if (currentSimklAnime.watched_episodes_count !== submitData.episodes) {
currentSimklAnime.watched_episodes_count !== submitData.episodes await SimklSyncEpisodes(currentSimklAnime, submitData.episodes).then(
) { (value: SimklAnime) => {
await SimklSyncEpisodes(
currentSimklAnime,
submitData.episodes,
).then((value: SimklAnime) => {
AddAnimeServiceToTable({ AddAnimeServiceToTable({
id: `s-${value.show.ids.simkl}`, id: `s-${value.show.ids.simkl}`,
title: value.show.title, title: value.show.title,
@@ -323,14 +299,13 @@
newValue = value; newValue = value;
return newValue; return newValue;
}); });
}); },
);
} }
if (currentSimklAnime.user_rating !== submitData.rating) { if (currentSimklAnime.user_rating !== submitData.rating) {
await SimklSyncRating( await SimklSyncRating(currentSimklAnime, submitData.rating).then(
currentSimklAnime, (value) => {
submitData.rating,
).then((value) => {
AddAnimeServiceToTable({ AddAnimeServiceToTable({
id: `s-${value.show.ids.simkl}`, id: `s-${value.show.ids.simkl}`,
title: value.show.title, title: value.show.title,
@@ -347,14 +322,13 @@
newValue = value; newValue = value;
return newValue; return newValue;
}); });
}); },
);
} }
if (currentSimklAnime.status !== submitData.status.simkl) { if (currentSimklAnime.status !== submitData.status.simkl) {
await SimklSyncStatus( await SimklSyncStatus(currentSimklAnime, submitData.status.simkl).then(
currentSimklAnime, (value) => {
submitData.status.simkl,
).then((value) => {
AddAnimeServiceToTable({ AddAnimeServiceToTable({
id: `s-${value.show.ids.simkl}`, id: `s-${value.show.ids.simkl}`,
title: value.show.title, title: value.show.title,
@@ -371,7 +345,8 @@
newValue = value; newValue = value;
return newValue; return newValue;
}); });
}); },
);
} }
} }
@@ -382,10 +357,7 @@
const deleteEntries = async () => { const deleteEntries = async () => {
submitting.set(true); submitting.set(true);
if ( if (isAniListLoggedIn && currentAniListAnime.data.MediaList.mediaId !== 0) {
isAniListLoggedIn &&
currentAniListAnime.data.MediaList.mediaId !== 0
) {
await AniListDeleteEntry(currentAniListAnime.data.MediaList.id); await AniListDeleteEntry(currentAniListAnime.data.MediaList.id);
AddAnimeServiceToTable({ AddAnimeServiceToTable({
id: `a-${currentAniListAnime.data.MediaList.mediaId}`, id: `a-${currentAniListAnime.data.MediaList.mediaId}`,
@@ -445,8 +417,7 @@
currentAniListAnime.data.MediaList.media.nextAiringEpisode.episode !== 0 currentAniListAnime.data.MediaList.media.nextAiringEpisode.episode !== 0
) { ) {
max = max =
currentAniListAnime.data.MediaList.media.nextAiringEpisode.episode - currentAniListAnime.data.MediaList.media.nextAiringEpisode.episode - 1;
1;
} }
</script> </script>
@@ -479,8 +450,17 @@
type="button" type="button"
id="decrement-button" id="decrement-button"
data-input-counter-decrement="quantity-input" data-input-counter-decrement="quantity-input"
on:click={() => on:click={() => {
(currentAniListAnime.data.MediaList.progress -= 1)} currentAniListAnime.data.MediaList.progress -= 1;
if (
currentAniListAnime.data.MediaList.progress <
currentAniListAnime.data.MediaList.media.episodes
) {
startingAnilistStatusOption = statusOptions[0];
if (currentAniListAnime.data.MediaList.repeat === 0)
completedAtDate = null;
}
}}
class="bg-gray-700 hover:bg-gray-600 border-gray-600 border rounded-s-lg p-3 h-11 focus:ring-gray-700 focus:ring-2 focus:outline-none" class="bg-gray-700 hover:bg-gray-600 border-gray-600 border rounded-s-lg p-3 h-11 focus:ring-gray-700 focus:ring-2 focus:outline-none"
> >
<svg <svg
@@ -507,30 +487,38 @@
id="episodes" id="episodes"
class="border border-x-0 p-2.5 h-11 text-center text-sm block w-full placeholder-gray-400 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none class="border border-x-0 p-2.5 h-11 text-center text-sm block w-full placeholder-gray-400 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none
{currentAniListAnime.data.MediaList.progress < 0 || {currentAniListAnime.data.MediaList.progress < 0 ||
(currentAniListAnime.data.MediaList.media.episodes > (currentAniListAnime.data.MediaList.media.episodes > 0 &&
0 &&
currentAniListAnime.data.MediaList.progress > currentAniListAnime.data.MediaList.progress >
currentAniListAnime.data.MediaList.media currentAniListAnime.data.MediaList.media.episodes) ||
.episodes) || (currentAniListAnime.data.MediaList.media.nextAiringEpisode
(currentAniListAnime.data.MediaList.media .episode > 0 &&
.nextAiringEpisode.episode > 0 &&
currentAniListAnime.data.MediaList.progress > currentAniListAnime.data.MediaList.progress >
currentAniListAnime.data.MediaList.media currentAniListAnime.data.MediaList.media.nextAiringEpisode
.nextAiringEpisode.episode - .episode -
1) 1)
? 'border-red-500 border-[2px] text-rose-300 focus:ring-red-500 focus:border-red-500' ? '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'} w-24"
bind:value={ bind:value={currentAniListAnime.data.MediaList.progress}
currentAniListAnime.data.MediaList.progress
}
required required
/> />
<button <button
type="button" type="button"
id="increment-button" id="increment-button"
data-input-counter-increment="quantity-input" data-input-counter-increment="quantity-input"
on:click={() => on:click={() => {
(currentAniListAnime.data.MediaList.progress += 1)} currentAniListAnime.data.MediaList.progress += 1;
if (
currentAniListAnime.data.MediaList.media.episodes ===
currentAniListAnime.data.MediaList.progress
) {
startingAnilistStatusOption = statusOptions[2];
completedAtDate = new Date();
}
if (currentAniListAnime.data.MediaList.progress - 1 === 0) {
startingAnilistStatusOption = statusOptions[0];
if (startedAtDate === null) startedAtDate = new Date();
}
}}
class="bg-gray-700 hover:bg-gray-600 border-gray-600 border rounded-e-lg p-3 h-11 focus:ring-gray-700 focus:ring-2 focus:outline-none" class="bg-gray-700 hover:bg-gray-600 border-gray-600 border rounded-e-lg p-3 h-11 focus:ring-gray-700 focus:ring-2 focus:outline-none"
> >
<svg <svg
@@ -551,21 +539,37 @@
</button> </button>
</div> </div>
<div> <div>
/ {currentAniListAnime.data.MediaList.media / {currentAniListAnime.data.MediaList.media.nextAiringEpisode
.nextAiringEpisode.episode !== 0 .episode !== 0
? currentAniListAnime.data.MediaList.media ? currentAniListAnime.data.MediaList.media.nextAiringEpisode
.nextAiringEpisode.episode - 1 .episode - 1
: currentAniListAnime.data.MediaList.media.episodes} : currentAniListAnime.data.MediaList.media.episodes}
</div> </div>
{#if currentAniListAnime.data.MediaList.media.nextAiringEpisode.episode !== 0} {#if currentAniListAnime.data.MediaList.media.nextAiringEpisode.episode !== 0}
<div> <div>
of {currentAniListAnime.data.MediaList.media of {currentAniListAnime.data.MediaList.media.episodes}
.episodes}
</div> </div>
{/if} {/if}
</div> </div>
<div>
<label
for="status"
class="text-left block mb-2 text-sm font-medium text-white"
>Status</label
>
<select
id="status"
name="status"
class="border text-sm rounded-lg
block p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400
text-white focus:ring-blue-500 focus:border-blue-500"
bind:value={startingAnilistStatusOption}
>
{#each statusOptions as option}
<option value={option}>{option.aniList}</option>
{/each}
</select>
</div>
</div> </div>
<div <div
class="flex flex-col md:flex-row md:pl-10 md:pr-10 pt-5 pb-5 justify-center md:gap-x-16 lg:gap-x-36" class="flex flex-col md:flex-row md:pl-10 md:pr-10 pt-5 pb-5 justify-center md:gap-x-16 lg:gap-x-36"
@@ -615,8 +619,7 @@
name="repeat" name="repeat"
min="0" min="0"
id="repeat" id="repeat"
class="border {currentAniListAnime.data.MediaList class="border {currentAniListAnime.data.MediaList.repeat < 0
.repeat < 0
? 'border-red-500 border-[2px] text-rose-300 focus:ring-red-500 focus:border-red-500' ? '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 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"
bind:value={currentAniListAnime.data.MediaList.repeat} bind:value={currentAniListAnime.data.MediaList.repeat}
@@ -649,7 +652,7 @@
<div <div
class="w-full mx-auto max-w-screen-xl p-4 md:flex md:items-center md:justify-end" class="w-full mx-auto max-w-screen-xl p-4 md:flex md:items-center md:justify-end"
> >
<Button <button
disabled={isSubmitting} disabled={isSubmitting}
id="sync-button" id="sync-button"
class="text-white {$submitSuccess class="text-white {$submitSuccess
@@ -678,8 +681,8 @@
/> />
</svg> </svg>
Sync Changes Sync Changes
</Button> </button>
<Button <button
class="text-white bg-gray-800 border border-gray-600 focus:outline-none hover:bg-gray-700 focus:ring-4 class="text-white bg-gray-800 border border-gray-600 focus:outline-none hover:bg-gray-700 focus:ring-4
focus:ring-gray-700 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 focus:ring-gray-700 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2
hover:border-gray-600" hover:border-gray-600"
@@ -689,7 +692,7 @@
}} }}
> >
Go Home Go Home
</Button> </button>
</div> </div>
</div> </div>
<AnimeTable /> <AnimeTable />
@@ -698,10 +701,10 @@
<div <div
class="w-full mx-auto max-w-screen-xl p-4 md:flex md:items-center md:justify-start" class="w-full mx-auto max-w-screen-xl p-4 md:flex md:items-center md:justify-start"
> >
<Button <button
disabled={isSubmitting} disabled={isSubmitting}
id="delete-button" id="delete-button"
class="text-white bg-red-700 {$submitSuccess class="text-white {$submitSuccess
? 'bg-green-600 hover:bg-green-700 focus:ring-4 focus:ring-green-800' ? '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" : '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"
on:click={deleteEntries} on:click={deleteEntries}
@@ -727,12 +730,12 @@
/> />
</svg> </svg>
Delete Entries Delete Entries
</Button> </button>
</div> </div>
<div <div
class="w-full mx-auto max-w-screen-xl p-4 md:flex md:items-center md:justify-end" class="w-full mx-auto max-w-screen-xl p-4 md:flex md:items-center md:justify-end"
> >
<Button <button
disabled={isSubmitting} disabled={isSubmitting}
id="sync-button" id="sync-button"
class="text-white {$submitSuccess class="text-white {$submitSuccess
@@ -761,8 +764,8 @@
/> />
</svg> </svg>
Sync Changes Sync Changes
</Button> </button>
<Button <button
class="text-white bg-gray-800 border border-gray-600 focus:outline-none hover:bg-gray-700 focus:ring-4 class="text-white bg-gray-800 border border-gray-600 focus:outline-none hover:bg-gray-700 focus:ring-4
focus:ring-gray-700 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 focus:ring-gray-700 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2
hover:border-gray-600" hover:border-gray-600"
@@ -772,7 +775,7 @@
}} }}
> >
Go Home Go Home
</Button> </button>
</div> </div>
</div> </div>

View File

@@ -12,6 +12,6 @@
}, },
"info": { "info": {
"productName": "AniTrack", "productName": "AniTrack",
"productVersion": "0.1.7" "productVersion": "0.1.8"
} }
} }