mirror of
https://github.com/NoCrypt/migu.git
synced 2026-04-04 16:49:42 +00:00
fix: torrent port on android
fix: better font select componennt fix: menubar being clickable on android feat: subtitle render height limit feat: higher memory limits for libass feat: better looking settings feat: hide settings unsupported by platform feat: better setting descriptions fix: miniplayer dragging on mobile
This commit is contained in:
parent
80447bdee9
commit
88ab29e6a1
22 changed files with 653 additions and 703 deletions
|
|
@ -1,5 +1,13 @@
|
|||
// feature support list, overriden per environment
|
||||
// feature support list, overriden per environment, capacitor
|
||||
|
||||
export const SUPPORTS = {
|
||||
offscreenRender: false
|
||||
offscreenRender: false,
|
||||
update: false,
|
||||
angle: false,
|
||||
doh: false,
|
||||
dht: false,
|
||||
discord: false,
|
||||
torrentPort: false,
|
||||
torrentPath: false,
|
||||
torrentPersist: false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,4 +25,4 @@ async function storageQuota () {
|
|||
return quota - usage
|
||||
}
|
||||
|
||||
globalThis.client = new TorrentClient(ipcRendererWebTorrent, storageQuota, 'browser', controller, { torrentPort: Math.random() * 65535 + 1 })
|
||||
globalThis.client = new TorrentClient(ipcRendererWebTorrent, storageQuota, 'browser', controller, { torrentPort: Math.floor(Math.random() * 65535 + 1) })
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import Home from './views/Home/Home.svelte'
|
||||
import MediaHandler from './views/Player/MediaHandler.svelte'
|
||||
import Settings from './views/Settings.svelte'
|
||||
import Settings from '@/views/Settings/Settings.svelte'
|
||||
import WatchTogether from './views/WatchTogether/WatchTogether.svelte'
|
||||
import Miniplayer from 'svelte-miniplayer'
|
||||
import Search from './views/Search.svelte'
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
<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>
|
||||
|
|
@ -61,7 +61,8 @@
|
|||
}
|
||||
@media (pointer: none), (pointer: coarse) {
|
||||
.navbar {
|
||||
display: none;
|
||||
display: none !important;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { getContext } from 'svelte'
|
||||
import { alID } from '@/modules/anilist.js'
|
||||
import { media } from '../views/Player/MediaHandler.svelte'
|
||||
import { platformMap } from '../views/Settings.svelte'
|
||||
import { platformMap } from '@/views/Settings/Settings.svelte'
|
||||
import { toast } from 'svelte-sonner'
|
||||
import { click } from '@/modules/click.js'
|
||||
import { logout } from './Logout.svelte'
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { getContext } from 'svelte'
|
||||
import { alID } from '@/modules/anilist.js'
|
||||
import { media } from '../views/Player/MediaHandler.svelte'
|
||||
import { platformMap } from '../views/Settings.svelte'
|
||||
import { platformMap } from '@/views/Settings/Settings.svelte'
|
||||
import { settings } from '@/modules/settings.js'
|
||||
import { toast } from 'svelte-sonner'
|
||||
import { click } from '@/modules/click.js'
|
||||
|
|
|
|||
|
|
@ -9,6 +9,12 @@
|
|||
registerTab(tab)
|
||||
</script>
|
||||
|
||||
<div class={'pointer border-bottom ' + ($selectedTab === tab ? 'bg-dark-light' : '')} use:click={() => selectTab(tab)}>
|
||||
<div class={'pointer my-5 rounded ' + ($selectedTab === tab ? 'bg-dark-light font-weight-bold' : '')} use:click={() => selectTab(tab)}>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
div {
|
||||
transition: background .2s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -160,6 +160,9 @@ export default class Subtitles {
|
|||
subContent: defaultHeader,
|
||||
fonts: this.fonts,
|
||||
offscreenRender: SUPPORTS.offscreenRender,
|
||||
libassMemoryLimit: 1024,
|
||||
libassGlyphLimit: 80000,
|
||||
maxRenderHeight: parseInt(settings.value.subtitleRenderHeight) || 0,
|
||||
fallbackFont: settings.value.font?.name || 'roboto medium',
|
||||
availableFonts: {
|
||||
'roboto medium': './Roboto.ttf'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,13 @@
|
|||
// feature support list, overriden per environment
|
||||
// feature support list, overriden per environment, global
|
||||
|
||||
export const SUPPORTS = {
|
||||
offscreenRender: true
|
||||
offscreenRender: true,
|
||||
update: true,
|
||||
angle: true,
|
||||
doh: true,
|
||||
dht: true,
|
||||
discord: true,
|
||||
torrentPort: true,
|
||||
torrentPath: true,
|
||||
torrentPersist: true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -157,6 +157,7 @@ export const defaults = {
|
|||
dhtPort: 0,
|
||||
missingFont: true,
|
||||
maxConns: 50,
|
||||
subtitleRenderHeight: 0,
|
||||
subtitleLanguage: 'eng',
|
||||
audioLanguage: 'jpn',
|
||||
enableDoH: false,
|
||||
|
|
|
|||
|
|
@ -6,11 +6,17 @@ import { defaults, fontRx, subRx, videoRx } from './util.js'
|
|||
|
||||
const LARGE_FILESIZE = 32_000_000_000
|
||||
|
||||
let storedSettings = {}
|
||||
|
||||
try {
|
||||
storedSettings = JSON.parse(localStorage.getItem('settings')) || {}
|
||||
} catch (error) {}
|
||||
|
||||
export default class TorrentClient extends WebTorrent {
|
||||
static excludedErrorMessages = ['WebSocket', 'User-Initiated Abort, reason=', 'Connection failed.']
|
||||
|
||||
constructor (ipc, storageQuota, serverMode, controller, settingOverrides = {}) {
|
||||
const settings = { ...defaults, ...(JSON.parse(localStorage.getItem('settings')) || {}), ...settingOverrides }
|
||||
const settings = { ...defaults, ...storedSettings, ...settingOverrides }
|
||||
super({
|
||||
dht: !settings.torrentDHT,
|
||||
maxConns: settings.maxConns,
|
||||
|
|
|
|||
|
|
@ -13,11 +13,12 @@
|
|||
"p2pt": "github:ThaUnknown/p2pt#modernise",
|
||||
"perfect-seekbar": "^1.1.0",
|
||||
"quartermoon": "^1.2.3",
|
||||
"simple-font-select": "^1.0.1",
|
||||
"simple-store-svelte": "^1.0.1",
|
||||
"svelte": "^4.2.3",
|
||||
"svelte-keybinds": "^1.0.5",
|
||||
"svelte-loader": "^3.1.9",
|
||||
"svelte-miniplayer": "^1.0.4",
|
||||
"svelte-miniplayer": "^1.0.5",
|
||||
"svelte-sonner": "^0.3.3",
|
||||
"webpack-merge": "^5.10.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,550 +0,0 @@
|
|||
<script context='module'>
|
||||
import { toast } from 'svelte-sonner'
|
||||
import { click } from '@/modules/click.js'
|
||||
import { resetSettings, settings } from '@/modules/settings.js'
|
||||
import IPC from '@/modules/ipc.js'
|
||||
|
||||
if (settings.value.enableDoH) IPC.emit('doh', settings.value.doHURL)
|
||||
export const platformMap = {
|
||||
aix: 'Aix',
|
||||
darwin: 'MacOS',
|
||||
android: 'Android',
|
||||
ios: 'iOS',
|
||||
freebsd: 'Linux',
|
||||
linux: 'Linux',
|
||||
openbsd: 'Linux',
|
||||
sunos: 'SunOS',
|
||||
win32: 'Windows'
|
||||
}
|
||||
let version = '1.0.0'
|
||||
IPC.on('version', data => (version = data))
|
||||
IPC.emit('version')
|
||||
function updateAngle () {
|
||||
IPC.emit('angle', settings.value.angle)
|
||||
}
|
||||
|
||||
let wasUpdated = false
|
||||
IPC.on('update-available', () => {
|
||||
if (!wasUpdated) {
|
||||
wasUpdated = true
|
||||
toast('Auto Updater', {
|
||||
description: 'A new version of Miru is available. Downloading!'
|
||||
})
|
||||
}
|
||||
})
|
||||
IPC.on('update-downloaded', () => {
|
||||
toast.success('Auto Updater', {
|
||||
description: 'A new version of Miru has downloaded. You can restart to update!'
|
||||
})
|
||||
})
|
||||
function checkUpdate () {
|
||||
IPC.emit('update')
|
||||
}
|
||||
setInterval(checkUpdate, 1200000)
|
||||
|
||||
const changeLog = (async () => {
|
||||
const res = await fetch('https://api.github.com/repos/ThaUnknown/miru/releases')
|
||||
const json = await res.json()
|
||||
return json.map(({ body, tag_name: version }) => ({ body, version }))
|
||||
})()
|
||||
IPC.emit('discord_status', settings.value.showDetailsInRPC)
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import { Tabs, TabLabel, Tab } from '../components/Tabination.js'
|
||||
import FontSelect from '../components/FontSelect.svelte'
|
||||
import { onDestroy } from 'svelte'
|
||||
import { variables } from '@/modules/themes.js'
|
||||
import { defaults } from '@/modules/util.js'
|
||||
import HomeSections from './Settings/HomeSectionsSettings.svelte'
|
||||
|
||||
onDestroy(() => {
|
||||
IPC.off('path')
|
||||
})
|
||||
|
||||
const groups = {
|
||||
player: {
|
||||
name: 'Player',
|
||||
icon: 'play_arrow',
|
||||
desc: 'Player configuration, playback behavior, and other.'
|
||||
},
|
||||
rss: {
|
||||
name: 'RSS',
|
||||
icon: 'rss_feed',
|
||||
desc: 'RSS configuration, URLs, quality, and options.'
|
||||
},
|
||||
torrent: {
|
||||
name: 'Torrent',
|
||||
icon: 'hub',
|
||||
desc: 'Torrent client settings, and preferences.'
|
||||
},
|
||||
discord: {
|
||||
name: 'Discord',
|
||||
icon: 'settings',
|
||||
desc: 'Discord Rich Presence settings.'
|
||||
},
|
||||
interface: {
|
||||
name: 'Interface',
|
||||
icon: 'settings',
|
||||
desc: 'Interface settings.'
|
||||
},
|
||||
changelog: {
|
||||
name: 'Changelog',
|
||||
icon: 'list',
|
||||
desc: 'Version change log.'
|
||||
}
|
||||
}
|
||||
$: IPC.emit('discord_status', $settings.showDetailsInRPC)
|
||||
function handleFolder () {
|
||||
IPC.emit('dialog')
|
||||
}
|
||||
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)
|
||||
toast.error('File Error', {
|
||||
description: `${error.message}<br>Try using a different font.`,
|
||||
duration: 8000
|
||||
})
|
||||
}
|
||||
}
|
||||
async function importSettings () {
|
||||
localStorage.setItem('settings', await navigator.clipboard.readText())
|
||||
location.reload()
|
||||
}
|
||||
function exportSettings () {
|
||||
navigator.clipboard.writeText(localStorage.getItem('settings'))
|
||||
toast('Copied to clipboard', {
|
||||
description: 'Copied settings to clipboard',
|
||||
duration: 5000
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<Tabs>
|
||||
<div class='d-flex w-full h-full position-relative'>
|
||||
<div class='d-flex flex-column h-full w-300 bg-dark position-relative'>
|
||||
<div class='px-20 py-15 font-size-20 font-weight-semi-bold border-bottom root'>Settings</div>
|
||||
{#each Object.values(groups) as group}
|
||||
<TabLabel>
|
||||
<div class='px-20 py-15 d-flex root'>
|
||||
<span class='material-symbols-outlined font-size-24 pr-10'>{group.icon}</span>
|
||||
<div>
|
||||
<div class='font-weight-bold font-size-16'>{group.name}</div>
|
||||
<div class='font-size-12'>{group.desc}</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabLabel>
|
||||
{/each}
|
||||
<button
|
||||
use:click={() => IPC.emit('open', 'https://github.com/sponsors/ThaUnknown/')}
|
||||
class='btn btn-primary mx-20 mt-auto'
|
||||
type='button'>
|
||||
Donate
|
||||
</button>
|
||||
<button
|
||||
use:click={importSettings}
|
||||
class='btn btn-primary mx-20 mt-10'
|
||||
type='button'>
|
||||
Import Settings From Clipboard
|
||||
</button>
|
||||
<button
|
||||
use:click={exportSettings}
|
||||
class='btn btn-primary mx-20 mt-10'
|
||||
type='button'>
|
||||
Export Settings To Clipboard
|
||||
</button>
|
||||
<button
|
||||
use:click={checkUpdate}
|
||||
class='btn btn-primary mx-20 mt-10'
|
||||
type='button'>
|
||||
Check For Updates
|
||||
</button>
|
||||
<button
|
||||
use:click={resetSettings}
|
||||
class='btn btn-danger mx-20 mt-10'
|
||||
type='button'
|
||||
data-toggle='tooltip'
|
||||
data-placement='top'
|
||||
data-title='Restores All Settings Back To Their Recommended Defaults'>
|
||||
Restore Defaults
|
||||
</button>
|
||||
<p class='text-muted px-20 py-10 m-0'>Restart may be required for some settings to take effect.</p>
|
||||
<p class='text-muted px-20 pb-10 m-0'>If you don't know what shit does, use default settings.</p>
|
||||
<p class='text-muted px-20 m-0 mb-20'>v{version} {platformMap[window.version.platform]} {window.version.arch}</p>
|
||||
</div>
|
||||
<div class='h-full w-full overflow-y-auto'>
|
||||
<Tab>
|
||||
<div class='root p-20 m-20'>
|
||||
<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-symbols-outlined 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='custom-switch mb-10 pl-10 font-size-16 w-300'
|
||||
data-toggle='tooltip'
|
||||
data-placement='bottom'
|
||||
data-title="Automatically Finds Fonts That Are Missing From A Video's Subtitles">
|
||||
<input type='checkbox' id='player-missingFont' bind:checked={$settings.missingFont} />
|
||||
<label for='player-missingFont'>Find Missing Fonts</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-symbols-outlined 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'
|
||||
data-placement='bottom'
|
||||
data-title='Automatically Starts Playing Next Episode When A Video Ends'>
|
||||
<input type='checkbox' id='player-autoplay' bind:checked={$settings.playerAutoplay} />
|
||||
<label for='player-autoplay'>Autoplay Next Episode</label>
|
||||
</div>
|
||||
<div
|
||||
class='custom-switch mb-10 pl-10 font-size-16 w-300'
|
||||
data-toggle='tooltip'
|
||||
data-placement='bottom'
|
||||
data-title='Pauses/Resumes Video Playback When Tabbing In/Out Of The App'>
|
||||
<input type='checkbox' id='player-pause' bind:checked={$settings.playerPause} />
|
||||
<label for='player-pause'>Pause When Tabbing Out</label>
|
||||
</div>
|
||||
<div
|
||||
class='custom-switch mb-10 pl-10 font-size-16 w-300'
|
||||
data-toggle='tooltip'
|
||||
data-placement='bottom'
|
||||
data-title='Disables Blur When Rendering Subtitles Reducing Lag, Will Cause Text And Subtitle Edges To Appear Sharper'>
|
||||
<input type='checkbox' id='player-sub-blur' bind:checked={$settings.disableSubtitleBlur} />
|
||||
<label for='player-sub-blur'>Fast Subtitle Rendering</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-symbols-outlined 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'
|
||||
data-placement='bottom'
|
||||
data-title='Automatically Marks Episodes As Complete On AniList When You Finish Watching Them, Requires AniList Login'>
|
||||
<input type='checkbox' id='player-autocomplete' bind:checked={$settings.playerAutocomplete} />
|
||||
<label for='player-autocomplete'>Autocomplete Episodes</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-symbols-outlined mr-10 font-size-30'>translate</div>
|
||||
Language Settings
|
||||
</div>
|
||||
</div>
|
||||
<div class='input-group mb-10 w-400 form-control-lg' data-toggle='tooltip' data-placement='top' data-title='What Subtitle Language To Automatically Select, If Not Found Defaults To English'>
|
||||
<div class='input-group-prepend'>
|
||||
<span class='input-group-text w-250 justify-content-center'>Preferred Subtitle Language</span>
|
||||
</div>
|
||||
<select class='form-control form-control-lg' bind:value={$settings.subtitleLanguage}>
|
||||
<option value=''>None</option>
|
||||
<option value='eng' selected>English</option>
|
||||
<option value='jpn'>Japanese</option>
|
||||
<option value='chi'>Chinese</option>
|
||||
<option value='por'>Portuguese</option>
|
||||
<option value='spa'>Spanish</option>
|
||||
<option value='ger'>German</option>
|
||||
<option value='pol'>Polish</option>
|
||||
<option value='cze'>Czech</option>
|
||||
<option value='dan'>Danish</option>
|
||||
<option value='gre'>Greek</option>
|
||||
<option value='fin'>Finnish</option>
|
||||
<option value='fre'>French</option>
|
||||
<option value='hun'>Hungarian</option>
|
||||
<option value='ita'>Italian</option>
|
||||
<option value='kor'>Korean</option>
|
||||
<option value='dut'>Dutch</option>
|
||||
<option value='nor'>Norwegian</option>
|
||||
<option value='rum'>Romanian</option>
|
||||
<option value='rus'>Russian</option>
|
||||
<option value='slo'>Slovak</option>
|
||||
<option value='swe'>Swedish</option>
|
||||
<option value='ara'>Arabic</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class='input-group mb-10 w-400 form-control-lg' data-toggle='tooltip' data-placement='top' data-title='What Audio Language To Automatically Select, If Not Found Defaults To Japanese'>
|
||||
<div class='input-group-prepend'>
|
||||
<span class='input-group-text w-250 justify-content-center'>Preferred Audio Language</span>
|
||||
</div>
|
||||
<select class='form-control form-control-lg' bind:value={$settings.audioLanguage}>
|
||||
<option value='eng'>English</option>
|
||||
<option value='jpn' selected>Japanese</option>
|
||||
<option value='chi'>Chinese</option>
|
||||
<option value='por'>Portuguese</option>
|
||||
<option value='spa'>Spanish</option>
|
||||
<option value='ger'>German</option>
|
||||
<option value='pol'>Polish</option>
|
||||
<option value='cze'>Czech</option>
|
||||
<option value='dan'>Danish</option>
|
||||
<option value='gre'>Greek</option>
|
||||
<option value='fin'>Finnish</option>
|
||||
<option value='fre'>French</option>
|
||||
<option value='hun'>Hungarian</option>
|
||||
<option value='ita'>Italian</option>
|
||||
<option value='kor'>Korean</option>
|
||||
<option value='dut'>Dutch</option>
|
||||
<option value='nor'>Norwegian</option>
|
||||
<option value='rum'>Romanian</option>
|
||||
<option value='rus'>Russian</option>
|
||||
<option value='slo'>Slovak</option>
|
||||
<option value='swe'>Swedish</option>
|
||||
<option value='ara'>Arabic</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab>
|
||||
<div class='root p-20 m-20'>
|
||||
{#each $settings.rssFeedsNew as _, i}
|
||||
<div
|
||||
class='input-group mb-10 w-700 form-control-lg'
|
||||
data-toggle='tooltip'
|
||||
data-placement='bottom'
|
||||
data-title='What RSS Feed To Fetch Releases From, Allows For Custom CORS Enabled Feeds'>
|
||||
<div class='input-group-prepend'>
|
||||
<span class='input-group-text w-100 justify-content-center'>Feed</span>
|
||||
</div>
|
||||
<input type='text' class='form-control form-control-lg w-150 flex-reset' placeholder='New Releases' autocomplete='off' bind:value={$settings.rssFeedsNew[i][0]} />
|
||||
<input id='rss-feed-{i}' type='text' list='rss-feed-list-{i}' class='w-400 form-control form-control-lg' placeholder={$settings.toshoURL + 'rss2?qx=1&q="[SubsPlease] "'} autocomplete='off' bind:value={$settings.rssFeedsNew[i][1]} />
|
||||
<datalist id='rss-feed-list-{i}'>
|
||||
<option value='SubsPlease'>{$settings.toshoURL + 'rss2?qx=1&q="[SubsPlease] "'}</option>
|
||||
<option value='NC-Raws'>{$settings.toshoURL + 'rss2?qx=1&q="[NC-Raws] "'}</option>
|
||||
<option value='Erai-raws [Multi-Sub]'>{$settings.toshoURL + 'rss2?qx=1&q="[Erai-raws] "'}</option>
|
||||
</datalist>
|
||||
<div class='input-group-append'>
|
||||
<button type='button' use:click={() => { $settings.rssFeedsNew.splice(i, 1); $settings.rssFeedsNew = $settings.rssFeedsNew }} class='btn btn-danger btn-lg input-group-append'>Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
<div class='input-group input-group-lg form-control-lg mb-10 w-500'>
|
||||
<button type='button' use:click={() => { $settings.rssFeedsNew[$settings.rssFeedsNew.length] = ['New Releases', null] }} class='btn btn-lg btn-primary mb-10'>Add Feed</button>
|
||||
</div>
|
||||
<div class='input-group mb-10 w-300 form-control-lg' data-toggle='tooltip' data-placement='top' data-title='What Quality To Find Torrents In'>
|
||||
<div class='input-group-prepend'>
|
||||
<span class='input-group-text w-100 justify-content-center'>Quality</span>
|
||||
</div>
|
||||
<select class='form-control form-control-lg' bind:value={$settings.rssQuality}>
|
||||
<option value='1080' selected>1080p</option>
|
||||
<option value='720'>720p</option>
|
||||
<option value='480||540'>SD</option>
|
||||
<option value="">None</option>
|
||||
</select>
|
||||
</div>
|
||||
<div
|
||||
class='custom-switch mb-10 pl-10 font-size-16 w-300'
|
||||
data-toggle='tooltip'
|
||||
data-placement='bottom'
|
||||
data-title='Skips The Torrent Selection Popup, Might Lead To Unwanted Videos Being
|
||||
Played'>
|
||||
<input type='checkbox' id='rss-autoplay' bind:checked={$settings.rssAutoplay} />
|
||||
<label for='rss-autoplay'>Auto-Play Torrents</label>
|
||||
</div>
|
||||
<div
|
||||
class='custom-switch mb-10 pl-10 font-size-16 w-300'
|
||||
data-toggle='tooltip'
|
||||
data-placement='bottom'
|
||||
data-title='Enables DNS Over HTTPS, Useful If Your ISP Blocks Certain Domains'>
|
||||
<input type='checkbox' id='rss-dohtoggle' bind:checked={$settings.enableDoH} />
|
||||
<label for='rss-dohtoggle'>Enable DoH</label>
|
||||
</div>
|
||||
<div
|
||||
class='input-group input-group-lg form-control-lg mb-10 w-500'
|
||||
data-toggle='tooltip'
|
||||
data-placement='bottom'
|
||||
data-title='What URL To Use For DoH'>
|
||||
<div class='input-group-prepend'>
|
||||
<span class='input-group-text w-150 justify-content-center'>DoH URL</span>
|
||||
</div>
|
||||
<input type='text' class='form-control' bind:value={$settings.doHURL} placeholder={defaults.doHURL} />
|
||||
</div>
|
||||
<div
|
||||
class='input-group input-group-lg form-control-lg mb-10 w-500'
|
||||
data-toggle='tooltip'
|
||||
data-placement='bottom'
|
||||
data-title='URL For The Cat, Change If You Want To Use Your Own Proxy'>
|
||||
<div class='input-group-prepend'>
|
||||
<span class='input-group-text w-150 justify-content-center'>Tosho URL</span>
|
||||
</div>
|
||||
<input type='text' class='form-control' bind:value={$settings.toshoURL} placeholder={defaults.toshoURL} />
|
||||
</div>
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab>
|
||||
<div class='root p-20 m-20'>
|
||||
<div
|
||||
class='input-group input-group-lg form-control-lg mb-10 w-500'
|
||||
data-toggle='tooltip'
|
||||
data-placement='bottom'
|
||||
data-title='Path To Folder Which To Use To Store Torrent Files'>
|
||||
<div class='input-group-prepend'>
|
||||
<button type='button' use:click={handleFolder} class='btn btn-primary input-group-append'>Select Folder</button>
|
||||
</div>
|
||||
<input type='text' class='form-control' bind:value={$settings.torrentPath} placeholder='Folder Path' />
|
||||
</div>
|
||||
<div
|
||||
class='input-group w-300 form-control-lg mb-10'
|
||||
data-toggle='tooltip'
|
||||
data-placement='bottom'
|
||||
data-title='Download/Upload Speed Limit For Torrents, Higher Values Increase CPU Usage'>
|
||||
<div class='input-group-prepend'>
|
||||
<span class='input-group-text w-150 justify-content-center'>Max Speed</span>
|
||||
</div>
|
||||
<input type='number' bind:value={$settings.torrentSpeed} min='0' max='50' class='form-control text-right form-control-lg' />
|
||||
<div class='input-group-append'>
|
||||
<span class='input-group-text'>MB/s</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class='input-group w-300 form-control-lg mb-10'
|
||||
data-toggle='tooltip'
|
||||
data-placement='bottom'
|
||||
data-title='Max Amount Of Connections Per Torrent'>
|
||||
<div class='input-group-prepend'>
|
||||
<span class='input-group-text w-200 justify-content-center'>Max Connections</span>
|
||||
</div>
|
||||
<input type='number' bind:value={$settings.maxConns} min='1' max='512' class='form-control text-right form-control-lg' />
|
||||
</div>
|
||||
<div
|
||||
class='input-group w-300 form-control-lg mb-10'
|
||||
data-toggle='tooltip'
|
||||
data-placement='bottom'
|
||||
data-title='Port Used For DHT Connections. 0 Is Automatic'>
|
||||
<div class='input-group-prepend'>
|
||||
<span class='input-group-text w-150 justify-content-center'>DHT Port</span>
|
||||
</div>
|
||||
<input type='number' bind:value={$settings.dhtPort} min='0' max='65536' class='form-control text-right form-control-lg' />
|
||||
</div>
|
||||
<div
|
||||
class='input-group w-300 form-control-lg mb-10'
|
||||
data-toggle='tooltip'
|
||||
data-placement='bottom'
|
||||
data-title='Port Used For Torrent Connections. 0 Is Automatic'>
|
||||
<div class='input-group-prepend'>
|
||||
<span class='input-group-text w-150 justify-content-center'>Torrent Port</span>
|
||||
</div>
|
||||
<input type='number' bind:value={$settings.torrentPort} min='0' max='65536' class='form-control text-right form-control-lg' />
|
||||
</div>
|
||||
<div
|
||||
class='custom-switch mb-10 pl-10 font-size-16 w-300'
|
||||
data-toggle='tooltip'
|
||||
data-placement='bottom'
|
||||
data-title="Doesn't Delete Files Of Old Torrents When A New Torrent Is Played">
|
||||
<input type='checkbox' id='torrent-persist' bind:checked={$settings.torrentPersist} />
|
||||
<label for='torrent-persist'>Persist Files</label>
|
||||
</div>
|
||||
<div
|
||||
class='custom-switch mb-10 pl-10 font-size-16 w-300'
|
||||
data-toggle='tooltip'
|
||||
data-placement='bottom'
|
||||
data-title='Disables Distributed Hash Tables For Use In Private Trackers To Improve Privacy'>
|
||||
<input type='checkbox' id='torrent-dht' bind:checked={$settings.torrentDHT} />
|
||||
<label for='torrent-dht'>Disable DHT</label>
|
||||
</div>
|
||||
<div
|
||||
class='custom-switch mb-10 pl-10 font-size-16 w-300'
|
||||
data-toggle='tooltip'
|
||||
data-placement='bottom'
|
||||
data-title='Disables Peer Exchange For Use In Private Trackers To Improve Privacy'>
|
||||
<input type='checkbox' id='torrent-pex' bind:checked={$settings.torrentPeX} />
|
||||
<label for='torrent-pex'>Disable PeX</label>
|
||||
</div>
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab>
|
||||
<div class='root p-20 m-20'>
|
||||
<div
|
||||
class='custom-switch mb-10 pl-10 font-size-16 w-300'
|
||||
data-toggle='tooltip'
|
||||
data-placement='bottom'
|
||||
data-title='Shows Currently Played Anime And Episode in Discord Rich Presence.'>
|
||||
<input type='checkbox' id='rpc-details' bind:checked={$settings.showDetailsInRPC} />
|
||||
<label for='rpc-details'>Show Details in Discord Rich Presence</label>
|
||||
</div>
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab>
|
||||
<div class='root p-20 m-20'>
|
||||
<div
|
||||
class='custom-switch mb-10 pl-10 font-size-16 w-300'
|
||||
data-toggle='tooltip'
|
||||
data-placement='bottom'
|
||||
data-title='Enables Smooth Scrolling For Long Vertical Containers. Impacts Performance.'>
|
||||
<input type='checkbox' id='smooth-scroll' bind:checked={$settings.smoothScroll} />
|
||||
<label for='smooth-scroll'>Enable Smooth Scrolling</label>
|
||||
</div>
|
||||
<div
|
||||
class='custom-switch mb-10 pl-10 font-size-16 w-300'
|
||||
data-toggle='tooltip'
|
||||
data-placement='bottom'
|
||||
data-title='Enables Sidebar Hover Animations'>
|
||||
<input type='checkbox' id='disable-sidebar' bind:checked={$settings.expandingSidebar} />
|
||||
<label for='disable-sidebar'>Enable Sidebar Animations</label>
|
||||
</div>
|
||||
<div class='form-group mb-10 pl-10 font-size-16 w-500'
|
||||
data-toggle='tooltip'
|
||||
data-placement='bottom'
|
||||
data-title='CSS Variables Used For Custom Themes'>
|
||||
<label for='css-variables'>CSS Variables</label>
|
||||
<textarea class='form-control' id='css-variables' placeholder='--accent-color: #e5204c;' bind:value={$variables} />
|
||||
</div>
|
||||
<div class='input-group mb-10 w-400 form-control-lg' data-toggle='tooltip' data-placement='top' data-title="What ANGLE Backend To Use. DON'T CHANGE WITHOUT REASON! On Some Windows Machines D3D9 Might Help With Flicker.">
|
||||
<div class='input-group-prepend'>
|
||||
<span class='input-group-text w-200 justify-content-center'>ANGLE Backend</span>
|
||||
</div>
|
||||
<select class='form-control form-control-lg' bind:value={$settings.angle} on:change={updateAngle}>
|
||||
<option value='default' selected>Default</option>
|
||||
<option value='d3d9'>D3D9</option>
|
||||
<option value='d3d11'>D3D11</option>
|
||||
<option value='warp'>Warp [Software D3D11]</option>
|
||||
<option value='gl'>GL</option>
|
||||
<option value='gles'>GLES</option>
|
||||
<option value='swiftshader'>SwiftShader</option>
|
||||
<option value='vulkan'>Vulkan</option>
|
||||
<option value='metal'>Metal</option>
|
||||
</select>
|
||||
</div>
|
||||
<HomeSections bind:homeSections={$settings.homeSections} />
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab>
|
||||
<div class='root m-20 px-20 pre-wrap'>
|
||||
{#await changeLog}
|
||||
<h1 class='font-weight-bold'>Loading changelog...</h1>
|
||||
{:then changes}
|
||||
{#each changes as { body, version }}
|
||||
<h1 class='font-weight-bold mt-20'>{version}</h1>{body}
|
||||
{/each}
|
||||
{/await}
|
||||
</div>
|
||||
</Tab>
|
||||
</div>
|
||||
</div>
|
||||
</Tabs>
|
||||
|
||||
<style>
|
||||
select.form-control:invalid {
|
||||
color: var(--dm-input-placeholder-text-color);
|
||||
}
|
||||
.pre-wrap {
|
||||
white-space: pre-wrap
|
||||
}
|
||||
</style>
|
||||
54
common/views/Settings/AppSettings.svelte
Normal file
54
common/views/Settings/AppSettings.svelte
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<script context='module'>
|
||||
import { click } from '@/modules/click.js'
|
||||
import { toast } from 'svelte-sonner'
|
||||
import { resetSettings } from '@/modules/settings.js'
|
||||
import IPC from '@/modules/ipc.js'
|
||||
import { SUPPORTS } from '@/modules/support.js'
|
||||
|
||||
async function importSettings () {
|
||||
localStorage.setItem('settings', await navigator.clipboard.readText())
|
||||
location.reload()
|
||||
}
|
||||
function exportSettings () {
|
||||
navigator.clipboard.writeText(localStorage.getItem('settings'))
|
||||
toast('Copied to clipboard', {
|
||||
description: 'Copied settings to clipboard',
|
||||
duration: 5000
|
||||
})
|
||||
}
|
||||
function checkUpdate () {
|
||||
IPC.emit('update')
|
||||
}
|
||||
setInterval(checkUpdate, 1200000)
|
||||
</script>
|
||||
<div class='d-inline-flex flex-column'>
|
||||
<button
|
||||
use:click={importSettings}
|
||||
class='btn btn-primary mx-20 mt-10'
|
||||
type='button'>
|
||||
Import Settings From Clipboard
|
||||
</button>
|
||||
<button
|
||||
use:click={exportSettings}
|
||||
class='btn btn-primary mx-20 mt-10'
|
||||
type='button'>
|
||||
Export Settings To Clipboard
|
||||
</button>
|
||||
{#if SUPPORTS.update}
|
||||
<button
|
||||
use:click={checkUpdate}
|
||||
class='btn btn-primary mx-20 mt-10'
|
||||
type='button'>
|
||||
Check For Updates
|
||||
</button>
|
||||
{/if}
|
||||
<button
|
||||
use:click={resetSettings}
|
||||
class='btn btn-danger mx-20 mt-10'
|
||||
type='button'
|
||||
data-toggle='tooltip'
|
||||
data-placement='top'
|
||||
data-title='Restores All Settings Back To Their Recommended Defaults'>
|
||||
Restore Default Settings
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -22,67 +22,57 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<div class='position-relative'>
|
||||
<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-symbols-outlined mr-10 font-size-30'>list</div>
|
||||
Home Sections Order
|
||||
{#if mouseYCoordinate}
|
||||
<div
|
||||
class='input-group mb-10 ghost w-full'
|
||||
style='top: {mouseYCoordinate + distanceTopGrabbedVsPointer}px;'>
|
||||
<div class='input-group-prepend'>
|
||||
<span class='input-group-text d-flex justify-content-center px-5 material-symbols-outlined font-size-20'>swap_vert</span>
|
||||
</div>
|
||||
<select class='form-control' value={draggingItem}>
|
||||
{#each allowedHomeSections as section}
|
||||
<option>{section}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<div class='input-group-append'>
|
||||
<button type='button' class='btn btn-danger input-group-append px-5 material-symbols-outlined font-size-20'>delete</button>
|
||||
</div>
|
||||
</div>
|
||||
{#if mouseYCoordinate}
|
||||
<div
|
||||
class='input-group mb-10 form-control-lg ghost w-full'
|
||||
style='top: {mouseYCoordinate + distanceTopGrabbedVsPointer}px;'>
|
||||
<div class='input-group-prepend'>
|
||||
<span class='input-group-text w-100 justify-content-center'>Feed</span>
|
||||
</div>
|
||||
<select class='form-control form-control-lg' value={draggingItem}>
|
||||
{#each allowedHomeSections as section}
|
||||
<option>{section}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<div class='input-group-append'>
|
||||
<button type='button' class='btn btn-danger btn-lg input-group-append'>Remove</button>
|
||||
</div>
|
||||
{/if}
|
||||
{#each homeSections as item, index}
|
||||
<div
|
||||
class='input-group mb-10'
|
||||
class:tp={draggingItem === item}
|
||||
draggable='true'
|
||||
role='menuitem'
|
||||
tabindex='0'
|
||||
on:dragstart={({ clientY, target }) => {
|
||||
mouseYCoordinate = clientY
|
||||
draggingItem = item
|
||||
draggingItemIndex = index
|
||||
distanceTopGrabbedVsPointer = target.offsetTop - clientY
|
||||
}}
|
||||
on:drag={e => { mouseYCoordinate = e.clientY }}
|
||||
on:dragover={() => { hoveredItemIndex = index }}
|
||||
on:dragend={() => {
|
||||
mouseYCoordinate = null
|
||||
draggingItem = null
|
||||
hoveredItemIndex = null
|
||||
}}>
|
||||
<div class='input-group-prepend grab'>
|
||||
<span class='input-group-text d-flex justify-content-center px-5 material-symbols-outlined font-size-20'>swap_vert</span>
|
||||
</div>
|
||||
{/if}
|
||||
{#each homeSections as item, index}
|
||||
<div
|
||||
class='input-group mb-10 form-control-lg'
|
||||
class:tp={draggingItem === item}
|
||||
draggable='true'
|
||||
role='menuitem'
|
||||
tabindex='0'
|
||||
on:dragstart={({ clientY, target }) => {
|
||||
mouseYCoordinate = clientY
|
||||
draggingItem = item
|
||||
draggingItemIndex = index
|
||||
distanceTopGrabbedVsPointer = target.offsetTop - clientY
|
||||
}}
|
||||
on:drag={e => { mouseYCoordinate = e.clientY }}
|
||||
on:dragover={() => { hoveredItemIndex = index }}
|
||||
on:dragend={() => {
|
||||
mouseYCoordinate = null
|
||||
draggingItem = null
|
||||
hoveredItemIndex = null
|
||||
}}>
|
||||
<div class='input-group-prepend grab'>
|
||||
<span class='input-group-text w-100 justify-content-center'>Feed</span>
|
||||
</div>
|
||||
<select class='form-control form-control-lg' bind:value={homeSections[index]}>
|
||||
{#each allowedHomeSections as section}
|
||||
<option>{section}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<div class='input-group-append'>
|
||||
<button type='button' use:click={() => { homeSections.splice(index, 1); homeSections = homeSections }} class='btn btn-danger btn-lg input-group-append'>Remove</button>
|
||||
</div>
|
||||
<select class='form-control bg-dark w-300 mw-full' bind:value={homeSections[index]}>
|
||||
{#each allowedHomeSections as section}
|
||||
<option>{section}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<div class='input-group-append'>
|
||||
<button type='button' use:click={() => { homeSections.splice(index, 1); homeSections = homeSections }} class='btn btn-danger input-group-append px-5 material-symbols-outlined font-size-20'>delete</button>
|
||||
</div>
|
||||
{/each}
|
||||
<div class='input-group input-group-lg form-control-lg mb-10 w-500'>
|
||||
<button type='button' use:click={() => { homeSections[homeSections.length] = 'Trending Now' }} class='btn btn-lg btn-primary mb-10'>Add Section</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
<button type='button' use:click={() => { homeSections[homeSections.length] = 'Trending Now' }} class='btn btn-primary'>Add Section</button>
|
||||
|
||||
<style>
|
||||
.ghost {
|
||||
|
|
|
|||
88
common/views/Settings/InterfaceSettings.svelte
Normal file
88
common/views/Settings/InterfaceSettings.svelte
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
<script>
|
||||
import { variables } from '@/modules/themes.js'
|
||||
import { click } from '@/modules/click.js'
|
||||
import HomeSections from './HomeSectionsSettings.svelte'
|
||||
import IPC from '@/modules/ipc.js'
|
||||
import SettingCard from './SettingCard.svelte'
|
||||
import { SUPPORTS } from '@/modules/support.js'
|
||||
function updateAngle () {
|
||||
IPC.emit('angle', settings.value.angle)
|
||||
}
|
||||
export let settings
|
||||
</script>
|
||||
|
||||
{#if SUPPORTS.discord}
|
||||
<h4 class='mb-10 font-weight-bold'>Rich Pressence Settings</h4>
|
||||
<SettingCard title='Show Details in Discord Rich Presence' description='Shows currently played anime and episode in Discord rich presence.'>
|
||||
<div class='custom-switch'>
|
||||
<input type='checkbox' id='rpc-details' bind:checked={settings.showDetailsInRPC} />
|
||||
<label for='rpc-details'>{settings.showDetailsInRPC ? 'On' : 'Off'}</label>
|
||||
</div>
|
||||
</SettingCard>
|
||||
{/if}
|
||||
|
||||
<h4 class='mb-10 font-weight-bold'>Interface Settings</h4>
|
||||
<SettingCard title='Enable Smooth Scrolling' description='Enables smooth scrolling for vertical containers. Turning this off might remove jitter when scrolling on devices without a GPU.'>
|
||||
<div class='custom-switch'>
|
||||
<input type='checkbox' id='smooth-scroll' bind:checked={settings.smoothScroll} />
|
||||
<label for='smooth-scroll'>{settings.smoothScroll ? 'On' : 'Off'}</label>
|
||||
</div>
|
||||
</SettingCard>
|
||||
<SettingCard title='Enable Sidebar Animation' description='Enables the sidebar expand hover animation.'>
|
||||
<div class='custom-switch'>
|
||||
<input type='checkbox' id='disable-sidebar' bind:checked={settings.expandingSidebar} />
|
||||
<label for='disable-sidebar'>{settings.expandingSidebar ? 'On' : 'Off'}</label>
|
||||
</div>
|
||||
</SettingCard>
|
||||
<SettingCard title='CSS Variables' description='Used for custom themes. Can change colors, sizes, spacing and more. Best way to discover variables is to use the built-in devtools via Ctrl+Shift+I or F12.'>
|
||||
<textarea class='form-control w-500 mw-full bg-dark' placeholder='--accent-color: #e5204c;' bind:value={$variables} />
|
||||
</SettingCard>
|
||||
|
||||
{#if SUPPORTS.angle}
|
||||
<h4 class='mb-10 font-weight-bold'>Rendering Settings</h4>
|
||||
<SettingCard title='ANGLE Backend' description="What ANGLE backend to use for rendering. DON'T CHANGE WITHOUT REASON! On some Windows machines D3D9 might help with flicker. Changing this setting to something your device doesn't support might prevent Miru from opening which will require a full reinstall. While Vulkan is an available option it might not be fully supported on Linux.">
|
||||
<select class='form-control bg-dark w-300 mw-full' bind:value={settings.angle} on:change={updateAngle}>
|
||||
<option value='default' selected>Default</option>
|
||||
<option value='d3d9'>D3D9</option>
|
||||
<option value='d3d11'>D3D11</option>
|
||||
<option value='warp'>Warp [Software D3D11]</option>
|
||||
<option value='gl'>GL</option>
|
||||
<option value='gles'>GLES</option>
|
||||
<option value='swiftshader'>SwiftShader</option>
|
||||
<option value='vulkan'>Vulkan</option>
|
||||
<option value='metal'>Metal</option>
|
||||
</select>
|
||||
</SettingCard>
|
||||
{/if}
|
||||
|
||||
<h4 class='mb-10 font-weight-bold'>Home Screen Settings</h4>
|
||||
<SettingCard title='RSS Feeds' description={'RSS feeds to display on the home screen. This needs to be a CORS enabled URL to a Nyaa or Tosho like RSS feed which cotains either an "infoHash" or "enclosure" tag.\nThis only shows the releases on the home screen, it doesn\'t automatically download the content.\nSince the feeds only provide the name of the file, Miru might not always detect the anime correctly!\nSome presets for popular groups are already provided as an example, custom feeds require the FULL URL.'}>
|
||||
<div>
|
||||
{#each settings.rssFeedsNew as _, i}
|
||||
<div class='input-group mb-10 w-500 mw-full'>
|
||||
<input type='text' class='form-control w-150 mw-full bg-dark flex-reset' placeholder='New Releases' autocomplete='off' bind:value={settings.rssFeedsNew[i][0]} />
|
||||
<input id='rss-feed-{i}' type='text' list='rss-feed-list-{i}' class='w-400 form-control mw-full bg-dark' placeholder={settings.toshoURL + 'rss2?qx=1&q="[SubsPlease] "'} autocomplete='off' bind:value={settings.rssFeedsNew[i][1]} />
|
||||
<datalist id='rss-feed-list-{i}'>
|
||||
<option value='SubsPlease'>{settings.toshoURL + 'rss2?qx=1&q="[SubsPlease] "'}</option>
|
||||
<option value='NC-Raws'>{settings.toshoURL + 'rss2?qx=1&q="[NC-Raws] "'}</option>
|
||||
<option value='Erai-raws [Multi-Sub]'>{settings.toshoURL + 'rss2?qx=1&q="[Erai-raws] "'}</option>
|
||||
</datalist>
|
||||
<div class='input-group-append'>
|
||||
<button type='button' use:click={() => { settings.rssFeedsNew.splice(i, 1); settings.rssFeedsNew = settings.rssFeedsNew }} class='btn btn-danger input-group-append px-5 material-symbols-outlined font-size-20'>delete</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
<button type='button' use:click={() => { settings.rssFeedsNew[settings.rssFeedsNew.length] = ['New Releases', null] }} class='btn btn-primary mb-10'>Add Feed</button>
|
||||
</div>
|
||||
</SettingCard>
|
||||
<SettingCard title='Sections And Order' description="Sections and their order on the home screen, if you want more RSS feeds to show up here, create them first in the RSS feed list. Adding many multiple normal lists doesn't impact performance, but adding a lot of RSS feeds will impact app startup times. Drag/drop these sections to re-order them.">
|
||||
<div>
|
||||
<HomeSections bind:homeSections={settings.homeSections} />
|
||||
</div>
|
||||
</SettingCard>
|
||||
|
||||
<style>
|
||||
textarea {
|
||||
min-height: 6.6rem;
|
||||
}
|
||||
</style>
|
||||
127
common/views/Settings/PlayerSettings.svelte
Normal file
127
common/views/Settings/PlayerSettings.svelte
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
<script>
|
||||
import { toast } from 'svelte-sonner'
|
||||
import FontSelect from 'simple-font-select'
|
||||
import SettingCard from './SettingCard.svelte'
|
||||
export let settings
|
||||
|
||||
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)
|
||||
toast.error('File Error', {
|
||||
description: `${error.message}\n Try using a different font.`,
|
||||
duration: 8000
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if ('queryLocalFonts' in self)}
|
||||
<h4 class='mb-10 font-weight-bold'>Subtitle Settings</h4>
|
||||
<SettingCard title='Default Subtitle Font' description={"What font to use when the current loaded video doesn't provide or specify one.\nThis uses fonts installed on your OS."}>
|
||||
<FontSelect class='form-control bg-dark w-300 mw-full' on:change={changeFont} value={settings.font?.value} />
|
||||
</SettingCard>
|
||||
<SettingCard title='Find Missing Subtitle Fonts' description="Automatically finds and loads fonts that are missing from a video's subtitles.">
|
||||
<div class='custom-switch'>
|
||||
<input type='checkbox' id='player-missingFont' bind:checked={settings.missingFont} />
|
||||
<label for='player-missingFont'>{settings.missingFont ? 'On' : 'Off'}</label>
|
||||
</div>
|
||||
</SettingCard>
|
||||
<SettingCard title='Fast Subtitle Rendering' description='Disables blur when rendering subtitles reducing lag. Will cause text and subtitle edges to appear sharper and in rare cases might break styling. If you want better rendering speeds without sacrificing accuracy lower the render resolution limit.'>
|
||||
<div class='custom-switch'>
|
||||
<input type='checkbox' id='player-sub-blur' bind:checked={settings.disableSubtitleBlur} />
|
||||
<label for='player-sub-blur'>{settings.disableSubtitleBlur ? 'On' : 'Off'}</label>
|
||||
</div>
|
||||
</SettingCard>
|
||||
<SettingCard title='Subtitle Render Resolution Limit' description="Max resolution to render subtitles at. If your resolution is higher than this setting the subtitles will be upscaled lineary. This will GREATLY improve rendering speeds for complex typesetting for slower devices. It's best to lower this on mobile devices which often have high pixel density where their effective resolution might be ~1440p while having small screens and slow processors.">
|
||||
<select class='form-control bg-dark w-300 mw-full' bind:value={settings.subtitleRenderHeight}>
|
||||
<option value='0' selected>None</option>
|
||||
<option value='1440'>1440p</option>
|
||||
<option value='1080'>1080p</option>
|
||||
<option value='720'>720p</option>
|
||||
<option value='480'>480p</option>
|
||||
</select>
|
||||
</SettingCard>
|
||||
{/if}
|
||||
|
||||
<h4 class='mb-10 font-weight-bold'>Language Settings</h4>
|
||||
<SettingCard title='Preferred Subtitle Language' description="What subtitle language to automatically select when a video is loaded if it exists. This won't find torrents with this language automatically. If not found defaults to English.">
|
||||
<select class='form-control bg-dark w-300 mw-full' bind:value={settings.subtitleLanguage}>
|
||||
<option value=''>None</option>
|
||||
<option value='eng' selected>English</option>
|
||||
<option value='jpn'>Japanese</option>
|
||||
<option value='chi'>Chinese</option>
|
||||
<option value='por'>Portuguese</option>
|
||||
<option value='spa'>Spanish</option>
|
||||
<option value='ger'>German</option>
|
||||
<option value='pol'>Polish</option>
|
||||
<option value='cze'>Czech</option>
|
||||
<option value='dan'>Danish</option>
|
||||
<option value='gre'>Greek</option>
|
||||
<option value='fin'>Finnish</option>
|
||||
<option value='fre'>French</option>
|
||||
<option value='hun'>Hungarian</option>
|
||||
<option value='ita'>Italian</option>
|
||||
<option value='kor'>Korean</option>
|
||||
<option value='dut'>Dutch</option>
|
||||
<option value='nor'>Norwegian</option>
|
||||
<option value='rum'>Romanian</option>
|
||||
<option value='rus'>Russian</option>
|
||||
<option value='slo'>Slovak</option>
|
||||
<option value='swe'>Swedish</option>
|
||||
<option value='ara'>Arabic</option>
|
||||
</select>
|
||||
</SettingCard>
|
||||
<SettingCard title='Preferred Audio Language' description="What audio language to automatically select when a video is loaded if it exists. This won't find torrents with this language automatically. If not found defaults to Japanese.">
|
||||
<select class='form-control bg-dark w-300 mw-full' bind:value={settings.audioLanguage}>
|
||||
<option value='eng'>English</option>
|
||||
<option value='jpn' selected>Japanese</option>
|
||||
<option value='chi'>Chinese</option>
|
||||
<option value='por'>Portuguese</option>
|
||||
<option value='spa'>Spanish</option>
|
||||
<option value='ger'>German</option>
|
||||
<option value='pol'>Polish</option>
|
||||
<option value='cze'>Czech</option>
|
||||
<option value='dan'>Danish</option>
|
||||
<option value='gre'>Greek</option>
|
||||
<option value='fin'>Finnish</option>
|
||||
<option value='fre'>French</option>
|
||||
<option value='hun'>Hungarian</option>
|
||||
<option value='ita'>Italian</option>
|
||||
<option value='kor'>Korean</option>
|
||||
<option value='dut'>Dutch</option>
|
||||
<option value='nor'>Norwegian</option>
|
||||
<option value='rum'>Romanian</option>
|
||||
<option value='rus'>Russian</option>
|
||||
<option value='slo'>Slovak</option>
|
||||
<option value='swe'>Swedish</option>
|
||||
<option value='ara'>Arabic</option>
|
||||
</select>
|
||||
</SettingCard>
|
||||
|
||||
<h4 class='mb-10 font-weight-bold'>Playback Settings</h4>
|
||||
<SettingCard title='Autoplay Next Episode' description='Automatically starts playing next episode when a video ends.'>
|
||||
<div class='custom-switch'>
|
||||
<input type='checkbox' id='player-autoplay' bind:checked={settings.playerAutoplay} />
|
||||
<label for='player-autoplay'>{settings.playerAutoplay ? 'On' : 'Off'}</label>
|
||||
</div>
|
||||
</SettingCard>
|
||||
<SettingCard title='Pause On Lost Focus' description='Pauses/Resumes video playback when tabbing in/out of the app.'>
|
||||
<div class='custom-switch'>
|
||||
<input type='checkbox' id='player-pause' bind:checked={settings.playerPause} />
|
||||
<label for='player-pause'>{settings.playerPause ? 'On' : 'Off'}</label>
|
||||
</div>
|
||||
</SettingCard>
|
||||
<SettingCard title='Auto-Complete Episodes' description='Automatically marks episodes as complete on AniList when you finish watching them. Requires AniList login.'>
|
||||
<div class='custom-switch'>
|
||||
<input type='checkbox' id='player-autocomplete' bind:checked={settings.playerAutocomplete} />
|
||||
<label for='player-autocomplete'>{settings.playerAutocomplete ? 'On' : 'Off'}</label>
|
||||
</div>
|
||||
</SettingCard>
|
||||
21
common/views/Settings/SettingCard.svelte
Normal file
21
common/views/Settings/SettingCard.svelte
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<script>
|
||||
export let title = ''
|
||||
export let description = ''
|
||||
</script>
|
||||
<div class='bg-dark-light rounded px-20 py-10 d-flex flex-column flex-md-row align-items-md-center justify-content-between mb-15 mw-1200 w-full'>
|
||||
<div class='mr-10 mb-5 mb-md-0'>
|
||||
<div class='font-size-16 font-weight-semi-bold'>
|
||||
{title}
|
||||
</div>
|
||||
<div class='text-muted pre-wrap'>
|
||||
{description}
|
||||
</div>
|
||||
</div>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.mw-1200 {
|
||||
max-width: 120rem;
|
||||
}
|
||||
</style>
|
||||
164
common/views/Settings/Settings.svelte
Normal file
164
common/views/Settings/Settings.svelte
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
<script context='module'>
|
||||
import { toast } from 'svelte-sonner'
|
||||
import { click } from '@/modules/click.js'
|
||||
import { settings } from '@/modules/settings.js'
|
||||
import IPC from '@/modules/ipc.js'
|
||||
|
||||
if (settings.value.enableDoH) IPC.emit('doh', settings.value.doHURL)
|
||||
export const platformMap = {
|
||||
aix: 'Aix',
|
||||
darwin: 'MacOS',
|
||||
android: 'Android',
|
||||
ios: 'iOS',
|
||||
freebsd: 'Linux',
|
||||
linux: 'Linux',
|
||||
openbsd: 'Linux',
|
||||
sunos: 'SunOS',
|
||||
win32: 'Windows'
|
||||
}
|
||||
let version = '1.0.0'
|
||||
IPC.on('version', data => (version = data))
|
||||
IPC.emit('version')
|
||||
|
||||
let wasUpdated = false
|
||||
IPC.on('update-available', () => {
|
||||
if (!wasUpdated) {
|
||||
wasUpdated = true
|
||||
toast('Auto Updater', {
|
||||
description: 'A new version of Miru is available. Downloading!'
|
||||
})
|
||||
}
|
||||
})
|
||||
IPC.on('update-downloaded', () => {
|
||||
toast.success('Auto Updater', {
|
||||
description: 'A new version of Miru has downloaded. You can restart to update!'
|
||||
})
|
||||
})
|
||||
|
||||
const changeLog = (async () => {
|
||||
const res = await fetch('https://api.github.com/repos/ThaUnknown/miru/releases')
|
||||
const json = await res.json()
|
||||
return json.map(({ body, tag_name: version }) => ({ body, version }))
|
||||
})()
|
||||
IPC.emit('discord_status', settings.value.showDetailsInRPC)
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import { Tabs, TabLabel, Tab } from '@/components/Tabination.js'
|
||||
import { onDestroy } from 'svelte'
|
||||
import PlayerSettings from './PlayerSettings.svelte'
|
||||
import TorrentSettings from './TorrentSettings.svelte'
|
||||
import InterfaceSettings from './InterfaceSettings.svelte'
|
||||
import AppSettings from './AppSettings.svelte'
|
||||
|
||||
onDestroy(() => {
|
||||
IPC.off('path')
|
||||
})
|
||||
|
||||
const groups = {
|
||||
player: {
|
||||
name: 'Player',
|
||||
icon: 'play_arrow'
|
||||
},
|
||||
torrent: {
|
||||
name: 'Torrent',
|
||||
icon: 'rss_feed'
|
||||
},
|
||||
interface: {
|
||||
name: 'Interface',
|
||||
icon: 'settings'
|
||||
},
|
||||
app: {
|
||||
name: 'App',
|
||||
icon: 'info'
|
||||
},
|
||||
changelog: {
|
||||
name: 'Changelog',
|
||||
icon: 'description'
|
||||
}
|
||||
}
|
||||
$: IPC.emit('discord_status', $settings.showDetailsInRPC)
|
||||
IPC.on('path', data => {
|
||||
$settings.torrentPath = data
|
||||
})
|
||||
</script>
|
||||
|
||||
<Tabs>
|
||||
<div class='d-flex w-full h-full position-relative settings root flex-md-row flex-column overflow-y-auto overflow-y-md-hidden'>
|
||||
<div class='d-flex flex-column flex-row h-full w-md-300 bg-dark position-relative px-20 px-md-0 flex-basis-0-md-custom'>
|
||||
<div class='px-20 py-15 font-size-24 font-weight-semi-bold'>Settings</div>
|
||||
{#each Object.values(groups) as group}
|
||||
<TabLabel>
|
||||
<div class='px-20 py-10 d-flex'>
|
||||
<span class='material-symbols-outlined font-size-24 pr-10 d-inline-flex justify-content-center align-items-center'>{group.icon}</span>
|
||||
<div class='font-size-16'>{group.name}</div>
|
||||
</div>
|
||||
</TabLabel>
|
||||
{/each}
|
||||
<div class='pointer my-5 rounded' tabindex='0' role='button' use:click={() => IPC.emit('open', 'https://github.com/sponsors/ThaUnknown/')}>
|
||||
<div class='px-20 py-10 d-flex'>
|
||||
<span class='material-symbols-outlined font-size-24 pr-10 d-inline-flex justify-content-center align-items-center'>favorite</span>
|
||||
<div class='font-weight-bold font-size-16'>Donate</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class='text-muted px-20 py-10 m-0 mt-md-auto'>Restart may be required for some settings to take effect.</p>
|
||||
<p class='text-muted px-20 pb-10 m-0'>If you don't know what settings do what, use defaults.</p>
|
||||
<p class='text-muted px-20 m-0 mb-md-20'>v{version} {platformMap[window.version.platform] || 'dev'} {window.version.arch || 'dev'}</p>
|
||||
</div>
|
||||
<Tab>
|
||||
<div class='root h-full w-full overflow-y-md-auto p-20'>
|
||||
<PlayerSettings bind:settings={$settings} />
|
||||
<!-- spacing element to make space for miniplayer on mobile -->
|
||||
<div class='h-250 d-md-none' />
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab>
|
||||
<div class='root h-full w-full overflow-y-md-auto p-20'>
|
||||
<TorrentSettings bind:settings={$settings} />
|
||||
<div class='h-250 d-md-none' />
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab>
|
||||
<div class='root h-full w-full overflow-y-md-auto p-20'>
|
||||
<InterfaceSettings bind:settings={$settings} />
|
||||
<div class='h-250 d-md-none' />
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab>
|
||||
<div class='root h-full w-full overflow-y-md-auto p-20'>
|
||||
<AppSettings />
|
||||
<div class='h-250 d-md-none' />
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab>
|
||||
<div class='root m-20 px-20 pre-wrap overflow-y-md-auto'>
|
||||
{#await changeLog}
|
||||
<h1 class='font-weight-bold'>Loading changelog...</h1>
|
||||
{:then changes}
|
||||
{#each changes as { body, version }}
|
||||
<h1 class='font-weight-bold mt-20'>{version}</h1>{body}
|
||||
{/each}
|
||||
{:catch error}
|
||||
<h1 class='font-weight-bold mt-20'>Failed to load changelog.</h1>
|
||||
{/await}
|
||||
<div class='h-250 d-md-none' />
|
||||
</div>
|
||||
</Tab>
|
||||
</div>
|
||||
</Tabs>
|
||||
|
||||
<style>
|
||||
.settings :global(select.form-control:invalid) {
|
||||
color: var(--dm-input-placeholder-text-color);
|
||||
}
|
||||
|
||||
.flex-basis-0-md-custom {
|
||||
flex-basis: 0%;
|
||||
}
|
||||
|
||||
@media (min-width: 769px) {
|
||||
.flex-basis-0-md-custom {
|
||||
flex-basis: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
96
common/views/Settings/TorrentSettings.svelte
Normal file
96
common/views/Settings/TorrentSettings.svelte
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
<script>
|
||||
import { click } from '@/modules/click.js'
|
||||
import { defaults } from '@/modules/util.js'
|
||||
import IPC from '@/modules/ipc.js'
|
||||
import SettingCard from './SettingCard.svelte'
|
||||
import { SUPPORTS } from '@/modules/support.js'
|
||||
export let settings
|
||||
|
||||
function handleFolder () {
|
||||
IPC.emit('dialog')
|
||||
}
|
||||
</script>
|
||||
|
||||
<h4 class='mb-10 font-weight-bold'>Lookup Settings</h4>
|
||||
<SettingCard title='Torrent Quality' description="What quality to use when trying to find torrents. None might rarely find less results than specific qualities. This doesn't exclude other qualities from being found like 4K or weird DVD resolutions.">
|
||||
<select class='form-control bg-dark w-300 mw-full' bind:value={settings.rssQuality}>
|
||||
<option value='1080' selected>1080p</option>
|
||||
<option value='720'>720p</option>
|
||||
<option value='480||540'>SD</option>
|
||||
<option value="">None</option>
|
||||
</select>
|
||||
</SettingCard>
|
||||
<SettingCard title='Auto-Select Torrents' description='Automatically selects torrents based on quality and amount of seeders. Disable this to have more precise control over played torrents.'>
|
||||
<div class='custom-switch'>
|
||||
<input type='checkbox' id='rss-autoplay' bind:checked={settings.rssAutoplay} />
|
||||
<label for='rss-autoplay'>{settings.rssAutoplay ? 'On' : 'Off'}</label>
|
||||
</div>
|
||||
</SettingCard>
|
||||
{#if SUPPORTS.doh}
|
||||
<SettingCard title='Use DNS Over HTTPS' description='Enables DNS Over HTTPS, useful if your ISP blocks certain domains.'>
|
||||
<div class='custom-switch'>
|
||||
<input type='checkbox' id='rss-dohtoggle' bind:checked={settings.enableDoH} />
|
||||
<label for='rss-dohtoggle'>{settings.rssAutoplay ? 'On' : 'Off'}</label>
|
||||
</div>
|
||||
</SettingCard>
|
||||
<SettingCard title='DNS Over HTTPS URL' description='What URL to use for querying DNS Over HTTPS.'>
|
||||
<input type='text' class='form-control bg-dark w-300 mw-full' bind:value={settings.doHURL} placeholder={defaults.doHURL} />
|
||||
</SettingCard>
|
||||
{/if}
|
||||
<SettingCard title='Torrent API URL' description='URL of the API used to query data for torrents. Useful for proxies if your ISP blocks some domains. Needs to be CORS enabled.'>
|
||||
<input type='text' class='form-control bg-dark w-300 mw-full' bind:value={settings.toshoURL} placeholder={defaults.toshoURL} />
|
||||
</SettingCard>
|
||||
|
||||
<h4 class='mb-10 font-weight-bold'>Client Settings</h4>
|
||||
{#if SUPPORTS.torrentPath}
|
||||
<SettingCard title='Torrent Download Location' description='Path to the folder used to store torrents. By default this is the TMP folder, which might loose data when your OS tries to reclaim storage.'>
|
||||
<div
|
||||
class='input-group w-300 mw-full'>
|
||||
<div class='input-group-prepend'>
|
||||
<button type='button' use:click={handleFolder} class='btn btn-primary input-group-append'>Select Folder</button>
|
||||
</div>
|
||||
<input type='text' class='form-control bg-dark' bind:value={settings.torrentPath} placeholder='/tmp' />
|
||||
</div>
|
||||
</SettingCard>
|
||||
{/if}
|
||||
{#if SUPPORTS.torrentPersist}
|
||||
<SettingCard title='Persist Files' description="Keeps torrents files instead of deleting them after a new torrent is played. This doesn't seed the files, only keeps them on your drive. This will quickly fill up your storage.">
|
||||
<div class='custom-switch'>
|
||||
<input type='checkbox' id='torrent-persist' bind:checked={settings.torrentPersist} />
|
||||
<label for='torrent-persist'>{settings.torrentPersist ? 'On' : 'Off'}</label>
|
||||
</div>
|
||||
</SettingCard>
|
||||
{/if}
|
||||
<SettingCard title='Transfer Speed Limit' description='Download/Upload speed limit for torrents, higher values increase CPU usage, and values higher than your storage write speeds will quickly fill up RAM.'>
|
||||
<div class='input-group w-100 mw-full'>
|
||||
<input type='number' bind:value={settings.torrentSpeed} min='0' max='50' class='form-control text-right bg-dark' />
|
||||
<div class='input-group-append'>
|
||||
<span class='input-group-text bg-dark'>MB/s</span>
|
||||
</div>
|
||||
</div>
|
||||
</SettingCard>
|
||||
<SettingCard title='Max Number of Connections' description='Numer of peers per torrent. Higher values will increase download speeds but might quickly fill up available ports if your ISP limits the maximum allowed number of open connections.'>
|
||||
<input type='number' bind:value={settings.maxConns} min='1' max='512' class='form-control text-right bg-dark w-100 mw-full' />
|
||||
</SettingCard>
|
||||
{#if SUPPORTS.torrentPort}
|
||||
<SettingCard title='Torrent Port' description='Port used for Torrent connections. 0 is automatic.'>
|
||||
<input type='number' bind:value={settings.torrentPort} min='0' max='65536' class='form-control text-right bg-dark w-150 mw-full' />
|
||||
</SettingCard>
|
||||
{/if}
|
||||
{#if SUPPORTS.dht}
|
||||
<SettingCard title='DHT Port' description='Port used for DHT connections. 0 is automatic.'>
|
||||
<input type='number' bind:value={settings.dhtPort} min='0' max='65536' class='form-control text-right bg-dark w-150 mw-full' />
|
||||
</SettingCard>
|
||||
<SettingCard title='Disable DHT' description='Disables Distributed Hash Tables for use in private trackers to improve privacy. Might greatly reduce the amount of discovered peers.'>
|
||||
<div class='custom-switch'>
|
||||
<input type='checkbox' id='torrent-dht' bind:checked={settings.torrentDHT} />
|
||||
<label for='torrent-dht'>{settings.torrentDHT ? 'On' : 'Off'}</label>
|
||||
</div>
|
||||
</SettingCard>
|
||||
{/if}
|
||||
<SettingCard title='Disable PeX' description='Disables Peer Exchange for use in private trackers to improve privacy. Might greatly reduce the amount of discovered peers.'>
|
||||
<div class='custom-switch'>
|
||||
<input type='checkbox' id='torrent-pex' bind:checked={settings.torrentPeX} />
|
||||
<label for='torrent-pex'>{settings.torrentPeX ? 'On' : 'Off'}</label>
|
||||
</div>
|
||||
</SettingCard>
|
||||
|
|
@ -189,6 +189,9 @@ importers:
|
|||
quartermoon:
|
||||
specifier: ^1.2.3
|
||||
version: 1.2.3
|
||||
simple-font-select:
|
||||
specifier: ^1.0.1
|
||||
version: 1.0.1
|
||||
simple-store-svelte:
|
||||
specifier: ^1.0.1
|
||||
version: 1.0.1
|
||||
|
|
@ -202,8 +205,8 @@ importers:
|
|||
specifier: ^3.1.9
|
||||
version: 3.1.9(svelte@4.2.3)
|
||||
svelte-miniplayer:
|
||||
specifier: ^1.0.4
|
||||
version: 1.0.4
|
||||
specifier: ^1.0.5
|
||||
version: 1.0.5
|
||||
svelte-sonner:
|
||||
specifier: ^0.3.3
|
||||
version: 0.3.3(svelte@4.2.3)
|
||||
|
|
@ -6805,6 +6808,10 @@ packages:
|
|||
/simple-concat@1.0.1:
|
||||
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
|
||||
|
||||
/simple-font-select@1.0.1:
|
||||
resolution: {integrity: sha512-3myV4hFaijwG6IZoMFxvCJ/k+zKVWUsblGd7VFBW0g8G9Ln7VaXPR0Tg5PpTzFOiPOqL+h5RbSvCGW7ZaMGOqA==}
|
||||
dev: false
|
||||
|
||||
/simple-get@4.0.1:
|
||||
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
|
||||
dependencies:
|
||||
|
|
@ -7189,8 +7196,8 @@ packages:
|
|||
svelte-hmr: 0.14.12(svelte@4.2.3)
|
||||
dev: false
|
||||
|
||||
/svelte-miniplayer@1.0.4:
|
||||
resolution: {integrity: sha512-U1WLsokF4aEtLrCgzjZ8T/TXG6uFjJJykc+DuYaG3YdZkYkt/RDz+CEoHBMXpk85WoUt6D+8tleWEx5CuzW6jQ==}
|
||||
/svelte-miniplayer@1.0.5:
|
||||
resolution: {integrity: sha512-jzYqqBuXcSH5KzoPDlYQL6CQVbpY2LQB4/wBPG4T5R75wE8Dqu4auMU6NnJxHBRhgNCGlH+XBQvxy9G6yX/XQw==}
|
||||
dev: false
|
||||
|
||||
/svelte-sonner@0.3.3(svelte@4.2.3):
|
||||
|
|
|
|||
Loading…
Reference in a new issue