feat: new better RSS View

This commit is contained in:
ThaUnknown 2023-07-14 18:22:30 +02:00
parent 4e68d5349f
commit 1dd08046d8
6 changed files with 129 additions and 36 deletions

View file

@ -1,6 +1,6 @@
{
"name": "Miru",
"version": "4.1.14",
"version": "4.1.15",
"author": "ThaUnknown_ <ThaUnknown@users.noreply.github.com>",
"description": "Stream anime torrents, real-time with no waiting for downloads.",
"main": "build/main.js",

View file

@ -21,8 +21,11 @@
--gray-color-light: hsl(var(--gray-color-light-hsl));
--gray-color-light-hsl: var(--gray-color-base-hue), 10%, 28%;
--gray-color-base-hue: 216;
--secondary-color: #fff;
--secondary-color-light: #ddd;
}
.btn-secondary {
--dm-button-secondary-bg-color: #fff !important;
--dm-button-secondary-bg-color-hover: #ddd !important;
}
.material-symbols-outlined {

View file

@ -3,6 +3,7 @@ import { fastPrettyBytes } from '../util.js'
import { exclusions } from '../rss.js'
import { set } from '@/views/Settings.svelte'
import { alRequest } from '../anilist.js'
import anitomyscript from 'anitomyscript'
export default async function tosho ({ media, episode }) {
const json = await getAniDBFromAL(media)
@ -17,7 +18,13 @@ export default async function tosho ({ media, episode }) {
if (!entries.length && !movie) entries = await getToshoEntries(media, aniDBEpisode, json)
return mapBestRelease(mapTosho2dDeDupedEntry(entries))
const mapped = mapBestRelease(mapTosho2dDeDupedEntry(entries))
const parseObjects = await anitomyscript(mapped.map(({ title }) => title))
for (const i in parseObjects) mapped[i].parseObject = parseObjects[i]
return mapped
}
window.tosho = tosho
@ -100,7 +107,7 @@ async function getToshoEntries (media, episode, { mappings }, quality) {
// look for batches and movies
const movie = isMovie(media)
if (mappings.anidb_id && media.status === 'FINISHED' && (movie || media.episodes !== 1)) {
promises.push(fetchBatches({ episodeCount: media.episodes, id: mappings.anidb_id, quality }))
promises.push(fetchBatches({ episodeCount: media.episodes, id: mappings.anidb_id, quality, movie }))
console.log('fetching batch', quality, movie)
if (!movie) {
const courRelation = getSplitCourRelation(media)
@ -204,12 +211,17 @@ function buildQuery (quality) {
return query
}
async function fetchBatches ({ episodeCount, id, quality }) {
async function fetchBatches ({ episodeCount, id, quality, movie }) {
const queryString = buildQuery(quality)
const torrents = await fetch(set.toshoURL + 'json?order=size-d&aid=' + id + queryString)
// safe if AL includes EP 0 or doesn't
const batches = (await torrents.json()).filter(entry => entry.num_files >= episodeCount)
if (!movie) {
for (const batch of batches) {
batch.batch = true
}
}
console.log({ batches })
return batches
}
@ -234,6 +246,7 @@ function mapTosho2dDeDupedEntry (entries) {
dupe.leechers ||= entry.leechers >= 100000 ? 0 : entry.leechers
dupe.downloads ||= entry.torrent_downloaded_count
dupe.size ||= entry.total_size && fastPrettyBytes(entry.total_size)
dupe.verified ||= !!entry.anidb_fid
dupe.date ||= entry.timestamp && new Date(entry.timestamp * 1000)
} else {
deduped[entry.info_hash] = {
@ -244,6 +257,8 @@ function mapTosho2dDeDupedEntry (entries) {
leechers: entry.leechers >= 100000 ? 0 : entry.leechers,
downloads: entry.torrent_downloaded_count,
size: entry.total_size && fastPrettyBytes(entry.total_size),
verified: !!entry.anidb_fid,
batch: entry.batch,
date: entry.timestamp && new Date(entry.timestamp * 1000)
}
}

View file

@ -71,37 +71,111 @@
}
let modal
$: table && modal?.focus()
const termMapping = {
5.1: '5.1',
'5.1CH': '5.1',
'TRUEHD5.1': 'TrueHD 5.1',
AAC: 'AAC',
AACX2: 'AAC',
AACX3: 'AAC',
AACX4: 'AAC',
AC3: 'AC3',
EAC3: 'EAC3',
'E-AC-3': 'EAC3',
FLAC: 'FLAC',
FLACX2: 'FLAC',
FLACX3: 'FLAC',
FLACX4: 'FLAC',
VORBIS: 'Vorbis',
DUALAUDIO: 'Dual Audio',
'Dual Audio': 'Dual Audio',
'10BIT': '10 Bit',
'10BITS': '10 Bit',
'10-BIT': '10 Bit',
'10-BITS': '10 Bit',
HI10: '10 Bit',
HI10P: '10 Bit',
HI444: 'HI444',
HI444P: 'HI444',
HI444PP: 'HI444',
H265: 'HEVC',
'H.265': 'HEVC',
X265: 'HEVC',
HEVC: 'HEVC',
AV1: 'AV1'
}
function sanitiseTerms ({ video_term: video, audio_term: audio, video_resolution: resolution }) {
if (!Array.isArray(video)) video = [video]
if (!Array.isArray(audio)) audio = [audio]
const terms = []
if (resolution) terms.push({ text: resolution, color: '#c6ec58' })
for (const text of audio) {
if (termMapping[text]) terms.push({ text: termMapping[text], color: '#f67255' })
}
for (const text of video) {
if (termMapping[text]) terms.push({ text: termMapping[text], color: '#0c8ce9' })
}
return terms
}
</script>
<div class='modal z-40' class:show={table} id='viewAnime'>
{#if table}
<div class='modal-dialog p-20' on:pointerup|self={close} on:keydown={checkClose} tabindex='-1' role='button' bind:this={modal}>
<div class='modal-content w-auto'>
<button class='close pointer z-30 right-0 position-absolute' type='button' use:click={close}> &times; </button>
<table class='table table-hover'>
<div class='modal-dialog' on:pointerup|self={close} on:keydown={checkClose} tabindex='-1' role='button' bind:this={modal}>
<div class='modal-content w-auto h-full mx-20 p-0 rounded overflow-x-hidden overflow-y-scroll'>
<div class='w-full bg-dark-light d-flex px-15 py-10 position-sticky top-0 z-10'>
<div class='material-symbols-outlined text-danger symbol-bold' title='Badges Are a Rough Guess of Information And Might Not Be Representative of Actual Data'>
warning
</div>
<button class='btn btn-square bg-dark rounded-circle ml-auto pointer' type='button' use:click={close}> &times; </button>
</div>
<table class='table table-hover font-size-14 position-relative'>
<thead>
<tr>
<th scope='col'>#</th>
<th scope='col'>Name</th>
<th scope='col'>Size</th>
<th scope='col'>Seed</th>
<th scope='col'>Leech</th>
<th scope='col'>Downloads</th>
<th scope='col'>Released</th>
<th scope='col'>Play</th>
<tr class='border-0'>
<td class='py-15 pl-20 pr-0' />
<td class='py-15 px-20'>Name</td>
<td class='py-15 px-20'>Size</td>
<td class='py-15 px-20'>Seed</td>
<td class='py-15 px-20'>Leech</td>
<td class='py-15 px-20'>Downloads</td>
<td class='py-15 px-20'>Released</td>
</tr>
</thead>
<tbody class='results pointer'>
{#each table as row, index}
<tr class:text-primary={row.best} use:click={() => play(row)}>
<th>{index + 1}</th>
<td>{row.title}</td>
<td>{row.size}</td>
<td>{row.seeders ?? '?'}</td>
<td>{row.leechers ?? '?'}</td>
<td>{row.downloads ?? '?'}</td>
<td>{since(row.date)}</td>
<td class='material-symbols-outlined font-size-20'>play_arrow</td>
<tbody class='pointer'>
{#each table as row}
<tr class='border-0' class:text-secondary={row.best} use:click={() => play(row)}>
<td class='py-10 pl-20 pr-0'>
{#if row.best}
<div class='material-symbols-outlined font-size-24 symbol-bold' title='Best Release'>
star
</div>
{:else if row.verified}
<div class='text-success material-symbols-outlined font-size-24 symbol-bold' title='Verified'>
verified
</div>
{:else if row.batch}
<div class='text-light material-symbols-outlined font-size-24 symbol-bold' title='Batch'>
database
</div>
{/if}
</td>
<td class='py-10 px-20'>{row.title}
<div class='d-flex flex-row text-dark font-weight-bold font-size-12'>
{#each sanitiseTerms(row.parseObject) as { text, color }}
<div style={'background:' + color} class='rounded px-15 mr-10 mt-5'>
{text}
</div>
{/each}
</div>
</td>
<td class='py-10 px-20 text-nowrap'>{row.size}</td>
<td class='py-10 px-20'>{row.seeders ?? '?'}</td>
<td class='py-10 px-20'>{row.leechers ?? '?'}</td>
<td class='py-10 px-20'>{row.downloads ?? '?'}</td>
<td class='py-10 px-20 text-nowrap'>{since(row.date)}</td>
</tr>
{/each}
</tbody>
@ -112,9 +186,10 @@
</div>
<style>
.close {
top: 4rem !important;
left: unset;
right: 2.5rem !important;
.modal-content {
margin: 8rem 6rem 0 6rem !important
}
.symbol-bold {
font-variation-settings: 'wght' 500;
}
</style>

View file

@ -31,7 +31,7 @@
items.value = []
key = {}
loadSearchData()
}, 150)
}, 300)
let canScroll = true

View file

@ -171,7 +171,7 @@
</ToggleList>
</div>
<div class='col-5 d-flex flex-column pl-20'>
<EpisodeList id={media.id} userProgress={media.mediaListEntry?.progress} episodeCount={media.episodes} duration={media.duration} {play} />
<EpisodeList id={media.id} userProgress={media.mediaListEntry && media.mediaListEntry.status === 'CURRENT' && media.mediaListEntry.progress} episodeCount={media.episodes} duration={media.duration} {play} />
</div>
</div>
</div>