# routes/public_routes.py - Public (No Auth) Routes for Published Audiobooks import json from flask import Blueprint, jsonify, send_from_directory, abort from db import get_db public_bp = Blueprint('public', __name__) @public_bp.route('/home') def public_home(): """Public homepage - Bookcase view of published audiobooks.""" return send_from_directory('templates', 'public_home.html') @public_bp.route('/read/') def public_reader(project_id): """Public reader page for a published audiobook.""" db = get_db() cursor = db.cursor() cursor.execute('SELECT id, is_published FROM projects WHERE id = ?', (project_id,)) project = cursor.fetchone() if not project or not project['is_published']: abort(404) # Increment view count cursor.execute('UPDATE projects SET view_count = view_count + 1 WHERE id = ?', (project_id,)) db.commit() return send_from_directory('templates', 'public_reader.html') @public_bp.route('/api/public/books', methods=['GET']) def list_published_books(): """List all published audiobooks (no auth required).""" db = get_db() cursor = db.cursor() cursor.execute(''' SELECT p.id, p.name, p.description, p.author, p.category, p.thumbnail_data, p.thumbnail_format, p.published_at, p.view_count, p.created_at, (SELECT COUNT(*) FROM chapters WHERE project_id = p.id) as chapter_count FROM projects p WHERE p.is_published = 1 ORDER BY p.published_at DESC ''') books = [] for row in cursor.fetchall(): books.append({ 'id': row['id'], 'name': row['name'], 'description': row['description'] or '', 'author': row['author'] or '', 'category': row['category'] or '', 'thumbnail_data': row['thumbnail_data'], 'thumbnail_format': row['thumbnail_format'] or 'png', 'published_at': row['published_at'], 'view_count': row['view_count'] or 0, 'chapter_count': row['chapter_count'] }) return jsonify({'books': books}) @public_bp.route('/api/public/books/', methods=['GET']) def get_published_book(project_id): """Get full published book content for the reader.""" db = get_db() cursor = db.cursor() cursor.execute(''' SELECT * FROM projects WHERE id = ? AND is_published = 1 ''', (project_id,)) project = cursor.fetchone() if not project: return jsonify({'error': 'Book not found or not published'}), 404 cursor.execute(''' SELECT * FROM chapters WHERE project_id = ? ORDER BY chapter_number ''', (project_id,)) chapters = cursor.fetchall() chapters_data = [] for chapter in chapters: cursor.execute(''' SELECT * FROM markdown_blocks WHERE chapter_id = ? ORDER BY block_order ''', (chapter['id'],)) blocks = cursor.fetchall() blocks_data = [] for block in blocks: cursor.execute(''' SELECT * FROM block_images WHERE block_id = ? ORDER BY id ''', (block['id'],)) images = cursor.fetchall() blocks_data.append({ 'id': block['id'], 'block_order': block['block_order'], 'block_type': block['block_type'], 'content': block['content'], 'audio_data': block['audio_data'], 'audio_format': block['audio_format'], 'transcription': json.loads(block['transcription']) if block['transcription'] else [], 'images': [{ 'data': img['image_data'], 'format': img['image_format'], 'alt_text': img['alt_text'], 'position': img['position'] } for img in images] }) chapters_data.append({ 'id': chapter['id'], 'chapter_number': chapter['chapter_number'], 'title': chapter['title'], 'blocks': blocks_data }) return jsonify({ 'id': project['id'], 'name': project['name'], 'description': project['description'] or '', 'author': project['author'] or '', 'thumbnail_data': project['thumbnail_data'], 'thumbnail_format': project['thumbnail_format'] or 'png', 'chapters': chapters_data })