mirror of
https://github.com/ThaUnknown/miru.git
synced 2026-03-11 20:45:33 +00:00
feat: NNTP streaming
This commit is contained in:
parent
de3da8acf8
commit
f2b6f12fcb
8 changed files with 75 additions and 19 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "ui",
|
"name": "ui",
|
||||||
"version": "6.4.150",
|
"version": "6.4.151",
|
||||||
"license": "BUSL-1.1",
|
"license": "BUSL-1.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "pnpm@9.15.5",
|
"packageManager": "pnpm@9.15.5",
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,7 @@ importers:
|
||||||
version: 1.8.13(@gql.tada/svelte-support@1.0.1(svelte@4.2.19)(typescript@5.9.2))(graphql@16.10.0)(typescript@5.9.2)
|
version: 1.8.13(@gql.tada/svelte-support@1.0.1(svelte@4.2.19)(typescript@5.9.2))(graphql@16.10.0)(typescript@5.9.2)
|
||||||
hayase-extensions:
|
hayase-extensions:
|
||||||
specifier: github:hayase-app/extensions
|
specifier: github:hayase-app/extensions
|
||||||
version: https://codeload.github.com/hayase-app/extensions/tar.gz/0fad214a4a0aacd826c0801255ced65bd1d49a13
|
version: https://codeload.github.com/hayase-app/extensions/tar.gz/2c43ec0bf157733901be1ad9188b64933aac2e15
|
||||||
jassub:
|
jassub:
|
||||||
specifier: ^1.8.6
|
specifier: ^1.8.6
|
||||||
version: 1.8.6
|
version: 1.8.6
|
||||||
|
|
@ -197,7 +197,7 @@ importers:
|
||||||
version: 2.1.3
|
version: 2.1.3
|
||||||
native:
|
native:
|
||||||
specifier: github:hayase-app/native
|
specifier: github:hayase-app/native
|
||||||
version: https://codeload.github.com/hayase-app/native/tar.gz/b77747898ca7e4991fac752559ea159c8c0f5083
|
version: https://codeload.github.com/hayase-app/native/tar.gz/e8045ec8878086117a9c3b52dcff069f4b9b48e8
|
||||||
rollup-plugin-license:
|
rollup-plugin-license:
|
||||||
specifier: ^3.6.0
|
specifier: ^3.6.0
|
||||||
version: 3.6.0(picomatch@4.0.3)(rollup@4.40.2)
|
version: 3.6.0(picomatch@4.0.3)(rollup@4.40.2)
|
||||||
|
|
@ -1711,8 +1711,8 @@ packages:
|
||||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
hayase-extensions@https://codeload.github.com/hayase-app/extensions/tar.gz/0fad214a4a0aacd826c0801255ced65bd1d49a13:
|
hayase-extensions@https://codeload.github.com/hayase-app/extensions/tar.gz/2c43ec0bf157733901be1ad9188b64933aac2e15:
|
||||||
resolution: {tarball: https://codeload.github.com/hayase-app/extensions/tar.gz/0fad214a4a0aacd826c0801255ced65bd1d49a13}
|
resolution: {tarball: https://codeload.github.com/hayase-app/extensions/tar.gz/2c43ec0bf157733901be1ad9188b64933aac2e15}
|
||||||
version: 1.0.6
|
version: 1.0.6
|
||||||
|
|
||||||
idb-keyval@6.2.2:
|
idb-keyval@6.2.2:
|
||||||
|
|
@ -2051,8 +2051,8 @@ packages:
|
||||||
engines: {node: ^18 || >=20}
|
engines: {node: ^18 || >=20}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
native@https://codeload.github.com/hayase-app/native/tar.gz/b77747898ca7e4991fac752559ea159c8c0f5083:
|
native@https://codeload.github.com/hayase-app/native/tar.gz/e8045ec8878086117a9c3b52dcff069f4b9b48e8:
|
||||||
resolution: {tarball: https://codeload.github.com/hayase-app/native/tar.gz/b77747898ca7e4991fac752559ea159c8c0f5083}
|
resolution: {tarball: https://codeload.github.com/hayase-app/native/tar.gz/e8045ec8878086117a9c3b52dcff069f4b9b48e8}
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
|
|
||||||
natural-compare@1.4.0:
|
natural-compare@1.4.0:
|
||||||
|
|
@ -4560,7 +4560,7 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
function-bind: 1.1.2
|
function-bind: 1.1.2
|
||||||
|
|
||||||
hayase-extensions@https://codeload.github.com/hayase-app/extensions/tar.gz/0fad214a4a0aacd826c0801255ced65bd1d49a13: {}
|
hayase-extensions@https://codeload.github.com/hayase-app/extensions/tar.gz/2c43ec0bf157733901be1ad9188b64933aac2e15: {}
|
||||||
|
|
||||||
idb-keyval@6.2.2: {}
|
idb-keyval@6.2.2: {}
|
||||||
|
|
||||||
|
|
@ -4865,7 +4865,7 @@ snapshots:
|
||||||
|
|
||||||
nanoid@5.1.5: {}
|
nanoid@5.1.5: {}
|
||||||
|
|
||||||
native@https://codeload.github.com/hayase-app/native/tar.gz/b77747898ca7e4991fac752559ea159c8c0f5083: {}
|
native@https://codeload.github.com/hayase-app/native/tar.gz/e8045ec8878086117a9c3b52dcff069f4b9b48e8: {}
|
||||||
|
|
||||||
natural-compare@1.4.0: {}
|
natural-compare@1.4.0: {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>Hayase</title>
|
<title>Hayase</title>
|
||||||
|
<link rel="preconnect" href="https://graphql.anilist.co/">
|
||||||
|
<link rel="preconnect" href="https://www.youtube-nocookie.com">
|
||||||
<link rel="icon" href="%sveltekit.assets%/logo_white_fit.svg" />
|
<link rel="icon" href="%sveltekit.assets%/logo_white_fit.svg" />
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
|
|
|
||||||
|
|
@ -13,5 +13,6 @@
|
||||||
<div class='text-muted-foreground'>
|
<div class='text-muted-foreground'>
|
||||||
{location.country}
|
{location.country}
|
||||||
</div>
|
</div>
|
||||||
|
{:catch}
|
||||||
{/await}
|
{/await}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import anitomyscript, { type AnitomyResult } from 'anitomyscript'
|
import anitomyscript, { type AnitomyResult } from 'anitomyscript'
|
||||||
import Debug from 'debug'
|
import Debug from 'debug'
|
||||||
import { get } from 'svelte/store'
|
import { get } from 'svelte/store'
|
||||||
|
import { toast } from 'svelte-sonner'
|
||||||
|
|
||||||
import { dedupeAiring, episodes, isMovie, type Media, getParentForSpecial, isSingleEpisode } from '../anilist'
|
import { dedupeAiring, episodes, isMovie, type Media, getParentForSpecial, isSingleEpisode } from '../anilist'
|
||||||
import { episodes as _episodes } from '../anizip'
|
import { episodes as _episodes } from '../anizip'
|
||||||
|
|
@ -203,13 +204,14 @@ export const extensions = new class Extensions {
|
||||||
debug(`Checking ${Object.keys(workers).length} extensions for ${media.id}:${media.title?.userPreferred} ${episode} ${resolution} ${checkMovie ? 'movie' : ''} ${checkBatch ? 'batch' : ''}`)
|
debug(`Checking ${Object.keys(workers).length} extensions for ${media.id}:${media.title?.userPreferred} ${episode} ${resolution} ${checkMovie ? 'movie' : ''} ${checkBatch ? 'batch' : ''}`)
|
||||||
|
|
||||||
for (const [id, worker] of Object.entries(workers)) {
|
for (const [id, worker] of Object.entries(workers)) {
|
||||||
if (!extopts[id]!.enabled) continue
|
const thisExtOpts = extopts[id]!
|
||||||
|
if (!thisExtOpts.enabled) continue
|
||||||
if (configs[id]!.type !== 'torrent') continue
|
if (configs[id]!.type !== 'torrent') continue
|
||||||
try {
|
try {
|
||||||
const promises: Array<Promise<TorrentResult[]>> = []
|
const promises: Array<Promise<TorrentResult[]>> = []
|
||||||
promises.push(worker.single(options))
|
promises.push(worker.single(options, thisExtOpts.options))
|
||||||
if (checkMovie) promises.push(worker.movie(options))
|
if (checkMovie) promises.push(worker.movie(options, thisExtOpts.options))
|
||||||
if (checkBatch) promises.push(worker.batch(options))
|
if (checkBatch) promises.push(worker.batch(options, thisExtOpts.options))
|
||||||
|
|
||||||
for (const result of await Promise.allSettled(promises)) {
|
for (const result of await Promise.allSettled(promises)) {
|
||||||
if (result.status === 'fulfilled') {
|
if (result.status === 'fulfilled') {
|
||||||
|
|
@ -246,6 +248,37 @@ export const extensions = new class Extensions {
|
||||||
return { results: navigator.onLine ? await this.updatePeerCounts(deduped) : deduped, errors }
|
return { results: navigator.onLine ? await this.updatePeerCounts(deduped) : deduped, errors }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getNZBResultsFromExtensions (hash: string) {
|
||||||
|
await storage.modules
|
||||||
|
const workers = storage.workers
|
||||||
|
const results: Array<{ nzb: string, options: Record<string, string> }> = []
|
||||||
|
const errors: Array<{ error: Error, extension: string }> = []
|
||||||
|
|
||||||
|
const extopts = get(extensionOptions)
|
||||||
|
const configs = get(saved)
|
||||||
|
|
||||||
|
for (const [id, worker] of Object.entries(workers)) {
|
||||||
|
const thisExtOpts = extopts[id]!
|
||||||
|
if (!thisExtOpts.enabled) continue
|
||||||
|
if (configs[id]!.type !== 'nzb') continue
|
||||||
|
try {
|
||||||
|
const nzb = await worker.query(hash, thisExtOpts.options)
|
||||||
|
if (!nzb) continue
|
||||||
|
results.push({ nzb, options: thisExtOpts.options })
|
||||||
|
} catch (error) {
|
||||||
|
errors.push({ error: error as Error, extension: id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors.length) {
|
||||||
|
for (const { error, extension } of errors) {
|
||||||
|
toast.error(`Error fetching NZB from ${configs[extension]?.name ?? extension}`, { description: error.message })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
async updatePeerCounts <T extends TorrentResult[]> (entries: T): Promise<T> {
|
async updatePeerCounts <T extends TorrentResult[]> (entries: T): Promise<T> {
|
||||||
debug(`Updating peer counts for ${entries.length} entries`)
|
debug(`Updating peer counts for ${entries.length} entries`)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
import { finalizer } from 'abslink'
|
import { finalizer } from 'abslink'
|
||||||
import { expose } from 'abslink/w3c'
|
import { expose } from 'abslink/w3c'
|
||||||
|
|
||||||
import type { SearchFunction, TorrentSource } from 'hayase-extensions'
|
import type { SearchFunction, TorrentSource, NZBorURLSource } from 'hayase-extensions'
|
||||||
|
|
||||||
export default expose({
|
export default expose({
|
||||||
mod: null as unknown as Promise<TorrentSource>,
|
mod: null as unknown as Promise<TorrentSource | NZBorURLSource>,
|
||||||
construct (code: string) {
|
construct (code: string) {
|
||||||
this.mod = this.load(code)
|
this.mod = this.load(code)
|
||||||
},
|
},
|
||||||
|
|
||||||
async load (code: string): Promise<TorrentSource> {
|
async load (code: string): Promise<TorrentSource | NZBorURLSource> {
|
||||||
// WARN: unsafe eval
|
// WARN: unsafe eval
|
||||||
const url = URL.createObjectURL(new Blob([code], { type: 'application/javascript' }))
|
const url = URL.createObjectURL(new Blob([code], { type: 'application/javascript' }))
|
||||||
const module = await import(/* @vite-ignore */url)
|
const module = await import(/* @vite-ignore */url)
|
||||||
|
|
@ -22,15 +22,19 @@ export default expose({
|
||||||
},
|
},
|
||||||
|
|
||||||
async single (...args: Parameters<SearchFunction>): ReturnType<SearchFunction> {
|
async single (...args: Parameters<SearchFunction>): ReturnType<SearchFunction> {
|
||||||
return await ((await this.mod)).single(...args)
|
return await ((await this.mod) as TorrentSource).single(...args)
|
||||||
},
|
},
|
||||||
|
|
||||||
async batch (...args: Parameters<SearchFunction>): ReturnType<SearchFunction> {
|
async batch (...args: Parameters<SearchFunction>): ReturnType<SearchFunction> {
|
||||||
return await ((await this.mod)).batch(...args)
|
return await ((await this.mod) as TorrentSource).batch(...args)
|
||||||
},
|
},
|
||||||
|
|
||||||
async movie (...args: Parameters<SearchFunction>): ReturnType<SearchFunction> {
|
async movie (...args: Parameters<SearchFunction>): ReturnType<SearchFunction> {
|
||||||
return await ((await this.mod)).movie(...args)
|
return await ((await this.mod) as TorrentSource).movie(...args)
|
||||||
|
},
|
||||||
|
|
||||||
|
async query (...args: Parameters<NZBorURLSource['query']>) {
|
||||||
|
return await ((await this.mod) as NZBorURLSource).query(...args)
|
||||||
},
|
},
|
||||||
|
|
||||||
async test () {
|
async test () {
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,7 @@ export default Object.assign<Native, Partial<Native>>({
|
||||||
navigate: async () => undefined,
|
navigate: async () => undefined,
|
||||||
downloadProgress: async () => undefined,
|
downloadProgress: async () => undefined,
|
||||||
updateProgress: async () => undefined,
|
updateProgress: async () => undefined,
|
||||||
|
createNZB: async () => undefined,
|
||||||
torrentInfo: async (): Promise<TorrentInfo> => ({
|
torrentInfo: async (): Promise<TorrentInfo> => ({
|
||||||
name: '',
|
name: '',
|
||||||
progress: 0,
|
progress: 0,
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@ import Debug from 'debug'
|
||||||
import { writable } from 'simple-store-svelte'
|
import { writable } from 'simple-store-svelte'
|
||||||
import { get } from 'svelte/store'
|
import { get } from 'svelte/store'
|
||||||
import { persisted } from 'svelte-persisted-store'
|
import { persisted } from 'svelte-persisted-store'
|
||||||
|
import { toast } from 'svelte-sonner'
|
||||||
|
|
||||||
import client from '../auth/client'
|
import client from '../auth/client'
|
||||||
|
import { extensions } from '../extensions'
|
||||||
import native from '../native'
|
import native from '../native'
|
||||||
import { SUPPORTS } from '../settings'
|
import { SUPPORTS } from '../settings'
|
||||||
import { w2globby } from '../w2g/lobby'
|
import { w2globby } from '../w2g/lobby'
|
||||||
|
|
@ -97,8 +99,21 @@ export const server = new class ServerClient {
|
||||||
const result = { id, media, episode, files: await native.playTorrent(id, media.id, episode) }
|
const result = { id, media, episode, files: await native.playTorrent(id, media.id, episode) }
|
||||||
debug('torrent play result', result)
|
debug('torrent play result', result)
|
||||||
this.downloaded.value = this.cachedSet()
|
this.downloaded.value = this.cachedSet()
|
||||||
|
this._addNZBs(result.files[0]!.hash)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _addNZBs (hash: string) {
|
||||||
|
const nzbs = await extensions.getNZBResultsFromExtensions(hash)
|
||||||
|
|
||||||
|
for (const { nzb, options } of nzbs) {
|
||||||
|
try {
|
||||||
|
await native.createNZB(hash, nzb, options.domain!, Number(options.port!), options.username!, options.password!, Number(options.poolSize!))
|
||||||
|
} catch (e) {
|
||||||
|
toast.error('Failed to add NZB', { description: (e as Error).message })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
requestIdleCallback(() => {
|
requestIdleCallback(() => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue