optimise the fuck out of card creation!

This commit is contained in:
ThaUnknown 2021-04-19 14:20:17 +02:00
parent 56785317e2
commit 40db9bdd83
4 changed files with 115 additions and 94 deletions

View file

@ -776,6 +776,53 @@
</section>
</div>
</div>
<template id="skeletonCardTemplate">
<div class="card m-0 p-0">
<div class="row h-full">
<div class="col-4 skeloader"></div>
<div class="col-8 bg-very-dark px-15 py-10">
<p class="skeloader w-300 h-25 rounded bg-dark"></p>
<p class="skeloader w-150 h-10 rounded bg-dark"></p>
<p class="skeloader w-150 h-10 rounded bg-dark"></p>
</div>
</div>
</div>
</template>
<template id="bareCardTemplate">
<div class="card m-0 p-0">
<div class="row h-full">
<div class="col-4 skeloader"></div>
<div class="col-8 bg-very-dark px-15 py-10">
<h5 class="m-0 text-capitalize font-weight-bold pb-10"></h5>
<p class="skeloader w-150 h-10 rounded bg-dark"></p>
<p class="skeloader w-150 h-10 rounded bg-dark"></p>
</div>
</div>
</div>
</template>
<template id="fullCardTemplate">
<div class="card m-0 p-0">
<div class="row h-full">
<div class="col-4">
<img loading="lazy" src="" class="cover-img w-full h-full">
</div>
<div class="col-8 h-full card-grid">
<div class="px-15 py-10 bg-very-dark">
<h5 class="m-0 text-capitalize font-weight-bold"></h5>
<span class='text-muted font-weight-bold'></span>
<p class="text-muted m-0 text-capitalize details">
<span></span>
<span></span>
<span></span>
<span></span>
</p>
</div>
<div class="overflow-y-auto px-15 pb-5 bg-very-dark card-desc"></div>
<div class="px-15 pb-10 pt-5 genres"></div>
</div>
</div>
</div>
</template>
<script src="https://cdn.jsdelivr.net/gh/halfmoonui/halfmoon@1.1.1/js/halfmoon.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/matroska-subtitles@latest/dist/matroska-subtitles.min.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/indexeddb-chunk-store@latest/idbchunkstore.min.js"></script> -->

View file

