diff --git a/README.md b/README.md index b1196ab..37cb7db 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ apt install linux-Miru-*.deb ## **Building and Development** -*dont* +*good luck* Dependencies: - Node 16 or above diff --git a/capacitor/android/app/src/main/AndroidManifest.xml b/capacitor/android/app/src/main/AndroidManifest.xml index 628c532..0afb5c8 100644 --- a/capacitor/android/app/src/main/AndroidManifest.xml +++ b/capacitor/android/app/src/main/AndroidManifest.xml @@ -54,6 +54,8 @@ + + diff --git a/capacitor/package.json b/capacitor/package.json index 0b2860b..79a5158 100644 --- a/capacitor/package.json +++ b/capacitor/package.json @@ -18,24 +18,27 @@ }, "devDependencies": { "@capacitor/assets": "github:thaunknown/capacitor-assets", - "@capacitor/cli": "^6.1.1", + "@capacitor/cli": "^6.1.2", "cordova-res": "^0.15.4", - "nodejs-mobile-gyp": "^0.3.1", + "nodejs-mobile-gyp": "^0.4.0", "npm-run-all": "^4.1.5", "webpack-cli": "^5.1.4", - "webpack-merge": "^5.10.0" + "webpack-merge": "^6.0.1" }, "dependencies": { - "@capacitor/android": "^6.1.1", - "@capacitor/app": "^6.0.0", - "@capacitor/browser": "^6.0.1", - "@capacitor/core": "^6.1.1", + "@capacitor/android": "^6.1.2", + "@capacitor/app": "^6.0.1", + "@capacitor/app-launcher": "^6.0.2", + "@capacitor/browser": "^6.0.2", + "@capacitor/core": "^6.1.2", "@capacitor/device": "^6.0.1", - "@capacitor/ios": "^6.1.1", - "@capacitor/local-notifications": "^6.0.0", - "@capacitor/status-bar": "^6.0.0", + "@capacitor/ios": "^6.1.2", + "@capacitor/local-notifications": "^6.1.0", + "@capacitor/status-bar": "^6.0.1", + "capacitor-folder-picker": "^0.0.2", + "capacitor-intent-uri": "^0.0.1", "capacitor-nodejs": "https://github.com/funniray/Capacitor-NodeJS/releases/download/nodejs-18/capacitor-nodejs-1.0.0-beta.6.tgz", - "capacitor-plugin-safe-area": "^2.0.6", + "capacitor-plugin-safe-area": "^3.0.3", "common": "workspace:*", "cordova-plugin-navigationbar": "^1.0.31", "cordova-plugin-pip": "^0.0.2", diff --git a/capacitor/src/capacitor.js b/capacitor/src/capacitor.js index dae06f2..92d0335 100644 --- a/capacitor/src/capacitor.js +++ b/capacitor/src/capacitor.js @@ -3,11 +3,18 @@ import { StatusBar, Style } from '@capacitor/status-bar' import { SafeArea } from 'capacitor-plugin-safe-area' import { App } from '@capacitor/app' import { Browser } from '@capacitor/browser' +import { IntentUri } from 'capacitor-intent-uri' import { LocalNotifications } from '@capacitor/local-notifications' import { Device } from '@capacitor/device' +import { FolderPicker } from 'capacitor-folder-picker' +import { toast } from 'svelte-sonner' import IPC from './ipc.js' IPC.on('open', url => Browser.open({ url })) +IPC.on('intent', async url => { + await IntentUri.openUri({ url }) + IPC.emit('intent-end') +}) App.addListener('appUrlOpen', ({ url }) => handleProtocol(url)) @@ -51,6 +58,30 @@ IPC.on('get-device-info', async () => { IPC.emit('device-info', JSON.stringify(deviceInfo)) }) +const STORAGE_TYPE_MAP = { + primary: '/sdcard/', + secondary: '/sdcard/' +} + +IPC.on('dialog', async () => { + const result = await FolderPicker.chooseFolder() + const normalizedPath = decodeURIComponent(result.path) + + const [, uri, ...path] = normalizedPath.split(':') + const [,, app, subpath, type, ...rest] = uri.split('/') + + if (app !== 'com.android.externalstorage.documents') return toast.error('Unverified app', { description: 'Expected com.android.externalstorage.documents, got: ' + app }) + if (rest.length) return toast.error('Unsupported uri', { description: 'Unxpected access type, got: tree/' + rest.join('/') }) + if (subpath !== 'tree') return toast.error('Unsupported subpath type', { description: 'Expected tree subpath, got: ' + subpath }) + + let base = STORAGE_TYPE_MAP[type] + if (!base) { + if (!/[a-z0-9]{4}-[a-z0-9]{4}/i.test(type)) return toast.error('Unsupported storage type') + base = `/storage/${type}/` + } + IPC.emit('path', base + path.join('')) +}) + // schema: miru://key/value const protocolMap = { auth: token => sendToken(token), diff --git a/capacitor/src/main.js b/capacitor/src/main.js index 758115b..e851b74 100644 --- a/capacitor/src/main.js +++ b/capacitor/src/main.js @@ -16,6 +16,8 @@ if (typeof localStorage === 'undefined') { } } +let client + channel.on('port-init', data => { localStorage.setItem('settings', data) const port = { @@ -24,11 +26,18 @@ channel.on('port-init', data => { channel.send('ipc', { data }) } } + let storedSettings = {} + + try { + storedSettings = JSON.parse(localStorage.getItem('settings')) || {} + } catch (error) {} channel.on('ipc', a => port.onmessage(a)) - channel.emit('port', { - ports: [port] - }) -}) + if (!client) { + client = new TorrentClient(channel, storageQuota, 'node', storedSettings.torrentPathNew || env.TMPDIR) -globalThis.client = new TorrentClient(channel, storageQuota, 'node', env.TMPDIR) + channel.emit('port', { + ports: [port] + }) + } +}) diff --git a/capacitor/src/support.js b/capacitor/src/support.js index 4a06c6f..233ecc0 100644 --- a/capacitor/src/support.js +++ b/capacitor/src/support.js @@ -5,11 +5,7 @@ export const SUPPORTS = { update: false, angle: false, doh: false, - dht: true, discord: false, - torrentPort: true, - torrentPath: false, - torrentPersist: false, keybinds: false, isAndroid: true, externalPlayer: false, diff --git a/common/App.svelte b/common/App.svelte index 6f003b7..dc1a2b2 100644 --- a/common/App.svelte +++ b/common/App.svelte @@ -3,6 +3,7 @@ import { writable } from 'simple-store-svelte' import { anilistClient } from '@/modules/anilist.js' import IPC from '@/modules/ipc.js' + import { rss } from './views/TorrentSearch/TorrentModal.svelte' export const page = writable('home') export const overlay = writable('none') @@ -15,6 +16,37 @@ IPC.on('schedule', () => { page.set('schedule') }) + + let ignoreNext = false + function addPage (value, type) { + if (ignoreNext) { + ignoreNext = false + return + } + history.pushState({ type, value }, '', location.origin + location.pathname + '?id=' + Math.trunc(Math.random() * Number.MAX_SAFE_INTEGER).toString()) + } + page.subscribe((value) => { + addPage(value, 'page') + }) + view.subscribe((value) => { + addPage(value, 'view') + }) + + addPage('home', 'page') + + window.addEventListener('popstate', e => { + const { state } = e + if (!state) return + ignoreNext = true + view.set(null) + rss.set(null) + if (document.fullscreenElement) document.exitFullscreen() + if (state.type === 'page') { + page.set(state.value) + } else { + view.set(state.value) + } + }) -
+
-
title
+
Title
- search +
-
theater_comedy
+
Genres
@@ -392,7 +390,7 @@
-
tag
+
Tags
@@ -417,7 +415,7 @@
-
calendar_month
+
Season
@@ -439,7 +437,7 @@
-
monitor
+
Format
@@ -455,7 +453,7 @@
-
live_tv
+
Status
@@ -470,7 +468,7 @@
-
sort
+
Sort
@@ -497,13 +495,15 @@
@@ -511,30 +511,36 @@
-
-
@@ -546,14 +552,14 @@ {@const filteredBadges = sanitisedSearch.filter(badge => badge.key !== 'hideStatus' && (search.userList || badge.key !== 'title'))}
{#if filteredBadges.length > 0} - sell + {/if} {#each badgeKeys as key} {@const matchingBadges = filteredBadges.filter(badge => badge.key === key)} {#each matchingBadges as badge} {#if badge.key === key && (badge.key !== 'hideStatus' && (search.userList || badge.key !== 'title')) }
-
{getBadgeDisplayName(badge.key)}
+
{badge.key === 'sort' ? getSortDisplayName(badge.value) : (badge.key === 'hideMyAnime' ? 'Hide My Anime' : badge.key === 'hideSubs' ? 'Dubbed' : ('' + badge.value).replace(/_/g, ' ').toLowerCase())}
@@ -564,15 +570,12 @@ {/if}
- changeCardMode('small')}>grid_on - changeCardMode('full')}>grid_view + changeCardMode('small')}> + changeCardMode('full')}>
diff --git a/common/components/banner/FullBanner.svelte b/common/components/banner/FullBanner.svelte index eec1252..7700498 100644 --- a/common/components/banner/FullBanner.svelte +++ b/common/components/banner/FullBanner.svelte @@ -5,6 +5,8 @@ import Scoring from '@/views/ViewAnime/Scoring.svelte' import AudioLabel from '@/views/ViewAnime/AudioLabel.svelte' import Helper from "@/modules/helper.js" + import { Heart } from 'lucide-svelte' + export let mediaList let current = mediaList[0] @@ -92,8 +94,8 @@ use:click={() => playMedia(current)}> Watch Now -
diff --git a/common/components/cards/EpisodeCard.svelte b/common/components/cards/EpisodeCard.svelte index 191b5f2..78766e6 100644 --- a/common/components/cards/EpisodeCard.svelte +++ b/common/components/cards/EpisodeCard.svelte @@ -8,6 +8,7 @@ import { getContext } from 'svelte' import { liveAnimeEpisodeProgress } from '@/modules/animeprogress.js' import { anilistClient } from '@/modules/anilist.js' + import { Play } from 'lucide-svelte' export let data let preview = false @@ -37,19 +38,14 @@ const completed = !watched && media?.mediaListEntry?.progress >= data?.episode -
prompt.set(false)} use:hoverClick={[setClickState, setHoverState]}> +
prompt.set(false)} use:hoverClick={[setClickState, setHoverState]} on:contextmenu|preventDefault={viewMedia} role='none'> {#if preview} {/if}
cover - {#if data.failed} -
- sync_problem -
- {/if} -
play_arrow
+
{#if media?.duration} {media.duration}m @@ -122,9 +118,6 @@ .opacity-half { opacity: 30%; } - .material-symbols-outlined { - font-size: 3rem; - } .title { display: -webkit-box; -webkit-line-clamp: 1; diff --git a/common/components/cards/EpisodePreviewCard.svelte b/common/components/cards/EpisodePreviewCard.svelte index ee77643..8a75f6b 100644 --- a/common/components/cards/EpisodePreviewCard.svelte +++ b/common/components/cards/EpisodePreviewCard.svelte @@ -6,6 +6,8 @@ import { anilistClient } from "@/modules/anilist" import AudioLabel from '@/views/ViewAnime/AudioLabel.svelte' import { getContext } from 'svelte' + import { CalendarDays, Play, Tv } from 'lucide-svelte' + export let data export let prompt /** @type {import('@/modules/al.d.ts').Media | null} */ @@ -38,12 +40,7 @@ on:loadeddata={() => { hide = false }} autoplay /> {/if} - {#if data.failed} -
- sync_problem -
- {/if} -
play_arrow
+
{#if media?.duration} {media.duration}m @@ -111,13 +108,13 @@
{#if media}
-
- calendar_month - {media.seasonYear || 'N/A'} +
+ + {media.seasonYear || 'N/A'}
- {formatMap[media.format]} - monitor + {formatMap[media.format]} +
{/if} @@ -128,9 +125,7 @@ use:click={() => { data.onclick() || viewMedia() }}> - - play_arrow - + Continue Anyway?
@@ -140,9 +135,6 @@ .overlay { background-color: rgba(28, 28, 28, 0.9); } - .material-symbols-outlined { - font-size: 3rem; - } .description { display: -webkit-box !important; -webkit-line-clamp: 3; diff --git a/common/components/cards/PreviewCard.svelte b/common/components/cards/PreviewCard.svelte index 1efb31c..b0a441b 100644 --- a/common/components/cards/PreviewCard.svelte +++ b/common/components/cards/PreviewCard.svelte @@ -5,6 +5,8 @@ import Scoring from '@/views/ViewAnime/Scoring.svelte' import AudioLabel from '@/views/ViewAnime/AudioLabel.svelte' import Helper from "@/modules/helper.js" + import { Heart, Play, VolumeX, Volume2, ThumbsUp, ThumbsDown } from 'lucide-svelte' + /** @type {import('@/modules/al.d.ts').Media} */ export let media export let type = null @@ -37,9 +39,6 @@ playMedia(media) } - function volume (video) { - video.volume = 0.1 - } let muted = true function toggleMute () { muted = !muted @@ -47,13 +46,19 @@
-