mirror of
https://github.com/imputnet/cobalt.git
synced 2026-10-24 02:52:18 +00:00
fix: improve Android media folder integration
This commit is contained in:
parent
4c04a34fda
commit
2235546d56
9 changed files with 61 additions and 4 deletions
|
|
@ -31,7 +31,21 @@ android {
|
|||
|
||||
kotlinOptions { jvmTarget = "1.8" }
|
||||
|
||||
sourceSets["main"].assets.srcDir("../../../apps/client/dist")
|
||||
val clientDist = file("../../../apps/client/dist")
|
||||
val webAssets = layout.buildDirectory.dir("generated/web-assets")
|
||||
val prepareWebAssets = tasks.register<Sync>("prepareWebAssets") {
|
||||
from(clientDist)
|
||||
into(webAssets.map { it.dir("public") })
|
||||
doFirst {
|
||||
check(clientDist.isDirectory) {
|
||||
"Client assets are missing. Run 'pnpm --dir apps/client build' first."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets["main"].assets.srcDir(webAssets)
|
||||
tasks.matching { it.name.startsWith("merge") && it.name.endsWith("Assets") }
|
||||
.configureEach { dependsOn(prepareWebAssets) }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@ interface ArchiveDao {
|
|||
@Query("DELETE FROM media WHERE jobId NOT IN (SELECT id FROM jobs)")
|
||||
suspend fun deleteOrphanMedia()
|
||||
|
||||
@Query("UPDATE jobs SET status = 'queued', progress = 0, error = NULL, updatedAt = :updatedAt WHERE status IN ('resolving', 'downloading')")
|
||||
suspend fun requeueInterrupted(updatedAt: String): Int
|
||||
|
||||
@Transaction
|
||||
suspend fun replaceMedia(jobId: String, media: List<MediaEntity>) {
|
||||
deleteMedia(jobId)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package com.xmedia.archive.plugin
|
|||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.DocumentsContract
|
||||
import com.getcapacitor.JSObject
|
||||
import com.getcapacitor.Plugin
|
||||
import com.getcapacitor.PluginCall
|
||||
|
|
@ -71,6 +73,30 @@ class LocalArchivePlugin : Plugin() {
|
|||
@PluginMethod
|
||||
fun openMedia(call: PluginCall) = launchMediaIntent(call, Intent.ACTION_VIEW)
|
||||
|
||||
@PluginMethod
|
||||
fun openMediaFolder(call: PluginCall) {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
putExtra(
|
||||
DocumentsContract.EXTRA_INITIAL_URI,
|
||||
DocumentsContract.buildTreeDocumentUri(
|
||||
"com.android.externalstorage.documents",
|
||||
"primary:Download/X Media Archive",
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
runCatching {
|
||||
context.startActivity(intent)
|
||||
call.resolve()
|
||||
}.onFailure { error ->
|
||||
call.reject(error.message ?: "无法打开媒体文件夹")
|
||||
}
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
fun shareMedia(call: PluginCall) = launchMediaIntent(call, Intent.ACTION_SEND)
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ class ArchiveRepository(context: Context) {
|
|||
|
||||
suspend fun update(job: JobEntity) = dao.upsertJob(job.copy(updatedAt = Instant.now().toString()))
|
||||
|
||||
suspend fun requeueInterrupted() = dao.requeueInterrupted(Instant.now().toString())
|
||||
|
||||
suspend fun replaceMedia(jobId: String, media: List<MediaEntity>) = dao.replaceMedia(jobId, media)
|
||||
|
||||
suspend fun clearHistory(): Int {
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ class DownloadForegroundService : Service() {
|
|||
|
||||
private suspend fun runQueue() {
|
||||
try {
|
||||
repository.requeueInterrupted()
|
||||
while (true) {
|
||||
val jobs = ArchiveDatabase.get(this).dao().listJobs().filter { it.status == JobStatus.QUEUED.name.lowercase() }
|
||||
if (jobs.isEmpty()) break
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="Theme.XMediaArchive" parent="android:style/Theme.Material.Light.NoActionBar">
|
||||
<style name="Theme.XMediaArchive" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="android:fontFamily">sans</item>
|
||||
<item name="android:colorAccent">#F05A3B</item>
|
||||
<item name="android:navigationBarColor">#F3F0E9</item>
|
||||
|
|
|
|||
|
|
@ -142,7 +142,17 @@ function JobCard({ job, onAction }: { job: DownloadJob; onAction: (action: 'canc
|
|||
<div className="job-actions">
|
||||
{active && <button className="text-button danger-text" onClick={() => onAction('cancel', job.id)}><X size={15} />取消</button>}
|
||||
{(job.status === 'failed' || job.status === 'canceled') && <button className="text-button" onClick={() => onAction('retry', job.id)}><RotateCcw size={14} />重试</button>}
|
||||
{complete && <a className="text-button" href={job.media[0]?.downloadUrl}><ArrowDownToLine size={15} />{job.media.length > 1 ? '逐个下载' : '下载文件'}</a>}
|
||||
{complete && <a
|
||||
className="text-button"
|
||||
href={job.media[0]?.downloadUrl}
|
||||
onClick={(event) => {
|
||||
const firstMedia = job.media[0];
|
||||
if (firstMedia?.downloadUrl.startsWith('content:')) {
|
||||
event.preventDefault();
|
||||
void openMedia(firstMedia.id);
|
||||
}
|
||||
}}
|
||||
><ArrowDownToLine size={15} />{job.media.length > 1 ? '逐个下载' : '下载文件'}</a>}
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export function clearHistory() {
|
|||
}
|
||||
|
||||
export function openMediaFolder() {
|
||||
if (native) return Promise.resolve({ ok: true as const });
|
||||
if (native) return LocalArchive.openMediaFolder().then(() => ({ ok: true as const }));
|
||||
return request<{ ok: true }>('/api/open-media-folder', { method: 'POST' });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ export interface LocalArchivePlugin {
|
|||
clearHistory(): Promise<{ removed: number }>;
|
||||
getHealth(): Promise<{ ok: boolean; local: boolean }>;
|
||||
openMedia(input: { id: string }): Promise<void>;
|
||||
openMediaFolder(): Promise<void>;
|
||||
shareMedia(input: { id: string }): Promise<void>;
|
||||
addListener(eventName: 'jobsChanged', listenerFunc: (event: { jobs: DownloadJob[] }) => void): Promise<PluginListenerHandle>;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue