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))
|