@ -303,24 +303,20 @@ mutation ($id: Int, $status: MediaListStatus, $episode: Int, $repeat: Int) {
}
let alResponse
async function searchAnime (a) { // search bar functionality
const frag = document.createDocumentFragment()
const cards = []
const browse = document.querySelector('.browse')
browse.innerHTML = ''
browse.appendChild(skeletonCard)
// browse.appendChild(skeletonCard) // TODO: fix
a ? alResponse = await alRequest({ method: 'SearchName', name: a }) : alResponse = await alRequest({ method: 'Trending' })
try {
alResponse.data.Page.media.forEach(media => {
const template = cardCreator({ media: media })
template.onclick = () => {
viewAnime(media)
}
frag.appendChild(template)
cards.push(cardCreator({ media: media, onclick: () => viewAnime(media) }))
})
} catch (e) {
console.error(e)
}
browse.innerHTML = ''
browse.appendChild(frag)
browse.append(...cards)
}
// these really shouldnt be global
@ -482,53 +478,6 @@ function countdown (s) {
(d || h || m) && tmp.push(m + 'm')
return tmp.join(' ')
}
function cardCreator (opts) {
const template = document.createElement('div')
template.classList.add('card', 'm-0', 'p-0')
if (opts?.media) {
template.innerHTML = `
<div class="row h-full" style="--color:${opts.media.coverImage.color || '#1890ff'};">
<div class="col-4">
<img loading="lazy" src="${opts.media.coverImage.extraLarge || ''}"
class="cover-img w-full h-full">
</div>
<div class="col-8 h-full card-grid">
<div class="px-15 py-10 bg-very-dark">
<h5 class="m-0 text-capitalize font-weight-bold">${opts.media.title.userPreferred}${opts.episodeNumber ? ' - ' + opts.episodeNumber : ''}</h5>
${opts.schedule && opts.media.nextAiringEpisode ? "<span class='text-muted font-weight-bold'>EP " + opts.media.nextAiringEpisode.episode + ' in ' + countdown(opts.media.nextAiringEpisode.timeUntilAiring) + '</span>' : ''}
<p class="text-muted m-0 text-capitalize details">
${(opts.media.format ? (opts.media.format === 'TV' ? '<span>' + opts.media.format + ' Show' : '<span>' + opts.media.format.toLowerCase().replace(/_/g, ' ')) : '') + '</span>'}
${opts.media.episodes ? '<span>' + opts.media.episodes + ' Episodes</span>' : opts.media.duration ? '<span>' + opts.media.duration + ' Minutes</span>' : ''}
${opts.media.status ? '<span>' + opts.media.status.toLowerCase().replace(/_/g, ' ') + '</span>' : ''}
${opts.media.season || opts.media.seasonYear ? '<span>' + ((opts.media.season.toLowerCase() || '') + ' ') + (opts.media.seasonYear || '') + '</span>' : ''}
</p>
</div>
<div class="overflow-y-auto px-15 pb-5 bg-very-dark card-desc">
${opts.media.description}
</div>
<div class="px-15 pb-10 pt-5">
${opts.media.genres.map(key => (`<span class="badge badge-pill badge-color text-dark mt-5 font-weight-bold">${key}</span> `)).join('')}
</div>
</div>
</div>
`
} else {
template.innerHTML = `
<div class="row h-full">
<div class="col-4 skeloader">
</div>
<div class="col-8 bg-very-dark px-15 py-10">
${opts?.parseObject ? `<h5 class="m-0 text-capitalize font-weight-bold pb-10">${opts.parseObject.anime_title + ' - ' + opts.parseObject.episode_number}</h5>` : '<p class="skeloader w-300 h-25 rounded bg-dark">'}
<p class="skeloader w-150 h-10 rounded bg-dark"></p>
<p class="skeloader w-150 h-10 rounded bg-dark"></p>
</p>
</div>
</div>
`
}
return template
}
const skeletonCard = cardCreator({})
const DOMPARSER = new DOMParser().parseFromString.bind(new DOMParser())
@ -713,8 +662,9 @@ function getRSSurl () {
return settings.torrent4 + settings.torrent1 // add custom RSS
}
}
async function releasesCards (items, frag, limit) {
async function releasesCards (items, limit) {
const mediaItems = []
const cards = []
for (let l = 0; l < (limit || items.length); l++) {
const i = items[l].querySelector.bind(items[l])
mediaItems.push(resolveFileMedia({ fileName: i('title').innerHTML, method: 'SearchName', isRelease: true }))
@ -722,25 +672,25 @@ async function releasesCards (items, frag, limit) {
await Promise.all(mediaItems).then(results => {
results.forEach((mediaInformation, index) => {
const o = items[index].querySelector.bind(items[index])
template = cardCreator(mediaInformation)
template.onclick = () => client.addTorrent(o('link').innerHTML, { media: mediaInformation, episode: mediaInformation.episode })
frag.appendChild(template)
mediaInformation.onclick = () => client.addTorrent(o('link').innerHTML, { media: mediaInformation, episode: mediaInformation.episode })
cards.push(cardCreator(mediaInformation))
})
})
localStorage.setItem('store', JSON.stringify(store))
return cards
}
async function releasesRss (limit) {
const frag = document.createDocumentFragment()
let cards
await fetch(getRSSurl()).then(res => res.text().then(async xmlTxt => {
try {
const doc = DOMPARSER(xmlTxt, 'text/xml')
const items = doc.querySelectorAll('item')
await releasesCards(items, frag, limit)
cards = await releasesCards(items, limit)
} catch (e) {
console.error(e)
}
}))
return frag
return cards
}
let alID // login icon
async function loadAnime () {

View file

@ -10,10 +10,10 @@ async function loadHomePage () {
},
releases: async function () {
gallerySkeleton(browseGallery)
const frag = await releasesRss()
browseGallery.innerHTML = ''
const cards = await releasesRss()
browseGallery.textContent = ''
browseGallery.append(...cards)
browse.classList.remove('loading')
browseGallery.appendChild(frag)
browseGallery.scrollTop = 0
browse.onscroll = undefined
},
@ -57,12 +57,11 @@ async function loadHomePage () {
releases: async function () { // this could be cleaner, but oh well
await fetch(getRSSurl()).then(res => res.text().then(async xmlTxt => {
const doc = DOMPARSER(xmlTxt, 'text/xml')
const pubDate = doc.querySelector('pubDate').innerHTML
const pubDate = doc.querySelector('pubDate').textContent
if (lastRSSDate !== pubDate) {
if (lastRSSDate) {
homeReleases.innerHTML = ''
homeReleases.appendChild(gallerySkeletonFrag(5))
resolveFileMedia({ fileName: doc.querySelector('item').querySelector('title').innerHTML, method: 'SearchName', isRelease: true }).then(mediaInformation => {
homeReleases.append(...gallerySkeletonFrag(5))
resolveFileMedia({ fileName: doc.querySelector('item').querySelector('title').textContent, method: 'SearchName', isRelease: true }).then(mediaInformation => {
if (settings.other1) {
const notification = new Notification(mediaInformation.media.title.userPreferred, {
body: `Episode ${mediaInformation.episode} was just released!`,
@ -70,17 +69,16 @@ async function loadHomePage () {
})
notification.onclick = async () => {
window.parent.focus()
client.addTorrent(doc.querySelector('item').querySelector('link').innerHTML, { media: mediaInformation, episode: mediaInformation.episode })
client.addTorrent(doc.querySelector('item').querySelector('link').textContent, { media: mediaInformation, episode: mediaInformation.episode })
store[mediaInformation.parseObject.anime_title] = await alRequest({ id: mediaInformation.media.id, method: 'SearchIDSingle' }).then(res => res.data.Media)
}
}
})
}
const frag = document.createDocumentFragment()
lastRSSDate = pubDate
await releasesCards(doc.querySelectorAll('item'), frag, 5)
homeReleases.innerHTML = ''
homeReleases.appendChild(frag)
const cards = await releasesCards(doc.querySelectorAll('item'), 5)
homeReleases.textContent = ''
homeReleases.append(...cards)
}
}))
setTimeout(homePreviewFunctions.releases, 30000)
@ -107,11 +105,11 @@ async function loadHomePage () {
}
}
const gallerySkeletonFrag = function (limit) {
const frag = document.createDocumentFragment()
for (let i = 0; i < limit; i++) {
frag.appendChild(cardCreator({}))
const cards = []
while (limit--) {
cards.push(skeletonCard.cloneNode(true))
}
return frag
return cards
}
let loadTimeout
let lastDate
@ -120,8 +118,7 @@ async function loadHomePage () {
function gallerySkeleton (gallery) {
browse.classList.add('loading')
gallery.innerHTML = ''
gallery.appendChild(gallerySkeletonFrag(10))
gallery.append(...gallerySkeletonFrag(10))
}
function galleryAppend (opts) {
if (opts.page === 1) {
@ -139,26 +136,24 @@ async function loadHomePage () {
}
}
if (!opts.page || opts.page === 1) {
opts.gallery.innerHTML = ''
opts.gallery.textContent = ''
}
const frag = document.createDocumentFragment()
const date = new Date()
const cards = []
opts.media.forEach(media => {
if (opts.schedule) {
if (media.timeUntilAiring && (!lastDate || (new Date(+date + media.timeUntilAiring * 1000).getDay() !== lastDate.getDay()))) {
const div = document.createElement('div')
lastDate = new Date(+date + media.timeUntilAiring * 1000)
div.classList.add('day-row', 'font-size-24', 'font-weight-bold', 'h-50', 'd-flex', 'align-items-end')
div.innerHTML = lastDate.toLocaleDateString('en-US', { weekday: 'long' })
frag.appendChild(div)
div.textContent = lastDate.toLocaleDateString('en-US', { weekday: 'long' })
cards.push(div)
}
media = media.media
}
const template = cardCreator({ media: media, schedule: opts.schedule })
template.onclick = () => viewAnime(media)
frag.appendChild(template)
cards.push(cardCreator({ media: media, schedule: opts.schedule, onclick: () => viewAnime(media) }))
})
opts.gallery.appendChild(frag)
opts.gallery.append(...cards)
canScroll = true
}
@ -173,10 +168,40 @@ async function loadHomePage () {
navHome.onclick = () => {
lastRSSDate = undefined
for (const item of homePreviewElements) {
item.innerHTML = ''
item.appendChild(gallerySkeletonFrag(5))
item.textContent = ''
item.append(...gallerySkeletonFrag(5))
homePreviewFunctions[item.dataset.function]()
}
document.querySelector('.browse').innerHTML = ''
document.querySelector('.browse').textContent = ''
}
}
const skeletonCard = skeletonCardTemplate.cloneNode(true).content
const bareCard = bareCardTemplate.cloneNode(true).content
const fullCard = fullCardTemplate.cloneNode(true).content
function cardCreator (opts) {
if (opts.media) {
const card = fullCard.cloneNode(true)
const nodes = card.querySelectorAll('*')
nodes[0].onclick = () => opts.onclick()
nodes[0].style = `--color:${opts.media.coverImage.color || '#1890ff'};`
nodes[3].src = opts.media.coverImage.extraLarge || ''
nodes[6].textContent = [opts.media.title.userPreferred, opts.episodeNumber].filter(s => s).join(' - ')
if (opts.schedule && opts.media.nextAiringEpisode) nodes[7] = opts.media.nextAiringEpisode.episode + ' in ' + countdown(opts.media.nextAiringEpisode.timeUntilAiring)
nodes[8].innerHTML = '<span>' + [
opts.media.format === 'TV' ? 'TV Show' : opts.media.format?.toLowerCase().replace(/_/g, ' '),
opts.media.episodes ? opts.media.episodes + ' Episodes' : opts.media.duration ? opts.media.duration + ' Minutes' : undefined,
opts.media.status?.toLowerCase().replace(/_/g, ' '),
[opts.media.season?.toLowerCase(), opts.media.seasonYear].filter(s => s).join(' ')
].filter(s => s).join('</span><span>') + '</span>'
nodes[13].innerHTML = opts.media.description
nodes[14].innerHTML = opts.media.genres.map(key => (`<span class="badge badge-pill badge-color text-dark mt-5 font-weight-bold">${key}</span> `)).join('')
return card
} else if (opts.parseObject) {
const card = bareCard.cloneNode(true)
card.querySelector('h5').textContent = [opts.parseObject.anime_title, opts.parseObject.episode_number].filter(s => s).join(' - ')
return card
} else {
return skeletonCard.cloneNode(true)
}
}

View file

@ -987,8 +987,7 @@ Style: Default,${options.defaultSSAStyles || 'Roboto Medium,26,&H00FFFFFF,&H0000
const frag = document.createDocumentFragment()
for (const file of this.videoFiles) {
const mediaInformation = await resolveFileMedia({ fileName: file.name, method: 'SearchName' })
const template = cardCreator(mediaInformation)
template.onclick = () => {
mediaInformation.onclick = () => {
this.cleanupVideo()
this.buildVideo(torrent, {
media: mediaInformation,
@ -1039,8 +1038,8 @@ Style: Default,${options.defaultSSAStyles || 'Roboto Medium,26,&H00FFFFFF,&H0000
localStorage.setItem('offlineTorrents', JSON.stringify(this.offlineTorrents))
}
const mediaInformation = await resolveFileMedia({ fileName: torrent.name, method: 'SearchName' })
mediaInformation.onclick = () => this.addTorrent(torrent, { media: mediaInformation, episode: mediaInformation.episode })
const template = cardCreator(mediaInformation)
template.onclick = () => this.addTorrent(torrent, { media: mediaInformation, episode: mediaInformation.episode })
document.querySelector('.downloads').appendChild(template)
})
}