mirror of
https://github.com/ThaUnknown/miru.git
synced 2026-01-12 02:21:49 +00:00
feat: previously known as miru on first setup page
fix: initial subtitle override state wip: animethemes
This commit is contained in:
parent
d632ad5b49
commit
bc35557ebd
15 changed files with 173 additions and 22 deletions
1
.vscode/extensions.json
vendored
1
.vscode/extensions.json
vendored
|
|
@ -3,6 +3,7 @@
|
|||
"usernamehw.errorlens",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"GraphQL.vscode-graphql-syntax",
|
||||
"bierner.comment-tagged-templates",
|
||||
"YoavBls.pretty-ts-errors",
|
||||
"svelte.svelte-vscode", // 109.5.2 or older NOT NEWER
|
||||
"ardenivanov.svelte-intellisense",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "ui",
|
||||
"version": "6.3.33",
|
||||
"version": "6.3.34",
|
||||
"license": "BUSL-1.1",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@9.14.4",
|
||||
|
|
|
|||
|
|
@ -132,12 +132,7 @@ a {
|
|||
}
|
||||
}
|
||||
|
||||
.donate {
|
||||
filter: drop-shadow(0 0 1rem #fa68b6);
|
||||
animation: glow 1s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes glow {
|
||||
@keyframes hearbeat {
|
||||
from {
|
||||
transform: translate3d(var(--tw-translate-x), var(--tw-translate-y), 0) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(1) scaleY(1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -252,7 +252,7 @@ export default class Subtitles {
|
|||
lastSubtitleStyle: typeof defaults.subtitleStyle | undefined = undefined
|
||||
_applyStyleOverride (subtitleStyle: typeof defaults.subtitleStyle) {
|
||||
if (this.lastSubtitleStyle === subtitleStyle) return
|
||||
this.lastSubtitleStyle = subtitleStyle
|
||||
if (this.renderer) this.lastSubtitleStyle = subtitleStyle
|
||||
if (subtitleStyle !== 'none') {
|
||||
const font = OVERRIDE_FONTS[subtitleStyle]
|
||||
if (font && !this.fonts.includes(font)) {
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
<Download size={18} />
|
||||
</SidebarButton>
|
||||
<Button variant='ghost' id='sidebar-donate' data-up='#sidebar-client' on:click={() => native.openURL('https://github.com/sponsors/ThaUnknown/')} class='px-2 w-full relative mt-auto select:!bg-transparent text-[#fa68b6] select:text-[#fa68b6]'>
|
||||
<Heart size={18} fill='currentColor' class={cn(active && 'donate')} />
|
||||
<Heart size={18} fill='currentColor' class={cn('drop-shadow-[0_0_1rem_#fa68b6]', active && 'animate-[hearbeat_1s_ease-in-out_infinite_alternate]')} />
|
||||
</Button>
|
||||
<SidebarButton href='/app/settings/'>
|
||||
<Settings size={18} />
|
||||
|
|
|
|||
49
src/lib/components/ui/themes/Themes.svelte
Normal file
49
src/lib/components/ui/themes/Themes.svelte
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<script lang='ts'>
|
||||
import type { Media } from '$lib/modules/anilist'
|
||||
|
||||
import { themes } from '$lib/modules/animethemes'
|
||||
|
||||
export let media: Media
|
||||
|
||||
const themesRes = themes(media.id)
|
||||
// https://api.animethemes.moe/anime/?fields[audio]=id,basename,link,size&fields[video]=id,basename,link,tags&filter[external_id]=113717&filter[has]=resources&filter[site]=AniList&include=animethemes.animethemeentries.videos,animethemes.animethemeentries.videos.audio,animethemes.song,animethemes.song.artists
|
||||
// https://animethemes.moe/anime/momentary_lily
|
||||
// https://animethemes.moe/anime/ousama_ranking
|
||||
</script>
|
||||
|
||||
{#await themesRes}
|
||||
Loading
|
||||
|
||||
{:then themes}
|
||||
{#if themes?.anime?.[0]?.animethemes?.length}
|
||||
{#each themes.anime[0].animethemes as theme (theme.id)}
|
||||
{theme.type}
|
||||
{/each}
|
||||
{/if}
|
||||
{/await}
|
||||
<div class='bg-neutral-950 rounded-md px-7 py-4 gap-4 flex flex-col text-xs'>
|
||||
<div class='flex h-8 items-center'>
|
||||
<div class='w-12'>
|
||||
OP
|
||||
</div>
|
||||
<div class='text-base font-bold'>
|
||||
Oishi Survivor <span class='text-xs text-muted-foreground font-medium'>by</span> HANABIE
|
||||
</div>
|
||||
</div>
|
||||
<div class='flex h-8 items-center text-muted-foreground'>
|
||||
<div class='w-12'>
|
||||
v1
|
||||
</div>
|
||||
<div>
|
||||
Episodes 1-2
|
||||
</div>
|
||||
</div>
|
||||
<div class='flex h-8 items-center text-muted-foreground'>
|
||||
<div class='w-12'>
|
||||
v2
|
||||
</div>
|
||||
<div>
|
||||
Episodes 3-13
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
1
src/lib/components/ui/themes/index.ts
Normal file
1
src/lib/components/ui/themes/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default as Themes } from './Themes.svelte'
|
||||
7
src/lib/modules/animethemes/index.ts
Normal file
7
src/lib/modules/animethemes/index.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import type { AnimeThemesResponse } from './types'
|
||||
|
||||
import { safefetch } from '$lib/utils'
|
||||
|
||||
export function themes (id: number, _fetch = fetch) {
|
||||
return safefetch<AnimeThemesResponse>(_fetch, `https://api.animethemes.moe/anime/?fields[audio]=id,basename,link,size&fields[video]=id,basename,link,tags&filter[external_id]=${id}&filter[has]=resources&filter[site]=AniList&include=animethemes.animethemeentries.videos,animethemes.animethemeentries.videos.audio,animethemes.song,animethemes.song.artists`)
|
||||
}
|
||||
84
src/lib/modules/animethemes/types.d.ts
vendored
Normal file
84
src/lib/modules/animethemes/types.d.ts
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
export interface AnimeThemesResponse {
|
||||
anime?: Anime[]
|
||||
links?: Links
|
||||
meta?: Meta
|
||||
}
|
||||
|
||||
export interface Anime {
|
||||
id?: number
|
||||
name?: string
|
||||
media_format?: string
|
||||
season?: string
|
||||
slug?: string
|
||||
synopsis?: string
|
||||
year?: number
|
||||
animethemes?: AnimeTheme[]
|
||||
}
|
||||
|
||||
export interface AnimeTheme {
|
||||
id?: number
|
||||
sequence?: null
|
||||
slug?: string
|
||||
type?: string
|
||||
song?: Song
|
||||
animethemeentries?: AnimeThemeEntry[]
|
||||
}
|
||||
|
||||
export interface AnimeThemeEntry {
|
||||
id?: number
|
||||
episodes?: string
|
||||
notes?: null
|
||||
nsfw?: boolean
|
||||
spoiler?: boolean
|
||||
version?: null
|
||||
videos?: Video[]
|
||||
}
|
||||
|
||||
export interface Video {
|
||||
id?: number
|
||||
basename?: string
|
||||
tags?: string
|
||||
link?: string
|
||||
audio?: Audio
|
||||
}
|
||||
|
||||
export interface Audio {
|
||||
id?: number
|
||||
basename?: string
|
||||
size?: number
|
||||
link?: string
|
||||
}
|
||||
|
||||
export interface Song {
|
||||
id?: number
|
||||
title?: string
|
||||
artists?: Artist[]
|
||||
}
|
||||
|
||||
export interface Artist {
|
||||
id?: number
|
||||
name?: string
|
||||
slug?: string
|
||||
information?: null
|
||||
artistsong?: ArtistSong
|
||||
}
|
||||
|
||||
export interface ArtistSong {
|
||||
alias?: null
|
||||
as?: null
|
||||
}
|
||||
|
||||
export interface Links {
|
||||
first?: string
|
||||
last?: null
|
||||
prev?: null
|
||||
next?: null
|
||||
}
|
||||
|
||||
export interface Meta {
|
||||
current_page?: number
|
||||
from?: number
|
||||
path?: string
|
||||
per_page?: number
|
||||
to?: number
|
||||
}
|
||||
|
|
@ -1,13 +1,6 @@
|
|||
import type { EpisodesResponse, MappingsResponse } from './types'
|
||||
|
||||
const safefetch = async <T> (_fetch: typeof fetch, ...args: Parameters<typeof fetch>): Promise<T | null> => {
|
||||
try {
|
||||
const res = await _fetch(...args)
|
||||
return await res.json()
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
import { safefetch } from '$lib/utils'
|
||||
|
||||
// const episodes = safefetch<EpisodesResponse>(`https://hayase.ani.zip/v1/episodes?anilist_id=${params.id}`)
|
||||
// const mappings = safefetch<MappingsResponse>(fetch, `https://hayase.ani.zip/v1/mappings?anilist_id=${params.id}`)
|
||||
|
|
|
|||
|
|
@ -9,8 +9,9 @@ export function progress ({ mediaListEntry, id }: Pick<Media, 'mediaListEntry' |
|
|||
return mediaListEntry.progress
|
||||
}
|
||||
|
||||
export function fav (media: Pick<Media, 'isFavourite' | 'id'>): boolean {
|
||||
return media.isFavourite || (local.get(media.id)?.isFavourite ?? false)
|
||||
export function fav (media: Pick<Media, 'mediaListEntry' | 'isFavourite' | 'id'>): boolean {
|
||||
// TODO: idk how to handle this properly
|
||||
return media.mediaListEntry ? media.isFavourite : (local.get(media.id)?.isFavourite ?? false)
|
||||
}
|
||||
|
||||
export function list (media: Pick<Media, 'mediaListEntry' | 'id'>): 'CURRENT' | 'PLANNING' | 'COMPLETED' | 'DROPPED' | 'PAUSED' | 'REPEATING' | null | undefined {
|
||||
|
|
|
|||
|
|
@ -88,8 +88,8 @@ export default Object.assign<Native, Partial<Native>>({
|
|||
setZoom: async () => undefined,
|
||||
// @ts-expect-error yeah
|
||||
navigate: async (cb) => { globalThis.___navigate = cb },
|
||||
downloadProgress: async (percent: number) => undefined,
|
||||
updateProgress: async (cb: (progress: number) => void) => undefined,
|
||||
downloadProgress: async () => undefined,
|
||||
updateProgress: async () => undefined,
|
||||
torrentStats: async (): Promise<TorrentInfo> => ({ peers: rnd(), seeders: rnd(), leechers: rnd(), progress: Math.random(), down: rnd(100000000), up: rnd(100000000), name: 'Amebku.webm', downloaded: rnd(100000), hash: '1234567890abcdef', size: 1234567890, eta: rnd() }),
|
||||
torrents: async (): Promise<TorrentInfo[]> => [{ peers: rnd(), seeders: rnd(), leechers: rnd(), progress: Math.random(), down: rnd(100000000), up: rnd(100000000), name: 'Amebku.webm', downloaded: rnd(100000), hash: '1234567890abcdef', size: 1234567890, eta: rnd() }]
|
||||
// @ts-expect-error idk
|
||||
|
|
|
|||
|
|
@ -266,3 +266,12 @@ export const videoRx = new RegExp(`.(${videoExtensions.join('|')})$`, 'i')
|
|||
|
||||
export const fontExtensions = ['ttf', 'ttc', 'woff', 'woff2', 'otf', 'cff', 'otc', 'pfa', 'pfb', 'pcf', 'fnt', 'bdf', 'pfr', 'eot']
|
||||
export const fontRx = new RegExp(`.(${fontExtensions.join('|')})$`, 'i')
|
||||
|
||||
export const safefetch = async <T> (_fetch: typeof fetch, ...args: Parameters<typeof fetch>): Promise<T | null> => {
|
||||
try {
|
||||
const res = await _fetch(...args)
|
||||
return await res.json()
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
import { Threads } from '$lib/components/ui/forums'
|
||||
import { Load } from '$lib/components/ui/img'
|
||||
import * as Tabs from '$lib/components/ui/tabs'
|
||||
import { Themes } from '$lib/components/ui/themes'
|
||||
import { format, relation } from '$lib/modules/anilist'
|
||||
import { authAggregator } from '$lib/modules/auth'
|
||||
import { dragScroll } from '$lib/modules/navigate'
|
||||
|
|
@ -77,4 +78,11 @@
|
|||
{/if}
|
||||
{/key}
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value='themes' tabindex={-1}>
|
||||
{#key mediaId}
|
||||
{#if value === 'themes'}
|
||||
<Themes {media} />
|
||||
{/if}
|
||||
{/key}
|
||||
</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,10 @@
|
|||
|
||||
<div class='space-y-3 lg:max-w-4xl h-full overflow-y-auto w-full py-8 flex flex-col items-center justify-center' use:dragScroll>
|
||||
<Logo class='w-52 h-52 object-contain mb-14 shrink-0' />
|
||||
<div class='font-bold text-5xl'>Welcome to Hayase</div>
|
||||
<div class='font-bold text-5xl relative'>
|
||||
Welcome to Hayase
|
||||
<div class='animate-[hearbeat_1.5s_ease-in-out_infinite_alternate] absolute text-lg text-theme -right-20 -top-2 rotate-12'>Previously known as Miru!</div>
|
||||
</div>
|
||||
<div class='text-muted-foreground pt-3'>Let's set up your perfect streaming environment.</div>
|
||||
<div class='flex items-center space-x-2 pt-12 pb-3'>
|
||||
<Checkbox id='terms' bind:checked />
|
||||
|
|
|
|||
Loading…
Reference in a new issue