Files
audiobook-maker-pro-v4/routes/export_routes.py
Ashim Kumar 8e02b9ad09 first commit
2026-02-20 13:53:36 +06:00

175 lines
6.6 KiB
Python

# routes/export_routes.py - Export Routes
import io
import os
import json
import base64
import zipfile
from flask import Blueprint, request, jsonify, send_file
from db import get_db
from utils import sanitize_filename, strip_markdown
from auth import login_required
export_bp = Blueprint('export', __name__)
@export_bp.route('/api/export/<int:project_id>', methods=['GET'])
@login_required
def export_project(project_id):
"""Export project as ZIP file. Only includes chapters with audio."""
db = get_db()
cursor = db.cursor()
cursor.execute('SELECT * FROM projects WHERE id = ?', (project_id,))
project = cursor.fetchone()
if not project:
return jsonify({'error': 'Project not found'}), 404
project_name = sanitize_filename(project['name'])
cursor.execute('''
SELECT * FROM chapters WHERE project_id = ? ORDER BY chapter_number
''', (project_id,))
chapters = cursor.fetchall()
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf:
manifest = {
'title': project['name'],
'assets': [],
'images': []
}
for chapter in chapters:
chapter_num = chapter['chapter_number']
cursor.execute('''
SELECT * FROM markdown_blocks WHERE chapter_id = ? ORDER BY block_order
''', (chapter['id'],))
blocks = cursor.fetchall()
chapter_has_audio = False
for block in blocks:
is_image_block = (
(block['content'] and block['content'].strip().startswith('![')) or
block['block_type'] == 'image'
)
if not is_image_block and block['audio_data']:
chapter_has_audio = True
break
if not chapter_has_audio:
continue
for block in blocks:
block_order = block['block_order']
prefix = f"{chapter_num}.{block_order}"
content = block['content']
is_image_block = (
(content and content.strip().startswith('![')) or
block['block_type'] == 'image'
)
cursor.execute('''
SELECT * FROM block_images WHERE block_id = ? ORDER BY id
''', (block['id'],))
images = cursor.fetchall()
image_idx = 0
for img in images:
if img['position'] == 'before':
image_filename = f"book/{prefix}_img{image_idx}.{img['image_format']}"
image_bytes = base64.b64decode(img['image_data'])
zf.writestr(image_filename, image_bytes)
manifest['images'].append({
'sortKey': prefix,
'file': image_filename
})
image_idx += 1
if is_image_block:
for img in images:
if img['position'] == 'after':
next_prefix = f"{chapter_num}.{block_order + 1}"
image_filename = f"book/{next_prefix}_img{image_idx}.{img['image_format']}"
image_bytes = base64.b64decode(img['image_data'])
zf.writestr(image_filename, image_bytes)
manifest['images'].append({
'sortKey': next_prefix,
'file': image_filename
})
image_idx += 1
continue
plain_text = strip_markdown(content)
if not plain_text.strip():
continue
if not block['audio_data']:
continue
text_filename = f"book/{prefix}_{project_name}.txt"
zf.writestr(text_filename, plain_text)
asset_entry = {
'prefix': f"{prefix}_",
'sortKey': prefix,
'textFile': text_filename,
'audioFile': None,
'jsonFile': None
}
audio_filename = f"book/{prefix}_{project_name}.{block['audio_format'] or 'mp3'}"
audio_bytes = base64.b64decode(block['audio_data'])
zf.writestr(audio_filename, audio_bytes)
asset_entry['audioFile'] = audio_filename
if block['transcription']:
json_filename = f"book/{prefix}_{project_name}.json"
zf.writestr(json_filename, block['transcription'])
asset_entry['jsonFile'] = json_filename
manifest['assets'].append(asset_entry)
for img in images:
if img['position'] == 'after':
next_prefix = f"{chapter_num}.{block_order + 1}"
image_filename = f"book/{next_prefix}_img{image_idx}.{img['image_format']}"
image_bytes = base64.b64decode(img['image_data'])
zf.writestr(image_filename, image_bytes)
manifest['images'].append({
'sortKey': next_prefix,
'file': image_filename
})
image_idx += 1
zf.writestr('manifest.json', json.dumps(manifest, indent=2))
reader_templates_dir = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
'reader_templates'
)
index_path = os.path.join(reader_templates_dir, 'index.html')
if os.path.exists(index_path):
with open(index_path, 'r', encoding='utf-8') as f:
zf.writestr('index.html', f.read())
reader_path = os.path.join(reader_templates_dir, 'Reader.html')
if os.path.exists(reader_path):
with open(reader_path, 'r', encoding='utf-8') as f:
zf.writestr('Reader.html', f.read())
zip_buffer.seek(0)
return send_file(
zip_buffer,
mimetype='application/zip',
as_attachment=True,
download_name=f"{project_name}.zip"
)