Metadata ve çeviri ile ilgili düzeltmeler. UI değişiklikleri.

This commit is contained in:
2025-11-17 11:03:39 +03:00
parent daf39e35c0
commit b6c9fb795b
14 changed files with 859 additions and 226 deletions

View File

@@ -4,10 +4,11 @@ import cors from 'cors';
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import { tmpdir } from 'os';
import { join } from 'path';
import { dirname, join } from 'path';
import { promises as fs } from 'fs';
import { v4 as uuidV4 } from 'uuid';
import Epub from 'epub-gen';
import { fileURLToPath } from 'url';
const requiredEnv = [
'SUPABASE_URL',
@@ -22,6 +23,7 @@ requiredEnv.forEach((key) => {
}
});
const __dirname = dirname(fileURLToPath(import.meta.url));
const app = express();
const PORT = process.env.PORT || 4000;
const ORIGIN = process.env.CLIENT_ORIGIN || 'http://localhost:5173';
@@ -260,8 +262,10 @@ app.post('/translate', async (req, res) => {
return res.status(400).json({ message: 'Çevrilecek metin bulunamadı.' });
}
console.log('[Translate] İstek alındı', { length: text.length, snippet: text.slice(0, 60) });
try {
const translated = await translateWithGlm(text);
console.log('[Translate] Çeviri başarıyla döndü');
return res.json({ text: translated });
} catch (error) {
console.error('GLM çeviri hatası:', error);
@@ -275,9 +279,17 @@ app.post('/generate-epub', async (req, res) => {
return res.status(400).json({ message: 'text is required' });
}
const title = meta?.title || 'imgPub OCR Export';
const author = meta?.author || 'imgPub';
const title = meta?.title?.trim() || 'imgPub OCR Export';
const filename = meta?.filename || `imgpub${Date.now()}.epub`;
const authors =
Array.isArray(meta?.authors) && meta.authors.length
? meta.authors.filter(Boolean)
: meta?.author
? [meta.author]
: ['imgPub'];
const publisher = meta?.publisher || 'imgPub';
const language = meta?.language || 'tr';
const description = meta?.description || title;
const content = [
{
@@ -288,6 +300,18 @@ app.post('/generate-epub', async (req, res) => {
const outputPath = join(tmpdir(), `imgpub-${uuidV4()}.epub`);
let coverPath;
const metadataPayload = {
subtitle: meta?.subtitle,
description: meta?.description,
categories: Array.isArray(meta?.categories) ? meta.categories : [],
publishedDate: meta?.publishedDate,
language: meta?.language,
pageCount: meta?.pageCount,
averageRating: meta?.averageRating,
ratingsCount: meta?.ratingsCount,
identifiers: Array.isArray(meta?.identifiers) ? meta.identifiers : [],
infoLink: meta?.infoLink,
};
try {
if (cover?.data) {
@@ -298,7 +322,16 @@ app.post('/generate-epub', async (req, res) => {
await fs.writeFile(coverPath, coverBuffer);
}
const epubOptions = { title, author, content };
const epubOptions = {
title,
author: authors,
publisher,
description,
lang: language,
content,
bookMetadata: metadataPayload,
customOpfTemplatePath: join(__dirname, 'templates', 'content.opf.ejs'),
};
if (coverPath) {
epubOptions.cover = coverPath;
}

View File

@@ -112,6 +112,12 @@ export const translateWithGlm = async (text) => {
};
}
console.log('[GLM] İstek hazırlanıyor', {
endpoint: GLM_API_URL,
model: GLM_MODEL,
snippet: text.slice(0, 80),
});
const response = await fetch(GLM_API_URL, {
method: 'POST',
headers: {
@@ -125,7 +131,20 @@ export const translateWithGlm = async (text) => {
body: JSON.stringify(body),
});
const payload = await response.json().catch(() => ({}));
let payload = {};
try {
payload = await response.json();
} catch (error) {
console.error('[GLM] JSON parse başarısız', error);
}
console.log('[GLM] Yanıt alındı', {
status: response.status,
ok: response.ok,
hasOutput: Boolean(payload?.output || payload?.choices || payload?.content),
error: payload?.error,
});
if (!response.ok) {
const message =
payload?.error?.message ||
@@ -136,7 +155,9 @@ export const translateWithGlm = async (text) => {
const translated = extractContent(payload);
if (!translated) {
console.error('[GLM] Boş içerik döndü', payload);
throw new Error('GLM çıktısı boş döndü.');
}
console.log('[GLM] Çeviri tamamlandı');
return translated;
};

View File

@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://www.idpf.org/2007/opf"
version="3.0"
unique-identifier="BookId"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:dcterms="http://purl.org/dc/terms/"
xml:lang="en"
xmlns:media="http://www.idpf.org/epub/vocab/overlays/#"
prefix="ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/">
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:opf="http://www.idpf.org/2007/opf">
<dc:identifier id="BookId"><%= id %></dc:identifier>
<meta refines="#BookId" property="identifier-type" scheme="onix:codelist5">22</meta>
<meta property="dcterms:identifier" id="meta-identifier">BookId</meta>
<dc:title><%= title %></dc:title>
<% if (bookMetadata && bookMetadata.subtitle) { %>
<meta property="dcterms:alternative"><%= bookMetadata.subtitle %></meta>
<% } %>
<meta property="dcterms:title" id="meta-title"><%= title %></meta>
<dc:language><%= lang || "en" %></dc:language>
<meta property="dcterms:language" id="meta-language"><%= lang || "en" %></meta>
<meta property="dcterms:modified"><%= (new Date()).toISOString().split(".")[0]+ "Z" %></meta>
<dc:creator id="creator"><%= author.length ? author.join(",") : author %></dc:creator>
<meta refines="#creator" property="file-as"><%= author.length ? author.join(",") : author %></meta>
<meta property="dcterms:publisher"><%= publisher || "anonymous" %></meta>
<dc:publisher><%= publisher || "anonymous" %></dc:publisher>
<% var date = new Date(); var year = date.getFullYear(); var month = date.getMonth() + 1; var day = date.getDate(); var stringDate = "" + year + "-" + month + "-" + day; %>
<meta property="dcterms:date"><%= bookMetadata && bookMetadata.publishedDate ? bookMetadata.publishedDate : stringDate %></meta>
<dc:date><%= bookMetadata && bookMetadata.publishedDate ? bookMetadata.publishedDate : stringDate %></dc:date>
<% if (bookMetadata && bookMetadata.description) { %>
<dc:description><%= bookMetadata.description %></dc:description>
<meta property="dcterms:description"><%= bookMetadata.description %></meta>
<% } %>
<% if (bookMetadata && bookMetadata.categories && bookMetadata.categories.length) { bookMetadata.categories.forEach(function(category){ %>
<dc:subject><%= category %></dc:subject>
<% }); } %>
<% if (bookMetadata && bookMetadata.identifiers && bookMetadata.identifiers.length) { bookMetadata.identifiers.forEach(function(identifier, idx){ %>
<meta property="dcterms:identifier" id="extra-id-<%= idx %>"><%= identifier.identifier %></meta>
<% }); } %>
<% if (bookMetadata && bookMetadata.pageCount) { %>
<meta property="schema:pageCount"><%= bookMetadata.pageCount %></meta>
<% } %>
<% if (bookMetadata && bookMetadata.averageRating) { %>
<meta property="schema:ratingValue"><%= bookMetadata.averageRating %></meta>
<% } %>
<% if (bookMetadata && bookMetadata.ratingsCount) { %>
<meta property="schema:ratingCount"><%= bookMetadata.ratingsCount %></meta>
<% } %>
<% if (bookMetadata && bookMetadata.infoLink) { %>
<meta property="dcterms:source"><%= bookMetadata.infoLink %></meta>
<% } %>
<meta property="dcterms:rights">All rights reserved</meta>
<dc:rights>Copyright &#x00A9; <%= (new Date()).getFullYear() %> by <%= publisher || "anonymous" %></dc:rights>
<meta name="cover" content="image_cover"/>
<meta name="generator" content="epub-gen" />
<meta property="ibooks:specified-fonts">true</meta>
</metadata>
<manifest>
<item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml" />
<item id="toc" href="toc.xhtml" media-type="application/xhtml+xml" properties="nav"/>
<item id="css" href="style.css" media-type="text/css" />
<% if(locals.cover) { %>
<item id="image_cover" href="cover.<%= _coverExtension %>" media-type="<%= _coverMediaType %>" />
<% } %>
<% images.forEach(function(image, index){ %>
<item id="image_<%= index %>" href="images/<%= image.id %>.<%= image.extension %>" media-type="<%= image.mediaType %>" />
<% }) %>
<% content.forEach(function(content, index){ %>
<item id="content_<%= index %>_<%= content.id %>" href="<%= content.href %>" media-type="application/xhtml+xml" />
<% }) %>
<% fonts.forEach(function(font, index){%>
<item id="font_<%= index%>" href="fonts/<%= font %>" media-type="application/x-font-ttf" />
<%})%>
</manifest>
<spine toc="ncx">
<% content.forEach(function(content, index){ %>
<% if(content.beforeToc && !content.excludeFromToc){ %>
<itemref idref="content_<%= index %>_<%= content.id %>"/>
<% } %>
<% }) %>
<itemref idref="toc" />
<% content.forEach(function(content, index){ %>
<% if(!content.beforeToc && !content.excludeFromToc){ %>
<itemref idref="content_<%= index %>_<%= content.id %>"/>
<% } %>
<% }) %>
</spine>
<guide>
<reference type="text" title="Table of Content" href="toc.xhtml"/>
</guide>
</package>