feat: working zip file
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
* Handles all communication with Flask API
|
* Handles all communication with Flask API
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const API_BASE_URL = 'http://localhost:5000/api';
|
const API_BASE_URL= 'http://localhost:5000/api';
|
||||||
|
|
||||||
class ApiClient {
|
class ApiClient {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -153,11 +153,24 @@ class ApiClient {
|
|||||||
return this.request(`/posts/${postId}/metadata`);
|
return this.request(`/posts/${postId}/metadata`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== Export/Download ====================
|
||||||
|
|
||||||
async exportPost(postId) {
|
async exportPost(postId) {
|
||||||
const response = await fetch(`/api/posts/${postId}/download`, { method: "GET" });
|
const response = await fetch(`${this.baseUrl}/posts/${postId}/download`, {
|
||||||
if (!response.ok) throw new Error(`Failed to download post: ${response.statusText}`);
|
method: "GET",
|
||||||
return await response.blob(); // returns proper Blob ready for download
|
});
|
||||||
}
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorData = await response.json().catch(() => ({}));
|
||||||
|
throw new Error(errorData.error || `Failed to download post: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get binary data
|
||||||
|
const buffer = await response.arrayBuffer();
|
||||||
|
|
||||||
|
// Convert to a Blob so the browser can download it
|
||||||
|
return new Blob([buffer], { type: "application/zip" });
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== RAG Search ====================
|
// ==================== RAG Search ====================
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Play, Pause, Volume2, MoreVertical, Clock, ChevronDown, ChevronUp } from 'lucide-react'
|
import { Play, Pause, Volume2, MoreVertical, Clock, ChevronDown, ChevronUp, Download } from 'lucide-react'
|
||||||
import { useState, useRef, useEffect } from 'react'
|
import { useState, useRef, useEffect } from 'react'
|
||||||
import { api } from '../api'
|
import { api } from '../api'
|
||||||
|
|
||||||
@@ -10,6 +10,7 @@ export default function AudioPostCard({ post }) {
|
|||||||
const [transcript, setTranscript] = useState(null)
|
const [transcript, setTranscript] = useState(null)
|
||||||
const [loadingTranscript, setLoadingTranscript] = useState(false)
|
const [loadingTranscript, setLoadingTranscript] = useState(false)
|
||||||
const [transcriptExpanded, setTranscriptExpanded] = useState(false)
|
const [transcriptExpanded, setTranscriptExpanded] = useState(false)
|
||||||
|
const [downloading, setDownloading] = useState(false)
|
||||||
const audioRef = useRef(null)
|
const audioRef = useRef(null)
|
||||||
|
|
||||||
// DEBUG: Log post data to console
|
// DEBUG: Log post data to console
|
||||||
@@ -73,6 +74,29 @@ export default function AudioPostCard({ post }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleDownload = async () => {
|
||||||
|
if (downloading) return
|
||||||
|
|
||||||
|
setDownloading(true)
|
||||||
|
try {
|
||||||
|
const zipBlob = await api.exportPost(post.post_id)
|
||||||
|
|
||||||
|
const url = window.URL.createObjectURL(zipBlob)
|
||||||
|
const a = document.createElement("a")
|
||||||
|
a.href = url
|
||||||
|
a.download = `${post.title.replace(/[^a-zA-Z0-9]/g, "_")}_${post.post_id}.zip`
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
a.remove()
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Failed to download post:", err)
|
||||||
|
alert(`Download failed: ${err.message}`)
|
||||||
|
} finally {
|
||||||
|
setDownloading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const togglePlay = () => {
|
const togglePlay = () => {
|
||||||
if (!audioRef.current) return
|
if (!audioRef.current) return
|
||||||
|
|
||||||
@@ -122,24 +146,6 @@ export default function AudioPostCard({ post }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDownload = async () => {
|
|
||||||
try {
|
|
||||||
const zipBlob = await api.exportPost(post.post_id);
|
|
||||||
|
|
||||||
const url = window.URL.createObjectURL(zipBlob);
|
|
||||||
const a = document.createElement("a");
|
|
||||||
a.href = url;
|
|
||||||
a.download = `${post.title.replace(/\s+/g, "_")}.zip`;
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
|
||||||
a.remove();
|
|
||||||
window.URL.revokeObjectURL(url);
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Failed to download post:", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const handleEnded = () => {
|
const handleEnded = () => {
|
||||||
setIsPlaying(false)
|
setIsPlaying(false)
|
||||||
setCurrentTime(0)
|
setCurrentTime(0)
|
||||||
@@ -174,15 +180,26 @@ export default function AudioPostCard({ post }) {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{post.status === "ready" && (
|
{post.status === 'ready' && (
|
||||||
<button
|
<button
|
||||||
onClick={handleDownload}
|
onClick={handleDownload}
|
||||||
className="mt-2 px-3 py-1 bg-[#f4b840] text-black rounded hover:bg-[#e5a930] transition-colors"
|
disabled={downloading}
|
||||||
|
className="flex items-center gap-2 px-3 py-1.5 bg-[#f4b840] hover:bg-[#e5a930] text-[#1a1a1a] rounded text-sm font-medium transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
title="Download post as ZIP"
|
||||||
>
|
>
|
||||||
Download Post
|
{downloading ? (
|
||||||
|
<>
|
||||||
|
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-[#1a1a1a]"></div>
|
||||||
|
<span>Downloading...</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Download size={16} />
|
||||||
|
<span>Download</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<button className="text-gray-500 hover:text-gray-700">
|
<button className="text-gray-500 hover:text-gray-700">
|
||||||
<MoreVertical size={18} />
|
<MoreVertical size={18} />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user