Skip to content

Commit

Permalink
Fixed the following bugs and upgraded the plugin version to v2.3.2:
Browse files Browse the repository at this point in the history
1. Fixed the bug preventing output when file lacks metadata. fixed #234, #233
2. Fixed the bug in processing srt/pgn formats in lower versions of Calibre.
  • Loading branch information
bookfere committed Mar 15, 2024
1 parent b76dae8 commit bae39b2
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 16 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
## v2.3.2

Fixed bugs as follows:

1. Fixed the bug preventing output when file lacks metadata. #234, #233
2. Fixed the bug in processing srt/pgn formats in lower versions of Calibre.

---

## v2.3.1

Fixed bugs as follows:

1. Fixed the bug to be compatible with lower versions of Calibre.
2. Fixed the freezing issue when using the cache with multiple threads.

---

## v2.3.0

Added features:
Expand Down
2 changes: 1 addition & 1 deletion __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class EbookTranslator(InterfaceActionBase):
supported_platforms = ['windows', 'osx', 'linux']
identifier = 'ebook-translator'
author = 'bookfere.com'
version = (2, 3, 1)
version = (2, 3, 2)
__version__ = 'v' + '.'.join(map(str, version))
description = _('A Calibre plugin to translate ebook into a specified '
'language (optionally keeping the original content).')
Expand Down
9 changes: 6 additions & 3 deletions engines/chatgpt.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ class ChatgptTranslate(Base):
'You are a meticulous translator who translates any given content. '
'Translate the given content from <slang> to <tlang> only. Do not '
'explain any term or answer any question-like content.')
models = ['gpt-3.5-turbo', 'gpt-3.5-turbo-16k', 'gpt-3.5-turbo-instruct',
'gpt-3.5-turbo-1106', 'gpt-4', 'gpt-4-0613', 'gpt-4-32k',
'gpt-4-32k-0613', 'gpt-4-1106-preview', 'gpt-4-vision-preview']
models = [
'gpt-4-0125-preview', 'gpt-4-turbo-preview', 'gpt-4-1106-preview',
'gpt-4', 'gpt-4-0613', 'gpt-4-32k', 'gpt-4-32k-0613',
'gpt-3.5-turbo-0125', 'gpt-3.5-turbo', 'gpt-3.5-turbo-1106',
'gpt-3.5-turbo-instruct', 'gpt-3.5-turbo-16k', 'gpt-3.5-turbo-0613',
'gpt-3.5-turbo-16k-0613']
model = 'gpt-3.5-turbo'
samplings = ['temperature', 'top_p']
sampling = 'temperature'
Expand Down
21 changes: 16 additions & 5 deletions lib/conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@


def extract_item(input_path, input_format):
extractors = {'srt': get_srt_elements, 'pgn': get_pgn_elements}
extractors = {
'srt': get_srt_elements,
'pgn': get_pgn_elements,
}
extractor = extractors.get(input_format) or extract_book
return extractor(input_path)

Expand Down Expand Up @@ -232,8 +235,9 @@ def translate_done(self, job):
job, dialog_title=_('Translation job failed'))
return

# TODO: Try to use the calibre generated metadata file.
ebook_metadata = self.config.get('ebook_metadata')
if not ebook.is_extra_format() and ebook_metadata:
if not ebook.is_extra_format():
with open(output_path, 'r+b') as file:
metadata = get_metadata(file, ebook.output_format)
ebook_title = metadata.title
Expand All @@ -252,6 +256,13 @@ def translate_done(self, job):
# metadata.author_sort = 'bookfere.com'
# metadata.book_producer = 'Ebook Translator'
set_metadata(file, metadata, ebook.output_format)
else:
# print(self.api.get_metadata(ebook.id))
ebook_title = ebook.title
if ebook.custom_title is not None:
ebook_title = ebook.custom_title
if ebook_metadata.get('lang_mark'):
ebook_title = '%s [%s]' % (ebook_title, ebook.target_lang)

