feat: sub, dub, partial dub audio labels

This commit is contained in:
RockinChaos 2024-05-31 15:13:08 -07:00
parent 0d53446a05
commit 9367679319
9 changed files with 158 additions and 5 deletions

View file

@ -2,6 +2,7 @@
import { formatMap, setStatus, playMedia } from '@/modules/anime.js'
import { anilistClient } from '@/modules/anilist.js'
import { click } from '@/modules/click.js'
import AudioLabel from '@/views/ViewAnime/AudioLabel.svelte'
export let mediaList
let current = mediaList[0]
@ -69,6 +70,9 @@
{current.duration + ' Minutes'}
</span>
{/if}
<span class='text-nowrap d-flex align-items-center'>
<AudioLabel media={current} banner={true}/>
</span>
{#if current.season || current.seasonYear}
<span class='text-nowrap d-flex align-items-center'>
{[current.season?.toLowerCase(), current.seasonYear].filter(s => s).join(' ')}

View file

@ -4,6 +4,7 @@
import { click } from '@/modules/click.js'
import { countdown } from '@/modules/util.js'
import { page } from '@/App.svelte'
import AudioLabel from '@/views/ViewAnime/AudioLabel.svelte'
/** @type {import('@/modules/al.d.ts').Media} */
export let media
@ -17,8 +18,9 @@
<div class='card m-0 p-0 overflow-hidden pointer content-visibility-auto'
style:--color={media.coverImage.color || '#1890ff'}>
<div class='row h-full'>
<div class='col-4'>
<div class='col-4 d-inline-block position-relative'>
<img loading='lazy' src={media.coverImage.extraLarge || ''} alt='cover' class='cover-img w-full h-full' />
<AudioLabel {media} />
</div>
<div class='col-8 h-full card-grid'>
<div class='px-15 py-10 bg-very-dark'>
@ -59,6 +61,9 @@
{:else if media.duration}
<span class='text-nowrap'>{media.duration + ' Minutes'}</span>
{/if}
<span class='text-nowrap'>
<AudioLabel {media} banner={true}/>
</span>
{#if media.status}
<span class='text-nowrap'>{media.status?.toLowerCase().replace(/_/g, ' ')}</span>
{/if}

View file

@ -2,6 +2,7 @@
import { formatMap, setStatus, playMedia } from '@/modules/anime.js'
import { anilistClient } from '@/modules/anilist.js'
import { click } from '@/modules/click.js'
import AudioLabel from '@/views/ViewAnime/AudioLabel.svelte'
/** @type {import('@/modules/al.d.ts').Media} */
export let media
@ -119,6 +120,9 @@
{media.duration + ' Minutes'}
</span>
{/if}
<span class='text-nowrap d-flex align-items-center'>
<AudioLabel {media} banner={true}/>
</span>
{#if media.season || media.seasonYear}
<span class='text-nowrap d-flex align-items-center'>
{[media.season?.toLowerCase(), media.seasonYear].filter(s => s).join(' ')}

View file

@ -4,6 +4,7 @@
import { formatMap, statusColorMap } from '@/modules/anime.js'
import { hoverClick } from '@/modules/click.js'
import { countdown } from '@/modules/util.js'
import AudioLabel from '@/views/ViewAnime/AudioLabel.svelte'
import { page } from '@/App.svelte'
/** @type {import('@/modules/al.d.ts').Media} */
@ -36,8 +37,10 @@
{/if}
</div>
{/if}
<img loading='lazy' src={media.coverImage.extraLarge || ''} alt='cover' class='cover-img w-full rounded' style:--color={media.coverImage.color || '#1890ff'} />
<div class="d-inline-block position-relative">
<img loading='lazy' src={media.coverImage.extraLarge || ''} alt='cover' class='cover-img w-full rounded' style:--color={media.coverImage.color || '#1890ff'} />
<AudioLabel {media} />
</div>
<div class='text-white font-weight-very-bold font-size-16 pt-15 title overflow-hidden'>
{#if media.mediaListEntry?.status}
<div style:--statusColor={statusColorMap[media.mediaListEntry.status]} class='list-status-circle d-inline-flex overflow-hidden mr-5' title={media.mediaListEntry.status} />

View file

@ -1,5 +1,6 @@
export type Media = {
id: number
idMal: number
title: {
romaji?: string
english?: string

View file

@ -6,7 +6,7 @@ import { alToken } from '@/modules/settings.js'
import { toast } from 'svelte-sonner'
import { sleep } from './util.js'
const codes = {
export const codes = {
400: 'Bad Request',
401: 'Unauthorized',
402: 'Payment Required',
@ -465,7 +465,7 @@ class AnilistClient {
async getUserLists (variables) {
const userId = this.userID?.viewer?.data?.Viewer.id
variables.id = userId
if (variables.sort) { variables.sort = variables.sort.replace('USER_SCORE_DESC', 'SCORE_DESC')}
variables.sort = variables.sort?.replace('USER_SCORE_DESC', 'SCORE_DESC');
const query = /* js */`
query($id: Int $sort: [MediaListSort]){
MediaListCollection(userId: $id, type: ANIME, sort: $sort, forceSingleCompletedList: true) {

View file

@ -0,0 +1,55 @@
import { toast } from 'svelte-sonner'
import { writable } from 'simple-store-svelte'
import { codes } from '@/modules/anilist.js'
export const dubInfo = writable()
const initialized = writable(false)
export async function cacheDubs() {
await initialized.subscribe(async value => {
if (!value) {
dubInfo.value = await getDubInfo()
initialized.set(true)
// update dubInfo every hour
setInterval(() => dubInfo.value = getDubInfo(), 1000 * 60 * 60)
}
})
}
async function getDubInfo() {
let res = {}
try {
res = await fetch('https://raw.githubusercontent.com/MAL-Dubs/MAL-Dubs/main/data/dubInfo.json')
} catch (e) {
if (!res || res.status !== 404) throw e
}
if (!res.ok && (res.status === 429 || res.status === 500)) {
throw res
}
let json = null
try {
json = await res.json()
} catch (error) {
if (res.ok) printError(error)
}
if (!res.ok) {
if (json) {
for (const error of json?.errors || []) {
printError(error)
}
} else {
printError(res)
}
}
return json
}
function printError(error) {
console.warn(error)
toast.error('Dub Caching Failed', {
description: `Failed to load dub information!\nTry again in a minute.\n${error.status || 429} - ${error.message || codes[error.status || 429]}`,
duration: 3000
})
}

View file

@ -0,0 +1,77 @@
<script>
import { onMount } from 'svelte'
import { settings } from '@/modules/settings.js'
import { cacheDubs } from '@/modules/audiolabel.js'
import { dubInfo } from '@/modules/audiolabel.js'
/** @type {import('@/modules/al.d.ts').Media} */
export let media = null
export let banner = false
export let viewAnime = false
export let example = false
let isDubbed = false
let isPartial = false
/**
* @param {number} id
*/
async function setLabel(id) {
const info = await dubInfo
if (info && info.value) {
isDubbed = info.value.dubbed.includes(id)
isPartial = info.value.incomplete.includes(id)
}
}
onMount(async () => {
await cacheDubs()
if (media != null) {
await setLabel(media.idMal)
}
})
</script>
{#if !banner && !viewAnime && !example && settings.value.cardAudio}
<span class='material-symbols-outlined font-size-24 label position-absolute {isDubbed ? 'dubbed' : isPartial ? 'incomplete' : 'subbed'}'>
{isDubbed ? 'mic' : isPartial ? 'mic_off' : 'closed_caption'}
</span>
{:else if !viewAnime && !example}
{isDubbed ? 'Dub' : isPartial ? 'Partial Dub' : 'Sub'}
{:else if !example}
<span class='material-symbols-outlined mx-10 font-size-24'>
{isDubbed ? 'mic' : isPartial ? 'mic_off' : 'closed_caption'}
</span>
<span class='mr-20'>
{isDubbed ? 'Dub' : isPartial ? 'Partial Dub' : 'Sub'}
</span>
{:else}
<div>
<span class='material-symbols-outlined font-size-24 label ml-20 position-relative subbed'>closed_caption</span>
<span class='position-relative ml-2'>Sub Only</span>
<span class='material-symbols-outlined font-size-24 label ml-20 position-relative incomplete'>mic_off</span>
<span class='position-relative ml-2'>Partial Dub</span>
<span class='material-symbols-outlined font-size-24 label ml-20 position-relative dubbed'>mic</span>
<span class='position-relative ml-2'>Dub</span>
</div>
{/if}
<style>
.label {
padding: 0.4rem 0.2rem;
border-radius: 0.8rem;
top: 0.625rem;
right: 0.625rem;
z-index: 1;
}
.dubbed {
background-color: rgba(121, 97, 9, 0.7);
}
.subbed {
background-color: rgba(76, 41, 124, 0.7);
}
.incomplete {
background-color: rgba(221, 100, 25, 0.7);
}
</style>

View file

@ -8,6 +8,7 @@
import Details from './Details.svelte'
import EpisodeList from './EpisodeList.svelte'
import ToggleList from './ToggleList.svelte'
import AudioLabel from './AudioLabel.svelte'
import Following from './Following.svelte'
import smoothScroll from '@/modules/scroll.js'
import IPC from '@/modules/ipc.js'
@ -114,6 +115,9 @@
</span>
</div>
{/if}
<div class='d-flex flex-row mt-10'>
<AudioLabel {media} viewAnime={true}/>
</div>
</div>
<div class='d-flex flex-row flex-wrap'>
<button class='btn btn-lg btn-secondary w-250 text-dark font-weight-bold shadow-none border-0 d-flex align-items-center justify-content-center mr-10 mt-20'