import os import uuid import threading import requests import re import shlex import fitz from urllib.parse import urlparse from flask import current_app, request, jsonify, redirect, url_for, render_template from werkzeug.utils import secure_filename from .common import main_bp, get_db_connection, login_required, current_user, upload_progress from strings import ROUTE_INDEX_V2, ROUTE_IMAGES, ROUTE_UPLOAD_IMAGES, METHOD_POST def convert_google_drive_url(url): try: match = re.search(r'/file/d/([a-zA-Z0-9_-]+)|id=([a-zA-Z0-9_-]+)', url) if match: file_id = match.group(1) or match.group(2) if file_id: return f'https://drive.google.com/uc?export=download&id={file_id}' except Exception: pass return url def _parse_curl_command(command): try: parts = shlex.split(command) except ValueError: parts = command.split() url, output_filename = None, None try: if '-o' in parts: o_index = parts.index('-o') if o_index + 1 < len(parts): output_filename = parts[o_index + 1] except ValueError: pass for part in parts: if part.startswith('http'): url = part; break if url and not output_filename: output_filename = os.path.basename(urlparse(url).path) or "downloaded_pdf.pdf" if not output_filename.lower().endswith('.pdf'): output_filename += '.pdf' return url, output_filename def process_pdf_background(session_id, user_id, original_filename, pdf_content, app_config): upload_progress[session_id] = {'status': 'processing', 'progress': 0, 'message': 'Starting...'} try: conn = get_db_connection() pdf_filename = f"{session_id}_{original_filename}" pdf_path = os.path.join(app_config['UPLOAD_FOLDER'], pdf_filename) with open(pdf_path, 'wb') as f: f.write(pdf_content) doc = fitz.open(pdf_path) total_pages = len(doc) upload_progress[session_id]['total'] = total_pages user_row = conn.execute("SELECT dpi FROM users WHERE id = ?", (user_id,)).fetchone() dpi = user_row['dpi'] if user_row else 150 for i, page in enumerate(doc): pix = page.get_pixmap(dpi=dpi) page_filename = f"{session_id}_page_{i}.png" page_path = os.path.join(app_config['UPLOAD_FOLDER'], page_filename) pix.save(page_path) conn.execute('INSERT INTO images (session_id, image_index, filename, original_name, image_type) VALUES (?, ?, ?, ?, ?)', (session_id, i, page_filename, f"Page {i+1}", 'original')) progress = int(((i + 1) / total_pages) * 100) upload_progress[session_id].update({'progress': progress, 'message': f'Processed page {i+1}/{total_pages}'}) conn.commit(); conn.close(); doc.close() upload_progress[session_id] = {'status': 'completed', 'progress': 100, 'message': 'Done'} except Exception as e: upload_progress[session_id] = {'status': 'error', 'message': str(e)} if 'conn' in locals(): conn.close() @main_bp.route('/process_final_pdf/') @login_required def process_final_pdf(pdf_id): conn = get_db_connection() pdf_info = conn.execute('SELECT filename FROM generated_pdfs WHERE id = ? AND user_id = ?', (pdf_id, current_user.id)).fetchone() if not pdf_info: conn.close(); flash("PDF not found", "warning"); return redirect(url_for('main.index_v2')) original_filename = pdf_info['filename'] pdf_path = os.path.join(current_app.config['OUTPUT_FOLDER'], original_filename) if not os.path.exists(pdf_path): conn.close(); flash("File missing", "danger"); return redirect(url_for('main.index_v2')) session_id = str(uuid.uuid4()) conn.execute('INSERT INTO sessions (id, original_filename, user_id) VALUES (?, ?, ?)', (session_id, original_filename, current_user.id)) doc = fitz.open(pdf_path) for i, page in enumerate(doc): pix = page.get_pixmap(dpi=current_user.dpi) page_filename = f"{session_id}_page_{i}.png" pix.save(os.path.join(current_app.config['UPLOAD_FOLDER'], page_filename)) conn.execute('INSERT INTO images (session_id, image_index, filename, original_name, image_type) VALUES (?, ?, ?, ?, ?)', (session_id, i, page_filename, f"Page {i+1}", 'original')) conn.commit(); conn.close(); doc.close() return redirect(url_for('main.crop_interface_v2', session_id=session_id, image_index=0)) @main_bp.route(ROUTE_INDEX_V2) @login_required def index_v2(): conn = get_db_connection() pdfs = conn.execute('SELECT id, filename, subject, tags, notes, persist FROM generated_pdfs WHERE user_id = ? ORDER BY created_at DESC', (current_user.id,)).fetchall() conn.close() return render_template('indexv2.html', pdfs=[dict(row) for row in pdfs]) @main_bp.route('/v2/upload', methods=['POST']) @login_required def v2_upload(): session_id = str(uuid.uuid4()) pdf_content, original_filename = None, None try: if 'pdf' in request.files and request.files['pdf'].filename: file = request.files['pdf'] if file and file.filename.lower().endswith('.pdf'): original_filename = secure_filename(file.filename) pdf_content = file.read() else: return jsonify({'error': 'Invalid file type'}), 400 elif 'pdf_url' in request.form and request.form['pdf_url']: pdf_url = convert_google_drive_url(request.form['pdf_url']) response = requests.get(pdf_url, allow_redirects=True) response.raise_for_status() if 'Content-Disposition' in response.headers: cd = response.headers['Content-Disposition'] fname_match = re.search(r'filename="?([^";]+)"?', cd) if fname_match: original_filename = fname_match.group(1) if not original_filename: original_filename = os.path.basename(urlparse(pdf_url).path) if not original_filename or not original_filename.lower().endswith('.pdf'): original_filename = 'downloaded_document.pdf' pdf_content = response.content elif 'curl_command' in request.form and request.form['curl_command']: command = request.form['curl_command'].strip().split('\n')[0] url, filename = _parse_curl_command(command) if not url or not filename: return jsonify({'error': f"Could not parse cURL"}), 400 url = convert_google_drive_url(url) response = requests.get(url, allow_redirects=True) response.raise_for_status() original_filename = filename pdf_content = response.content else: return jsonify({'error': 'No PDF provided'}), 400 session_type = request.form.get('type', 'standard') conn = get_db_connection() conn.execute('INSERT INTO sessions (id, original_filename, name, user_id, session_type) VALUES (?, ?, ?, ?, ?)', (session_id, original_filename, original_filename, current_user.id, session_type)) conn.commit(); conn.close() if request.args.get('async') == 'true': thread = threading.Thread(target=process_pdf_background, args=(session_id, current_user.id, original_filename, pdf_content, current_app.config.copy())) thread.start() return jsonify({'session_id': session_id, 'status': 'processing'}) conn = get_db_connection() pdf_path = os.path.join(current_app.config['UPLOAD_FOLDER'], f"{session_id}_{original_filename}") with open(pdf_path, 'wb') as f: f.write(pdf_content) doc = fitz.open(pdf_path) page_files = [] for i, page in enumerate(doc): pix = page.get_pixmap(dpi=current_user.dpi) page_filename = f"{session_id}_page_{i}.png" pix.save(os.path.join(current_app.config['UPLOAD_FOLDER'], page_filename)) conn.execute('INSERT INTO images (session_id, image_index, filename, original_name, image_type) VALUES (?, ?, ?, ?, ?)', (session_id, i, page_filename, f"Page {i+1}", 'original')) page_files.append({'filename': page_filename, 'original_name': f"Page {i+1}", 'index': i}) conn.commit(); conn.close(); doc.close() return jsonify({'session_id': session_id, 'files': page_files}) except Exception as e: return jsonify({'error': str(e)}), 500 @main_bp.route(ROUTE_IMAGES) @login_required def image_upload(): return render_template('image_upload.html') @main_bp.route(ROUTE_UPLOAD_IMAGES, methods=[METHOD_POST]) @login_required def upload_images(): session_id = str(uuid.uuid4()) files = request.files.getlist('images') if not files or all(f.filename == '' for f in files): return jsonify({'error': 'No files'}), 400 session_type = request.form.get('type', 'standard') conn = get_db_connection() original_filename = secure_filename(files[0].filename) if len(files) == 1 else f"{len(files)} images" conn.execute('INSERT INTO sessions (id, original_filename, name, user_id, session_type) VALUES (?, ?, ?, ?, ?)', (session_id, original_filename, original_filename, current_user.id, session_type)) uploaded_files = [] for i, file in enumerate(files): if file and file.filename != '': filename = f"{session_id}_{secure_filename(file.filename)}" file.save(os.path.join(current_app.config['UPLOAD_FOLDER'], filename)) conn.execute('INSERT INTO images (session_id, image_index, filename, original_name, image_type) VALUES (?, ?, ?, ?, ?)', (session_id, i, filename, secure_filename(file.filename), 'original')) uploaded_files.append({'filename': filename, 'original_name': secure_filename(file.filename), 'index': i}) conn.commit(); conn.close() return jsonify({'session_id': session_id, 'files': uploaded_files}) @main_bp.route('/upload_final_pdf') @login_required def upload_final_pdf(): return render_template('upload_final_pdf.html') @main_bp.route('/handle_final_pdf_upload', methods=[METHOD_POST]) @login_required def handle_final_pdf_upload(): subject = request.form.get('subject') if not subject: return 'Subject is required', 400 tags, notes = request.form.get('tags'), request.form.get('notes') conn = get_db_connection() def process_and_save_pdf(file_content, original_filename): sid = str(uuid.uuid4()) # Associate session with user and mark as final_pdf conn.execute('INSERT INTO sessions (id, original_filename, user_id, session_type) VALUES (?, ?, ?, ?)', (sid, original_filename, current_user.id, 'final_pdf')) out_name = f"{sid}_{secure_filename(original_filename)}" with open(os.path.join(current_app.config['OUTPUT_FOLDER'], out_name), 'wb') as f: f.write(file_content) conn.execute('INSERT INTO generated_pdfs (session_id, filename, subject, tags, notes, source_filename, user_id) VALUES (?, ?, ?, ?, ?, ?, ?)', (sid, out_name, subject, tags, notes, original_filename, current_user.id)) conn.commit() try: if 'pdf' in request.files and request.files['pdf'].filename: process_and_save_pdf(request.files['pdf'].read(), request.files['pdf'].filename) elif 'pdf_url' in request.form and request.form['pdf_url']: pdf_url = convert_google_drive_url(request.form['pdf_url']) res = requests.get(pdf_url, allow_redirects=True); res.raise_for_status() process_and_save_pdf(res.content, os.path.basename(urlparse(pdf_url).path) or 'document.pdf') elif 'curl_command' in request.form and request.form['curl_command']: for cmd in request.form['curl_command'].strip().split(','): u, f = _parse_curl_command(cmd.strip()) if u and f: res = requests.get(convert_google_drive_url(u), allow_redirects=True); res.raise_for_status() process_and_save_pdf(res.content, f) else: return 'No PDF provided', 400 except Exception as e: return str(e), 500 finally: conn.close() return redirect(url_for('main.pdf_manager'))