mirror of
https://github.com/ThaUnknown/miru.git
synced 2026-03-11 22:15:35 +00:00
feat: enable rescan and deletion
perf: in-world messageport
This commit is contained in:
parent
e7b168ca09
commit
ced4296fae
6 changed files with 1277 additions and 32 deletions
|
|
@ -42,6 +42,7 @@
|
|||
"svelte-radix": "^1.1.1",
|
||||
"svelte-sonner": "^0.3.28",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"torrent-client": "github:hayase-app/torrent-client",
|
||||
"typescript": "^5.9.2",
|
||||
"vaul-svelte": "^0.3.2",
|
||||
"vite": "^5.4.11",
|
||||
|
|
|
|||
1193
pnpm-lock.yaml
1193
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
|
@ -112,16 +112,13 @@
|
|||
}
|
||||
|
||||
const { filterValue } = pluginStates.filter
|
||||
const { selectedDataIds } = pluginStates.select
|
||||
const { selectedDataIds, someRowsSelected } = pluginStates.select
|
||||
|
||||
function getSelected () {
|
||||
return Object.keys($selectedDataIds).map(id => $lib[id as unknown as number]?.hash).filter(e => e) as string[]
|
||||
}
|
||||
|
||||
// TODO: enable
|
||||
function rescanTorrents () {
|
||||
return null
|
||||
// eslint-disable-next-line no-unreachable
|
||||
toast.promise(native.rescanTorrents(getSelected()), {
|
||||
loading: 'Rescanning torrents...',
|
||||
success: 'Rescan complete',
|
||||
|
|
@ -129,21 +126,23 @@
|
|||
console.error(e)
|
||||
return 'Failed to rescan torrents\n' + ('stack' in (e as object) ? (e as Error).stack : 'Unknown error')
|
||||
},
|
||||
description: 'This may take a long while depending on the number of torrents.'
|
||||
description: 'This may take a VERY long while depending on the number of torrents.'
|
||||
})
|
||||
}
|
||||
function deleteTorrents () {
|
||||
return null
|
||||
// eslint-disable-next-line no-unreachable
|
||||
toast.promise(native.deleteTorrents(getSelected()), {
|
||||
loading: 'Deleting torrents...',
|
||||
success: 'Torrents deleted',
|
||||
error: e => {
|
||||
console.error(e)
|
||||
return 'Failed to delete torrents\n' + ('stack' in (e as object) ? (e as Error).stack : 'Unknown error')
|
||||
},
|
||||
description: 'This may take a while depending on the number of torrents.'
|
||||
})
|
||||
toast.promise(
|
||||
native.deleteTorrents(getSelected())
|
||||
.then(() => server.updateLibrary()
|
||||
.then(() => pluginStates.select.allPageRowsSelected.set(false))
|
||||
), {
|
||||
loading: 'Deleting torrents...',
|
||||
success: 'Torrents deleted',
|
||||
error: e => {
|
||||
console.error(e)
|
||||
return 'Failed to delete torrents\n' + ('stack' in (e as object) ? (e as Error).stack : 'Unknown error')
|
||||
},
|
||||
description: 'This may take a while depending on the library size.'
|
||||
})
|
||||
}
|
||||
|
||||
// TODO once new resolver is implemented
|
||||
|
|
@ -158,10 +157,10 @@
|
|||
bind:value={$filterValue} />
|
||||
<MagnifyingGlass class='h-4 w-4 shrink-0 opacity-50 absolute left-3 text-muted-foreground z-10 pointer-events-none' />
|
||||
</div>
|
||||
<Button variant='secondary' size='icon' class='border-0 animated-icon !pointer-events-auto cursor-not-allowed' disabled on:click={rescanTorrents}>
|
||||
<Button variant='secondary' size='icon' class='border-0 animated-icon' on:click={rescanTorrents} disabled={!$someRowsSelected}>
|
||||
<FolderSync class={cn('size-4')} />
|
||||
</Button>
|
||||
<Button variant='destructive' size='icon' class='border-0 animated-icon !pointer-events-auto cursor-not-allowed' disabled on:click={deleteTorrents}>
|
||||
<Button variant='destructive' size='icon' class='border-0 animated-icon' on:click={deleteTorrents} disabled={!$someRowsSelected}>
|
||||
<Trash class={cn('size-4')} />
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import SUPPORTS from './settings/supports'
|
|||
|
||||
import type { AuthResponse, Native, TorrentInfo } from 'native'
|
||||
|
||||
import electron from '$lib/modules/torrent/electron'
|
||||
import { sleep } from '$lib/utils'
|
||||
|
||||
const rnd = (range = 100) => Math.floor(Math.random() * range)
|
||||
|
|
@ -50,7 +51,7 @@ const dummyFiles = [
|
|||
// dummyPeerInfo.push(makeRandomPeer())
|
||||
// }
|
||||
|
||||
export default Object.assign<Native, Partial<Native>>({
|
||||
const native: Native = {
|
||||
authAL: (url: string) => {
|
||||
return new Promise<AuthResponse>((resolve, reject) => {
|
||||
const popup = open(url, 'authframe', SUPPORTS.isAndroid ? 'popup' : 'popup,width=382,height=582')
|
||||
|
|
@ -130,8 +131,8 @@ export default Object.assign<Native, Partial<Native>>({
|
|||
version: async () => 'v6.4.4',
|
||||
updateSettings: async () => undefined,
|
||||
setDOH: async () => undefined,
|
||||
cachedTorrents: async () => ['40a9047de61859035659e449d7b84286934486b0'],
|
||||
spawnPlayer: () => sleep(rnd(100_000)),
|
||||
cachedTorrents: async () => [],
|
||||
spawnPlayer: async () => undefined,
|
||||
setHideToTray: async () => undefined,
|
||||
transparency: async () => undefined,
|
||||
setZoom: async () => undefined,
|
||||
|
|
@ -162,6 +163,10 @@ export default Object.assign<Native, Partial<Native>>({
|
|||
defaultTransparency: () => false,
|
||||
errors: async () => undefined,
|
||||
debug: async () => undefined,
|
||||
profile: async () => undefined
|
||||
profile: async () => undefined,
|
||||
// @ts-expect-error idk
|
||||
}, globalThis.native as Partial<Native>)
|
||||
...globalThis.native as Partial<Native>,
|
||||
...electron
|
||||
}
|
||||
|
||||
export default native
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import Debug from 'debug'
|
||||
import { readable, writable } from 'simple-store-svelte'
|
||||
import { writable } from 'simple-store-svelte'
|
||||
import { get } from 'svelte/store'
|
||||
import { persisted } from 'svelte-persisted-store'
|
||||
|
||||
|
|
@ -30,18 +30,18 @@ export const server = new class ServerClient {
|
|||
active = writable<Promise<{ media: Media, id: string, episode: number, files: TorrentFile[] } | null>>()
|
||||
downloaded = writable(this.cachedSet())
|
||||
|
||||
stats = this._timedSafeReadable(defaultTorrentInfo, native.torrentInfo, SUPPORTS.isUnderPowered ? 3000 : 200)
|
||||
stats = this._timedSafeStore(defaultTorrentInfo, native.torrentInfo, SUPPORTS.isUnderPowered ? 3000 : 200)
|
||||
|
||||
protocol = this._timedSafeReadable(defaultProtocolStatus, native.protocolStatus)
|
||||
protocol = this._timedSafeStore(defaultProtocolStatus, native.protocolStatus)
|
||||
|
||||
peers = this._timedSafeReadable([], native.peerInfo)
|
||||
peers = this._timedSafeStore([], native.peerInfo)
|
||||
|
||||
files = this._timedSafeReadable([], native.fileInfo)
|
||||
files = this._timedSafeStore([], native.fileInfo)
|
||||
|
||||
library = this._timedSafeReadable([], native.library, 120_000)
|
||||
library = this._timedSafeStore([], native.library, 120_000)
|
||||
|
||||
_timedSafeReadable<T> (defaultData: T, fn: (id: string) => Promise<T>, duration = SUPPORTS.isUnderPowered ? 15000 : 5000) {
|
||||
return readable<T>(defaultData, set => {
|
||||
_timedSafeStore<T> (defaultData: T, fn: (id: string) => Promise<T>, duration = SUPPORTS.isUnderPowered ? 15000 : 5000) {
|
||||
return writable<T>(defaultData, set => {
|
||||
let listener = 0
|
||||
|
||||
const update = async () => {
|
||||
|
|
@ -71,6 +71,12 @@ export const server = new class ServerClient {
|
|||
})
|
||||
}
|
||||
|
||||
async updateLibrary () {
|
||||
const library = native.library()
|
||||
this.downloaded.value = library.then(lib => new Set(lib.map(({ hash }) => hash)))
|
||||
this.library.value = await library
|
||||
}
|
||||
|
||||
async cachedSet () {
|
||||
debug('fetching cached torrents')
|
||||
return new Set(await native.cachedTorrents())
|
||||
|
|
|
|||
43
src/lib/modules/torrent/electron.ts
Normal file
43
src/lib/modules/torrent/electron.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { proxy, type Remote } from 'abslink'
|
||||
import { wrap } from 'abslink/w3c'
|
||||
|
||||
import type { Native } from 'native'
|
||||
import type TorrentClient from 'torrent-client'
|
||||
|
||||
const torrent = new Promise<Remote<TorrentClient>>(resolve => {
|
||||
window.addEventListener('message', ({ data, ports, source }) => {
|
||||
if (data === 'TORRENT_PORT' && ports[0] && source === window) {
|
||||
ports[0].start()
|
||||
resolve(wrap<TorrentClient>(ports[0]) as unknown as Remote<TorrentClient>)
|
||||
}
|
||||
})
|
||||
// @ts-expect-error custom
|
||||
window.sendPort?.()
|
||||
})
|
||||
|
||||
const electronNative: Partial<Native> =
|
||||
// @ts-expect-error custom
|
||||
window.sendPort
|
||||
? {
|
||||
checkAvailableSpace: async () => await (await torrent).checkAvailableSpace(),
|
||||
checkIncomingConnections: async (port) => await (await torrent).checkIncomingConnections(port),
|
||||
updatePeerCounts: async (hashes) => await (await torrent).scrape(hashes),
|
||||
playTorrent: async (id, mediaID, episode) => await (await torrent).playTorrent(id, mediaID, episode),
|
||||
rescanTorrents: async (hashes) => await (await torrent).rescanTorrents(hashes),
|
||||
deleteTorrents: async (hashes) => await (await torrent).deleteTorrents(hashes),
|
||||
library: async () => await (await torrent).library(),
|
||||
attachments: async (hash, id) => await (await torrent).attachments.attachments(hash, id),
|
||||
tracks: async (hash, id) => await (await torrent).attachments.tracks(hash, id),
|
||||
subtitles: async (hash, id, cb) => await (await torrent).attachments.subtitle(hash, id, proxy(cb)),
|
||||
errors: async (cb) => await (await torrent).errors(proxy(cb)),
|
||||
chapters: async (hash, id) => await (await torrent).attachments.chapters(hash, id),
|
||||
torrentInfo: async (hash) => await (await torrent).torrentInfo(hash),
|
||||
peerInfo: async (hash) => await (await torrent).peerInfo(hash),
|
||||
fileInfo: async (hash) => await (await torrent).fileInfo(hash),
|
||||
protocolStatus: async (hash) => await (await torrent).protocolStatus(hash),
|
||||
cachedTorrents: async () => await (await torrent).cached(),
|
||||
debug: async (levels) => await (await torrent).debug(levels)
|
||||
}
|
||||
: {}
|
||||
|
||||
export default electronNative
|
||||
Loading…
Reference in a new issue