mirror of
https://github.com/NoCrypt/migu.git
synced 2026-03-15 23:45:54 +00:00
225 lines
No EOL
10 KiB
JavaScript
225 lines
No EOL
10 KiB
JavaScript
import { anilistClient, currentSeason, currentYear } from '@/modules/anilist.js'
|
|
import { writable } from 'simple-store-svelte'
|
|
import { settings, alToken } from '@/modules/settings.js'
|
|
import { RSSManager } from '@/modules/rss.js'
|
|
|
|
export const hasNextPage = writable(true)
|
|
|
|
export default class SectionsManager {
|
|
constructor (data = []) {
|
|
this.sections = []
|
|
for (const section of data) this.add(section)
|
|
}
|
|
|
|
/**
|
|
* @param {object} data
|
|
*/
|
|
add (data) {
|
|
if (!data) return
|
|
const { title, variables = {}, type, load = SectionsManager.createFallbackLoad(variables, type), preview = writable() } = data
|
|
const section = { ...data, load, title, preview, variables }
|
|
this.sections.push(section)
|
|
return section
|
|
}
|
|
|
|
static createFallbackLoad (variables, type) {
|
|
return (page = 1, perPage = 50, search = variables) => {
|
|
const res = anilistClient.search({ page, perPage, ...SectionsManager.sanitiseObject(search) })
|
|
return SectionsManager.wrapResponse(res, perPage, type)
|
|
}
|
|
}
|
|
|
|
static wrapResponse (res, length, type) {
|
|
res.then(res => {
|
|
hasNextPage.value = res?.data?.Page.pageInfo.hasNextPage
|
|
})
|
|
return Array.from({ length }, (_, i) => ({ type, data: SectionsManager.fromPending(res, i) }))
|
|
}
|
|
|
|
static sanitiseObject (object = {}) {
|
|
const safe = {}
|
|
for (const [key, value] of Object.entries(object)) {
|
|
if (value) safe[key] = value
|
|
}
|
|
return safe
|
|
}
|
|
|
|
static async fromPending (arr, i) {
|
|
const { data } = await arr
|
|
return data?.Page.media[i]
|
|
}
|
|
|
|
static isUserSort(variables) {
|
|
return ['UPDATED_TIME_DESC', 'STARTED_ON_DESC', 'FINISHED_ON_DESC', 'PROGRESS_DESC', 'USER_SCORE_DESC'].includes(variables.sort);
|
|
}
|
|
|
|
static async getPaginatedMediaList(_page, perPage, variables, mediaList) {
|
|
const ids = mediaList.filter(({ media }) => {
|
|
if ((!variables.search || (media.title.userPreferred && media.title.userPreferred.toLowerCase().includes(variables.search.toLowerCase())) || (media.title.english && media.title.english.toLowerCase().includes(variables.search.toLowerCase())) || (media.title.romaji && media.title.romaji.toLowerCase().includes(variables.search.toLowerCase())) || (media.title.native && media.title.native.toLowerCase().includes(variables.search.toLowerCase()))) &&
|
|
(!variables.genre || variables.genre.map(genre => genre.trim().toLowerCase()).every(genre => media.genres.map(genre => genre.trim().toLowerCase()).includes(genre))) &&
|
|
(!variables.tag || variables.tag.map(tag => tag.trim().toLowerCase()).every(tag => media.tags.map(tag => tag.name.trim().toLowerCase()).includes(tag))) &&
|
|
(!variables.season || variables.season === media.season) &&
|
|
(!variables.year || variables.year === media.seasonYear) &&
|
|
(!variables.format || variables.format === media.format) &&
|
|
(!variables.status || variables.status === media.status) &&
|
|
(!variables.continueWatching || (media.status === 'FINISHED' || media.mediaListEntry?.progress < media.nextAiringEpisode?.episode - 1))) {
|
|
return true;
|
|
}
|
|
}).map(({ media }) => (this.isUserSort(variables) ? media : media.id));
|
|
if (!ids.length) return {}
|
|
if (this.isUserSort(variables)) {
|
|
const startIndex = (perPage * (_page - 1));
|
|
const endIndex = startIndex + perPage;
|
|
const paginatedIds = ids.slice(startIndex, endIndex);
|
|
const hasNextPage = ids.length > endIndex;
|
|
return {
|
|
data: {
|
|
Page: {
|
|
pageInfo: {
|
|
hasNextPage: hasNextPage
|
|
},
|
|
media: paginatedIds
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
return anilistClient.searchIDS({ _page, perPage, id: ids, ...SectionsManager.sanitiseObject(variables) })
|
|
}
|
|
}
|
|
}
|
|
|
|
// list of all possible home screen sections
|
|
export let sections = []
|
|
|
|
settings.subscribe(() => {
|
|
for (const section of sections) clearInterval(section.interval)
|
|
sections = createSections()
|
|
})
|
|
|
|
function createSections () {
|
|
return [
|
|
// RSS feeds
|
|
...settings.value.rssFeedsNew.map(([title, url]) => {
|
|
const section = {
|
|
title,
|
|
load: (page = 1, perPage = 12) => RSSManager.getMediaForRSS(page, perPage, url),
|
|
preview: writable(RSSManager.getMediaForRSS(1, 12, url)),
|
|
variables: { disableSearch: true }
|
|
}
|
|
|
|
// update every 30 seconds
|
|
section.interval = setInterval(async () => {
|
|
if (await RSSManager.getContentChanged(1, 12, url)) {
|
|
section.preview.value = RSSManager.getMediaForRSS(1, 12, url, true)
|
|
}
|
|
}, 30000)
|
|
|
|
return section
|
|
}),
|
|
// user specific sections
|
|
{
|
|
title: 'Sequels You Missed',
|
|
load: (page = 1, perPage = 50, variables = {}) => {
|
|
const res = anilistClient.userLists.value.then(res => {
|
|
const mediaList = res.data.MediaListCollection.lists.find(({ status }) => status === 'COMPLETED')?.entries
|
|
if (!mediaList) return {}
|
|
const ids = mediaList.flatMap(({ media }) => {
|
|
return media.relations.edges.filter(edge => edge.relationType === 'SEQUEL')
|
|
}).map(({ node }) => node.id)
|
|
if (!ids.length) return {}
|
|
return anilistClient.searchIDS({ page, perPage, id: ids, ...SectionsManager.sanitiseObject(variables), status: ['FINISHED', 'RELEASING'], onList: false })
|
|
})
|
|
return SectionsManager.wrapResponse(res, perPage)
|
|
},
|
|
hide: !alToken
|
|
},
|
|
{
|
|
title: 'Continue Watching', variables: { sort: 'UPDATED_TIME_DESC', userList: true, continueWatching: true },
|
|
load: (_page = 1, perPage = 50, variables = {}) => {
|
|
const userLists = (!SectionsManager.isUserSort(variables) || variables.sort === 'UPDATED_TIME_DESC') ? anilistClient.userLists.value : anilistClient.getUserLists({ sort: variables.sort })
|
|
const res = userLists.then(res => {
|
|
const mediaList = res.data.MediaListCollection.lists.reduce((filtered, { status, entries }) => {
|
|
return (status === 'CURRENT' || status === 'REPEATING') ? filtered.concat(entries) : filtered
|
|
}, []);
|
|
if (!mediaList) return {}
|
|
return SectionsManager.getPaginatedMediaList(_page, perPage, variables, mediaList)
|
|
});
|
|
return SectionsManager.wrapResponse(res, perPage);
|
|
},
|
|
hide: !alToken
|
|
},
|
|
{
|
|
title: 'Watching List', variables : { sort: 'UPDATED_TIME_DESC', userList: true },
|
|
load: (_page = 1, perPage = 50, variables = {}) => {
|
|
const userLists = (!SectionsManager.isUserSort(variables) || variables.sort === 'UPDATED_TIME_DESC') ? anilistClient.userLists.value : anilistClient.getUserLists({ sort: variables.sort })
|
|
const res = userLists.then(res => {
|
|
const mediaList = res.data.MediaListCollection.lists.find(({ status }) => status === 'CURRENT')?.entries
|
|
if (!mediaList) return {}
|
|
return SectionsManager.getPaginatedMediaList(_page, perPage, variables, mediaList)
|
|
})
|
|
return SectionsManager.wrapResponse(res, perPage)
|
|
},
|
|
hide: !alToken
|
|
},
|
|
{
|
|
title: 'Completed List', variables : { sort: 'UPDATED_TIME_DESC', userList: true, completedList: true },
|
|
load: (_page = 1, perPage = 50, variables = {}) => {
|
|
const userLists = (!SectionsManager.isUserSort(variables) || variables.sort === 'UPDATED_TIME_DESC') ? anilistClient.userLists.value : anilistClient.getUserLists({ sort: variables.sort })
|
|
const res = userLists.then(res => {
|
|
const mediaList = res.data.MediaListCollection.lists.find(({ status }) => status === 'COMPLETED')?.entries
|
|
if (!mediaList) return {}
|
|
return SectionsManager.getPaginatedMediaList(_page, perPage, variables, mediaList)
|
|
})
|
|
return SectionsManager.wrapResponse(res, perPage)
|
|
},
|
|
hide: !alToken
|
|
},
|
|
{
|
|
title: 'Planning List', variables : { sort: 'POPULARITY_DESC', userList: true },
|
|
load: (_page = 1, perPage = 50, variables = {}) => {
|
|
const res = anilistClient.userLists.value.then(res => {
|
|
const mediaList = res.data.MediaListCollection.lists.find(({ status }) => status === 'PLANNING')?.entries
|
|
if (!mediaList) return {}
|
|
return SectionsManager.getPaginatedMediaList(_page, perPage, variables, mediaList)
|
|
})
|
|
return SectionsManager.wrapResponse(res, perPage)
|
|
},
|
|
hide: !alToken
|
|
},
|
|
{
|
|
title: 'Paused List', variables : { sort: 'UPDATED_TIME_DESC', userList: true },
|
|
load: (_page = 1, perPage = 50, variables = {}) => {
|
|
const userLists = (!SectionsManager.isUserSort(variables) || variables.sort === 'UPDATED_TIME_DESC') ? anilistClient.userLists.value : anilistClient.getUserLists({ sort: variables.sort })
|
|
const res = userLists.then(res => {
|
|
const mediaList = res.data.MediaListCollection.lists.find(({ status }) => status === 'PAUSED')?.entries
|
|
if (!mediaList) return {}
|
|
return SectionsManager.getPaginatedMediaList(_page, perPage, variables, mediaList)
|
|
})
|
|
return SectionsManager.wrapResponse(res, perPage)
|
|
},
|
|
hide: !alToken
|
|
},
|
|
{
|
|
title: 'Dropped List', variables : { sort: 'UPDATED_TIME_DESC', userList: true},
|
|
load: (_page = 1, perPage = 50, variables = {}) => {
|
|
const userLists = (!SectionsManager.isUserSort(variables) || variables.sort === 'UPDATED_TIME_DESC') ? anilistClient.userLists.value : anilistClient.getUserLists({ sort: variables.sort })
|
|
const res = userLists.then(res => {
|
|
const mediaList = res.data.MediaListCollection.lists.find(({ status }) => status === 'DROPPED')?.entries
|
|
if (!mediaList) return {}
|
|
return SectionsManager.getPaginatedMediaList(_page, perPage, variables, mediaList)
|
|
})
|
|
return SectionsManager.wrapResponse(res, perPage)
|
|
},
|
|
hide: !alToken
|
|
},
|
|
// common, non-user specific sections
|
|
{ title: 'Popular This Season', variables: { sort: 'POPULARITY_DESC'} },
|
|
{ title: 'Trending Now', variables: { sort: 'TRENDING_DESC'} },
|
|
{ title: 'All Time Popular', variables: { sort: 'POPULARITY_DESC'} },
|
|
{ title: 'Romance', variables: { sort: 'TRENDING_DESC', genre: ['Romance']} },
|
|
{ title: 'Action', variables: { sort: 'TRENDING_DESC', genre: ['Action']} },
|
|
{ title: 'Adventure', variables: { sort: 'TRENDING_DESC', genre: ['Adventure'] } },
|
|
{ title: 'Fantasy', variables: { sort: 'TRENDING_DESC', genre: ['Fantasy']} },
|
|
{ title: 'Comedy', variables: { sort: 'TRENDING_DESC', genre: ['Comedy'] } }
|
|
]
|
|
} |