mirror of
https://github.com/NoCrypt/migu.git
synced 2026-03-11 17:45:32 +00:00
Merge branch 'master' of https://github.com/RockinChaos/miru
This commit is contained in:
commit
37689a9023
20 changed files with 260 additions and 154 deletions
|
|
@ -111,7 +111,6 @@
|
|||
|
||||
<div class="page-wrapper with-transitions bg-dark position-relative" data-sidebar-type='overlayed-all'>
|
||||
<Menubar bind:page={$page} />
|
||||
<Profiles />
|
||||
<Sidebar bind:page={$page} />
|
||||
<div class='overflow-hidden content-wrapper h-full'>
|
||||
<Toaster visibleToasts={6} position='top-right' theme='dark' richColors duration={10000} closeButton toastOptions={{
|
||||
|
|
@ -119,6 +118,7 @@
|
|||
closeButton: SUPPORTS.isAndroid ? "toast-close-button" : ""
|
||||
}
|
||||
}} />
|
||||
<Profiles />
|
||||
<ViewAnime bind:overlay={$overlay} />
|
||||
<UpdateModal/>
|
||||
<TorrentModal bind:overlay={$overlay} />
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
<script>
|
||||
import { getContext } from 'svelte'
|
||||
import { media } from '../views/Player/MediaHandler.svelte'
|
||||
import { rss } from '@/views/TorrentSearch/TorrentModal.svelte'
|
||||
import { media } from '@/views/Player/MediaHandler.svelte'
|
||||
import { profileView } from './Profiles.svelte'
|
||||
import { click } from '@/modules/click.js'
|
||||
import IPC from '@/modules/ipc.js'
|
||||
import NavbarLink from './NavbarLink.svelte'
|
||||
// import { click } from '@/modules/click.js'
|
||||
// import IPC from '@/modules/ipc.js'
|
||||
import { MagnifyingGlass } from 'svelte-radix'
|
||||
import { Users, Clock, Settings, Heart, ListVideo, House } from 'lucide-svelte'
|
||||
const view = getContext('view')
|
||||
|
|
@ -29,25 +30,27 @@
|
|||
<NavbarLink click={() => { page = 'home'; noModals()}} _page='home' icon='home' {page} let:active>
|
||||
<House size='3.3rem' class='flex-shrink-0 p-5 m-5 rounded' strokeWidth='2.5' color={active ? 'currentColor':'#888'} />
|
||||
</NavbarLink>
|
||||
<NavbarLink click={() => { page = 'search'; noModals()}} _page='search' icon='search' {page}>
|
||||
<MagnifyingGlass size='3.3rem' class='flex-shrink-0 p-5 m-5 rounded' stroke-width='0.9' style="color: {page==='search' ? 'currentColor':'#888'}" stroke='currentColor'/>
|
||||
<NavbarLink click={() => { page = 'search'; noModals()}} _page='search' css='ml-auto' icon='search' {page} overlay={($view || $profileView || $rss) && 'active'} let:active>
|
||||
<MagnifyingGlass size='3.3rem' class='flex-shrink-0 p-5 m-5 rounded' stroke-width={active ? '1' : '0.9'} style="color: {page==='search' ? 'currentColor':'#888'}" stroke='currentColor'/>
|
||||
</NavbarLink>
|
||||
{#if $media?.media}
|
||||
<NavbarLink click={() => { noModals(false) }} icon='queue_music' {page} let:active>
|
||||
{@const currentMedia = $view}
|
||||
{@const active = $view && !$profileView && 'active'}
|
||||
<NavbarLink click={() => { $view = (currentMedia?.id === $media.media.id && active ? null : $media.media) }} icon='queue_music' {page} overlay={active} nowPlaying={$view === $media.media} let:active>
|
||||
<ListVideo size='3.3rem' class='flex-shrink-0 p-5 m-5 rounded' strokeWidth='2.5' color={active ? 'currentColor':'#888'} />
|
||||
</NavbarLink>
|
||||
{:else}
|
||||
<NavbarLink click={() => { page = 'schedule'; noModals() }} _page='schedule' icon='schedule' {page} let:active>
|
||||
<NavbarLink click={() => { page = 'schedule'; noModals() }} _page='schedule' icon='schedule' {page} overlay={($view || $profileView || $rss) && 'active'} let:active>
|
||||
<Clock size='3.3rem' class='flex-shrink-0 p-5 m-5 rounded' strokeWidth='2.5' color={active ? 'currentColor':'#888'} />
|
||||
</NavbarLink>
|
||||
{/if}
|
||||
<NavbarLink click={() => { page = 'watchtogether'; noModals() }} _page='watchtogether' icon='groups' {page} let:active>
|
||||
<NavbarLink click={() => { page = 'watchtogether'; noModals() }} _page='watchtogether' icon='groups' {page} overlay={($view || $profileView || $rss) && 'active'} let:active>
|
||||
<Users size='3.3rem' class='flex-shrink-0 p-5 m-5 rounded' strokeWidth='2.5' color={active ? 'currentColor':'#888'} />
|
||||
</NavbarLink>
|
||||
<!-- <NavbarLink click={() => { IPC.emit('open', 'https://github.com/sponsors/ThaUnknown/') }} icon='favorite' css='ml-auto donate' {page} let:active>
|
||||
<Heart size='3.3rem' class='flex-shrink-0 p-5 m-5 rounded donate' strokeWidth='2.5' fill={active ? 'currentColor':'#888'} />
|
||||
</NavbarLink> -->
|
||||
<NavbarLink click={() => { page = 'settings'; noModals() }} _page='settings' icon='settings' css='ml-auto' {page} let:active>
|
||||
<NavbarLink click={() => { page = 'settings'; noModals() }} _page='settings' icon='settings' css='ml-auto' {page} overlay={($view || $profileView || $rss) && 'active'} let:active>
|
||||
<Settings size='3.3rem' class='flex-shrink-0 p-5 m-5 rounded' strokeWidth='2.5' color={active ? 'currentColor':'#888'} />
|
||||
</NavbarLink>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -7,13 +7,16 @@
|
|||
export let _page = ''
|
||||
export let css = ''
|
||||
export let icon = ''
|
||||
export let nowPlaying = false
|
||||
export let overlay = ''
|
||||
</script>
|
||||
|
||||
<div
|
||||
class='navbar-link navbar-link-with-icon pointer overflow-hidden mx-auto {css}'
|
||||
use:click={() => { _click(); if (!icon.includes("favorite")) { window.dispatchEvent(new Event('overlay-check')) } } }>
|
||||
use:click={() => { if (!icon.includes("favorite")) { window.dispatchEvent(new Event('overlay-check')) } _click() } }>
|
||||
|
||||
<span class='rounded d-flex'>
|
||||
<slot active={page === _page}>{icon}</slot>
|
||||
<slot active={(page === _page && overlay !== 'active') || (overlay === 'active' && nowPlaying)}>{icon}</slot>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -86,9 +86,15 @@
|
|||
if (keyCode === 27) close()
|
||||
}
|
||||
$: $profileView && modal?.focus()
|
||||
|
||||
window.addEventListener('overlay-check', () => {
|
||||
if ($profileView) {
|
||||
close()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class='modal z-101' class:show={$profileView}>
|
||||
<div class='modal z-55' class:show={$profileView}>
|
||||
{#if $profileView}
|
||||
<div class='modal-dialog' on:pointerup|self={close} on:keydown={checkClose} tabindex='-1' role='button' bind:this={modal}>
|
||||
<div class='modal-content w-auto mw-400 d-flex justify-content-center flex-column'>
|
||||
|
|
@ -183,6 +189,9 @@
|
|||
.mt-2 {
|
||||
margin-top: .4rem;
|
||||
}
|
||||
.z-55 {
|
||||
z-index: 55;
|
||||
}
|
||||
.mw-400 {
|
||||
min-width: 35rem;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script context='module'>
|
||||
const badgeKeys = ['title', 'search', 'genre', 'tag', 'season', 'year', 'format', 'status', 'sort', 'hideSubs', 'hideMyAnime', 'hideStatus']
|
||||
const badgeDisplayNames = { title: BookUser, search: Type, genre: Drama, tag: Hash, season: CalendarRange, year: Leaf, format: Tv, status: MonitorPlay, sort: ArrowDownWideNarrow, hideMyAnime: SlidersHorizontal, hideSubs: Mic }
|
||||
const sortOptions = { START_DATE_DESC: 'Release Date', SCORE_DESC: 'Score', POPULARITY_DESC: 'Popularity', TRENDING_DESC: 'Trending', UPDATED_AT_DESC: 'Updated Date', UPDATED_TIME_DESC: 'Last Updated', STARTED_ON_DESC: 'Started On', FINISHED_ON_DESC: 'Finished On', PROGRESS_DESC: 'Your Progress', USER_SCORE_DESC: 'Your Score' }
|
||||
const sortOptions = { TITLE_ROMAJI: 'Title', START_DATE_DESC: 'Release Date', SCORE_DESC: 'Score', POPULARITY_DESC: 'Popularity', UPDATED_AT_DESC: 'Date Updated', UPDATED_TIME_DESC: 'Last Updated', STARTED_ON_DESC: 'Start Date', FINISHED_ON_DESC: 'Completed Date', PROGRESS_DESC: 'Your Progress', USER_SCORE_DESC: 'Your Score' }
|
||||
|
||||
export function searchCleanup (search, badge) {
|
||||
return Object.fromEntries(Object.entries(search).map((entry) => {
|
||||
|
|
@ -479,22 +479,27 @@
|
|||
</div>
|
||||
<div class='input-group'>
|
||||
<select class='form-control bg-dark-light' required bind:value={search.sort} on:change={clearTags} disabled={search.disableSearch}>
|
||||
<option value selected>Name</option>
|
||||
<option value='START_DATE_DESC'>Release Date</option>
|
||||
<option value='SCORE_DESC'>Score</option>
|
||||
<option value selected>Trending</option>
|
||||
<option value='POPULARITY_DESC'>Popularity</option>
|
||||
<option value='TRENDING_DESC'>Trending</option>
|
||||
<option value='TITLE_ROMAJI'>Title</option>
|
||||
<option value='SCORE_DESC'>Score</option>
|
||||
<option value='START_DATE_DESC'>Release Date</option>
|
||||
<option value='UPDATED_AT_DESC'>Updated Date</option>
|
||||
{#if search.userList && search.title && !search.title.includes("Sequels")}
|
||||
<option value='UPDATED_TIME_DESC'>Last Updated</option>
|
||||
<option value='STARTED_ON_DESC'>Started On</option>
|
||||
{#if search.completedList}
|
||||
<option value='FINISHED_ON_DESC'>Finished On</option>
|
||||
<option value='USER_SCORE_DESC'>Your Score</option>
|
||||
{:else}
|
||||
<option value='PROGRESS_DESC'>Your Progress</option>
|
||||
{#if search.userList && search.title && !search.missedList}
|
||||
{#if search.completedList}
|
||||
<option value='FINISHED_ON_DESC'>Completed Date</option>
|
||||
{/if}
|
||||
{#if !search.planningList}
|
||||
<option value='STARTED_ON_DESC'>Start Date</option>
|
||||
{/if}
|
||||
<option value='UPDATED_TIME_DESC'>Last Updated</option>
|
||||
{#if !search.completedList && !search.planningList}
|
||||
<option value='PROGRESS_DESC'>Your Progress</option>
|
||||
{/if}
|
||||
{#if search.completedList || search.droppedList}
|
||||
<option value='USER_SCORE_DESC'>Your Score</option>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
<script>
|
||||
import { getContext } from 'svelte'
|
||||
import { rss } from '@/views/TorrentSearch/TorrentModal.svelte'
|
||||
import { media } from '@/views/Player/MediaHandler.svelte'
|
||||
import { profileView } from './Profiles.svelte'
|
||||
import { settings } from '@/modules/settings.js'
|
||||
// import { toast } from 'svelte-sonner'
|
||||
import { profileView } from './Profiles.svelte'
|
||||
import Helper from '@/modules/helper.js'
|
||||
// import IPC from '@/modules/ipc.js'
|
||||
import SidebarLink from './SidebarLink.svelte'
|
||||
|
|
@ -27,39 +28,41 @@
|
|||
<div class='sidebar z-30 d-md-block' class:animated={$settings.expandingSidebar}>
|
||||
<div class='sidebar-overlay pointer-events-none h-full position-absolute' />
|
||||
<div class='sidebar-menu h-full d-flex flex-column justify-content-center align-items-center m-0 pb-5' class:animate={page !== 'player'}>
|
||||
<SidebarLink click={() => { $profileView = true }} icon='login' text={Helper.getUser() ? 'Profiles' : 'Login'} css='mt-auto' {page} image={Helper.getUserAvatar()}>
|
||||
<SidebarLink click={() => { $profileView = !$profileView }} icon='login' text={Helper.getUser() ? 'Profiles' : 'Login'} css='mt-auto' {page} overlay={$profileView && 'profile'} image={Helper.getUserAvatar()}>
|
||||
<LogIn size='2rem' class='flex-shrink-0 p-5 w-30 h-30 m-5 rounded' />
|
||||
</SidebarLink>
|
||||
<SidebarLink click={() => { page = 'home'; if ($view) $view = null }} _page='home' text='Home' {page} let:active>
|
||||
<Home size='2rem' class='flex-shrink-0 p-5 w-30 h-30 m-5 rounded' strokeWidth={active ? '3.5' : '2'} />
|
||||
<SidebarLink click={() => { page = 'home'; if ($view) $view = null }} _page='home' text='Home' {page} overlay={($view || $profileView || $rss) && 'active'} let:active>
|
||||
<Home size='2rem' class='flex-shrink-0 p-5 w-30 h-30 m-5 rounded' strokeWidth={active ? '3' : '2'} />
|
||||
</SidebarLink>
|
||||
<SidebarLink click={() => { page = 'search'; if ($view) $view = null }} _page='search' text='Search' {page} let:active>
|
||||
<MagnifyingGlass size='2rem' class='flex-shrink-0 p-5 w-30 h-30 m-5 rounded' stroke-width={active ? '2' : '0'} stroke='currentColor' />
|
||||
<SidebarLink click={() => { page = 'search'; if ($view) $view = null }} _page='search' text='Search' {page} overlay={($view || $profileView || $rss) && 'active'} let:active>
|
||||
<MagnifyingGlass size='2rem' class='flex-shrink-0 p-5 w-30 h-30 m-5 rounded' stroke-width={active ? '1' : '0'} stroke='currentColor' />
|
||||
</SidebarLink>
|
||||
<SidebarLink click={() => { page = 'schedule' }} _page='schedule' icon='schedule' text='Schedule' {page} let:active>
|
||||
<Clock size='2rem' class='flex-shrink-0 p-5 w-30 h-30 m-5 rounded' strokeWidth={active ? '3.5' : '2'} />
|
||||
<SidebarLink click={() => { page = 'schedule' }} _page='schedule' icon='schedule' text='Schedule' {page} overlay={($view || $profileView || $rss) && 'active'} let:active>
|
||||
<Clock size='2rem' class='flex-shrink-0 p-5 w-30 h-30 m-5 rounded' strokeWidth={active ? '3' : '2'} />
|
||||
</SidebarLink>
|
||||
{#if $media?.media}
|
||||
<SidebarLink click={() => { $view = $media.media }} icon='queue_music' text='Now Playing' {page} let:active>
|
||||
<ListVideo size='2rem' class='flex-shrink-0 p-5 w-30 h-30 m-5 rounded' strokeWidth={active ? '3.5' : '2'} />
|
||||
{@const currentMedia = $view}
|
||||
{@const active = $view && !$profileView && 'active'}
|
||||
<SidebarLink click={() => { $view = (currentMedia?.id === $media.media.id && active ? null : $media.media); }} icon='queue_music' text='Now Playing' {page} overlay={active} nowPlaying={$view === $media.media} let:active>
|
||||
<ListVideo size='2rem' class='flex-shrink-0 p-5 w-30 h-30 m-5 rounded' strokeWidth={active ? '3' : '2'} />
|
||||
</SidebarLink>
|
||||
{/if}
|
||||
<SidebarLink click={() => { page = 'watchtogether' }} _page='watchtogether' icon='groups' text='Watch Together' {page} let:active>
|
||||
<Users size='2rem' class='flex-shrink-0 p-5 w-30 h-30 m-5 rounded' strokeWidth={active ? '3.5' : '2'} />
|
||||
<SidebarLink click={() => { page = 'watchtogether' }} _page='watchtogether' icon='groups' text='Watch Together' {page} overlay={($view || $profileView || $rss) && 'active'} let:active>
|
||||
<Users size='2rem' class='flex-shrink-0 p-5 w-30 h-30 m-5 rounded' strokeWidth={active ? '3' : '2'} />
|
||||
</SidebarLink>
|
||||
<!-- <SidebarLink click={() => { IPC.emit('open', 'https://github.com/sponsors/ThaUnknown/') }} icon='favorite' text='Support This App' css='mt-auto' {page} let:active>
|
||||
<Heart size='2rem' class='flex-shrink-0 p-5 w-30 h-30 m-5 rounded donate' strokeWidth={active ? '3.5' : '2'} fill='currentColor' />
|
||||
<Heart size='2rem' class='flex-shrink-0 p-5 w-30 h-30 m-5 rounded donate' strokeWidth={active ? '3' : '2'} fill='currentColor' />
|
||||
</SidebarLink> -->
|
||||
<!-- {#if updateState === 'downloading'}
|
||||
<SidebarLink click={() => { toast('Update is downloading...') }} icon='download' text='Update Downloading...' {page} let:active>
|
||||
<Download size='2rem' class='flex-shrink-0 p-5 w-30 h-30 m-5 rounded' strokeWidth={active ? '3.5' : '2'} />
|
||||
<Download size='2rem' class='flex-shrink-0 p-5 w-30 h-30 m-5 rounded' strokeWidth={active ? '3' : '2'} />
|
||||
</SidebarLink>
|
||||
{:else if updateState === 'ready'}
|
||||
<SidebarLink click={() => { IPC.emit('quit-and-install') }} icon='download' text='Update Ready!' {page} let:active>
|
||||
<Download size='2rem' class='flex-shrink-0 p-5 w-30 h-30 m-5 rounded update' strokeWidth={active ? '3.5' : '2'} />
|
||||
<Download size='2rem' class='flex-shrink-0 p-5 w-30 h-30 m-5 rounded update' strokeWidth={active ? '3' : '2'} />
|
||||
</SidebarLink>
|
||||
{/if} -->
|
||||
<SidebarLink click={() => { page = 'settings' }} _page='settings' icon='settings' text='Settings' css='mt-auto' {page} let:active>
|
||||
<SidebarLink click={() => { page = 'settings' }} _page='settings' icon='settings' text='Settings' css='mt-auto' {page} overlay={($view || $profileView || $rss) && 'active'} let:active>
|
||||
<Settings size='2rem' class='flex-shrink-0 p-5 w-30 h-30 m-5 rounded' strokeWidth={active ? '3.5' : '2'} />
|
||||
</SidebarLink>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,21 +9,24 @@
|
|||
export let css = ''
|
||||
export let text = ''
|
||||
export let icon = ''
|
||||
export let nowPlaying = false
|
||||
export let overlay = ''
|
||||
</script>
|
||||
|
||||
<div class='sidebar-link sidebar-link-with-icon pointer overflow-hidden {css}'
|
||||
use:click={() => { _click(); if (!icon.includes("login") && !icon.includes("favorite")) { window.dispatchEvent(new Event('overlay-check')) } } }>
|
||||
use:click={() => { if (!icon.includes("login") && !icon.includes("favorite")) { window.dispatchEvent(new Event('overlay-check')) } _click() } }>
|
||||
<span class='text-nowrap d-flex align-items-center w-full h-full'>
|
||||
{#if image}
|
||||
<span class='rounded d-flex'>
|
||||
<img src={image} class='h-30 rounded' alt='logo' />
|
||||
</span>
|
||||
<span class='text ml-20'>{text}</span>
|
||||
<span class='text ml-20 {overlay === "profile" ? "font-weight-bolder font-size-18" : ""}'>{text}</span>
|
||||
{:else}
|
||||
{@const active = (page === _page && overlay !== 'active') || (overlay === 'active' && nowPlaying)}
|
||||
<span class='rounded d-flex'>
|
||||
<slot active={page === _page}>{icon}</slot>
|
||||
<slot active={active}>{icon}</slot>
|
||||
</span>
|
||||
<span class='text ml-20'>{text}</span>
|
||||
<span class='text ml-20 {active ? "font-weight-bolder font-size-18" : ""}'>{text}</span>
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
1
common/modules/al.d.ts
vendored
1
common/modules/al.d.ts
vendored
|
|
@ -149,6 +149,7 @@ export type Viewer = {
|
|||
}
|
||||
name: string
|
||||
id: number
|
||||
sync: boolean
|
||||
mediaListOptions?: {
|
||||
animeList?: {
|
||||
customLists?: string[]
|
||||
|
|
|
|||
|
|
@ -251,6 +251,7 @@ class AnilistClient {
|
|||
variables: {
|
||||
page: 1,
|
||||
perPage: 50,
|
||||
sort: 'TRENDING_DESC',
|
||||
status_in: '[CURRENT,PLANNING,COMPLETED,DROPPED,PAUSED,REPEATING]',
|
||||
...variables
|
||||
}
|
||||
|
|
@ -569,7 +570,6 @@ class AnilistClient {
|
|||
|
||||
async search (variables = {}) {
|
||||
debug(`Searching ${JSON.stringify(variables)}`)
|
||||
variables.sort ||= 'SEARCH_MATCH'
|
||||
const query = /* js */`
|
||||
query($page: Int, $perPage: Int, $sort: [MediaSort], $search: String, $onList: Boolean, $status: MediaStatus, $status_not: MediaStatus, $season: MediaSeason, $year: Int, $genre: [String], $tag: [String], $format: MediaFormat, $id_not: [Int], $idMal_not: [Int], $idMal: [Int]) {
|
||||
Page(page: $page, perPage: $perPage) {
|
||||
|
|
|
|||
|
|
@ -256,30 +256,36 @@ export default class Helper {
|
|||
}
|
||||
|
||||
Object.assign(variables, this.getFuzzyDate(media, status))
|
||||
if (media.mediaListEntry?.status !== variables.status || media.mediaListEntry?.progress !== variables.episode || media.mediaListEntry?.score !== variables.score || media.mediaListEntry?.repeat !== variables.repeat) {
|
||||
let res
|
||||
const description = `Title: ${anilistClient.title(media)}\nStatus: ${this.statusName[variables.status]}\nEpisode: ${videoEpisode} / ${media.episodes ? media.episodes : '?'}`
|
||||
if (this.isAniAuth()) {
|
||||
res = await anilistClient.alEntry(lists, variables)
|
||||
} else if (this.isMalAuth()) {
|
||||
res = await malClient.malEntry(media, variables)
|
||||
}
|
||||
this.listToast(res, description, false)
|
||||
|
||||
let res
|
||||
const description = `Title: ${anilistClient.title(media)}\nStatus: ${this.statusName[variables.status]}\nEpisode: ${videoEpisode} / ${media.episodes ? media.episodes : '?'}`
|
||||
if (this.isAniAuth()) {
|
||||
res = await anilistClient.alEntry(lists, variables)
|
||||
} else if (this.isMalAuth()) {
|
||||
res = await malClient.malEntry(media, variables)
|
||||
}
|
||||
this.listToast(res, description, false)
|
||||
|
||||
if (this.getUser().sync) { // handle profile entry syncing
|
||||
const mediaId = media.id
|
||||
for (const profile of get(profiles)) {
|
||||
if (profile.viewer?.data?.Viewer.sync) {
|
||||
let res
|
||||
if (profile.viewer?.data?.Viewer?.avatar) {
|
||||
const currentLists = (await anilistClient.getUserLists({userID: profile.viewer.data.Viewer.id, token: profile.token}))?.data?.MediaListCollection?.lists?.flatMap(list => list.entries).find(({ media }) => media.id === mediaId)?.media?.mediaListEntry?.customLists?.filter(list => list.enabled).map(list => list.name) || []
|
||||
res = await anilistClient.alEntry(currentLists, {...variables, token: profile.token})
|
||||
} else {
|
||||
res = await malClient.malEntry(media, {...variables, token: profile.token})
|
||||
if (this.getUser().sync) { // handle profile entry syncing
|
||||
const mediaId = media.id
|
||||
for (const profile of get(profiles)) {
|
||||
if (profile.viewer?.data?.Viewer.sync) {
|
||||
let res
|
||||
if (profile.viewer?.data?.Viewer?.avatar) {
|
||||
const currentLists = (await anilistClient.getUserLists({
|
||||
userID: profile.viewer.data.Viewer.id,
|
||||
token: profile.token
|
||||
}))?.data?.MediaListCollection?.lists?.flatMap(list => list.entries).find(({media}) => media.id === mediaId)?.media?.mediaListEntry?.customLists?.filter(list => list.enabled).map(list => list.name) || []
|
||||
res = await anilistClient.alEntry(currentLists, {...variables, token: profile.token})
|
||||
} else {
|
||||
res = await malClient.malEntry(media, {...variables, token: profile.token})
|
||||
}
|
||||
this.listToast(res, description, profile)
|
||||
}
|
||||
this.listToast(res, description, profile)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debug(`No entry changes detected for ${media.title.userPreferred}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1
common/modules/mal.d.ts
vendored
1
common/modules/mal.d.ts
vendored
|
|
@ -40,6 +40,7 @@ export type Viewer = {
|
|||
id: number
|
||||
name: string
|
||||
picture: string
|
||||
sync: boolean
|
||||
}
|
||||
|
||||
export type MediaList = {
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ function createSections () {
|
|||
}),
|
||||
// user specific sections
|
||||
{
|
||||
title: 'Sequels You Missed', variables : { sort: 'POPULARITY_DESC', userList: true, disableHide: true },
|
||||
title: 'Sequels You Missed', variables : { sort: 'POPULARITY_DESC', userList: true, missedList: true, disableHide: true },
|
||||
load: (page = 1, perPage = 50, variables = {}) => {
|
||||
if (Helper.isMalAuth()) return {} // not going to bother handling this, see below.
|
||||
const res = Helper.userLists(variables).then(res => {
|
||||
|
|
@ -100,6 +100,24 @@ function createSections () {
|
|||
},
|
||||
hide: !Helper.isAuthorized() || Helper.isMalAuth() // disable this section when authenticated with MyAnimeList. API for userLists fail to return relations and likely will never be fixed on their end.
|
||||
},
|
||||
{
|
||||
title: 'Stories You Missed', variables : { sort: 'POPULARITY_DESC', userList: true, missedList: true, disableHide: true },
|
||||
load: (page = 1, perPage = 50, variables = {}) => {
|
||||
if (Helper.isMalAuth()) return {} // same as Sequels You Missed
|
||||
const res = Helper.userLists(variables).then(res => {
|
||||
const mediaList = res.data.MediaListCollection.lists.find(({ status }) => status === 'COMPLETED')?.entries
|
||||
const excludeIds = res.data.MediaListCollection.lists.reduce((filtered, { status, entries }) => { return (['CURRENT', 'REPEATING', 'COMPLETED', 'DROPPED', 'PAUSED'].includes(status)) ? filtered.concat(entries) : filtered}, []).map(({ media }) => media.id) || []
|
||||
if (!mediaList) return {}
|
||||
const ids = mediaList.flatMap(({ media }) => {
|
||||
return media.relations.edges.filter(edge => !['SEQUEL', 'CHARACTER', 'OTHER'].includes(edge.relationType))
|
||||
}).map(({ node }) => node.id)
|
||||
if (!ids.length) return {}
|
||||
return anilistClient.searchIDS({ page, perPage, id: ids, id_not: excludeIds, ...SectionsManager.sanitiseObject(variables), status: ['FINISHED', 'RELEASING'] })
|
||||
})
|
||||
return SectionsManager.wrapResponse(res, perPage)
|
||||
},
|
||||
hide: !Helper.isAuthorized() || Helper.isMalAuth()
|
||||
},
|
||||
{
|
||||
title: 'Continue Watching', variables: { sort: 'UPDATED_TIME_DESC', userList: true, continueWatching: true, disableHide: true },
|
||||
load: (page = 1, perPage = 50, variables = {}) => {
|
||||
|
|
@ -143,7 +161,7 @@ function createSections () {
|
|||
hide: !Helper.isAuthorized()
|
||||
},
|
||||
{
|
||||
title: 'Planning List', variables : { test: 'Planning', sort: 'POPULARITY_DESC', userList: true, disableHide: true },
|
||||
title: 'Planning List', variables : { test: 'Planning', sort: 'POPULARITY_DESC', planningList: true, userList: true, disableHide: true },
|
||||
load: (page = 1, perPage = 50, variables = {}) => {
|
||||
const res = Helper.userLists(variables).then(res => {
|
||||
const mediaList = Helper.isAniAuth()
|
||||
|
|
@ -171,7 +189,7 @@ function createSections () {
|
|||
hide: !Helper.isAuthorized()
|
||||
},
|
||||
{
|
||||
title: 'Dropped List', variables : { sort: 'UPDATED_TIME_DESC', userList: true, disableHide: true },
|
||||
title: 'Dropped List', variables : { sort: 'UPDATED_TIME_DESC', droppedList: true, userList: true, disableHide: true },
|
||||
load: (page = 1, perPage = 50, variables = {}) => {
|
||||
const res = Helper.userLists(variables).then(res => {
|
||||
const mediaList = Helper.isAniAuth()
|
||||
|
|
@ -185,12 +203,13 @@ function createSections () {
|
|||
hide: !Helper.isAuthorized()
|
||||
},
|
||||
// common, non-user specific sections
|
||||
{ title: 'Popular This Season', variables: { sort: 'POPULARITY_DESC', season: currentSeason, year: currentYear, hideMyAnime: settings.value.hideMyAnime, hideStatus: ['COMPLETED', 'DROPPED'] } },
|
||||
{ title: 'Trending Now', variables: { sort: 'TRENDING_DESC', hideMyAnime: settings.value.hideMyAnime, hideStatus: ['COMPLETED', 'DROPPED'] } },
|
||||
{ title: 'All Time Popular', variables: { sort: 'POPULARITY_DESC', hideMyAnime: settings.value.hideMyAnime, hideStatus: ['COMPLETED', 'DROPPED'] } },
|
||||
{ title: 'Romance', variables: { sort: 'TRENDING_DESC', genre: ['Romance'], hideMyAnime: settings.value.hideMyAnime, hideStatus: ['COMPLETED', 'DROPPED'] } },
|
||||
{ title: 'Action', variables: { sort: 'TRENDING_DESC', genre: ['Action'], hideMyAnime: settings.value.hideMyAnime, hideStatus: ['COMPLETED', 'DROPPED'] } },
|
||||
{ title: 'Adventure', variables: { sort: 'TRENDING_DESC', genre: ['Adventure'], hideMyAnime: settings.value.hideMyAnime, hideStatus: ['COMPLETED', 'DROPPED'] } },
|
||||
{ title: 'Fantasy', variables: { sort: 'TRENDING_DESC', genre: ['Fantasy'], hideMyAnime: settings.value.hideMyAnime, hideStatus: ['COMPLETED', 'DROPPED'] } }
|
||||
{ title: 'Popular This Season', variables: { sort: 'POPULARITY_DESC', season: currentSeason, year: currentYear, hideMyAnime: settings.value.hideMyAnime, hideStatus: ['CURRENT', 'REPEATING', 'COMPLETED', 'DROPPED'] } },
|
||||
{ title: 'Trending Now', variables: { sort: 'TRENDING_DESC', hideMyAnime: settings.value.hideMyAnime, hideStatus: ['CURRENT', 'REPEATING', 'COMPLETED', 'DROPPED'] } },
|
||||
{ title: 'All Time Popular', variables: { sort: 'POPULARITY_DESC', hideMyAnime: settings.value.hideMyAnime, hideStatus: ['CURRENT', 'REPEATING', 'COMPLETED', 'DROPPED'] } },
|
||||
{ title: 'Romance', variables: { sort: 'TRENDING_DESC', genre: ['Romance'], hideMyAnime: settings.value.hideMyAnime, hideStatus: ['CURRENT', 'REPEATING', 'COMPLETED', 'DROPPED'] } },
|
||||
{ title: 'Action', variables: { sort: 'TRENDING_DESC', genre: ['Action'], hideMyAnime: settings.value.hideMyAnime, hideStatus: ['CURRENT', 'REPEATING', 'COMPLETED', 'DROPPED'] } },
|
||||
{ title: 'Adventure', variables: { sort: 'TRENDING_DESC', genre: ['Adventure'], hideMyAnime: settings.value.hideMyAnime, hideStatus: ['CURRENT', 'REPEATING', 'COMPLETED', 'DROPPED'] } },
|
||||
{ title: 'Fantasy', variables: { sort: 'TRENDING_DESC', genre: ['Fantasy'], hideMyAnime: settings.value.hideMyAnime, hideStatus: ['CURRENT', 'REPEATING', 'COMPLETED', 'DROPPED'] } },
|
||||
{ title: 'Comedy', variables: { sort: 'TRENDING_DESC', genre: ['Comedy'], hideMyAnime: settings.value.hideMyAnime, hideStatus: ['CURRENT', 'REPEATING', 'COMPLETED', 'DROPPED'] } }
|
||||
]
|
||||
}
|
||||
|
|
@ -134,8 +134,9 @@ async function handleMalToken (code, state) {
|
|||
export async function refreshMalToken (token) {
|
||||
const { clientID } = await import('./myanimelist.js')
|
||||
const refresh = malToken?.token === token ? malToken.refresh : get(profiles).find(profile => profile.token === token)?.refresh
|
||||
debug(`Attempting to refresh authorization token ${token} with the refresh token ${refresh}`)
|
||||
let response
|
||||
if (!refresh || !(refresh.length > 0)) {
|
||||
if (refresh && refresh.length > 0) {
|
||||
response = await fetch('https://myanimelist.net/v1/oauth2/token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
|
|
@ -148,9 +149,9 @@ export async function refreshMalToken (token) {
|
|||
})
|
||||
})
|
||||
}
|
||||
if (!refresh || !(refresh.length > 0) || !response?.ok) {
|
||||
if (!refresh || refresh.length <= 0 || !response?.ok) {
|
||||
toast.error('Failed to re-authenticate with MyAnimeList. You will need to log in again.', { description: JSON.stringify(response?.status || response) })
|
||||
debug(`Failed to refresh MyAnimeList User Token ${ !refresh || !(refresh.length > 0) ? 'as the refresh token could not be fetched!' : ': ' + JSON.stringify(response)}`)
|
||||
debug(`Failed to refresh MyAnimeList User Token ${ !refresh || refresh.length <= 0 ? 'as the refresh token could not be fetched!' : ': ' + JSON.stringify(response)}`)
|
||||
if (malToken?.token === token) {
|
||||
swapProfiles(null)
|
||||
location.reload()
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ class TorrentWorker extends EventTarget {
|
|||
if (torrentRx.exec(text)) {
|
||||
media.set(null)
|
||||
add(text)
|
||||
this.dispatch('info', 'A Magnet Link has been detected and is being processed. Files will be loaded shortly...')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
for (const sectionTitle of settings.value.homeSections) manager.add(mappedSections[sectionTitle])
|
||||
|
||||
if (Helper.getUser()) {
|
||||
const userSections = ['Continue Watching', 'Sequels You Missed', 'Planning List', 'Completed List', 'Paused List', 'Dropped List', 'Watching List']
|
||||
const userSections = ['Continue Watching', 'Sequels You Missed', 'Stories You Missed', 'Planning List', 'Completed List', 'Paused List', 'Dropped List', 'Watching List']
|
||||
Helper.getClient().userLists.subscribe(value => {
|
||||
if (!value) return
|
||||
for (const section of manager.sections) {
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@
|
|||
import AnimeResolver from '@/modules/animeresolver.js'
|
||||
import { videoRx } from '@/modules/util.js'
|
||||
import { tick } from 'svelte'
|
||||
import { state } from '../WatchTogether/WatchTogether.svelte'
|
||||
import IPC from '@/modules/ipc.js'
|
||||
import { anilistClient } from "@/modules/anilist.js"
|
||||
import Debug from 'debug'
|
||||
|
||||
|
|
@ -69,7 +67,6 @@
|
|||
episodeTitle: streamingEpisode && episodeRx.exec(streamingEpisode.title)[2],
|
||||
thumbnail: streamingEpisode?.thumbnail || media?.coverImage.extraLarge
|
||||
}
|
||||
setDiscordRPC(np)
|
||||
setMediaSession(np)
|
||||
nowPlaying.set(np)
|
||||
}
|
||||
|
|
@ -210,53 +207,6 @@
|
|||
: new MediaMetadata({ title: name })
|
||||
navigator.mediaSession.metadata = metadata
|
||||
}
|
||||
|
||||
function setDiscordRPC (np = nowPlaying.value) {
|
||||
const w2g = state.value?.code
|
||||
const details = [np.title, np.episodeTitle].filter(i => i).join(' - ') || undefined
|
||||
const activity = {
|
||||
details,
|
||||
state: details && 'Watching Episode ' + ((!np.media?.episodes && np.episode) || ''),
|
||||
timestamps: {
|
||||
start: Date.now()
|
||||
},
|
||||
party: {
|
||||
size: (np.episode && np.media?.episodes && [np.episode, np.media.episodes]) || undefined
|
||||
},
|
||||
assets: {
|
||||
large_text: np.title,
|
||||
large_image: np.thumbnail,
|
||||
small_image: 'https://raw.githubusercontent.com/NoCrypt/migu/main/common/public/logo_filled.png',
|
||||
small_text: 'https://github.com/NoCrypt/migu'
|
||||
},
|
||||
instance: true,
|
||||
type: 3
|
||||
}
|
||||
// cannot have buttons and secrets at once
|
||||
if (w2g) {
|
||||
activity.secrets = {
|
||||
join: w2g,
|
||||
match: w2g + 'm'
|
||||
}
|
||||
activity.party.id = w2g + 'p'
|
||||
} else {
|
||||
activity.buttons = [
|
||||
{
|
||||
label: 'Download app',
|
||||
url: 'https://github.com/NoCrypt/migu/releases/latest'
|
||||
},
|
||||
{
|
||||
label: 'Watch on Migu',
|
||||
url: `https://miguapp.pages.dev/anime/${np.media?.id}`
|
||||
}
|
||||
]
|
||||
}
|
||||
IPC.emit('discord', { activity })
|
||||
}
|
||||
state.subscribe(() => {
|
||||
setDiscordRPC()
|
||||
return noop
|
||||
})
|
||||
</script>
|
||||
|
||||
<script>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
import { swipeControls } from '@/modules/swipecontrol.js';
|
||||
import { volumeScroll } from '@/modules/volumescroll.js';
|
||||
import GestureLock from '@/components/GestureLock.svelte';
|
||||
import { ArrowDown, ArrowUp, Captions, Cast, CircleHelp, Contrast, FastForward, RefreshCw, Keyboard, List, ListMusic, ListVideo, Maximize, Minimize, Pause, PictureInPicture, PictureInPicture2, Play, Proportions, RefreshCcw, Rewind, RotateCcw, RotateCw, ScreenShare, SkipBack, SkipForward, Users, Volume1, Volume2, VolumeX, Lock, CircleGauge, Minus, Plus } from 'lucide-svelte'
|
||||
import { ArrowDown, ArrowUp, Captions, Cast, CircleHelp, Contrast, FastForward, Keyboard, List, ListMusic, ListVideo, Maximize, Minimize, Pause, PictureInPicture, PictureInPicture2, Play, Proportions, RefreshCcw, Rewind, RotateCcw, RotateCw, ScreenShare, SkipBack, SkipForward, Users, Volume1, Volume2, VolumeX, RefreshCw, CircleGauge, Minus, Plus, Lock} from 'lucide-svelte'
|
||||
|
||||
const emit = createEventDispatcher()
|
||||
|
||||
|
|
@ -77,6 +77,13 @@
|
|||
let playbackRate = 1
|
||||
$: localStorage.setItem('volume', (volume || 0).toString())
|
||||
$: safeduration = (isFinite(duration) ? duration : currentTime) || 0
|
||||
$: {
|
||||
if (hidden) {
|
||||
setDiscordRPC(media, video?.currentTime)
|
||||
} else {
|
||||
setDiscordRPC(media, (paused && miniplayer))
|
||||
}
|
||||
}
|
||||
|
||||
function checkAudio () {
|
||||
if ('audioTracks' in HTMLVideoElement.prototype) {
|
||||
|
|
@ -291,7 +298,8 @@
|
|||
}
|
||||
const handleVisibility = visibility => {
|
||||
if (!video?.ended && $settings.playerPause && !pip) {
|
||||
if (visibility === 'hidden') {
|
||||
hidden = (visibility === 'hidden')
|
||||
if (hidden) {
|
||||
visibilityPaused = paused
|
||||
paused = true
|
||||
} else {
|
||||
|
|
@ -299,6 +307,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
let hidden = false
|
||||
let visibilityPaused = true
|
||||
document.addEventListener('visibilitychange', () => handleVisibility(document.visibilityState))
|
||||
IPC.on('visibilitychange', handleVisibility)
|
||||
|
|
@ -376,7 +385,7 @@
|
|||
for (const track of video.audioTracks) {
|
||||
track.enabled = track.id === id
|
||||
}
|
||||
seek(-0.2) // stupid fix because video freezes up when chaging tracks
|
||||
seek(-0.2) // stupid fix because video freezes up when changing tracks
|
||||
}
|
||||
}
|
||||
function selectVideo (id) {
|
||||
|
|
@ -1007,7 +1016,7 @@
|
|||
console.warn('A network error caused the video download to fail part-way.', target.error)
|
||||
toast.error('Video Network Error', {
|
||||
description: 'A network error caused the video download to fail part-way. Dismiss this toast to reload the video.',
|
||||
duration: 10000,
|
||||
duration: Infinity,
|
||||
onDismiss: () => target.load()
|
||||
})
|
||||
break
|
||||
|
|
@ -1015,7 +1024,7 @@
|
|||
console.warn('The video playback was aborted due to a corruption problem or because the video used features your browser did not support.', target.error)
|
||||
toast.error('Video Decode Error', {
|
||||
description: 'The video playback was aborted due to a corruption problem. Dismiss this toast to reload the video.',
|
||||
duration: 10000,
|
||||
duration: Infinity,
|
||||
onDismiss: () => target.load()
|
||||
})
|
||||
break
|
||||
|
|
@ -1054,6 +1063,76 @@
|
|||
}
|
||||
|
||||
let isLocked = false;
|
||||
function setDiscordRPC (np = media, browsing) {
|
||||
if ((!np || Object.keys(np).length === 0) && !browsing) return
|
||||
if (hidden) {
|
||||
IPC.emit('discord-hidden')
|
||||
return
|
||||
}
|
||||
let activity
|
||||
if (!browsing) {
|
||||
const w2g = state.value?.code
|
||||
const details = np.title || undefined
|
||||
const timeLeft = safeduration - targetTime;
|
||||
const timestamps = !paused ? {
|
||||
start: Date.now() - (targetTime > 0 ? targetTime * 1000 : 0),
|
||||
end: Date.now() + timeLeft * 1000
|
||||
} : undefined
|
||||
activity = {
|
||||
details,
|
||||
state: (details && (np.media?.format === 'MOVIE' ? 'The Movie' : (np.episode ? 'Episode: ' + np.episode + (np.media?.episodes ? ' of ' + np.media.episodes : '') : 'Streaming the Universe'))),
|
||||
timestamps,
|
||||
assets: {
|
||||
large_text: np.title,
|
||||
large_image: np.thumbnail,
|
||||
small_image: !paused ? 'logo' : 'paused',
|
||||
small_text: (!paused ? '(Playing)' : '(Paused)') + ' https://github.com/NoCrypt/migu'
|
||||
},
|
||||
instance: true,
|
||||
type: 3
|
||||
}
|
||||
// cannot have buttons and secrets at once
|
||||
if (w2g) {
|
||||
activity.secrets = {
|
||||
join: w2g,
|
||||
match: w2g + 'm'
|
||||
}
|
||||
activity.party.id = w2g + 'p'
|
||||
} else {
|
||||
activity.buttons = [
|
||||
{
|
||||
label: 'Download app',
|
||||
url: 'https://github.com/NoCrypt/migu/releases/latest'
|
||||
},
|
||||
{
|
||||
label: 'Watch on Migu',
|
||||
url: `migu://anime/${np.media?.id}`
|
||||
}
|
||||
]
|
||||
}
|
||||
} else {
|
||||
activity = {
|
||||
timestamps: { start: Date.now() },
|
||||
details: 'Stream anime torrents',
|
||||
state: 'Browsing for anime',
|
||||
assets: {
|
||||
large_image: 'logo',
|
||||
large_text: 'https://github.com/NoCrypt/migu',
|
||||
small_image: 'search',
|
||||
small_text: 'Browsing for anime',
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
label: 'Download app',
|
||||
url: 'https://github.com/NoCrypt/migu/releases/latest'
|
||||
}
|
||||
],
|
||||
instance: true,
|
||||
type: 3
|
||||
}
|
||||
}
|
||||
IPC.emit('discord', { activity })
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if SUPPORTS.isAndroid}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@
|
|||
|
||||
<h4 class='mb-10 font-weight-bold'>Home Screen Settings</h4>
|
||||
{#if Helper.isAuthorized()}
|
||||
<SettingCard title='Hide My Anime' description={'The anime on your Completed or Dropped list will automatically be hidden from the default sections, this excludes manually added RSS feeds and user specific feeds.'}>
|
||||
<SettingCard title='Hide My Anime' description={'The anime on your Watching, Rewatching, Completed, and Dropped list will automatically be hidden from the default sections, this excludes manually added RSS feeds and user specific feeds.'}>
|
||||
<div class='custom-switch'>
|
||||
<input type='checkbox' id='hide-my-anime' bind:checked={settings.hideMyAnime} />
|
||||
<label for='hide-my-anime'>{settings.hideMyAnime ? 'On' : 'Off'}</label>
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@
|
|||
status = 'NOT IN LIST'
|
||||
if (media.mediaListEntry) {
|
||||
const res = await Helper.delete(Helper.isAniAuth() ? {id: media.mediaListEntry.id} : {idMal: media.idMal})
|
||||
const description = `${anilistClient.title(media)} has been deleted from your list`
|
||||
const description = `${anilistClient.title(media)} has been deleted from your list.`
|
||||
printToast(res, description, false, false)
|
||||
|
||||
if (res) media.mediaListEntry = undefined
|
||||
|
|
@ -99,25 +99,43 @@
|
|||
lists,
|
||||
...fuzzyDate
|
||||
}
|
||||
const res = await Helper.entry(media, variables)
|
||||
if (media?.mediaListEntry?.status !== variables.status || media?.mediaListEntry?.progress !== variables.episode || media?.mediaListEntry?.score !== variables.score || media?.mediaListEntry?.repeat !== variables.repeat) {
|
||||
const res = await Helper.entry(media, variables)
|
||||
|
||||
if (res?.data?.SaveMediaListEntry) { media.mediaListEntry = res?.data?.SaveMediaListEntry }
|
||||
if (res?.data?.SaveMediaListEntry) {
|
||||
media.mediaListEntry = res?.data?.SaveMediaListEntry
|
||||
}
|
||||
|
||||
const description = `Title: ${anilistClient.title(media)}\nStatus: ${Helper.statusName[status]}\nEpisode: ${episode} / ${totalEpisodes}${score !== 0 ? `\nYour Score: ${score}` : ''}`
|
||||
printToast(res, description, true, false)
|
||||
if (Helper.getUser().sync) { // handle profile syncing
|
||||
const mediaId = media.id
|
||||
for (const profile of get(profiles)) {
|
||||
if (profile.viewer?.data?.Viewer.sync) {
|
||||
const anilist = profile.viewer?.data?.Viewer?.avatar
|
||||
const currentLists = (anilist ? (await anilistClient.getUserLists({userID: profile.viewer.data.Viewer.id, token: profile.token}))?.data?.MediaListCollection?.lists?.flatMap(list => list.entries).find(({ media }) => media.id === mediaId)?.media?.mediaListEntry?.customLists?.filter(list => list.enabled).map(list => list.name) || [] : lists)
|
||||
if (!currentLists.includes('Watched using Migu')) {
|
||||
currentLists.push('Watched using Migu')
|
||||
const description = `Title: ${anilistClient.title(media)}\nStatus: ${Helper.statusName[status]}\nEpisode: ${episode} / ${totalEpisodes}${score !== 0 ? `\nYour Score: ${score}` : ''}`
|
||||
printToast(res, description, true, false)
|
||||
if (Helper.getUser().sync) { // handle profile syncing
|
||||
const mediaId = media.id
|
||||
for (const profile of get(profiles)) {
|
||||
if (profile.viewer?.data?.Viewer.sync) {
|
||||
const anilist = profile.viewer?.data?.Viewer?.avatar
|
||||
const currentLists = (anilist ? (await anilistClient.getUserLists({
|
||||
userID: profile.viewer.data.Viewer.id,
|
||||
token: profile.token
|
||||
}))?.data?.MediaListCollection?.lists?.flatMap(list => list.entries).find(({media}) => media.id === mediaId)?.media?.mediaListEntry?.customLists?.filter(list => list.enabled).map(list => list.name) || [] : lists)
|
||||
if (!currentLists.includes('Watched using Miru')) {
|
||||
currentLists.push('Watched using Miru')
|
||||
}
|
||||
const res = await Helper.entry(media, {
|
||||
...variables,
|
||||
lists: currentLists,
|
||||
score: (anilist ? (score * 10) : score),
|
||||
token: profile.token,
|
||||
anilist
|
||||
})
|
||||
printToast(res, description, true, profile)
|
||||
}
|
||||
const res = await Helper.entry(media, { ...variables, lists: currentLists, score: (anilist ? (score * 10) : score), token: profile.token, anilist })
|
||||
printToast(res, description, true, profile)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
toast.warning('No Changes to List', {
|
||||
description: `Title: ${anilistClient.title(media)}\nStatus: ${Helper.statusName[status]}\nEpisode: ${episode} / ${totalEpisodes}${score !== 0 ? `\nYour Score: ${score}` : ''}`,
|
||||
duration: 6000
|
||||
})
|
||||
}
|
||||
} else {
|
||||
await deleteEntry()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export default class Discord {
|
|||
defaultStatus = {
|
||||
activity: {
|
||||
timestamps: { start: Date.now() },
|
||||
details: 'Stream anime torrents, real-time.',
|
||||
details: 'Stream anime torrents',
|
||||
state: 'Watching anime',
|
||||
assets: {
|
||||
small_image: 'logo',
|
||||
|
|
@ -48,6 +48,10 @@ export default class Discord {
|
|||
this.toggleRPC(data)
|
||||
})
|
||||
|
||||
ipcMain.on('discord-hidden', () => {
|
||||
this.debouncedDiscordRPC(undefined, true)
|
||||
})
|
||||
|
||||
this.discord.on('ready', async () => {
|
||||
if (this.rpcEnabled) {
|
||||
this.setDiscordRPC(this.cachedPresence || this.defaultStatus)
|
||||
|
|
|
|||
Loading…
Reference in a new issue