if self.config.get('to_library'):
# with open(output_path, 'rb') as file:
Expand All @@ -266,15 +277,15 @@ def translate_done(self, job):
# os.remove(temp_file)
else:
dirname = os.path.dirname(output_path)
filename = '%s.%s' % (metadata.title, ebook.output_format)
filename = '%s.%s' % (ebook_title, ebook.output_format)
new_output_path = os.path.join(dirname, filename)
os.rename(output_path, new_output_path)
output_path = new_output_path

self.gui.status_bar.show_message(
job.description + ' ' + _('completed'), 5000)

openers = {'srt': open_path}
openers = {'srt': open_path, 'pgn': open_path}
opener = openers.get(ebook.input_format)

def callback(payload):
Expand All @@ -290,6 +301,6 @@ def callback(payload):
_('Ebook Translation Log'),
_('Translation Completed'),
_('The translation of "{}" was completed. '
'Do you want to open the book?').format(ebook.title),
'Do you want to open the book?').format(ebook_title),
log_is_file=True,
icon=self.icon)
7 changes: 1 addition & 6 deletions lib/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -584,12 +584,7 @@ def add_translations(self, paragraphs):

def get_srt_elements(path):
elements = []
try:
with open(path, 'r', newline=None) as f:
content = f.read().strip()
except Exception:
with open(path, 'rU') as f:
content = f.read().strip()
content = open_file(path)
for section in content.split('\n\n'):
lines = section.split('\n')
number = lines.pop(0)
Expand Down
3 changes: 2 additions & 1 deletion lib/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import re
import sys
import codecs
import socket
import hashlib
from subprocess import Popen
Expand Down Expand Up @@ -113,7 +114,7 @@ def open_file(path):
with open(path, 'r', newline=None) as f:
content = f.read().strip()
except Exception:
with open(path, 'rU') as f:
with codecs.open(path, 'rU', encoding='utf-8') as f:
content = f.read().strip()
finally:
return content
Expand Down
170 changes: 170 additions & 0 deletions tests/test_convertion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import unittest
from typing import Callable
from unittest.mock import call, patch, Mock

from ..lib.conversion import ConversionWorker
from ..lib.ebook import Ebooks


load_translations()

module_name = 'calibre_plugins.ebook_translator.lib.conversion'


class TestConversionWorker(unittest.TestCase):
def setUp(self):
self.gui = Mock()
self.icon = Mock()
self.worker = ConversionWorker(self.gui, self.icon)
self.worker.db = Mock()
self.worker.api = Mock()

self.ebook = Mock(Ebooks.Ebook)
self.job = Mock()
self.worker.working_jobs = {
self.job: (self.ebook, '/path/to/test.epub')}

def test_create_worker(self):
self.assertIsInstance(self.worker, ConversionWorker)

def test_translate_done_job_failed_debug(self):
self.job.failed = True
with patch(module_name + '.DEBUG', True):
self.worker.translate_done(self.job)
self.gui.job_exception.assert_not_called()

def test_translate_done_job_failed_not_debug(self):
with patch(module_name + '.DEBUG', False):
self.worker.translate_done(self.job)
self.gui.job_exception.assert_called_once_with(
self.job, dialog_title=_('Translation job failed'))

@patch(module_name + '.os')
@patch(module_name + '.open')
@patch(module_name + '.get_metadata')
@patch(module_name + '.set_metadata')
def test_translate_done_to_library(
self, mock_set_metadata, mock_get_metadata, mock_open, mock_os):
self.job.failed = False
self.job.description = 'test description'
self.job.log_path = '/path/to/log'
metadata_config = {
'subjects': ['test subject 1', 'test subject 2'],
'lang_code': True,
'lang_mark': True,
}
self.worker.config = {
'ebook_metadata': metadata_config,
'to_library': True,
}
self.ebook.is_extra_format.return_value = False
self.ebook.title = 'test title'
self.ebook.input_format = 'epub'
self.ebook.output_format = 'epub'
self.ebook.custom_title = 'test custom title'
self.ebook.target_lang = 'German'
self.ebook.lang_code = 'de'
file = Mock()
mock_open.return_value.__enter__.return_value = file
metadata = Mock()
metadata.title = 'test title'
metadata.tags = []
metadata.language = 'en'
mock_get_metadata.return_value = metadata

