fix: fetch booklet PDF through Tauri backend to bypass CORS
PDF loading was failing because pdfjs-dist cannot fetch from static.qobuz.com due to CORS restrictions in the WebView. Added v2_fetch_url_bytes command that fetches URL bytes through Rust's reqwest (no CORS), then passes the binary data to pdfjs. Also added rotation support (R key or toolbar button) for vertical booklets.
This commit is contained in:
7 files changed
+63
-10
No files matched your search
@@ -10923,3 +10923,31 @@ pub async fn v2_extract_palette(
|
||||
.await
|
||||
.map_err(|e| format!("Palette extraction task failed: {}", e))?
|
||||
}
|
||||
|
||||
// ==================== Utility Commands ====================
|
||||
|
||||
/// Fetch a remote URL as bytes (bypasses WebView CORS restrictions).
|
||||
/// Used for loading PDF booklets from Qobuz CDN.
|
||||
#[tauri::command]
|
||||
pub async fn v2_fetch_url_bytes(url: String) -> Result<Vec<u8>, String> {
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(30))
|
||||
.build()
|
||||
.map_err(|e| format!("HTTP client error: {}", e))?;
|
||||
|
||||
let response = client
|
||||
.get(&url)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to fetch URL: {}", e))?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
return Err(format!("HTTP {}: {}", response.status(), url));
|
||||
}
|
||||
|
||||
response
|
||||
.bytes()
|
||||
.await
|
||||
.map(|b| b.to_vec())
|
||||
.map_err(|e| format!("Failed to read response: {}", e))
|
||||
}
|
||||
@@ -1425,6 +1425,7 @@ pub fn run() {
|
||||
commands_v2::v2_generate_theme_from_system_colors,
|
||||
commands_v2::v2_get_system_color_scheme,
|
||||
commands_v2::v2_extract_palette,
|
||||
commands_v2::v2_fetch_url_bytes,
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { X, ChevronLeft, ChevronRight, ZoomIn, ZoomOut, Maximize } from 'lucide-svelte';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { X, ChevronLeft, ChevronRight, ZoomIn, ZoomOut, Maximize, RotateCw } from 'lucide-svelte';
|
||||
import * as pdfjsLib from 'pdfjs-dist';
|
||||
|
||||
// Configure worker
|
||||
@@ -29,6 +29,7 @@
|
||||
let currentPage = $state(1);
|
||||
let totalPages = $state(0);
|
||||
let zoom = $state(1);
|
||||
let rotation = $state(0);
|
||||
let isLoading = $state(true);
|
||||
let error = $state('');
|
||||
let currentRenderTask: any = null;
|
||||
@@ -45,9 +46,14 @@
|
||||
error = '';
|
||||
|
||||
try {
|
||||
// Fetch PDF bytes through Tauri backend to bypass CORS
|
||||
const bytes = await invoke<number[]>('v2_fetch_url_bytes', { url });
|
||||
const data = new Uint8Array(bytes);
|
||||
|
||||
const loadingTask = pdfjsLib.getDocument({
|
||||
url,
|
||||
disableRange: false,
|
||||
data,
|
||||
disableRange: true,
|
||||
disableAutoFetch: true,
|
||||
});
|
||||
|
||||
pdfDoc = await loadingTask.promise;
|
||||
@@ -56,7 +62,7 @@
|
||||
await renderPage();
|
||||
} catch (err: any) {
|
||||
console.error('[BookletViewer] Failed to load PDF:', err);
|
||||
error = err?.message || 'Failed to load booklet';
|
||||
error = err?.message || String(err) || 'Failed to load booklet';
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
@@ -73,7 +79,8 @@
|
||||
try {
|
||||
const page = await pdfDoc.getPage(currentPage);
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
const viewport = page.getViewport({ scale: zoom * dpr * 1.5 });
|
||||
const scale = zoom * dpr * 1.5;
|
||||
const viewport = page.getViewport({ scale, rotation });
|
||||
|
||||
canvas.width = viewport.width;
|
||||
canvas.height = viewport.height;
|
||||
@@ -132,6 +139,11 @@
|
||||
renderPage();
|
||||
}
|
||||
|
||||
function rotate() {
|
||||
rotation = (rotation + 90) % 360;
|
||||
renderPage();
|
||||
}
|
||||
|
||||
function handleKeydown(e: KeyboardEvent) {
|
||||
if (!isOpen) return;
|
||||
|
||||
@@ -152,6 +164,9 @@
|
||||
case '-':
|
||||
zoomOut();
|
||||
break;
|
||||
case 'r':
|
||||
rotate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,6 +202,7 @@
|
||||
currentPage = 1;
|
||||
totalPages = 0;
|
||||
zoom = 1;
|
||||
rotation = 0;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -221,6 +237,10 @@
|
||||
<button class="toolbar-btn" onclick={fitToWidth} title={$t('album.bookletFitWidth')}>
|
||||
<Maximize size={16} />
|
||||
</button>
|
||||
<div class="toolbar-divider"></div>
|
||||
<button class="toolbar-btn" onclick={rotate} title={$t('album.bookletRotate')}>
|
||||
<RotateCw size={16} />
|
||||
</button>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<button class="toolbar-btn close-btn" onclick={onClose}>
|
||||
|
||||
@@ -232,7 +232,8 @@
|
||||
"viewBooklet": "Booklet anzeigen",
|
||||
"bookletLoading": "Booklet wird geladen...",
|
||||
"bookletError": "Booklet konnte nicht geladen werden",
|
||||
"bookletFitWidth": "An Breite anpassen"
|
||||
"bookletFitWidth": "An Breite anpassen",
|
||||
"bookletRotate": "Drehen"
|
||||
},
|
||||
"artist": {
|
||||
"albums": "Alben",
|
||||
|
||||
@@ -244,7 +244,8 @@
|
||||
"viewBooklet": "View Booklet",
|
||||
"bookletLoading": "Loading booklet...",
|
||||
"bookletError": "Could not load booklet",
|
||||
"bookletFitWidth": "Fit to width"
|
||||
"bookletFitWidth": "Fit to width",
|
||||
"bookletRotate": "Rotate"
|
||||
},
|
||||
"artist": {
|
||||
"albums": "Albums",
|
||||
|
||||
@@ -244,7 +244,8 @@
|
||||
"viewBooklet": "Ver libreto",
|
||||
"bookletLoading": "Cargando libreto...",
|
||||
"bookletError": "No se pudo cargar el libreto",
|
||||
"bookletFitWidth": "Ajustar al ancho"
|
||||
"bookletFitWidth": "Ajustar al ancho",
|
||||
"bookletRotate": "Rotar"
|
||||
},
|
||||
"artist": {
|
||||
"albums": "Álbumes",
|
||||
|
||||
@@ -232,7 +232,8 @@
|
||||
"viewBooklet": "Voir le livret",
|
||||
"bookletLoading": "Chargement du livret...",
|
||||
"bookletError": "Impossible de charger le livret",
|
||||
"bookletFitWidth": "Ajuster à la largeur"
|
||||
"bookletFitWidth": "Ajuster à la largeur",
|
||||
"bookletRotate": "Pivoter"
|
||||
},
|
||||
"artist": {
|
||||
"albums": "Albums",
|
||||
|
||||
Reference in new issue
Block a user