feat: custom default font

This commit is contained in:
ThaUnknown 2022-12-31 23:32:48 +01:00
parent bd6e302705
commit a19920729f
6 changed files with 188 additions and 64 deletions

View file

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

View file

@ -0,0 +1,81 @@
<script context='module'>
import { writable } from 'simple-store-svelte'
const fonts = writable({})
let availableFonts = null
async function loadFonts () {
const _fonts = {}
if (availableFonts) return
const styleSheet = new CSSStyleSheet()
try {
availableFonts = await queryLocalFonts()
for (const metadata of availableFonts) {
if (!_fonts[metadata.family]) {
_fonts[metadata.family] = []
}
_fonts[metadata.family].push(metadata)
}
} catch (err) {
console.warn(err.name, err.message)
}
for (const fontFamily of Object.keys(_fonts)) {
_fonts[fontFamily] = _fonts[fontFamily]
.map(font => {
// Replace font variation name "Arial" with "Arial Regular".
const variationName = font.fullName
.replace(fontFamily, '')
.replaceAll('-', '')
.trim()
font.variationName = variationName || 'Regular'
return font
})
.sort((a, b) => {
// "Regular" always comes first, else use alphabetic order.
if (a.variationName === 'Regular') {
return -1
} else if (b.variationName === 'Regular') {
return 1
} else if (a.variationName < b.variationName) {
return -1
} else if (a.variationName > b.variationName) {
return 1
}
return 0
})
for (const font of _fonts[fontFamily]) {
styleSheet.insertRule(`@font-face { font-family: '${font.postscriptName}'; src: local('${font.postscriptName}'), local('${font.fullName}'); }`)
}
}
document.adoptedStyleSheets = [
styleSheet,
...document.adoptedStyleSheets
]
fonts.value = _fonts
}
</script>
<script>
import { createEventDispatcher } from 'svelte'
const dispatch = createEventDispatcher()
function fontChange ({ target }) {
const value = target.value
const font = availableFonts.find(({ postscriptName }) => postscriptName === value)
dispatch('change', font)
}
export let value
loadFonts()
</script>
<!-- eslint-disable-next-line svelte/valid-compile -->
<select on:click|once={loadFonts} on:change={fontChange} {...$$restProps} style='font-family: {value}' bind:value>
{#each Object.entries($fonts) as [fontName, fonts]}
<optgroup label={fontName} style='font-family: {fontName}'>
{#each fonts as fontData}
<option style='font-family: {fontData.postscriptName}' value={fontData.postscriptName}>{fontData.fullName}</option>
{/each}
</optgroup>
{/each}
</select>

View file

@ -1,54 +1,54 @@
<script>
import { getContext } from 'svelte'
import { traceAnime } from '@/modules/anime.js'
import { getContext } from 'svelte'
import { traceAnime } from '@/modules/anime.js'
export let search
export let current
export let media = null
export let loadCurrent
let searchTimeout = null
let searchTextInput
export let search
export let current
export let media = null
export let loadCurrent
let searchTimeout = null
let searchTextInput
const view = getContext('view')
const view = getContext('view')
$: !$view && searchTextInput?.focus()
function searchClear () {
search = {
format: '',
genre: '',
season: '',
sort: '',
status: ''
}
current = null
searchTextInput?.focus()
}
function input () {
if (!searchTimeout) {
if (Object.values(search).filter(v => v).length) media = [new Promise(() => {})]
} else {
clearTimeout(searchTimeout)
}
searchTimeout = setTimeout(() => {
if (current === null) {
if (Object.values(search).filter(v => v).length) current = 'search'
} else {
if (Object.values(search).filter(v => v).length) {
loadCurrent(false)
} else {
current = null
}
$: !$view && searchTextInput?.focus()
function searchClear () {
search = {
format: '',
genre: '',
season: '',
sort: '',
status: ''
}
current = null
searchTextInput?.focus()
}
function input () {
if (!searchTimeout) {
if (Object.values(search).filter(v => v).length) media = [new Promise(() => {})]
} else {
clearTimeout(searchTimeout)
}
searchTimeout = setTimeout(() => {
if (current === null) {
if (Object.values(search).filter(v => v).length) current = 'search'
} else {
if (Object.values(search).filter(v => v).length) {
loadCurrent(false)
} else {
current = null
}
}
searchTimeout = null
}, 500)
}
function handleFile ({ target }) {
const { files } = target
if (files?.[0]) {
traceAnime(files[0], 'file')
target.value = null
}
searchTimeout = null
}, 500)
}
function handleFile ({ target }) {
const { files } = target
if (files?.[0]) {
traceAnime(files[0], 'file')
target.value = null
}
}
</script>
<div class='container-fluid row p-20' on:input={input}>
@ -61,7 +61,6 @@ function handleFile ({ target }) {
<div class='input-group-prepend'>
<span class='input-group-text d-flex material-icons bg-dark pr-0 font-size-18'>search</span>
</div>
<!-- svelte-ignore missing-declaration -->
<!-- svelte-ignore a11y-autofocus -->
<input
on:input={({ target }) => {
@ -170,7 +169,7 @@ function handleFile ({ target }) {
<option value='UPDATED_TIME_DESC' disabled hidden>Updated Date</option>
</select>
</div>
<input type='file' class='d-none' id='search-image' accept='image/*' on:input={handleFile}>
<input type='file' class='d-none' id='search-image' accept='image/*' on:input={handleFile} />
<div class='col-auto p-10 d-flex'>
<button class='btn bg-dark material-icons font-size-18 px-5 align-self-end shadow-lg border-0' type='button'>
<label for='search-image' class='pointer'>

View file

@ -72,6 +72,7 @@
<script>
import { Tabs, TabLabel, Tab } from './Tabination.js'
import FontSelect from './FontSelect.svelte'
import { onDestroy } from 'svelte'
onDestroy(() => {
@ -110,6 +111,25 @@
window.IPC.on('path', data => {
settings.torrentPath = data
})
async function changeFont ({ detail }) {
try {
const blob = await detail.blob()
const data = await blob.arrayBuffer()
settings.font = {
name: detail.fullName,
value: detail.postscriptName,
data: [...new Uint8Array(data)]
}
} catch (error) {
console.warn(error)
addToast({
text: /* html */`${error.message}<br>Try using a different font.`,
title: 'File Error',
type: 'secondary',
duration: 8000
})
}
}
</script>
<Tabs>
@ -161,6 +181,19 @@
<div class='h-full p-20 m-20'>
<Tab>
<div class='root'>
<div class='col p-10 d-flex flex-column justify-content-end'>
<div class='pb-10 font-size-24 font-weight-semi-bold d-flex'>
<div class='material-icons mr-10 font-size-30'>font_download</div>
Default Subtitle Font
</div>
<FontSelect class='form-control bg-dark shadow-lg w-300' on:change={changeFont} value={settings.font?.value} />
</div>
<div class='col p-10 d-flex flex-column justify-content-end'>
<div class='font-size-24 font-weight-semi-bold d-flex'>
<div class='material-icons mr-10 font-size-30'>play_arrow</div>
Playback Settings
</div>
</div>
<div
class='custom-switch mb-10 pl-10 font-size-16 w-300'
data-toggle='tooltip'
@ -177,6 +210,12 @@
<input type='checkbox' id='player-pause' bind:checked={settings.playerPause} />
<label for='player-pause'>Pause When Tabbing Out</label>
</div>
<div class='col p-10 d-flex flex-column justify-content-end'>
<div class='font-size-24 font-weight-semi-bold d-flex'>
<div class='material-icons mr-10 font-size-30'>list</div>
Anilist Settings
</div>
</div>
<div
class='custom-switch mb-10 pl-10 font-size-16 w-300'
data-toggle='tooltip'

View file

@ -92,15 +92,14 @@ const handleRequest = limiter.wrap(async opts => {
return json
})
export const alID = !!alToken && alRequest({ method: 'Viewer', token: alToken })
if (alID) {
alID.then(result => {
const lists = result?.data?.Viewer?.mediaListOptions?.animeList?.customLists || []
if (!lists.includes('Watched using Miru')) {
alRequest({ method: 'CustomList', lists })
}
})
}
export let alID = !!alToken
alID = alRequest({ method: 'Viewer', token: alToken }).then(result => {
const lists = result?.data?.Viewer?.mediaListOptions?.animeList?.customLists || []
if (!lists.includes('Watched using Miru')) {
alRequest({ method: 'CustomList', lists })
}
return result
})
function printError (error) {
console.warn(error)
@ -186,6 +185,7 @@ export async function alRequest (opts) {
Accept: 'application/json'
}
}
const userId = (await alID)?.data?.Viewer.id
const queryObjects = /* js */`
id,
title {
@ -349,7 +349,7 @@ query{
}`
break
} case 'UserLists': {
variables.id = (await alID)?.data?.Viewer?.id
variables.id = userId
query = /* js */`
query($page: Int, $perPage: Int, $id: Int, $status_in: [MediaListStatus]){
Page(page: $page, perPage: $perPage){
@ -365,7 +365,7 @@ query($page: Int, $perPage: Int, $id: Int, $status_in: [MediaListStatus]){
}`
break
} case 'NewSeasons': {
variables.id = (await alID)?.data?.Viewer?.id
variables.id = userId
query = /* js */`
query($id: Int){
MediaListCollection(userId: $id, status_in: [REPEATING, COMPLETED], type: ANIME, forceSingleCompletedList: true){
@ -387,7 +387,7 @@ query($id: Int){
}`
break
} case 'SearchIDStatus': {
variables.id = (await alID)?.data?.Viewer?.id
variables.id = userId
variables.mediaId = opts.id
query = /* js */`
query($id: Int, $mediaId: Int){

View file

@ -2,12 +2,13 @@ import JASSUB from 'jassub'
import workerUrl from 'jassub/dist/jassub-worker.js?url'
import 'jassub/dist/jassub-worker.wasm?url'
import { toTS, videoRx, subRx } from './util.js'
import { set } from '@/lib/Settings.svelte'
import { client } from '@/modules/torrent.js'
const defaultHeader = `[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default, Roboto Medium,26,&H00FFFFFF,&H000000FF,&H00020713,&H00000000,0,0,0,0,100,100,0,0,1,1.3,0,2,20,20,23,1
Style: Default, ${set.font?.name || 'Roboto Medium'},26,&H00FFFFFF,&H000000FF,&H00020713,&H00000000,0,0,0,0,100,100,0,0,1,1.3,0,2,20,20,23,1
[Events]
`
@ -130,16 +131,20 @@ export default class Subtitles {
initSubtitleRenderer () {
if (!this.renderer) {
this.renderer = new JASSUB({
const options = {
video: this.video,
subContent: this.headers[this.current].header.slice(0, -1),
fonts: this.fonts,
fallbackFont: 'roboto medium',
fallbackFont: set.font?.name || 'roboto medium',
availableFonts: {
'roboto medium': './Roboto.ttf'
},
workerUrl
})
}
if (set.font) {
options.availableFonts[set.font.name.toLowerCase()] = new Uint8Array(set.font.data)
}
this.renderer = new JASSUB(options)
this.selectCaptions(this.current)
}
}