mirror of
https://github.com/NoCrypt/migu.git
synced 2026-04-21 00:22:08 +00:00
fix: torrent client sometimes loading without settings on android
feat: persist files on android feat: custom download directory on android
This commit is contained in:
parent
c595dd7224
commit
94f8fafc6b
9 changed files with 442 additions and 535 deletions
|
|
@ -54,6 +54,8 @@
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-feature android:name="android.software.leanback" android:required="false" />
|
<uses-feature android:name="android.software.leanback" android:required="false" />
|
||||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
||||||
|
|
@ -18,24 +18,25 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@capacitor/assets": "github:thaunknown/capacitor-assets",
|
"@capacitor/assets": "github:thaunknown/capacitor-assets",
|
||||||
"@capacitor/cli": "^6.1.1",
|
"@capacitor/cli": "^6.1.2",
|
||||||
"cordova-res": "^0.15.4",
|
"cordova-res": "^0.15.4",
|
||||||
"nodejs-mobile-gyp": "^0.3.1",
|
"nodejs-mobile-gyp": "^0.4.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"webpack-cli": "^5.1.4",
|
"webpack-cli": "^5.1.4",
|
||||||
"webpack-merge": "^5.10.0"
|
"webpack-merge": "^6.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@capacitor/android": "^6.1.1",
|
"@capacitor/android": "^6.1.2",
|
||||||
"@capacitor/app": "^6.0.0",
|
"@capacitor/app": "^6.0.1",
|
||||||
"@capacitor/browser": "^6.0.1",
|
"@capacitor/browser": "^6.0.2",
|
||||||
"@capacitor/core": "^6.1.1",
|
"@capacitor/core": "^6.1.2",
|
||||||
"@capacitor/device": "^6.0.1",
|
"@capacitor/device": "^6.0.1",
|
||||||
"@capacitor/ios": "^6.1.1",
|
"@capacitor/ios": "^6.1.2",
|
||||||
"@capacitor/local-notifications": "^6.0.0",
|
"@capacitor/local-notifications": "^6.1.0",
|
||||||
"@capacitor/status-bar": "^6.0.0",
|
"@capacitor/status-bar": "^6.0.1",
|
||||||
|
"capacitor-folder-picker": "^0.0.2",
|
||||||
"capacitor-nodejs": "https://github.com/funniray/Capacitor-NodeJS/releases/download/nodejs-18/capacitor-nodejs-1.0.0-beta.6.tgz",
|
"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:*",
|
"common": "workspace:*",
|
||||||
"cordova-plugin-navigationbar": "^1.0.31",
|
"cordova-plugin-navigationbar": "^1.0.31",
|
||||||
"cordova-plugin-pip": "^0.0.2",
|
"cordova-plugin-pip": "^0.0.2",
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ 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 { Device } from '@capacitor/device'
|
||||||
|
import { FolderPicker } from 'capacitor-folder-picker'
|
||||||
|
import { toast } from 'svelte-sonner'
|
||||||
import IPC from './ipc.js'
|
import IPC from './ipc.js'
|
||||||
|
|
||||||
IPC.on('open', url => Browser.open({ url }))
|
IPC.on('open', url => Browser.open({ url }))
|
||||||
|
|
@ -51,6 +53,30 @@ IPC.on('get-device-info', async () => {
|
||||||
IPC.emit('device-info', JSON.stringify(deviceInfo))
|
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
|
// schema: miru://key/value
|
||||||
const protocolMap = {
|
const protocolMap = {
|
||||||
auth: token => sendToken(token),
|
auth: token => sendToken(token),
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,16 @@ channel.on('port-init', data => {
|
||||||
channel.send('ipc', { data })
|
channel.send('ipc', { data })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let storedSettings = {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
storedSettings = JSON.parse(localStorage.getItem('settings')) || {}
|
||||||
|
} catch (error) {}
|
||||||
|
|
||||||
|
if (!globalThis.client) globalThis.client = new TorrentClient(channel, storageQuota, 'node', storedSettings.torrentPathNew || env.TMPDIR)
|
||||||
|
|
||||||
channel.on('ipc', a => port.onmessage(a))
|
channel.on('ipc', a => port.onmessage(a))
|
||||||
channel.emit('port', {
|
channel.emit('port', {
|
||||||
ports: [port]
|
ports: [port]
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
globalThis.client = new TorrentClient(channel, storageQuota, 'node', env.TMPDIR)
|
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,7 @@ export const SUPPORTS = {
|
||||||
update: false,
|
update: false,
|
||||||
angle: false,
|
angle: false,
|
||||||
doh: false,
|
doh: false,
|
||||||
dht: true,
|
|
||||||
discord: false,
|
discord: false,
|
||||||
torrentPort: true,
|
|
||||||
torrentPath: false,
|
|
||||||
torrentPersist: false,
|
|
||||||
keybinds: false,
|
keybinds: false,
|
||||||
isAndroid: true,
|
isAndroid: true,
|
||||||
externalPlayer: false,
|
externalPlayer: false,
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,7 @@ export const SUPPORTS = {
|
||||||
update: true,
|
update: true,
|
||||||
angle: true,
|
angle: true,
|
||||||
doh: true,
|
doh: true,
|
||||||
dht: true,
|
|
||||||
discord: true,
|
discord: true,
|
||||||
torrentPort: true,
|
|
||||||
torrentPath: true,
|
|
||||||
torrentPersist: true,
|
|
||||||
keybinds: true,
|
keybinds: true,
|
||||||
extensions: true,
|
extensions: true,
|
||||||
isAndroid: false,
|
isAndroid: false,
|
||||||
|
|
|
||||||
|
|
@ -37,12 +37,6 @@ const ANNOUNCE = [
|
||||||
atob('aHR0cDovL3RyYWNrZXIuYW5pcmVuYS5jb206ODAvYW5ub3VuY2U=')
|
atob('aHR0cDovL3RyYWNrZXIuYW5pcmVuYS5jb206ODAvYW5ub3VuY2U=')
|
||||||
]
|
]
|
||||||
|
|
||||||
let storedSettings = {}
|
|
||||||
|
|
||||||
try {
|
|
||||||
storedSettings = JSON.parse(localStorage.getItem('settings')) || {}
|
|
||||||
} catch (error) {}
|
|
||||||
|
|
||||||
export default class TorrentClient extends WebTorrent {
|
export default class TorrentClient extends WebTorrent {
|
||||||
static excludedErrorMessages = ['WebSocket', 'User-Initiated Abort, reason=', 'Connection failed.']
|
static excludedErrorMessages = ['WebSocket', 'User-Initiated Abort, reason=', 'Connection failed.']
|
||||||
|
|
||||||
|
|
@ -54,6 +48,12 @@ export default class TorrentClient extends WebTorrent {
|
||||||
ipc
|
ipc
|
||||||
|
|
||||||
constructor (ipc, storageQuota, serverMode, torrentPath, controller) {
|
constructor (ipc, storageQuota, serverMode, torrentPath, controller) {
|
||||||
|
let storedSettings = {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
storedSettings = JSON.parse(localStorage.getItem('settings')) || {}
|
||||||
|
} catch (error) {}
|
||||||
|
|
||||||
const settings = { ...defaults, ...storedSettings }
|
const settings = { ...defaults, ...storedSettings }
|
||||||
debug('Initializing TorrentClient with settings: ' + JSON.stringify(settings))
|
debug('Initializing TorrentClient with settings: ' + JSON.stringify(settings))
|
||||||
super({
|
super({
|
||||||
|
|
|
||||||
|
|
@ -140,25 +140,25 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<h4 class='mb-10 font-weight-bold'>Client Settings</h4>
|
<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 lose data when your OS tries to reclaim storage. {SUPPORTS.isAndroid ? 'RESTART IS REQUIRED. /sdcard/ is internal storage, not external SD Cards. /storage/AB12-34CD/ is external storage, not internal. Thank you Android!' : ''}'>
|
||||||
<SettingCard title='Torrent Download Location' description='Path to the folder used to store torrents. By default this is the TMP folder, which might lose data when your OS tries to reclaim storage.'>
|
<div
|
||||||
<div
|
class='input-group w-300 mw-full'>
|
||||||
class='input-group w-300 mw-full'>
|
<div class='input-group-prepend'>
|
||||||
<div class='input-group-prepend'>
|
<button type='button' use:click={handleFolder} class='btn btn-primary input-group-append'>Select Folder</button>
|
||||||
<button type='button' use:click={handleFolder} class='btn btn-primary input-group-append'>Select Folder</button>
|
|
||||||
</div>
|
|
||||||
<input type='url' class='form-control bg-dark' readonly value={settings.torrentPathNew} placeholder='/tmp' />
|
|
||||||
</div>
|
</div>
|
||||||
</SettingCard>
|
{#if !SUPPORTS.isAndroid}
|
||||||
{/if}
|
<input type='url' class='form-control bg-dark' readonly bind:value={settings.torrentPathNew} placeholder='/tmp' />
|
||||||
{#if SUPPORTS.torrentPersist}
|
{:else}
|
||||||
<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.">
|
<input type='text' class='form-control bg-dark' bind:value={settings.torrentPathNew} placeholder='/tmp' />
|
||||||
<div class='custom-switch'>
|
{/if}
|
||||||
<input type='checkbox' id='torrent-persist' bind:checked={settings.torrentPersist} />
|
</div>
|
||||||
<label for='torrent-persist'>{settings.torrentPersist ? 'On' : 'Off'}</label>
|
</SettingCard>
|
||||||
</div>
|
<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.">
|
||||||
</SettingCard>
|
<div class='custom-switch'>
|
||||||
{/if}
|
<input type='checkbox' id='torrent-persist' bind:checked={settings.torrentPersist} />
|
||||||
|
<label for='torrent-persist'>{settings.torrentPersist ? 'On' : 'Off'}</label>
|
||||||
|
</div>
|
||||||
|
</SettingCard>
|
||||||
<SettingCard title='Streamed Download' description="Only downloads the single file that's currently being watched, instead of downloading an entire batch of episodes. Saves bandwidth and reduces strain on the peer swarm.">
|
<SettingCard title='Streamed Download' description="Only downloads the single file that's currently being watched, instead of downloading an entire batch of episodes. Saves bandwidth and reduces strain on the peer swarm.">
|
||||||
<div class='custom-switch'>
|
<div class='custom-switch'>
|
||||||
<input type='checkbox' id='torrent-streamed-download' bind:checked={settings.torrentStreamedDownload} />
|
<input type='checkbox' id='torrent-streamed-download' bind:checked={settings.torrentStreamedDownload} />
|
||||||
|
|
@ -176,22 +176,18 @@
|
||||||
<SettingCard title='Max Number of Connections' description='Number 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.'>
|
<SettingCard title='Max Number of Connections' description='Number 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' inputmode='numeric' pattern='[0-9]*' bind:value={settings.maxConns} min='1' max='512' class='form-control text-right bg-dark w-100 mw-full' />
|
<input type='number' inputmode='numeric' pattern='[0-9]*' bind:value={settings.maxConns} min='1' max='512' class='form-control text-right bg-dark w-100 mw-full' />
|
||||||
</SettingCard>
|
</SettingCard>
|
||||||
{#if SUPPORTS.torrentPort}
|
<SettingCard title='Torrent Port' description='Port used for Torrent connections. 0 is automatic.'>
|
||||||
<SettingCard title='Torrent Port' description='Port used for Torrent connections. 0 is automatic.'>
|
<input type='number' inputmode='numeric' pattern='[0-9]*' bind:value={settings.torrentPort} min='0' max='65536' class='form-control text-right bg-dark w-150 mw-full' />
|
||||||
<input type='number' inputmode='numeric' pattern='[0-9]*' bind:value={settings.torrentPort} min='0' max='65536' class='form-control text-right bg-dark w-150 mw-full' />
|
</SettingCard>
|
||||||
</SettingCard>
|
<SettingCard title='DHT Port' description='Port used for DHT connections. 0 is automatic.'>
|
||||||
{/if}
|
<input type='number' inputmode='numeric' pattern='[0-9]*' bind:value={settings.dhtPort} min='0' max='65536' class='form-control text-right bg-dark w-150 mw-full' />
|
||||||
{#if SUPPORTS.dht}
|
</SettingCard>
|
||||||
<SettingCard title='DHT Port' description='Port used for DHT connections. 0 is automatic.'>
|
<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.'>
|
||||||
<input type='number' inputmode='numeric' pattern='[0-9]*' bind:value={settings.dhtPort} min='0' max='65536' class='form-control text-right bg-dark w-150 mw-full' />
|
<div class='custom-switch'>
|
||||||
</SettingCard>
|
<input type='checkbox' id='torrent-dht' bind:checked={settings.torrentDHT} />
|
||||||
<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.'>
|
<label for='torrent-dht'>{settings.torrentDHT ? 'On' : 'Off'}</label>
|
||||||
<div class='custom-switch'>
|
</div>
|
||||||
<input type='checkbox' id='torrent-dht' bind:checked={settings.torrentDHT} />
|
</SettingCard>
|
||||||
<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.'>
|
<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'>
|
<div class='custom-switch'>
|
||||||
<input type='checkbox' id='torrent-pex' bind:checked={settings.torrentPeX} />
|
<input type='checkbox' id='torrent-pex' bind:checked={settings.torrentPeX} />
|
||||||
|
|
|
||||||
833
pnpm-lock.yaml
833
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue