mirror of
https://github.com/ThaUnknown/miru.git
synced 2026-04-21 00:22:17 +00:00
feat: better debug tooling, debug mode
fix: make account related buttons disabled when not signed in fix: improve DTS exclusions fix: softlock on navigation in androidTV
This commit is contained in:
parent
0ef55b1f34
commit
eeed27ffcf
23 changed files with 488 additions and 311 deletions
|
|
@ -30,6 +30,7 @@
|
||||||
"@capacitor/app": "^6.0.0",
|
"@capacitor/app": "^6.0.0",
|
||||||
"@capacitor/browser": "^6.0.1",
|
"@capacitor/browser": "^6.0.1",
|
||||||
"@capacitor/core": "^6.1.1",
|
"@capacitor/core": "^6.1.1",
|
||||||
|
"@capacitor/device": "^6.0.1",
|
||||||
"@capacitor/ios": "^6.1.1",
|
"@capacitor/ios": "^6.1.1",
|
||||||
"@capacitor/local-notifications": "^6.0.0",
|
"@capacitor/local-notifications": "^6.0.0",
|
||||||
"@capacitor/status-bar": "^6.0.0",
|
"@capacitor/status-bar": "^6.0.0",
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { SafeArea } from 'capacitor-plugin-safe-area'
|
||||||
import { App } from '@capacitor/app'
|
import { App } from '@capacitor/app'
|
||||||
import { Browser } from '@capacitor/browser'
|
import { Browser } from '@capacitor/browser'
|
||||||
import { LocalNotifications } from '@capacitor/local-notifications'
|
import { LocalNotifications } from '@capacitor/local-notifications'
|
||||||
|
import { Device } from '@capacitor/device'
|
||||||
import IPC from './ipc.js'
|
import IPC from './ipc.js'
|
||||||
|
|
||||||
IPC.on('open', url => Browser.open({ url }))
|
IPC.on('open', url => Browser.open({ url }))
|
||||||
|
|
@ -40,6 +41,16 @@ IPC.on('notification', noti => {
|
||||||
if (canShowNotifications) LocalNotifications.schedule({ notifications: [notification] })
|
if (canShowNotifications) LocalNotifications.schedule({ notifications: [notification] })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
IPC.on('get-device-info', async () => {
|
||||||
|
const deviceInfo = {
|
||||||
|
features: {},
|
||||||
|
info: await Device.getInfo(),
|
||||||
|
cpu: {},
|
||||||
|
ram: {}
|
||||||
|
}
|
||||||
|
IPC.emit('device-info', JSON.stringify(deviceInfo))
|
||||||
|
})
|
||||||
|
|
||||||
// schema: miru://key/value
|
// schema: miru://key/value
|
||||||
const protocolMap = {
|
const protocolMap = {
|
||||||
auth: token => sendToken(token),
|
auth: token => sendToken(token),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { persisted } from 'svelte-persisted-store'
|
||||||
import { getContext } from 'svelte'
|
import { getContext } from 'svelte'
|
||||||
import { click } from '@/modules/click.js'
|
import { click } from '@/modules/click.js'
|
||||||
import IPC from '@/modules/ipc.js'
|
import IPC from '@/modules/ipc.js'
|
||||||
|
|
@ -9,6 +10,8 @@
|
||||||
$view = null
|
$view = null
|
||||||
page = 'home'
|
page = 'home'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const debug = persisted('debug', '')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class='w-full z-101 navbar bg-transparent border-0 p-0 d-flex'>
|
<div class='w-full z-101 navbar bg-transparent border-0 p-0 d-flex'>
|
||||||
|
|
@ -27,8 +30,21 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{#if $debug}
|
||||||
|
<div class='ribbon right z-101 text-center position-fixed font-size-16 font-weight-bold'>Debug Mode!</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.ribbon {
|
||||||
|
background: #f63220;
|
||||||
|
box-shadow: 0 0 0 999px #f63220;
|
||||||
|
clip-path: inset(0 -100%);
|
||||||
|
pointer-events: none;
|
||||||
|
min-width: 120px;
|
||||||
|
inset: 0 auto auto 0;
|
||||||
|
transform-origin: 100% 0;
|
||||||
|
transform: translate(-29.3%) rotate(-45deg);
|
||||||
|
}
|
||||||
.navbar {
|
.navbar {
|
||||||
--navbar-height: 28px !important;
|
--navbar-height: 28px !important;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,58 +3,9 @@
|
||||||
import { media } from '../views/Player/MediaHandler.svelte'
|
import { media } from '../views/Player/MediaHandler.svelte'
|
||||||
import { click } from '@/modules/click.js'
|
import { click } from '@/modules/click.js'
|
||||||
import IPC from '@/modules/ipc.js'
|
import IPC from '@/modules/ipc.js'
|
||||||
|
import NavbarLink from './NavbarLink.svelte'
|
||||||
const view = getContext('view')
|
const view = getContext('view')
|
||||||
export let page
|
export let page
|
||||||
const links = [
|
|
||||||
{
|
|
||||||
click: () => {
|
|
||||||
page = 'search'
|
|
||||||
},
|
|
||||||
css: 'ml-auto',
|
|
||||||
page: 'search',
|
|
||||||
icon: 'search',
|
|
||||||
text: 'Search'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
click: () => {
|
|
||||||
page = 'schedule'
|
|
||||||
},
|
|
||||||
page: 'schedule',
|
|
||||||
icon: 'schedule',
|
|
||||||
text: 'Schedule'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
click: () => {
|
|
||||||
if ($media) $view = $media.media
|
|
||||||
},
|
|
||||||
icon: 'queue_music',
|
|
||||||
text: 'Now Playing'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
click: () => {
|
|
||||||
page = 'watchtogether'
|
|
||||||
},
|
|
||||||
page: 'watchtogether',
|
|
||||||
icon: 'groups',
|
|
||||||
text: 'Watch Together'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
click: () => {
|
|
||||||
IPC.emit('open', 'https://github.com/sponsors/ThaUnknown/')
|
|
||||||
},
|
|
||||||
icon: 'favorite',
|
|
||||||
text: 'Support This App',
|
|
||||||
css: 'ml-auto donate'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
click: () => {
|
|
||||||
page = 'settings'
|
|
||||||
},
|
|
||||||
page: 'settings',
|
|
||||||
icon: 'settings',
|
|
||||||
text: 'Settings'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
function close () {
|
function close () {
|
||||||
$view = null
|
$view = null
|
||||||
page = 'home'
|
page = 'home'
|
||||||
|
|
@ -64,99 +15,13 @@
|
||||||
<nav class='navbar navbar-fixed-bottom d-block d-md-none border-0 bg-dark'>
|
<nav class='navbar navbar-fixed-bottom d-block d-md-none border-0 bg-dark'>
|
||||||
<div class='navbar-menu h-full d-flex flex-row justify-content-center align-items-center m-0 pb-5' class:animate={page !== 'player'}>
|
<div class='navbar-menu h-full d-flex flex-row justify-content-center align-items-center m-0 pb-5' class:animate={page !== 'player'}>
|
||||||
<img src='./logo_filled.png' class='w-50 h-50 m-10 pointer p-5' alt='ico' use:click={close} />
|
<img src='./logo_filled.png' class='w-50 h-50 m-10 pointer p-5' alt='ico' use:click={close} />
|
||||||
{#each links as { click: _click, icon, text, image, css, page: _page }, i (i)}
|
<NavbarLink click={() => { page = 'search' }} _page='search' css='ml-auto' icon='search' {page} />
|
||||||
<div
|
<NavbarLink click={() => { page = 'schedule' }} _page='schedule' icon='schedule' {page} />
|
||||||
class='navbar-link navbar-link-with-icon pointer overflow-hidden {css}'
|
{#if $media?.media}
|
||||||
use:click={_click}>
|
<NavbarLink click={() => { $view = $media.media }} icon='queue_music' {page} />
|
||||||
{#if image}
|
{/if}
|
||||||
<span class='material-symbols-outlined rounded' class:filled={page === _page}>
|
<NavbarLink click={() => { page = 'watchtogether' }} _page='watchtogether' icon='groups' {page} />
|
||||||
<img src={image} class='h-30 rounded' alt='logo' />
|
<NavbarLink click={() => { IPC.emit('open', 'https://github.com/sponsors/ThaUnknown/') }} icon='favorite' css='ml-auto donate' {page} />
|
||||||
</span>
|
<NavbarLink click={() => { page = 'settings' }} _page='settings' icon='settings' {page} />
|
||||||
{:else}
|
|
||||||
<span class='material-symbols-outlined rounded' class:filled={page === _page}>{icon}</span>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<style>
|
|
||||||
nav {
|
|
||||||
height: var(--navbar-height);
|
|
||||||
}
|
|
||||||
@keyframes glow {
|
|
||||||
from {
|
|
||||||
text-shadow: 0 0 2rem #fa68b6;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
text-shadow: 0 0 1rem #fa68b6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.animate .donate .material-symbols-outlined {
|
|
||||||
animation: glow 1s ease-in-out infinite alternate;
|
|
||||||
}
|
|
||||||
.donate:hover .material-symbols-outlined {
|
|
||||||
background: #fff;
|
|
||||||
color: #fa68b6 !important;
|
|
||||||
}
|
|
||||||
.donate .material-symbols-outlined {
|
|
||||||
font-variation-settings: 'FILL' 1;
|
|
||||||
color: #fa68b6;
|
|
||||||
text-shadow: 0 0 1rem #fa68b6;
|
|
||||||
}
|
|
||||||
/* .sidebar-menu {
|
|
||||||
padding-top: 10rem;
|
|
||||||
} */
|
|
||||||
.text {
|
|
||||||
opacity: 1;
|
|
||||||
transition: opacity 0.8s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
||||||
display: inline-flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-link > span {
|
|
||||||
color: #fff;
|
|
||||||
border-radius: 0.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.material-symbols-outlined {
|
|
||||||
color: #fff;
|
|
||||||
transition: background .8s cubic-bezier(0.25, 0.8, 0.25, 1), color .8s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-link:hover > span {
|
|
||||||
background: #fff;
|
|
||||||
color: var(--dark-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-link {
|
|
||||||
font-size: 1.4rem;
|
|
||||||
padding: 0.75rem;
|
|
||||||
height: 5.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.material-symbols-outlined {
|
|
||||||
font-size: 2.2rem;
|
|
||||||
min-width: 4rem;
|
|
||||||
width: 4rem;
|
|
||||||
height: 4rem;
|
|
||||||
display: inline-flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-link img {
|
|
||||||
font-size: 2.2rem;
|
|
||||||
width: 3rem;
|
|
||||||
height: 3rem;
|
|
||||||
margin: 0.5rem;
|
|
||||||
display: inline-flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
margin-right: var(--sidebar-brand-image-margin-right);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
101
common/components/NavbarLink.svelte
Normal file
101
common/components/NavbarLink.svelte
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
<script>
|
||||||
|
import { click } from '@/modules/click.js'
|
||||||
|
|
||||||
|
let _click = () => {}
|
||||||
|
export { _click as click }
|
||||||
|
export let image = ''
|
||||||
|
export let page
|
||||||
|
export let _page = ''
|
||||||
|
export let css = ''
|
||||||
|
export let icon = ''
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class='navbar-link navbar-link-with-icon pointer overflow-hidden {css}'
|
||||||
|
use:click={_click}>
|
||||||
|
{#if image}
|
||||||
|
<span class='material-symbols-outlined rounded' class:filled={page === _page}>
|
||||||
|
<img src={image} class='h-30 rounded' alt='logo' />
|
||||||
|
</span>
|
||||||
|
{:else}
|
||||||
|
<span class='material-symbols-outlined rounded' class:filled={page === _page}>{icon}</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@keyframes glow {
|
||||||
|
from {
|
||||||
|
text-shadow: 0 0 2rem #fa68b6;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
text-shadow: 0 0 1rem #fa68b6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.animate .donate .material-symbols-outlined {
|
||||||
|
animation: glow 1s ease-in-out infinite alternate;
|
||||||
|
}
|
||||||
|
.donate:hover .material-symbols-outlined {
|
||||||
|
background: #fff;
|
||||||
|
color: #fa68b6 !important;
|
||||||
|
}
|
||||||
|
.donate .material-symbols-outlined {
|
||||||
|
font-variation-settings: 'FILL' 1;
|
||||||
|
color: #fa68b6;
|
||||||
|
text-shadow: 0 0 1rem #fa68b6;
|
||||||
|
}
|
||||||
|
/* .sidebar-menu {
|
||||||
|
padding-top: 10rem;
|
||||||
|
} */
|
||||||
|
.text {
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 0.8s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-link > span {
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-symbols-outlined {
|
||||||
|
color: #fff;
|
||||||
|
transition: background .8s cubic-bezier(0.25, 0.8, 0.25, 1), color .8s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-link:hover > span {
|
||||||
|
background: #fff;
|
||||||
|
color: var(--dark-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-link {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
padding: 0.75rem;
|
||||||
|
height: 5.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-symbols-outlined {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
min-width: 4rem;
|
||||||
|
width: 4rem;
|
||||||
|
height: 4rem;
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-link img {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
margin: 0.5rem;
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
margin-right: var(--sidebar-brand-image-margin-right);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -5,143 +5,56 @@
|
||||||
import { platformMap } from '@/views/Settings/Settings.svelte'
|
import { platformMap } from '@/views/Settings/Settings.svelte'
|
||||||
import { settings } from '@/modules/settings.js'
|
import { settings } from '@/modules/settings.js'
|
||||||
import { toast } from 'svelte-sonner'
|
import { toast } from 'svelte-sonner'
|
||||||
import { click } from '@/modules/click.js'
|
|
||||||
import { logout } from './Logout.svelte'
|
import { logout } from './Logout.svelte'
|
||||||
import IPC from '@/modules/ipc.js'
|
import IPC from '@/modules/ipc.js'
|
||||||
|
import SidebarLink from './SidebarLink.svelte'
|
||||||
|
|
||||||
let wasUpdated = false
|
let updateState = ''
|
||||||
|
|
||||||
globalThis.dd = IPC
|
|
||||||
|
|
||||||
IPC.on('update-available', () => {
|
IPC.on('update-available', () => {
|
||||||
console.log('uwu')
|
updateState = 'downloading'
|
||||||
if (!wasUpdated) {
|
|
||||||
// insert icon in 2nd to last position
|
|
||||||
links.splice(links.length - 1, 0, {
|
|
||||||
click: () => {
|
|
||||||
toast('Update is downloading...')
|
|
||||||
},
|
|
||||||
icon: 'download',
|
|
||||||
text: 'Update Downloading...'
|
|
||||||
})
|
|
||||||
links = links
|
|
||||||
}
|
|
||||||
wasUpdated = true
|
|
||||||
})
|
})
|
||||||
IPC.on('update-downloaded', () => {
|
IPC.on('update-downloaded', () => {
|
||||||
links[links.length - 2].css = 'update'
|
updateState = 'ready'
|
||||||
links[links.length - 2].text = 'Update Ready!'
|
|
||||||
links[links.length - 2].click = () => {
|
|
||||||
IPC.emit('quit-and-install')
|
|
||||||
}
|
|
||||||
links = links
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const view = getContext('view')
|
const view = getContext('view')
|
||||||
|
|
||||||
export let page
|
export let page
|
||||||
|
|
||||||
let links = [
|
function handleAlLogin () {
|
||||||
{
|
if (anilistClient.userID?.viewer?.data?.Viewer) {
|
||||||
click: () => {
|
$logout = true
|
||||||
if (anilistClient.userID?.viewer?.data?.Viewer) {
|
} else {
|
||||||
$logout = true
|
IPC.emit('open', 'https://anilist.co/api/v2/oauth/authorize?client_id=4254&response_type=token') // Change redirect_url to miru://auth
|
||||||
} else {
|
if (platformMap[window.version.platform] === 'Linux') {
|
||||||
IPC.emit('open', 'https://anilist.co/api/v2/oauth/authorize?client_id=4254&response_type=token') // Change redirect_url to miru://auth
|
toast('Support Notification', {
|
||||||
if (platformMap[window.version.platform] === 'Linux') {
|
description: "If your linux distribution doesn't support custom protocol handlers, you can simply paste the full URL into the app.",
|
||||||
toast('Support Notification', {
|
duration: 300000
|
||||||
description: "If your linux distribution doesn't support custom protocol handlers, you can simply paste the full URL into the app.",
|
})
|
||||||
duration: 300000
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
icon: 'login',
|
|
||||||
text: 'Login With AniList',
|
|
||||||
css: 'mt-auto'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
click: () => {
|
|
||||||
page = 'home'
|
|
||||||
},
|
|
||||||
page: 'home',
|
|
||||||
icon: 'home',
|
|
||||||
text: 'Home'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
click: () => {
|
|
||||||
page = 'search'
|
|
||||||
},
|
|
||||||
page: 'search',
|
|
||||||
icon: 'search',
|
|
||||||
text: 'Search'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
click: () => {
|
|
||||||
page = 'schedule'
|
|
||||||
},
|
|
||||||
page: 'schedule',
|
|
||||||
icon: 'schedule',
|
|
||||||
text: 'Schedule'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
click: () => {
|
|
||||||
if ($media) $view = $media.media
|
|
||||||
},
|
|
||||||
icon: 'queue_music',
|
|
||||||
text: 'Now Playing'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
click: () => {
|
|
||||||
page = 'watchtogether'
|
|
||||||
},
|
|
||||||
page: 'watchtogether',
|
|
||||||
icon: 'groups',
|
|
||||||
text: 'Watch Together'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
click: () => {
|
|
||||||
IPC.emit('open', 'https://github.com/sponsors/ThaUnknown/')
|
|
||||||
},
|
|
||||||
icon: 'favorite',
|
|
||||||
text: 'Support This App',
|
|
||||||
css: 'mt-auto donate'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
click: () => {
|
|
||||||
page = 'settings'
|
|
||||||
},
|
|
||||||
page: 'settings',
|
|
||||||
icon: 'settings',
|
|
||||||
text: 'Settings'
|
|
||||||
}
|
}
|
||||||
]
|
|
||||||
if (anilistClient.userID?.viewer?.data?.Viewer) {
|
|
||||||
links[0].image = anilistClient.userID.viewer.data.Viewer.avatar.medium
|
|
||||||
links[0].text = 'Logout'
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class='sidebar z-30 d-md-block' class:animated={$settings.expandingSidebar}>
|
<div class='sidebar z-30 d-md-block' class:animated={$settings.expandingSidebar}>
|
||||||
<div class='sidebar-overlay pointer-events-none h-full position-absolute' />
|
<div class='sidebar-overlay pointer-events-none h-full position-absolute' />
|
||||||
<div class='sidebar-menu h-full d-flex flex-column justify-content-center align-items-center m-0 pb-5' class:animate={page !== 'player'}>
|
<div class='sidebar-menu h-full d-flex flex-column justify-content-center align-items-center m-0 pb-5' class:animate={page !== 'player'}>
|
||||||
{#each links as { click: _click, icon, text, image, css, page: _page } (_click)}
|
<SidebarLink click={handleAlLogin} icon='login' text={anilistClient.userID?.viewer?.data?.Viewer ? 'Logout' : 'Login With AniList'} css='mt-auto' {page} image={anilistClient.userID?.viewer?.data?.Viewer?.avatar.medium} />
|
||||||
<div
|
<SidebarLink click={() => { page = 'home' }} _page='home' icon='home' text='Home' {page} />
|
||||||
class='sidebar-link sidebar-link-with-icon pointer overflow-hidden {css}'
|
<SidebarLink click={() => { page = 'search' }} _page='search' icon='search' text='Search' {page} />
|
||||||
use:click={_click}>
|
<SidebarLink click={() => { page = 'schedule' }} _page='schedule' icon='schedule' text='Schedule' {page} />
|
||||||
<span class='text-nowrap d-flex align-items-center w-full h-full'>
|
{#if $media?.media}
|
||||||
{#if image}
|
<SidebarLink click={() => { $view = $media.media }} icon='queue_music' text='Now Playing' {page} />
|
||||||
<span class='material-symbols-outlined rounded' class:filled={page === _page}>
|
{/if}
|
||||||
<img src={image} class='h-30 rounded' alt='logo' />
|
<SidebarLink click={() => { page = 'watchtogether' }} _page='watchtogether' icon='groups' text='Watch Together' {page} />
|
||||||
</span>
|
<SidebarLink click={() => { IPC.emit('open', 'https://github.com/sponsors/ThaUnknown/') }} icon='favorite' text='Support This App' css='mt-auto donate' {page} />
|
||||||
<span class='text ml-20'>{text}</span>
|
{#if updateState === 'downloading'}
|
||||||
{:else}
|
<SidebarLink click={() => { toast('Update is downloading...') }} icon='download' text='Update Downloading...' {page} />
|
||||||
<span class='material-symbols-outlined rounded' class:filled={page === _page}>{icon}</span>
|
{:else if updateState === 'ready'}
|
||||||
<span class='text ml-20'>{text}</span>
|
<SidebarLink click={() => { IPC.emit('quit-and-install') }} css='update' icon='download' text='Update Ready!' {page} />
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
<SidebarLink click={() => { page = 'settings' }} _page='settings' icon='settings' text='Settings' {page} />
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
129
common/components/SidebarLink.svelte
Normal file
129
common/components/SidebarLink.svelte
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
<script>
|
||||||
|
import { click } from '@/modules/click.js'
|
||||||
|
|
||||||
|
let _click = () => {}
|
||||||
|
export { _click as click }
|
||||||
|
export let image = ''
|
||||||
|
export let page
|
||||||
|
export let _page = ''
|
||||||
|
export let css = ''
|
||||||
|
export let text = ''
|
||||||
|
export let icon = ''
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class='sidebar-link sidebar-link-with-icon pointer overflow-hidden {css}'
|
||||||
|
use:click={_click}>
|
||||||
|
<span class='text-nowrap d-flex align-items-center w-full h-full'>
|
||||||
|
{#if image}
|
||||||
|
<span class='material-symbols-outlined rounded' class:filled={page === _page}>
|
||||||
|
<img src={image} class='h-30 rounded' alt='logo' />
|
||||||
|
</span>
|
||||||
|
<span class='text ml-20'>{text}</span>
|
||||||
|
{:else}
|
||||||
|
<span class='material-symbols-outlined rounded' class:filled={page === _page}>{icon}</span>
|
||||||
|
<span class='text ml-20'>{text}</span>
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@keyframes glow {
|
||||||
|
from {
|
||||||
|
text-shadow: 0 0 2rem #fa68b6;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
text-shadow: 0 0 1rem #fa68b6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.animate .donate .material-symbols-outlined {
|
||||||
|
animation: glow 1s ease-in-out infinite alternate;
|
||||||
|
}
|
||||||
|
.donate:hover .material-symbols-outlined {
|
||||||
|
background: #fff;
|
||||||
|
color: #fa68b6 !important;
|
||||||
|
}
|
||||||
|
.donate .material-symbols-outlined {
|
||||||
|
font-variation-settings: 'FILL' 1;
|
||||||
|
color: #fa68b6;
|
||||||
|
text-shadow: 0 0 1rem #fa68b6;
|
||||||
|
}
|
||||||
|
.update .material-symbols-outlined {
|
||||||
|
color: #47cb6a;
|
||||||
|
font-variation-settings: 'FILL' 1;
|
||||||
|
}
|
||||||
|
.sidebar-menu {
|
||||||
|
padding-top: 10rem;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 0.8s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link > span {
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-symbols-outlined {
|
||||||
|
color: #fff;
|
||||||
|
transition: background .8s cubic-bezier(0.25, 0.8, 0.25, 1), color .8s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link:hover > span > *:nth-child(1) {
|
||||||
|
background: #fff;
|
||||||
|
color: var(--dark-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link {
|
||||||
|
width: 100%;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
height: 5.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-symbols-outlined {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
min-width: 4rem;
|
||||||
|
width: 4rem;
|
||||||
|
height: 4rem;
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link img {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
margin: 0.5rem;
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
margin-right: var(--sidebar-brand-image-margin-right);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
transition: width .8s cubic-bezier(0.25, 0.8, 0.25, 1), left .8s cubic-bezier(0.25, 0.8, 0.25, 1) !important;
|
||||||
|
background: none !important;
|
||||||
|
overflow-y: unset;
|
||||||
|
overflow-x: visible;
|
||||||
|
left: unset;
|
||||||
|
}
|
||||||
|
.sidebar.animated:hover {
|
||||||
|
width: 22rem
|
||||||
|
}
|
||||||
|
.sidebar-overlay {
|
||||||
|
width: var(--sidebar-width);
|
||||||
|
transition: width .8s cubic-bezier(0.25, 0.8, 0.25, 1), left .8s cubic-bezier(0.25, 0.8, 0.25, 1) !important;
|
||||||
|
background: var(--sidebar-gradient);
|
||||||
|
backdrop-filter: blur(2px);
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
import { formatMap, setStatus, playMedia } from '@/modules/anime.js'
|
import { formatMap, setStatus, playMedia } from '@/modules/anime.js'
|
||||||
import { anilistClient } from '@/modules/anilist.js'
|
import { anilistClient } from '@/modules/anilist.js'
|
||||||
import { click } from '@/modules/click.js'
|
import { click } from '@/modules/click.js'
|
||||||
|
import { alToken } from '@/modules/settings.js'
|
||||||
export let mediaList
|
export let mediaList
|
||||||
|
|
||||||
let current = mediaList[0]
|
let current = mediaList[0]
|
||||||
|
|
@ -92,10 +93,10 @@
|
||||||
use:click={() => playMedia(current)}>
|
use:click={() => playMedia(current)}>
|
||||||
Watch Now
|
Watch Now
|
||||||
</button>
|
</button>
|
||||||
<button class='btn bg-dark-light btn-square ml-10 material-symbols-outlined font-size-16 shadow-none border-0' class:filled={current.isFavourite} use:click={toggleFavourite}>
|
<button class='btn bg-dark-light btn-square ml-10 material-symbols-outlined font-size-16 shadow-none border-0' class:filled={current.isFavourite} use:click={toggleFavourite} disabled={!alToken}>
|
||||||
favorite
|
favorite
|
||||||
</button>
|
</button>
|
||||||
<button class='btn bg-dark-light btn-square ml-10 material-symbols-outlined font-size-16 shadow-none border-0' class:filled={current.mediaListEntry} use:click={toggleStatus}>
|
<button class='btn bg-dark-light btn-square ml-10 material-symbols-outlined font-size-16 shadow-none border-0' class:filled={current.mediaListEntry} use:click={toggleStatus} disabled={!alToken}>
|
||||||
bookmark
|
bookmark
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
import { formatMap, setStatus, playMedia } from '@/modules/anime.js'
|
import { formatMap, setStatus, playMedia } from '@/modules/anime.js'
|
||||||
import { anilistClient } from '@/modules/anilist.js'
|
import { anilistClient } from '@/modules/anilist.js'
|
||||||
import { click } from '@/modules/click.js'
|
import { click } from '@/modules/click.js'
|
||||||
|
import { alToken } from '@/modules/settings.js'
|
||||||
/** @type {import('@/modules/al.d.ts').Media} */
|
/** @type {import('@/modules/al.d.ts').Media} */
|
||||||
export let media
|
export let media
|
||||||
|
|
||||||
|
|
@ -93,10 +94,10 @@
|
||||||
</span>
|
</span>
|
||||||
{playButtonText}
|
{playButtonText}
|
||||||
</button>
|
</button>
|
||||||
<button class='btn btn-square ml-10 material-symbols-outlined font-size-16 shadow-none border-0' class:filled={media.isFavourite} use:click={toggleFavourite}>
|
<button class='btn btn-square ml-10 material-symbols-outlined font-size-16 shadow-none border-0' class:filled={media.isFavourite} use:click={toggleFavourite} disabled={!alToken}>
|
||||||
favorite
|
favorite
|
||||||
</button>
|
</button>
|
||||||
<button class='btn btn-square ml-10 material-symbols-outlined font-size-16 shadow-none border-0' class:filled={media.mediaListEntry} use:click={toggleStatus}>
|
<button class='btn btn-square ml-10 material-symbols-outlined font-size-16 shadow-none border-0' class:filled={media.mediaListEntry} use:click={toggleStatus} disabled={!alToken}>
|
||||||
bookmark
|
bookmark
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ const DirectionKeyMap = { ArrowDown: 'down', ArrowUp: 'up', ArrowLeft: 'left', A
|
||||||
* @returns {number} - The direction between the two points.
|
* @returns {number} - The direction between the two points.
|
||||||
*/
|
*/
|
||||||
function getDirection (anchor, relative) {
|
function getDirection (anchor, relative) {
|
||||||
return Math.round((Math.atan2(relative.y - anchor.y, relative.x - anchor.x) * 180 / Math.PI + 180) / 90)
|
return Math.round((Math.atan2(relative.y - anchor.y, relative.x - anchor.x) * 180 / Math.PI + 180) / 90) || 4
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import AnimeResolver from '@/modules/animeresolver.js'
|
||||||
import { hasNextPage } from '@/modules/sections.js'
|
import { hasNextPage } from '@/modules/sections.js'
|
||||||
import IPC from '@/modules/ipc.js'
|
import IPC from '@/modules/ipc.js'
|
||||||
|
|
||||||
export const exclusions = ['DTS', '[EMBER]']
|
export const exclusions = ['DTS', 'TrueHD', '[EMBER]']
|
||||||
const isDev = location.hostname === 'localhost'
|
const isDev = location.hostname === 'localhost'
|
||||||
|
|
||||||
const video = document.createElement('video')
|
const video = document.createElement('video')
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { spawn } from 'node:child_process'
|
import { spawn } from 'node:child_process'
|
||||||
|
import Debug from 'debug'
|
||||||
import WebTorrent from 'webtorrent'
|
import WebTorrent from 'webtorrent'
|
||||||
import querystring from 'querystring'
|
import querystring from 'querystring'
|
||||||
import HTTPTracker from 'bittorrent-tracker/lib/client/http-tracker.js'
|
import HTTPTracker from 'bittorrent-tracker/lib/client/http-tracker.js'
|
||||||
|
|
@ -333,6 +334,10 @@ export default class TorrentClient extends WebTorrent {
|
||||||
this.addTorrent(data.data)
|
this.addTorrent(data.data)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case 'debug': {
|
||||||
|
Debug.disable()
|
||||||
|
if (data.data) Debug.enable(data.data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
"svelte-keybinds": "^1.0.6",
|
"svelte-keybinds": "^1.0.6",
|
||||||
"svelte-loader": "^3.1.9",
|
"svelte-loader": "^3.1.9",
|
||||||
"svelte-miniplayer": "^1.0.5",
|
"svelte-miniplayer": "^1.0.5",
|
||||||
|
"svelte-persisted-store": "^0.11.0",
|
||||||
"svelte-sonner": "^0.3.19",
|
"svelte-sonner": "^0.3.19",
|
||||||
"video-deband": "^1.0.5",
|
"video-deband": "^1.0.5",
|
||||||
"webpack-merge": "^5.10.0"
|
"webpack-merge": "^5.10.0"
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,19 @@
|
||||||
import { resetSettings } from '@/modules/settings.js'
|
import { resetSettings } from '@/modules/settings.js'
|
||||||
import IPC from '@/modules/ipc.js'
|
import IPC from '@/modules/ipc.js'
|
||||||
import { SUPPORTS } from '@/modules/support.js'
|
import { SUPPORTS } from '@/modules/support.js'
|
||||||
|
import SettingCard from './SettingCard.svelte'
|
||||||
|
|
||||||
async function importSettings () {
|
async function importSettings () {
|
||||||
localStorage.setItem('settings', await navigator.clipboard.readText())
|
try {
|
||||||
location.reload()
|
const settings = JSON.parse(await navigator.clipboard.readText())
|
||||||
|
localStorage.setItem('settings', JSON.stringify(settings))
|
||||||
|
location.reload()
|
||||||
|
} catch (error) {
|
||||||
|
toast.error('Failed to import settings', {
|
||||||
|
description: 'Failed to import settings from clipboard, make sure the copied data is valid JSON.',
|
||||||
|
duration: 5000
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function exportSettings () {
|
function exportSettings () {
|
||||||
navigator.clipboard.writeText(localStorage.getItem('settings'))
|
navigator.clipboard.writeText(localStorage.getItem('settings'))
|
||||||
|
|
@ -24,31 +33,102 @@
|
||||||
IPC.emit('update')
|
IPC.emit('update')
|
||||||
}
|
}
|
||||||
setInterval(checkUpdate, 1200000)
|
setInterval(checkUpdate, 1200000)
|
||||||
|
|
||||||
|
IPC.on('log-contents', log => {
|
||||||
|
navigator.clipboard.writeText(log)
|
||||||
|
toast.success('Copied to clipboard', {
|
||||||
|
description: 'Copied log contents to clipboard',
|
||||||
|
duration: 5000
|
||||||
|
})
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Debug from 'debug'
|
||||||
|
import { persisted } from 'svelte-persisted-store'
|
||||||
|
import { client } from '@/modules/torrent.js'
|
||||||
|
import { onDestroy } from 'svelte'
|
||||||
|
|
||||||
|
const debug = persisted('debug', '')
|
||||||
|
|
||||||
|
export let version = ''
|
||||||
|
export let settings
|
||||||
|
|
||||||
|
function updateDebug (debug) {
|
||||||
|
Debug.disable()
|
||||||
|
if (debug) Debug.enable(debug)
|
||||||
|
client.send('debug', debug)
|
||||||
|
}
|
||||||
|
|
||||||
|
$: updateDebug($debug)
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
IPC.off('device-info', writeAppInfo)
|
||||||
|
})
|
||||||
|
|
||||||
|
function writeAppInfo (info) {
|
||||||
|
const deviceInfo = JSON.parse(info)
|
||||||
|
deviceInfo.appInfo = {
|
||||||
|
version,
|
||||||
|
platform: window.version.platform,
|
||||||
|
userAgent: navigator.userAgent,
|
||||||
|
support: SUPPORTS,
|
||||||
|
settings
|
||||||
|
}
|
||||||
|
navigator.clipboard.writeText(JSON.stringify(deviceInfo, null, 2))
|
||||||
|
toast.success('Copied to clipboard', {
|
||||||
|
description: 'Copied device info to clipboard',
|
||||||
|
duration: 5000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC.on('device-info', writeAppInfo)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h4 class='mb-10 font-weight-bold'>Debug Settings</h4>
|
||||||
|
<SettingCard title='Logging Levels' description='Enable logging of specific parts of the app. These logs are saved to %appdata$/Miru/logs/main.log or ~/config/Miru/logs/main.log.'>
|
||||||
|
<select class='form-control bg-dark w-300 mw-full' bind:value={$debug}>
|
||||||
|
<option value='' selected>None</option>
|
||||||
|
<option value='*'>All</option>
|
||||||
|
<option value='torrent:worker,webtorrent:*,simple-peer,bittorrent-protocol,bittorrent-dht,bittorrent-lsd,torrent-discovery,bittorrent-tracker:*,ut_metadata,nat-pmp,nat-api'>Torrent</option>
|
||||||
|
<option value='ui:*'>Interface</option>
|
||||||
|
</select>
|
||||||
|
</SettingCard>
|
||||||
|
|
||||||
|
<SettingCard title='App and Device Info' description='Copy app and device debug info and capabilities, such as GPU information, GPU capabilities, version information and settings to clipboard.'>
|
||||||
|
<button type='button' use:click={() => IPC.emit('get-device-info')} class='btn btn-primary'>Copy To Clipboard</button>
|
||||||
|
</SettingCard>
|
||||||
|
|
||||||
|
{#if !SUPPORTS.isAndroid}
|
||||||
|
<SettingCard title='Log Output' description='Copy debug logs to clipboard. Once you enable a logging level you can use this to quickly copy the created logs to clipboard instead of navigating to the log file in directories.'>
|
||||||
|
<button type='button' use:click={() => IPC.emit('get-log-contents')} class='btn btn-primary'>Copy To Clipboard</button>
|
||||||
|
</SettingCard>
|
||||||
|
|
||||||
|
<SettingCard title='Open Torrent Devtools' description="Open devtools for the detached torrent process, this allows to inspect code execution and memory. DO NOT PASTE ANY CODE IN THERE, YOU'RE LIKELY BEING SCAMMED IF SOMEONE TELLS YOU TO!">
|
||||||
|
<button type='button' use:click={() => IPC.emit('torrent-devtools')} class='btn btn-primary'>Open Devtools</button>
|
||||||
|
</SettingCard>
|
||||||
|
|
||||||
|
<SettingCard title='Open UI Devtools' description="Open devtools for the UI process, this allows to inspect media playback information, rendering performance and more. DO NOT PASTE ANY CODE IN THERE, YOU'RE LIKELY BEING SCAMMED IF SOMEONE TELLS YOU TO!">
|
||||||
|
<button type='button' use:click={() => IPC.emit('ui-devtools')} class='btn btn-primary'>Open Devtools</button>
|
||||||
|
</SettingCard>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<h4 class='mb-10 font-weight-bold'>App Settings</h4>
|
||||||
<div class='d-inline-flex flex-column'>
|
<div class='d-inline-flex flex-column'>
|
||||||
<button
|
<button use:click={importSettings} class='btn btn-primary mt-10' type='button'>
|
||||||
use:click={importSettings}
|
|
||||||
class='btn btn-primary mx-20 mt-10'
|
|
||||||
type='button'>
|
|
||||||
Import Settings From Clipboard
|
Import Settings From Clipboard
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button use:click={exportSettings} class='btn btn-primary mt-10' type='button'>
|
||||||
use:click={exportSettings}
|
|
||||||
class='btn btn-primary mx-20 mt-10'
|
|
||||||
type='button'>
|
|
||||||
Export Settings To Clipboard
|
Export Settings To Clipboard
|
||||||
</button>
|
</button>
|
||||||
{#if SUPPORTS.update}
|
{#if SUPPORTS.update}
|
||||||
<button
|
<button use:click={checkUpdate} class='btn btn-primary mt-10' type='button'>
|
||||||
use:click={checkUpdate}
|
|
||||||
class='btn btn-primary mx-20 mt-10'
|
|
||||||
type='button'>
|
|
||||||
Check For Updates
|
Check For Updates
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
<button
|
<button
|
||||||
use:click={restoreSettigs}
|
use:click={restoreSettigs}
|
||||||
class='btn btn-danger mx-20 mt-10'
|
class='btn btn-danger mt-10'
|
||||||
type='button'
|
type='button'
|
||||||
data-toggle='tooltip'
|
data-toggle='tooltip'
|
||||||
data-placement='top'
|
data-placement='top'
|
||||||
|
|
|
||||||
|
|
@ -75,18 +75,18 @@
|
||||||
<button type='button' use:click={() => { homeSections[homeSections.length] = 'Trending Now' }} class='btn btn-primary'>Add Section</button>
|
<button type='button' use:click={() => { homeSections[homeSections.length] = 'Trending Now' }} class='btn btn-primary'>Add Section</button>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.ghost {
|
.ghost {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
position: absolute !important;
|
position: absolute !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tp {
|
.tp {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grab{
|
.grab{
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab>
|
<Tab>
|
||||||
<div class='root h-full w-full overflow-y-md-auto p-20' use:smoothScroll>
|
<div class='root h-full w-full overflow-y-md-auto p-20' use:smoothScroll>
|
||||||
<AppSettings />
|
<AppSettings {version} settings={$settings} />
|
||||||
<div class='h-250' />
|
<div class='h-250' />
|
||||||
</div>
|
</div>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
import Following from './Following.svelte'
|
import Following from './Following.svelte'
|
||||||
import smoothScroll from '@/modules/scroll.js'
|
import smoothScroll from '@/modules/scroll.js'
|
||||||
import IPC from '@/modules/ipc.js'
|
import IPC from '@/modules/ipc.js'
|
||||||
|
import { alToken } from '@/modules/settings.js'
|
||||||
|
|
||||||
const view = getContext('view')
|
const view = getContext('view')
|
||||||
function close () {
|
function close () {
|
||||||
|
|
@ -125,10 +126,10 @@
|
||||||
{playButtonText}
|
{playButtonText}
|
||||||
</button>
|
</button>
|
||||||
<div class='mt-20'>
|
<div class='mt-20'>
|
||||||
<button class='btn bg-dark btn-lg btn-square material-symbols-outlined font-size-20 shadow-none border-0' class:filled={media.isFavourite} use:click={toggleFavourite}>
|
<button class='btn bg-dark btn-lg btn-square material-symbols-outlined font-size-20 shadow-none border-0' class:filled={media.isFavourite} use:click={toggleFavourite} disabled={!alToken}>
|
||||||
favorite
|
favorite
|
||||||
</button>
|
</button>
|
||||||
<button class='btn bg-dark btn-lg btn-square ml-10 material-symbols-outlined font-size-20 shadow-none border-0' class:filled={media.mediaListEntry} use:click={toggleStatus}>
|
<button class='btn bg-dark btn-lg btn-square ml-10 material-symbols-outlined font-size-20 shadow-none border-0' class:filled={media.mediaListEntry} use:click={toggleStatus} disabled={!alToken}>
|
||||||
bookmark
|
bookmark
|
||||||
</button>
|
</button>
|
||||||
<button class='btn bg-dark btn-lg btn-square ml-10 material-symbols-outlined font-size-20 shadow-none border-0' use:click={() => copyToClipboard(`https://miru.watch/anime/${media.id}`)}>
|
<button class='btn bg-dark btn-lg btn-square ml-10 material-symbols-outlined font-size-20 shadow-none border-0' use:click={() => copyToClipboard(`https://miru.watch/anime/${media.id}`)}>
|
||||||
|
|
@ -181,7 +182,7 @@
|
||||||
<div class='ml-auto pl-20 font-size-12 more text-muted text-nowrap' use:click={() => { episodeOrder = !episodeOrder }}>Reverse</div>
|
<div class='ml-auto pl-20 font-size-12 more text-muted text-nowrap' use:click={() => { episodeOrder = !episodeOrder }}>Reverse</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class='col-lg-5 col-12 d-flex flex-column pl-lg-20'>
|
<div class='col-lg-5 col-12 d-flex flex-column pl-lg-20 overflow-x-hidden'>
|
||||||
<EpisodeList {media} {episodeOrder} userProgress={media.mediaListEntry?.status === 'CURRENT' && media.mediaListEntry.progress} watched={media.mediaListEntry?.status === 'COMPLETED'} episodeCount={getMediaMaxEp(media)} {play} />
|
<EpisodeList {media} {episodeOrder} userProgress={media.mediaListEntry?.status === 'CURRENT' && media.mediaListEntry.progress} watched={media.mediaListEntry?.status === 'COMPLETED'} episodeCount={getMediaMaxEp(media)} {play} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -218,7 +219,7 @@
|
||||||
aspect-ratio: 7/10;
|
aspect-ratio: 7/10;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.bg-dark:hover {
|
button.bg-dark:not([disabled]):hover {
|
||||||
background: #292d33 !important;
|
background: #292d33 !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "Miru",
|
"name": "Miru",
|
||||||
"version": "5.2.16",
|
"version": "5.3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"author": "ThaUnknown_ <ThaUnknown@users.noreply.github.com>",
|
"author": "ThaUnknown_ <ThaUnknown@users.noreply.github.com>",
|
||||||
"description": "Stream anime torrents, real-time with no waiting for downloads.",
|
"description": "Stream anime torrents, real-time with no waiting for downloads.",
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import Protocol from './protocol.js'
|
||||||
import Updater from './updater.js'
|
import Updater from './updater.js'
|
||||||
import Dialog from './dialog.js'
|
import Dialog from './dialog.js'
|
||||||
import store from './store.js'
|
import store from './store.js'
|
||||||
|
import Debug from './debugger.js'
|
||||||
|
|
||||||
export default class App {
|
export default class App {
|
||||||
webtorrentWindow = new BrowserWindow({
|
webtorrentWindow = new BrowserWindow({
|
||||||
|
|
@ -50,6 +51,7 @@ export default class App {
|
||||||
protocol = new Protocol(this.mainWindow)
|
protocol = new Protocol(this.mainWindow)
|
||||||
updater = new Updater(this.mainWindow, this.webtorrentWindow)
|
updater = new Updater(this.mainWindow, this.webtorrentWindow)
|
||||||
dialog = new Dialog(this.webtorrentWindow)
|
dialog = new Dialog(this.webtorrentWindow)
|
||||||
|
debug = new Debug()
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
this.mainWindow.setMenuBarVisibility(false)
|
this.mainWindow.setMenuBarVisibility(false)
|
||||||
|
|
@ -57,7 +59,8 @@ export default class App {
|
||||||
this.mainWindow.once('ready-to-show', () => this.mainWindow.show())
|
this.mainWindow.once('ready-to-show', () => this.mainWindow.show())
|
||||||
this.mainWindow.on('minimize', () => this.mainWindow.webContents.postMessage('visibilitychange', 'hidden'))
|
this.mainWindow.on('minimize', () => this.mainWindow.webContents.postMessage('visibilitychange', 'hidden'))
|
||||||
this.mainWindow.on('restore', () => this.mainWindow.webContents.postMessage('visibilitychange', 'visible'))
|
this.mainWindow.on('restore', () => this.mainWindow.webContents.postMessage('visibilitychange', 'visible'))
|
||||||
ipcMain.on('devtools', () => this.webtorrentWindow.webContents.openDevTools())
|
ipcMain.on('torrent-devtools', () => this.webtorrentWindow.webContents.openDevTools())
|
||||||
|
ipcMain.on('ui-devtools', ({ sender }) => sender.openDevTools())
|
||||||
|
|
||||||
this.mainWindow.on('closed', () => this.destroy())
|
this.mainWindow.on('closed', () => this.destroy())
|
||||||
ipcMain.on('close', () => this.destroy())
|
ipcMain.on('close', () => this.destroy())
|
||||||
|
|
|
||||||
29
electron/src/main/debugger.js
Normal file
29
electron/src/main/debugger.js
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { readFile } from 'node:fs/promises'
|
||||||
|
import os from 'node:os'
|
||||||
|
import { app, ipcMain } from 'electron'
|
||||||
|
import log from 'electron-log'
|
||||||
|
import { autoUpdater } from 'electron-updater'
|
||||||
|
|
||||||
|
log.initialize({ spyRendererConsole: true })
|
||||||
|
log.transports.file.level = 'info'
|
||||||
|
log.transports.file.maxSize = 10485760 // 10MB
|
||||||
|
autoUpdater.logger = log
|
||||||
|
|
||||||
|
export default class Debug {
|
||||||
|
constructor () {
|
||||||
|
ipcMain.on('get-log-contents', async ({ sender }) => {
|
||||||
|
sender.send('log-contents', await readFile(log.transports.file.getFile().path, 'utf8'))
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.on('get-device-info', async ({ sender }) => {
|
||||||
|
const { model, speed } = os.cpus()[0]
|
||||||
|
const deviceInfo = {
|
||||||
|
features: app.getGPUFeatureStatus(),
|
||||||
|
info: await app.getGPUInfo('complete'),
|
||||||
|
cpu: { model, speed },
|
||||||
|
ram: os.totalmem()
|
||||||
|
}
|
||||||
|
sender.send('device-info', JSON.stringify(deviceInfo))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
import log from 'electron-log'
|
|
||||||
import { autoUpdater } from 'electron-updater'
|
import { autoUpdater } from 'electron-updater'
|
||||||
import { ipcMain, shell } from 'electron'
|
import { ipcMain, shell } from 'electron'
|
||||||
|
|
||||||
log.initialize({ spyRendererConsole: true })
|
|
||||||
log.transports.file.level = 'info'
|
|
||||||
autoUpdater.logger = log
|
|
||||||
ipcMain.on('update', () => {
|
ipcMain.on('update', () => {
|
||||||
autoUpdater.checkForUpdatesAndNotify()
|
autoUpdater.checkForUpdatesAndNotify()
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,9 @@ importers:
|
||||||
'@capacitor/core':
|
'@capacitor/core':
|
||||||
specifier: ^6.1.1
|
specifier: ^6.1.1
|
||||||
version: 6.1.1
|
version: 6.1.1
|
||||||
|
'@capacitor/device':
|
||||||
|
specifier: ^6.0.1
|
||||||
|
version: 6.0.1(@capacitor/core@6.1.1)
|
||||||
'@capacitor/ios':
|
'@capacitor/ios':
|
||||||
specifier: ^6.1.1
|
specifier: ^6.1.1
|
||||||
version: 6.1.1(@capacitor/core@6.1.1)
|
version: 6.1.1(@capacitor/core@6.1.1)
|
||||||
|
|
@ -156,7 +159,7 @@ importers:
|
||||||
version: 4.4.1
|
version: 4.4.1
|
||||||
jassub:
|
jassub:
|
||||||
specifier: latest
|
specifier: latest
|
||||||
version: 1.7.15
|
version: 1.7.16
|
||||||
js-levenshtein:
|
js-levenshtein:
|
||||||
specifier: ^1.1.6
|
specifier: ^1.1.6
|
||||||
version: 1.1.6
|
version: 1.1.6
|
||||||
|
|
@ -187,6 +190,9 @@ importers:
|
||||||
svelte-miniplayer:
|
svelte-miniplayer:
|
||||||
specifier: ^1.0.5
|
specifier: ^1.0.5
|
||||||
version: 1.0.5
|
version: 1.0.5
|
||||||
|
svelte-persisted-store:
|
||||||
|
specifier: ^0.11.0
|
||||||
|
version: 0.11.0(svelte@4.2.12)
|
||||||
svelte-sonner:
|
svelte-sonner:
|
||||||
specifier: ^0.3.19
|
specifier: ^0.3.19
|
||||||
version: 0.3.19(svelte@4.2.12)
|
version: 0.3.19(svelte@4.2.12)
|
||||||
|
|
@ -400,6 +406,14 @@ packages:
|
||||||
tslib: 2.6.3
|
tslib: 2.6.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@capacitor/device@6.0.1(@capacitor/core@6.1.1):
|
||||||
|
resolution: {integrity: sha512-Tlz67DAO5GKb5YAfupXiENZxDww6mHnG9iKI+8D5SVF82VLpEv5r9qwKtiounuQB2y2HWiHV8tlOk7DqnLVUqQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@capacitor/core': ^6.0.0
|
||||||
|
dependencies:
|
||||||
|
'@capacitor/core': 6.1.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@capacitor/ios@6.1.1(@capacitor/core@6.1.1):
|
/@capacitor/ios@6.1.1(@capacitor/core@6.1.1):
|
||||||
resolution: {integrity: sha512-he6+Fhj6x1dSnOzM98xaPvioOU8MNO+qpodCJwnHE3mIRonTpFutXJK8DP8fnNhjDPk2km9VafLSfOeiZXNv3Q==}
|
resolution: {integrity: sha512-he6+Fhj6x1dSnOzM98xaPvioOU8MNO+qpodCJwnHE3mIRonTpFutXJK8DP8fnNhjDPk2km9VafLSfOeiZXNv3Q==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|
@ -1383,7 +1397,7 @@ packages:
|
||||||
vite: ^5.0.0
|
vite: ^5.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sveltejs/vite-plugin-svelte': 3.0.2(svelte@4.2.12)(vite@5.1.5)
|
'@sveltejs/vite-plugin-svelte': 3.0.2(svelte@4.2.12)(vite@5.1.5)
|
||||||
debug: 4.3.4
|
debug: 4.3.6
|
||||||
svelte: 4.2.12
|
svelte: 4.2.12
|
||||||
vite: 5.1.5
|
vite: 5.1.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
|
@ -6017,8 +6031,8 @@ packages:
|
||||||
minimatch: 3.1.2
|
minimatch: 3.1.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/jassub@1.7.15:
|
/jassub@1.7.16:
|
||||||
resolution: {integrity: sha512-8yKAJc++Y1gNfATOPRo3APk0JUhshKl5l7bRkT6WkJ8XP4RvYfVPb6ieH6WDxsMq523exwGzNvjjPEEWT+Z1nQ==}
|
resolution: {integrity: sha512-JDzKtGqQeApqQtEDWM5KzuGs5HKvUOWS35HXKaka6MuHV8HxLfW/F77HDE6gsZVn+Vuhh8omzXv6THevF6z5Jg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
rvfc-polyfill: 1.0.7
|
rvfc-polyfill: 1.0.7
|
||||||
dev: false
|
dev: false
|
||||||
|
|
@ -8705,6 +8719,15 @@ packages:
|
||||||
resolution: {integrity: sha512-jzYqqBuXcSH5KzoPDlYQL6CQVbpY2LQB4/wBPG4T5R75wE8Dqu4auMU6NnJxHBRhgNCGlH+XBQvxy9G6yX/XQw==}
|
resolution: {integrity: sha512-jzYqqBuXcSH5KzoPDlYQL6CQVbpY2LQB4/wBPG4T5R75wE8Dqu4auMU6NnJxHBRhgNCGlH+XBQvxy9G6yX/XQw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/svelte-persisted-store@0.11.0(svelte@4.2.12):
|
||||||
|
resolution: {integrity: sha512-9RgJ5DrawGyyfK22A80cfu8Jose3CV8YjEZKz9Tn94rQ0tWyEmYr+XI+wrVF6wjRbW99JMDSVcFRiM3XzVJj/w==}
|
||||||
|
engines: {node: '>=0.14'}
|
||||||
|
peerDependencies:
|
||||||
|
svelte: ^3.48.0 || ^4.0.0 || ^5.0.0-next.0
|
||||||
|
dependencies:
|
||||||
|
svelte: 4.2.12
|
||||||
|
dev: false
|
||||||
|
|
||||||
/svelte-preprocess@5.1.4(postcss@8.4.38)(svelte@4.2.12)(typescript@5.3.3):
|
/svelte-preprocess@5.1.4(postcss@8.4.38)(svelte@4.2.12)(typescript@5.3.3):
|
||||||
resolution: {integrity: sha512-IvnbQ6D6Ao3Gg6ftiM5tdbR6aAETwjhHV+UKGf5bHGYR69RQvF1ho0JKPcbUON4vy4R7zom13jPjgdOWCQ5hDA==}
|
resolution: {integrity: sha512-IvnbQ6D6Ao3Gg6ftiM5tdbR6aAETwjhHV+UKGf5bHGYR69RQvF1ho0JKPcbUON4vy4R7zom13jPjgdOWCQ5hDA==}
|
||||||
engines: {node: '>= 16.0.0'}
|
engines: {node: '>= 16.0.0'}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { formatMap } from './anime.js'
|
import { formatMap } from './anime.js'
|
||||||
import { click } from '@/modules/click.js'
|
import { click } from '@/modules/click.js'
|
||||||
|
import { alToken } from '@/modules/settings.js'
|
||||||
export let media
|
export let media
|
||||||
|
|
||||||
let hide = true
|
let hide = true
|
||||||
|
|
@ -82,10 +83,10 @@
|
||||||
</span>
|
</span>
|
||||||
{playButtonText}
|
{playButtonText}
|
||||||
</button>
|
</button>
|
||||||
<button class='btn btn-square ml-10 material-symbols-outlined font-size-16 shadow-none border-0' class:filled={media.isFavourite} use:click={noop}>
|
<button class='btn btn-square ml-10 material-symbols-outlined font-size-16 shadow-none border-0' class:filled={media.isFavourite} use:click={noop} disabled={!alToken}>
|
||||||
favorite
|
favorite
|
||||||
</button>
|
</button>
|
||||||
<button class='btn btn-square ml-10 material-symbols-outlined font-size-16 shadow-none border-0' class:filled={media.mediaListEntry} use:click={noop}>
|
<button class='btn btn-square ml-10 material-symbols-outlined font-size-16 shadow-none border-0' class:filled={media.mediaListEntry} use:click={noop} disabled={!alToken}>
|
||||||
bookmark
|
bookmark
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue