Skip to content

Commit 7f37a79

Browse files
committed
Final fix: Manually updated FetchLibriVoxAudiobooks command to remove Archive.org references
1 parent e5ac965 commit 7f37a79

File tree

4 files changed

+136
-127
lines changed

4 files changed

+136
-127
lines changed

app/Console/Commands/FetchLibriVoxAudiobooks.php

Lines changed: 127 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -167,153 +167,153 @@ public function handle()
167167
} elseif (isset($apiBook['url_cover_image']) && filter_var($apiBook['url_cover_image'], FILTER_VALIDATE_URL)) {
168168
// Fallback to url_cover_image if it's a valid URL
169169
$coverImage = $apiBook['url_cover_image'];
170-
} elseif (isset($apiBook['url_librivox'])) {
171-
// Last resort: try to construct from url_librivox, removing potential double slashes
172-
$constructedUrl = rtrim($apiBook['url_librivox'], '/') . '/cover_small.jpg';
173-
if (filter_var($constructedUrl, FILTER_VALIDATE_URL)) {
174-
$coverImage = $constructedUrl;
170+
} elseif (isset($apiBook['url_librivox'])) {
171+
// Last resort: try to construct from url_librivox, removing potential double slashes
172+
$constructedUrl = rtrim($apiBook['url_librivox'], '/') . '/cover_small.jpg';
173+
if (filter_var($constructedUrl, FILTER_VALIDATE_URL)) {
174+
$coverImage = $constructedUrl;
175+
}
175176
}
176-
}
177177

178-
// Final fallback to a generic placeholder if no valid image URL is found
179-
if (empty($coverImage) || !filter_var($coverImage, FILTER_VALIDATE_URL)) {
180-
$coverImage = 'https://librivox.org/images/librivox_logo_small.png'; // Default placeholder
181-
}
182-
183-
// Main Audiobook Data
184-
$audiobookData = [
185-
'title' => $title,
186-
'author' => $authorName,
187-
'narrator' => $narratorName,
188-
'description' => $description,
189-
'cover_image' => $coverImage,
190-
'duration' => $durationStr,
191-
'source_url' => null, // Main source_url is null, sections will have actual files
192-
'category_id' => $category->id,
193-
'language' => $fullLanguageName,
194-
'librivox_url' => $apiBook['url_librivox'] ?? null, // Direct LibriVox project page URL
195-
'librivox_id' => $librivoxId, // Use LibriVox project ID as librivox_id
196-
];
197-
198-
// Generate a unique slug for the audiobook
199-
$baseSlug = Str::slug($title);
200-
$slug = $baseSlug;
201-
$counter = 1;
202-
203-
// Check for slug uniqueness and append counter if necessary
204-
while (Audiobook::where('slug', $slug)->where('librivox_id', '!=', $librivoxId)->exists()) {
205-
$slug = $baseSlug . '-' . $counter++;
206-
}
207-
$audiobookData['slug'] = $slug;
208-
209-
$this->info("Processing book: {$title} (LibriVox ID: {$librivoxId}), Slug: {$slug}");
210-
Log::info("Data for updateOrCreate:", $audiobookData);
211-
212-
$book = null;
213-
if (!$isDryRun) {
214-
// Create or Update the main Audiobook record
215-
$book = Audiobook::updateOrCreate(
216-
['librivox_id' => $librivoxId],
217-
$audiobookData
218-
);
219-
220-
if ($book->wasRecentlyCreated) {
221-
$createdCount++;
222-
$this->info("Created audiobook: {$book->title} (Slug: {$book->slug})");
223-
} elseif ($book->wasChanged()) {
224-
$updatedCount++;
225-
$this->info("Updated audiobook: {$book->title} (Slug: {$book->slug})");
226-
} else {
227-
$this->info("Audiobook matched existing record, no changes: {$book->title} (Slug: {$book->slug})");
178+
// Final fallback to a generic placeholder if no valid image URL is found
179+
if (empty($coverImage) || !filter_var($coverImage, FILTER_VALIDATE_URL)) {
180+
$coverImage = 'https://librivox.org/images/librivox_logo_small.png'; // Default placeholder
228181
}
229-
} else {
230-
$this->info("DRY RUN: Would create/update audiobook: {$title} (Slug: {$slug})");
231-
$book = (object)['id' => 99999, 'librivox_id' => $librivoxId, 'title' => $title, 'narrator' => $narratorName]; // Mock book for dry run
232-
}
233182

183+
// Main Audiobook Data
184+
$audiobookData = [
185+
'title' => $title,
186+
'author' => $authorName,
187+
'narrator' => $narratorName,
188+
'description' => $description,
189+
'cover_image' => $coverImage,
190+
'duration' => $durationStr,
191+
'source_url' => null, // Main source_url is null, sections will have actual files
192+
'category_id' => $category->id,
193+
'language' => $fullLanguageName,
194+
'librivox_url' => $apiBook['url_librivox'] ?? null, // Direct LibriVox project page URL
195+
'librivox_id' => $librivoxId, // Use LibriVox project ID as librivox_id
196+
];
197+
198+
// Generate a unique slug for the audiobook
199+
$baseSlug = Str::slug($title);
200+
$slug = $baseSlug;
201+
$counter = 1;
202+
203+
// Check for slug uniqueness and append counter if necessary
204+
while (Audiobook::where('slug', $slug)->where('librivox_id', '!=', $librivoxId)->exists()) {
205+
$slug = $baseSlug . '-' . $counter++;
206+
}
207+
$audiobookData['slug'] = $slug;
234208

235-
// --- Section Processing ---
236-
// Fetch detailed track metadata for sections using the LibriVox audiotracks API
237-
$audioTracks = $this->libriVoxService->fetchAudiobookTracks($librivoxId);
209+
$this->info("Processing book: {$title} (LibriVox ID: {$librivoxId}), Slug: {$slug}");
210+
Log::info("Data for updateOrCreate:", $audiobookData);
238211

239-
if (!empty($audioTracks)) {
212+
$book = null;
240213
if (!$isDryRun) {
241-
// Clear existing sections for this audiobook to prevent duplicates/stale data
242-
AudiobookSection::where('audiobook_id', $book->id)->delete();
243-
$this->info("Cleared existing sections for audiobook: {$book->title}");
214+
// Create or Update the main Audiobook record
215+
$book = Audiobook::updateOrCreate(
216+
['librivox_id' => $librivoxId],
217+
$audiobookData
218+
);
219+
220+
if ($book->wasRecentlyCreated) {
221+
$createdCount++;
222+
$this->info("Created audiobook: {$book->title} (Slug: {$book->slug})");
223+
} elseif ($book->wasChanged()) {
224+
$updatedCount++;
225+
$this->info("Updated audiobook: {$book->title} (Slug: {$book->slug})");
226+
} else {
227+
$this->info("Audiobook matched existing record, no changes: {$book->title} (Slug: {$book->slug})");
228+
}
244229
} else {
245-
$this->info("DRY RUN: Would clear existing sections for audiobook: {$book->title}");
230+
$this->info("DRY RUN: Would create/update audiobook: {$title} (Slug: {$slug})");
231+
$book = (object)['id' => 99999, 'librivox_id' => $librivoxId, 'title' => $title, 'narrator' => $narratorName]; // Mock book for dry run
246232
}
247233

248-
$sectionNumber = 1;
249-
foreach ($audioTracks as $track) {
250-
try {
251-
$sectionTitle = $track['section_title'] ?? 'Part ' . $sectionNumber;
252-
$sourceUrl = $track['listen_url'] ?? null; // Direct listen URL from LibriVox API
253-
$duration = $track['playtime'] ?? null; // Playtime in H:M:S format
254234

255-
if (empty($sourceUrl)) {
256-
$this->warn("Skipping section '{$sectionTitle}' for book '{$book->title}' due to missing source URL.");
257-
continue;
258-
}
235+
// --- Section Processing ---
236+
// Fetch detailed track metadata for sections using the LibriVox audiotracks API
237+
$audioTracks = $this->libriVoxService->fetchAudiobookTracks($librivoxId);
238+
239+
if (!empty($audioTracks)) {
240+
if (!$isDryRun) {
241+
// Clear existing sections for this audiobook to prevent duplicates/stale data
242+
AudiobookSection::where('audiobook_id', $book->id)->delete();
243+
$this->info("Cleared existing sections for audiobook: {$book->title}");
244+
} else {
245+
$this->info("DRY RUN: Would clear existing sections for audiobook: {$book->title}");
246+
}
259247

260-
if (!$isDryRun) {
261-
AudiobookSection::create([
262-
'audiobook_id' => $book->id,
263-
'title' => $sectionTitle,
264-
'section_number' => $sectionNumber,
265-
'source_url' => $sourceUrl,
266-
'duration' => $duration,
267-
'reader_name' => $track['reader'] ?? $book->narrator, // Prefer section-specific reader, fallback to book narrator
248+
$sectionNumber = 1;
249+
foreach ($audioTracks as $track) {
250+
try {
251+
$sectionTitle = $track['section_title'] ?? 'Part ' . $sectionNumber;
252+
$sourceUrl = $track['listen_url'] ?? null; // Direct listen URL from LibriVox API
253+
$duration = $track['playtime'] ?? null; // Playtime in H:M:S format
254+
255+
if (empty($sourceUrl)) {
256+
$this->warn("Skipping section '{$sectionTitle}' for book '{$book->title}' due to missing source URL.");
257+
continue;
258+
}
259+
260+
if (!$isDryRun) {
261+
AudiobookSection::create([
262+
'audiobook_id' => $book->id,
263+
'title' => $sectionTitle,
264+
'section_number' => $sectionNumber,
265+
'source_url' => $sourceUrl,
266+
'duration' => $duration,
267+
'reader_name' => $track['reader'] ?? $book->narrator, // Prefer section-specific reader, fallback to book narrator
268+
]);
269+
} else {
270+
$this->info("DRY RUN: Would create section '{$sectionTitle}' for audiobook: {$book->title}");
271+
}
272+
$sectionNumber++;
273+
} catch (\Exception $e) {
274+
Log::error("Failed to process section for book ID {$librivoxId}, section number {$sectionNumber}: " . $e->getMessage(), [
275+
'exception' => $e,
276+
'track_data' => $track,
268277
]);
269-
} else {
270-
$this->info("DRY RUN: Would create section '{$sectionTitle}' for audiobook: {$book->title}");
278+
$this->error("Error processing section for book '{$book->title}'. See logs for details.");
271279
}
272-
$sectionNumber++;
273-
} catch (\Exception $e) {
274-
Log::error("Failed to process section for book ID {$librivoxId}, section number {$sectionNumber}: " . $e->getMessage(), [
275-
'exception' => $e,
276-
'track_data' => $track,
277-
]);
278-
$this->error("Error processing section for book '{$book->title}'. See logs for details.");
279280
}
281+
$this->info("Imported " . (count($audioTracks)) . " sections for audiobook: {$book->title}");
282+
} else {
283+
$this->warn("No audio tracks found for sections for book: {$book->title} (LibriVox ID: {$librivoxId}).");
280284
}
281-
$this->info("Imported " . (count($audioTracks)) . " sections for audiobook: {$book->title}");
282-
} else {
283-
$this->warn("No audio tracks found for sections for book: {$book->title} (LibriVox ID: {$librivoxId}).");
285+
286+
$progressBar->advance();
287+
} catch (\Exception $e) {
288+
Log::error("Failed to import audiobook with LibriVox ID {$librivoxId}: " . $e->getMessage(), [
289+
'exception' => $e,
290+
'api_book_data' => $apiBook,
291+
]);
292+
$this->error("Error importing audiobook '{$title}'. See logs for details. Skipping to next book.");
293+
$progressBar->advance(); // Ensure progress bar advances even on error
284294
}
295+
}
285296

286-
$progressBar->advance();
287-
} catch (\Exception $e) {
288-
Log::error("Failed to import audiobook with LibriVox ID {$librivoxId}: " . $e->getMessage(), [
289-
'exception' => $e,
290-
'api_book_data' => $apiBook,
291-
]);
292-
$this->error("Error importing audiobook '{$title}'. See logs for details. Skipping to next book.");
293-
$progressBar->advance(); // Ensure progress bar advances even on error
297+
$progressBar->finish();
298+
$this->info("\nProcessing complete.");
299+
if ($isDryRun) {
300+
$this->info("DRY RUN finished. No changes were made to the database.");
301+
} else {
302+
$this->info("{$createdCount} audiobooks created, {$updatedCount} audiobooks updated.");
294303
}
295-
}
296304

297-
$progressBar->finish();
298-
$this->info("\nProcessing complete.");
299-
if ($isDryRun) {
300-
$this->info("DRY RUN finished. No changes were made to the database.");
301-
} else {
302-
$this->info("{$createdCount} audiobooks created, {$updatedCount} audiobooks updated.");
305+
return Command::SUCCESS;
303306
}
304307

305-
return Command::SUCCESS;
306-
}
307-
308-
/**
309-
* Converts ISO 639-2/3 language codes to full language names.
310-
*
311-
* @param string $code The ISO language code.
312-
* @return string The full language name.
313-
*/
314-
private function convertLanguageCodeToName(string $code): string
315-
{
316-
// Use the language mapping from the config file
317-
return Config::get('languages.' . strtolower($code), ucfirst(strtolower($code)));
308+
/**
309+
* Converts ISO 639-2/3 language codes to full language names.
310+
*
311+
* @param string $code The ISO language code.
312+
* @return string The full language name.
313+
*/
314+
private function convertLanguageCodeToName(string $code): string
315+
{
316+
// Use the language mapping from the config file
317+
return Config::get('languages.' . strtolower($code), ucfirst(strtolower($code)));
318+
}
318319
}
319-
}

memory-bank/productContext.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Product Context
2+
3+
This document explains the purpose of the project, the problems it solves, how it should function, and the user experience goals.

memory-bank/projectbrief.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Project Brief
2+
3+
This is the foundational document for the project. It outlines the core requirements and goals.

memory-bank/systemPatterns.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# System Patterns
2+
3+
This document describes the system architecture, key technical decisions, design patterns in use, component relationships, and critical implementation paths.

0 commit comments

Comments
 (0)