Spaces:
Sleeping
Sleeping
Nagesh Muralidhar
commited on
Commit
·
b4dc0bf
1
Parent(s):
d7391ba
midterm-submission
Browse files- podcraft/src/pages/Podcasts.tsx +60 -44
- server/main.py +31 -30
podcraft/src/pages/Podcasts.tsx
CHANGED
@@ -41,7 +41,8 @@ const Podcasts: React.FC = () => {
|
|
41 |
);
|
42 |
}
|
43 |
|
44 |
-
|
|
|
45 |
|
46 |
} catch (err) {
|
47 |
console.error('Delete error:', err);
|
@@ -60,16 +61,33 @@ const Podcasts: React.FC = () => {
|
|
60 |
|
61 |
const files = await response.json();
|
62 |
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
const podcastList: Podcast[] = files.map((file: any, index: number) => {
|
64 |
const filename = file.filename;
|
65 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
const category = categoryWithExt.replace('.mp3', '');
|
67 |
|
68 |
return {
|
69 |
id: index + 1,
|
70 |
title: `${descriptionPart.replace(/_/g, ' ').replace(/^\w/, c => c.toUpperCase())}`,
|
71 |
description: `A debate exploring ${queryPart.replace(/_/g, ' ')}`,
|
72 |
-
audio_file: file.path
|
73 |
filename: filename,
|
74 |
category: category.replace(/_/g, ' ')
|
75 |
};
|
@@ -77,6 +95,7 @@ const Podcasts: React.FC = () => {
|
|
77 |
|
78 |
setPodcasts(podcastList);
|
79 |
} catch (err) {
|
|
|
80 |
setError(err instanceof Error ? err.message : 'An error occurred');
|
81 |
} finally {
|
82 |
setLoading(false);
|
@@ -91,14 +110,6 @@ const Podcasts: React.FC = () => {
|
|
91 |
);
|
92 |
}
|
93 |
|
94 |
-
if (error !== "") {
|
95 |
-
return (
|
96 |
-
<div className="podcasts-container">
|
97 |
-
<div className="error-message">Error: {error}</div>
|
98 |
-
</div>
|
99 |
-
);
|
100 |
-
}
|
101 |
-
|
102 |
return (
|
103 |
<div className="podcasts-container">
|
104 |
<header className="podcasts-header">
|
@@ -106,42 +117,47 @@ const Podcasts: React.FC = () => {
|
|
106 |
<p>Listen to AI-generated debate podcasts on various topics</p>
|
107 |
</header>
|
108 |
|
|
|
|
|
|
|
|
|
109 |
<div className="podcasts-grid">
|
110 |
-
{podcasts.
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
<div className="podcast-
|
119 |
-
<
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
e
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
<audio
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
|
|
|
|
140 |
</div>
|
141 |
</div>
|
142 |
-
|
143 |
-
)
|
144 |
-
{podcasts.length === 0 && (
|
145 |
<div className="no-podcasts-message">
|
146 |
No podcasts found. Generate your first podcast from the home page!
|
147 |
</div>
|
|
|
41 |
);
|
42 |
}
|
43 |
|
44 |
+
// Refresh the podcast list after successful deletion
|
45 |
+
await fetchPodcasts();
|
46 |
|
47 |
} catch (err) {
|
48 |
console.error('Delete error:', err);
|
|
|
61 |
|
62 |
const files = await response.json();
|
63 |
|
64 |
+
if (!Array.isArray(files) || files.length === 0) {
|
65 |
+
setPodcasts([]);
|
66 |
+
setLoading(false);
|
67 |
+
return;
|
68 |
+
}
|
69 |
+
|
70 |
const podcastList: Podcast[] = files.map((file: any, index: number) => {
|
71 |
const filename = file.filename;
|
72 |
+
const parts = filename.split('-');
|
73 |
+
let queryPart = '', descriptionPart = '', categoryWithExt = '';
|
74 |
+
|
75 |
+
// Handle filenames that might not have all parts
|
76 |
+
if (parts.length >= 3) {
|
77 |
+
[queryPart, descriptionPart, categoryWithExt] = parts;
|
78 |
+
} else {
|
79 |
+
queryPart = parts[0] || '';
|
80 |
+
descriptionPart = parts[1] || queryPart;
|
81 |
+
categoryWithExt = parts[2] || 'general.mp3';
|
82 |
+
}
|
83 |
+
|
84 |
const category = categoryWithExt.replace('.mp3', '');
|
85 |
|
86 |
return {
|
87 |
id: index + 1,
|
88 |
title: `${descriptionPart.replace(/_/g, ' ').replace(/^\w/, c => c.toUpperCase())}`,
|
89 |
description: `A debate exploring ${queryPart.replace(/_/g, ' ')}`,
|
90 |
+
audio_file: `${API_URL}${file.path}`, // Add API_URL for audio files
|
91 |
filename: filename,
|
92 |
category: category.replace(/_/g, ' ')
|
93 |
};
|
|
|
95 |
|
96 |
setPodcasts(podcastList);
|
97 |
} catch (err) {
|
98 |
+
console.error('Fetch error:', err);
|
99 |
setError(err instanceof Error ? err.message : 'An error occurred');
|
100 |
} finally {
|
101 |
setLoading(false);
|
|
|
110 |
);
|
111 |
}
|
112 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
return (
|
114 |
<div className="podcasts-container">
|
115 |
<header className="podcasts-header">
|
|
|
117 |
<p>Listen to AI-generated debate podcasts on various topics</p>
|
118 |
</header>
|
119 |
|
120 |
+
{error && (
|
121 |
+
<div className="error-message">Error: {error}</div>
|
122 |
+
)}
|
123 |
+
|
124 |
<div className="podcasts-grid">
|
125 |
+
{podcasts.length > 0 ? (
|
126 |
+
podcasts.map(podcast => (
|
127 |
+
<div
|
128 |
+
key={podcast.id}
|
129 |
+
className="podcast-card"
|
130 |
+
onClick={() => navigate(`/podcast/${podcast.id}`)}
|
131 |
+
style={{ cursor: 'pointer' }}
|
132 |
+
>
|
133 |
+
<div className="podcast-content">
|
134 |
+
<div className="podcast-header">
|
135 |
+
<h2 className="podcast-title">{podcast.title}</h2>
|
136 |
+
<button
|
137 |
+
className="delete-button"
|
138 |
+
onClick={(e) => {
|
139 |
+
e.stopPropagation();
|
140 |
+
handleDelete(podcast);
|
141 |
+
}}
|
142 |
+
aria-label="Delete podcast"
|
143 |
+
>
|
144 |
+
×
|
145 |
+
</button>
|
146 |
+
</div>
|
147 |
+
<div className="category-pill">{podcast.category}</div>
|
148 |
+
<p className="description">{podcast.description}</p>
|
149 |
+
<div className="audio-player" onClick={e => e.stopPropagation()}>
|
150 |
+
<audio
|
151 |
+
controls
|
152 |
+
src={podcast.audio_file}
|
153 |
+
>
|
154 |
+
Your browser does not support the audio element.
|
155 |
+
</audio>
|
156 |
+
</div>
|
157 |
</div>
|
158 |
</div>
|
159 |
+
))
|
160 |
+
) : (
|
|
|
161 |
<div className="no-podcasts-message">
|
162 |
No podcasts found. Generate your first podcast from the home page!
|
163 |
</div>
|
server/main.py
CHANGED
@@ -133,9 +133,10 @@ async def list_audio_files():
|
|
133 |
"path": f"/audio-files/{file}",
|
134 |
"size": os.path.getsize(file_path)
|
135 |
})
|
136 |
-
return audio_files
|
137 |
except Exception as e:
|
138 |
-
|
|
|
139 |
|
140 |
@api_router.get("/audio/{filename}")
|
141 |
async def get_audio_file(filename: str):
|
@@ -143,56 +144,56 @@ async def get_audio_file(filename: str):
|
|
143 |
try:
|
144 |
file_path = os.path.join(audio_dir, filename)
|
145 |
if not os.path.exists(file_path):
|
|
|
146 |
raise HTTPException(status_code=404, detail="File not found")
|
147 |
-
return FileResponse(file_path)
|
148 |
except Exception as e:
|
|
|
149 |
raise HTTPException(status_code=500, detail=str(e))
|
150 |
|
151 |
@api_router.delete("/audio/{filename}")
|
152 |
async def delete_audio_file(filename: str):
|
153 |
"""Delete an audio file and its corresponding transcript."""
|
154 |
try:
|
155 |
-
#
|
156 |
file_path = os.path.join(audio_dir, filename)
|
157 |
if not os.path.exists(file_path):
|
|
|
158 |
raise HTTPException(status_code=404, detail="File not found")
|
159 |
|
160 |
-
# Get all audio files to determine the podcast ID
|
161 |
-
audio_files = [f for f in os.listdir(audio_dir) if f.endswith(('.mp3', '.wav'))]
|
162 |
try:
|
163 |
-
#
|
164 |
-
|
165 |
-
logger.info(f"
|
166 |
|
167 |
-
#
|
168 |
-
|
169 |
|
170 |
-
#
|
|
|
171 |
if os.path.exists(transcripts_file):
|
172 |
with open(transcripts_file, 'r') as f:
|
173 |
transcripts = json.load(f)
|
174 |
|
175 |
-
#
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
|
|
|
|
183 |
|
184 |
-
|
185 |
-
os.remove(file_path)
|
186 |
-
logger.info(f"Deleted audio file: {filename}")
|
187 |
-
|
188 |
-
return {"message": "File and transcript deleted successfully"}
|
189 |
|
190 |
-
except
|
191 |
-
logger.error(f"
|
192 |
-
|
193 |
-
os.remove(file_path)
|
194 |
-
return {"message": "Audio file deleted, but transcript could not be removed"}
|
195 |
|
|
|
|
|
196 |
except Exception as e:
|
197 |
logger.error(f"Error in delete_audio_file: {str(e)}")
|
198 |
raise HTTPException(status_code=500, detail=str(e))
|
|
|
133 |
"path": f"/audio-files/{file}",
|
134 |
"size": os.path.getsize(file_path)
|
135 |
})
|
136 |
+
return audio_files if audio_files else []
|
137 |
except Exception as e:
|
138 |
+
logger.error(f"Error listing audio files: {str(e)}")
|
139 |
+
return []
|
140 |
|
141 |
@api_router.get("/audio/{filename}")
|
142 |
async def get_audio_file(filename: str):
|
|
|
144 |
try:
|
145 |
file_path = os.path.join(audio_dir, filename)
|
146 |
if not os.path.exists(file_path):
|
147 |
+
logger.error(f"Audio file not found: {filename}")
|
148 |
raise HTTPException(status_code=404, detail="File not found")
|
149 |
+
return FileResponse(file_path, media_type="audio/mpeg")
|
150 |
except Exception as e:
|
151 |
+
logger.error(f"Error serving audio file: {str(e)}")
|
152 |
raise HTTPException(status_code=500, detail=str(e))
|
153 |
|
154 |
@api_router.delete("/audio/{filename}")
|
155 |
async def delete_audio_file(filename: str):
|
156 |
"""Delete an audio file and its corresponding transcript."""
|
157 |
try:
|
158 |
+
# Check if file exists before attempting deletion
|
159 |
file_path = os.path.join(audio_dir, filename)
|
160 |
if not os.path.exists(file_path):
|
161 |
+
logger.error(f"File not found for deletion: {filename}")
|
162 |
raise HTTPException(status_code=404, detail="File not found")
|
163 |
|
|
|
|
|
164 |
try:
|
165 |
+
# Delete the audio file first
|
166 |
+
os.remove(file_path)
|
167 |
+
logger.info(f"Deleted audio file: {filename}")
|
168 |
|
169 |
+
# Get all remaining audio files
|
170 |
+
audio_files = [f for f in os.listdir(audio_dir) if f.endswith(('.mp3', '.wav'))]
|
171 |
|
172 |
+
# Try to update transcripts if they exist
|
173 |
+
transcripts_file = os.path.join(os.path.dirname(__file__), "transcripts", "podcasts.json")
|
174 |
if os.path.exists(transcripts_file):
|
175 |
with open(transcripts_file, 'r') as f:
|
176 |
transcripts = json.load(f)
|
177 |
|
178 |
+
# Find the index of the deleted file in the original list
|
179 |
+
try:
|
180 |
+
podcast_id = audio_files.index(filename) + 1
|
181 |
+
if len(transcripts) >= podcast_id:
|
182 |
+
transcripts.pop(podcast_id - 1)
|
183 |
+
with open(transcripts_file, 'w') as f:
|
184 |
+
json.dump(transcripts, f, indent=2)
|
185 |
+
logger.info(f"Updated transcripts after deletion")
|
186 |
+
except ValueError:
|
187 |
+
logger.warning(f"Could not find podcast ID for {filename} in transcripts")
|
188 |
|
189 |
+
return {"message": "File deleted successfully"}
|
|
|
|
|
|
|
|
|
190 |
|
191 |
+
except Exception as e:
|
192 |
+
logger.error(f"Error during file deletion process: {str(e)}")
|
193 |
+
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
194 |
|
195 |
+
except HTTPException as he:
|
196 |
+
raise he
|
197 |
except Exception as e:
|
198 |
logger.error(f"Error in delete_audio_file: {str(e)}")
|
199 |
raise HTTPException(status_code=500, detail=str(e))
|