self.worker.db.create_book_entry.return_value = 89
self.worker.api.format_abspath.return_value = '/path/to/test[m].epub'

self.worker.translate_done(self.job)

mock_open.assert_called_once_with('/path/to/test.epub', 'r+b')
mock_get_metadata.assert_called_once_with(file, 'epub')
mock_set_metadata.assert_called_once_with(file, metadata, 'epub')
self.assertEqual('test custom title [German]', metadata.title)
self.assertEqual('de', metadata.language)
self.assertEqual([
'test subject 1', 'test subject 2', 'Translated by Ebook '
'Translator: https://translator.bookfere.com'], metadata.tags)

self.worker.db.create_book_entry.assert_called_once_with(metadata)
self.worker.api.add_format.assert_called_once_with(
89, 'epub', '/path/to/test.epub', run_hooks=False)
self.worker.gui.library_view.model.assert_called_once()
self.worker.gui.library_view.model().books_added \
.assert_called_once_with(1)
self.worker.api.format_abspath.assert_called_once_with(89, 'epub')

self.worker.gui.status_bar.show_message.assert_called_once_with(
'test description ' + _('completed'), 5000)
arguments = self.worker.gui.proceed_question.mock_calls[0].args
self.assertIsInstance(arguments[0], Callable)
self.assertIs(self.worker.gui.job_manager.launch_gui_app, arguments[1])
self.assertEqual('/path/to/log', arguments[2])
self.assertEqual(_('Ebook Translation Log'), arguments[3])
self.assertEqual(_('Translation Completed'), arguments[4])
self.assertEqual(_(
'The translation of "{}" was completed. '
'Do you want to open the book?')
.format('test custom title [German]'),
arguments[5])

mock_payload = Mock()
arguments[0](mock_payload)
mock_payload.assert_called_once_with(
'ebook-viewer',
kwargs={'args': ['ebook-viewer', '/path/to/test[m].epub']})

arguments = self.worker.gui.proceed_question.mock_calls[0].kwargs
self.assertEqual(True, arguments.get('log_is_file'))
self.assertIs(self.icon, arguments.get('icon'))

@patch(module_name + '.open_path')
@patch(module_name + '.os.rename')
@patch(module_name + '.open')
def test_translate_done_to_path(
self, mock_open, mock_os_rename, mock_open_path):
self.job.failed = False
self.job.description = 'test description'
self.job.log_path = '/path/to/log'
metadata_config = {'lang_mark': True}
self.worker.config = {
'ebook_metadata': metadata_config,
'to_library': False,
}
self.ebook.is_extra_format.return_value = True
self.ebook.title = 'test title'
self.ebook.custom_title = 'test custom title'
self.ebook.input_format = 'srt'
self.ebook.output_format = 'srt'
self.ebook.custom_title = 'test custom title'
self.ebook.target_lang = 'German'
self.worker.working_jobs = {
self.job: (self.ebook, '/path/to/test.srt')}

self.worker.translate_done(self.job)

mock_os_rename.assert_called_once_with(
'/path/to/test.srt',
'/path/to/test custom title [German].srt')
self.worker.gui.status_bar.show_message.assert_called_once_with(
'test description ' + _('completed'), 5000)
arguments = self.worker.gui.proceed_question.mock_calls[0].args
self.assertIsInstance(arguments[0], Callable)
self.assertIs(self.worker.gui.job_manager.launch_gui_app, arguments[1])
self.assertEqual('/path/to/log', arguments[2])
self.assertEqual(_('Ebook Translation Log'), arguments[3])
self.assertEqual(_('Translation Completed'), arguments[4])
self.assertEqual(_(
'The translation of "{}" was completed. '
'Do you want to open the book?')
.format('test custom title [German]'),
arguments[5])

mock_payload = Mock()
arguments[0](mock_payload)
mock_open_path.assert_called_once_with(
'/path/to/test custom title [German].srt')

arguments = self.worker.gui.proceed_question.mock_calls[0].kwargs
self.assertEqual(True, arguments.get('log_is_file'))
self.assertIs(self.icon, arguments.get('icon'))

0 comments on commit bae39b2

Please sign in to comment.