Files

203 lines
7.2 KiB
Python
Raw Permalink Normal View History

"""
Platform Gateway Kindle integration (send books via SMTP2GO).
"""
import json
import base64
import urllib.request
from pathlib import Path
from config import (
BOOKLORE_URL, BOOKLORE_BOOKS_DIR, BOOKDROP_DIR,
SMTP2GO_API_KEY, SMTP2GO_FROM_EMAIL, SMTP2GO_FROM_NAME,
KINDLE_EMAIL_1, KINDLE_EMAIL_2,
)
from proxy import proxy_request
from integrations.booklore import booklore_auth
def _find_book_file(book_id: str) -> tuple:
"""Find the actual ebook file for a Booklore book ID.
Returns (file_path, book_metadata) or (None, None)."""
token = booklore_auth()
if not token:
return None, None
# Get book metadata from Booklore
s, _, r = proxy_request(
f"{BOOKLORE_URL}/api/v1/books/{book_id}", "GET",
{"Authorization": f"Bearer {token}"}, timeout=10
)
if s != 200:
return None, None
book = json.loads(r)
meta = book.get("metadata", {})
title = meta.get("title", "")
if not title or not BOOKLORE_BOOKS_DIR.exists():
return None, meta
# Search for the file in the library directory
title_lower = title.lower()
title_words = set(title_lower.split()[:4]) # First 4 words for matching
best_match = None
best_score = 0
for ext in ["epub", "pdf", "mobi", "azw3"]:
for filepath in BOOKLORE_BOOKS_DIR.rglob(f"*.{ext}"):
fname = filepath.stem.lower()
# Check if title words appear in filename
matches = sum(1 for w in title_words if w in fname)
score = matches / len(title_words) if title_words else 0
# Prefer epub > pdf > mobi > azw3
ext_bonus = {"epub": 0.1, "pdf": 0.05, "mobi": 0.03, "azw3": 0.02}.get(ext, 0)
score += ext_bonus
if score > best_score:
best_score = score
best_match = filepath
if best_match and best_score >= 0.5:
return best_match, meta
return None, meta
def handle_send_to_kindle(handler, book_id: str, body: bytes):
"""Send a book file to a Kindle email via SMTP2GO API."""
if not SMTP2GO_API_KEY or not SMTP2GO_FROM_EMAIL:
handler._send_json({"error": "Email not configured"}, 502)
return
try:
data = json.loads(body)
except Exception as e:
handler._send_json({"error": "Invalid JSON"}, 400)
return
target = data.get("target", "1")
kindle_email = KINDLE_EMAIL_1 if target == "1" else KINDLE_EMAIL_2
if not kindle_email:
handler._send_json({"error": f"Kindle target {target} not configured"}, 400)
return
# Find the book file
file_path, meta = _find_book_file(book_id)
if not file_path or not file_path.exists():
handler._send_json({"error": "Book file not found on disk"}, 404)
return
title = meta.get("title", "Book") if meta else "Book"
author = ", ".join(meta.get("authors", [])) if meta else ""
# Read file and encode as base64
file_data = file_path.read_bytes()
file_b64 = base64.b64encode(file_data).decode("ascii")
filename = file_path.name
# Determine MIME type
ext = file_path.suffix.lower()
mime_map = {".epub": "application/epub+zip", ".pdf": "application/pdf", ".mobi": "application/x-mobipocket-ebook", ".azw3": "application/x-mobi8-ebook"}
mime_type = mime_map.get(ext, "application/octet-stream")
# Send via SMTP2GO API
email_payload = {
"api_key": SMTP2GO_API_KEY,
"sender": f"{SMTP2GO_FROM_NAME} <{SMTP2GO_FROM_EMAIL}>",
"to": [kindle_email],
"subject": f"{title}" + (f" - {author}" if author else ""),
"text_body": f"Sent from Platform: {title}" + (f" by {author}" if author else ""),
"attachments": [{
"filename": filename,
"fileblob": file_b64,
"mimetype": mime_type,
}]
}
try:
req_body = json.dumps(email_payload).encode("utf-8")
req = urllib.request.Request(
"https://api.smtp2go.com/v3/email/send",
data=req_body,
headers={"Content-Type": "application/json"},
)
with urllib.request.urlopen(req, timeout=30) as resp:
result = json.loads(resp.read())
if result.get("data", {}).get("succeeded", 0) > 0:
handler._send_json({
"success": True,
"title": title,
"sentTo": kindle_email,
"format": ext.lstrip(".").upper(),
"size": len(file_data),
})
else:
handler._send_json({"error": "Email send failed", "detail": result}, 500)
except Exception as e:
handler._send_json({"error": f"SMTP2GO error: {str(e)}"}, 500)
def handle_send_file_to_kindle(handler, body: bytes):
"""Send a downloaded file to Kindle by filename from bookdrop directory."""
if not SMTP2GO_API_KEY or not SMTP2GO_FROM_EMAIL:
handler._send_json({"error": "Email not configured"}, 502)
return
try:
data = json.loads(body)
except Exception as e:
handler._send_json({"error": "Invalid JSON"}, 400)
return
filename = data.get("filename", "")
target = data.get("target", "1")
title = data.get("title", filename)
kindle_email = KINDLE_EMAIL_1 if target == "1" else KINDLE_EMAIL_2
if not kindle_email:
handler._send_json({"error": f"Kindle target {target} not configured"}, 400)
return
# Find file in bookdrop or booklore-books
file_path = None
for search_dir in [BOOKDROP_DIR, BOOKLORE_BOOKS_DIR]:
if not search_dir.exists():
continue
for fp in search_dir.rglob("*"):
if fp.is_file() and fp.name == filename:
file_path = fp
break
if file_path:
break
if not file_path or not file_path.exists():
handler._send_json({"error": f"File not found: {filename}"}, 404)
return
file_data = file_path.read_bytes()
file_b64 = base64.b64encode(file_data).decode("ascii")
ext = file_path.suffix.lower()
mime_map = {".epub": "application/epub+zip", ".pdf": "application/pdf", ".mobi": "application/x-mobipocket-ebook", ".azw3": "application/x-mobi8-ebook"}
mime_type = mime_map.get(ext, "application/octet-stream")
email_payload = {
"api_key": SMTP2GO_API_KEY,
"sender": f"{SMTP2GO_FROM_NAME} <{SMTP2GO_FROM_EMAIL}>",
"to": [kindle_email],
"subject": title,
"text_body": f"Sent from Platform: {title}",
"attachments": [{"filename": filename, "fileblob": file_b64, "mimetype": mime_type}]
}
try:
req_body = json.dumps(email_payload).encode("utf-8")
req = urllib.request.Request("https://api.smtp2go.com/v3/email/send", data=req_body, headers={"Content-Type": "application/json"})
with urllib.request.urlopen(req, timeout=30) as resp:
result = json.loads(resp.read())
if result.get("data", {}).get("succeeded", 0) > 0:
handler._send_json({"success": True, "title": title, "sentTo": kindle_email, "format": ext.lstrip(".").upper()})
else:
handler._send_json({"error": "Email send failed", "detail": result}, 500)
except Exception as e:
handler._send_json({"error": f"SMTP2GO error: {str(e)}"}, 500)