diff --git a/.gitignore b/.gitignore
index 9f22510..84696d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -132,4 +132,5 @@ dmypy.json
.kindle_session
# remove MacOS .DS_Store
-.DS_Store
\ No newline at end of file
+.DS_Store
+EPUB/
\ No newline at end of file
diff --git a/gui/icon_rc.py b/gui/icon_rc.py
index 7783cd3..00a0a19 100644
--- a/gui/icon_rc.py
+++ b/gui/icon_rc.py
@@ -1813,10 +1813,17 @@ qt_resource_struct = b"\
\x00\x00\x01\x81Cj\xfeG\
"
+
def qInitResources():
- QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+ QtCore.qRegisterResourceData(
+ 0x03, qt_resource_struct, qt_resource_name, qt_resource_data
+ )
+
def qCleanupResources():
- QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+ QtCore.qUnregisterResourceData(
+ 0x03, qt_resource_struct, qt_resource_name, qt_resource_data
+ )
+
qInitResources()
diff --git a/gui/ui_kindle.py b/gui/ui_kindle.py
index 2a13a02..d7d8614 100644
--- a/gui/ui_kindle.py
+++ b/gui/ui_kindle.py
@@ -8,265 +8,293 @@
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
-from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
- QMetaObject, QObject, QPoint, QRect,
- QSize, QTime, QUrl, Qt)
-from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
- QFont, QFontDatabase, QGradient, QIcon,
- QImage, QKeySequence, QLinearGradient, QPainter,
- QPalette, QPixmap, QRadialGradient, QTransform)
-from PySide6.QtWidgets import (QAbstractItemView, QApplication, QCheckBox, QDialog, QGroupBox,
- QHBoxLayout, QHeaderView, QLabel, QLineEdit,
- QPlainTextEdit, QPushButton, QRadioButton, QSizePolicy,
- QSpinBox, QTableView, QTextBrowser, QVBoxLayout,
- QWidget)
+from PySide6.QtCore import (
+ QCoreApplication,
+ QDate,
+ QDateTime,
+ QLocale,
+ QMetaObject,
+ QObject,
+ QPoint,
+ QRect,
+ QSize,
+ QTime,
+ QUrl,
+ Qt,
+)
+from PySide6.QtGui import (
+ QBrush,
+ QColor,
+ QConicalGradient,
+ QCursor,
+ QFont,
+ QFontDatabase,
+ QGradient,
+ QIcon,
+ QImage,
+ QKeySequence,
+ QLinearGradient,
+ QPainter,
+ QPalette,
+ QPixmap,
+ QRadialGradient,
+ QTransform,
+)
+from PySide6.QtWidgets import (
+ QAbstractItemView,
+ QApplication,
+ QCheckBox,
+ QDialog,
+ QGroupBox,
+ QHBoxLayout,
+ QHeaderView,
+ QLabel,
+ QLineEdit,
+ QPlainTextEdit,
+ QPushButton,
+ QRadioButton,
+ QSizePolicy,
+ QSpinBox,
+ QTableView,
+ QTextBrowser,
+ QVBoxLayout,
+ QWidget,
+)
import gui.icon_rc as icon_rc
+
class Ui_MainDialog(object):
def setupUi(self, MainDialog):
if not MainDialog.objectName():
- MainDialog.setObjectName(u"MainDialog")
+ MainDialog.setObjectName("MainDialog")
MainDialog.resize(1190, 872)
icon = QIcon()
- icon.addFile(u":/icon/resource/kindle.png", QSize(), QIcon.Normal, QIcon.Off)
+ icon.addFile(":/icon/resource/kindle.png", QSize(), QIcon.Normal, QIcon.Off)
MainDialog.setWindowIcon(icon)
self.horizontalLayout = QHBoxLayout(MainDialog)
self.horizontalLayout.setSpacing(10)
- self.horizontalLayout.setObjectName(u"horizontalLayout")
+ self.horizontalLayout.setObjectName("horizontalLayout")
self.leftLayout = QVBoxLayout()
- self.leftLayout.setObjectName(u"leftLayout")
+ self.leftLayout.setObjectName("leftLayout")
self.listBox = QGroupBox(MainDialog)
- self.listBox.setObjectName(u"listBox")
+ self.listBox.setObjectName("listBox")
self.verticalLayout_5 = QVBoxLayout(self.listBox)
- self.verticalLayout_5.setObjectName(u"verticalLayout_5")
+ self.verticalLayout_5.setObjectName("verticalLayout_5")
self.widget = QWidget(self.listBox)
- self.widget.setObjectName(u"widget")
+ self.widget.setObjectName("widget")
self.widget.setMinimumSize(QSize(0, 0))
self.widget.setBaseSize(QSize(0, 0))
self.horizontalLayout_6 = QHBoxLayout(self.widget)
- self.horizontalLayout_6.setObjectName(u"horizontalLayout_6")
+ self.horizontalLayout_6.setObjectName("horizontalLayout_6")
self.horizontalLayout_6.setContentsMargins(0, 0, 0, 0)
self.radioEBOK = QRadioButton(self.widget)
- self.radioEBOK.setObjectName(u"radioEBOK")
+ self.radioEBOK.setObjectName("radioEBOK")
self.radioEBOK.setChecked(True)
self.horizontalLayout_6.addWidget(self.radioEBOK)
self.radioPDO = QRadioButton(self.widget)
- self.radioPDO.setObjectName(u"radioPDO")
+ self.radioPDO.setObjectName("radioPDO")
self.horizontalLayout_6.addWidget(self.radioPDO)
self.fetchButton = QPushButton(self.widget)
- self.fetchButton.setObjectName(u"fetchButton")
+ self.fetchButton.setObjectName("fetchButton")
self.horizontalLayout_6.addWidget(self.fetchButton)
-
self.verticalLayout_5.addWidget(self.widget, 0, Qt.AlignRight)
self.bookView = QTableView(self.listBox)
- self.bookView.setObjectName(u"bookView")
+ self.bookView.setObjectName("bookView")
self.bookView.setSelectionBehavior(QAbstractItemView.SelectRows)
self.verticalLayout_5.addWidget(self.bookView)
self.horizontalLayout_4 = QHBoxLayout()
- self.horizontalLayout_4.setObjectName(u"horizontalLayout_4")
+ self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.label_2 = QLabel(self.listBox)
- self.label_2.setObjectName(u"label_2")
+ self.label_2.setObjectName("label_2")
self.horizontalLayout_4.addWidget(self.label_2)
self.outDirEdit = QLineEdit(self.listBox)
- self.outDirEdit.setObjectName(u"outDirEdit")
+ self.outDirEdit.setObjectName("outDirEdit")
self.horizontalLayout_4.addWidget(self.outDirEdit)
self.browseButton = QPushButton(self.listBox)
- self.browseButton.setObjectName(u"browseButton")
+ self.browseButton.setObjectName("browseButton")
self.horizontalLayout_4.addWidget(self.browseButton)
self.downloadButton = QPushButton(self.listBox)
- self.downloadButton.setObjectName(u"downloadButton")
+ self.downloadButton.setObjectName("downloadButton")
self.horizontalLayout_4.addWidget(self.downloadButton)
self.selectedButton = QPushButton(self.listBox)
- self.selectedButton.setObjectName(u"selectedButton")
+ self.selectedButton.setObjectName("selectedButton")
self.horizontalLayout_4.addWidget(self.selectedButton)
self.dedrmCkb = QCheckBox(self.listBox)
- self.dedrmCkb.setObjectName(u"dedrmCkb")
+ self.dedrmCkb.setObjectName("dedrmCkb")
self.horizontalLayout_4.addWidget(self.dedrmCkb)
-
self.verticalLayout_5.addLayout(self.horizontalLayout_4)
-
self.leftLayout.addWidget(self.listBox)
self.groupBox_2 = QGroupBox(MainDialog)
- self.groupBox_2.setObjectName(u"groupBox_2")
+ self.groupBox_2.setObjectName("groupBox_2")
self.groupBox_2.setMaximumSize(QSize(16777215, 200))
self.verticalLayout_7 = QVBoxLayout(self.groupBox_2)
- self.verticalLayout_7.setObjectName(u"verticalLayout_7")
+ self.verticalLayout_7.setObjectName("verticalLayout_7")
self.logBrowser = QTextBrowser(self.groupBox_2)
- self.logBrowser.setObjectName(u"logBrowser")
+ self.logBrowser.setObjectName("logBrowser")
self.verticalLayout_7.addWidget(self.logBrowser)
-
self.leftLayout.addWidget(self.groupBox_2)
-
self.horizontalLayout.addLayout(self.leftLayout)
self.rightLayout = QVBoxLayout()
self.rightLayout.setSpacing(6)
- self.rightLayout.setObjectName(u"rightLayout")
+ self.rightLayout.setObjectName("rightLayout")
self.settingsBox = QGroupBox(MainDialog)
- self.settingsBox.setObjectName(u"settingsBox")
+ self.settingsBox.setObjectName("settingsBox")
self.settingsBox.setMaximumSize(QSize(400, 16777215))
self.verticalLayout = QVBoxLayout(self.settingsBox)
- self.verticalLayout.setObjectName(u"verticalLayout")
+ self.verticalLayout.setObjectName("verticalLayout")
self.loginGroupBox = QGroupBox(self.settingsBox)
- self.loginGroupBox.setObjectName(u"loginGroupBox")
+ self.loginGroupBox.setObjectName("loginGroupBox")
self.horizontalLayout_2 = QHBoxLayout(self.loginGroupBox)
- self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.verticalLayout_2 = QVBoxLayout()
- self.verticalLayout_2.setObjectName(u"verticalLayout_2")
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
self.radioCOM = QRadioButton(self.loginGroupBox)
- self.radioCOM.setObjectName(u"radioCOM")
+ self.radioCOM.setObjectName("radioCOM")
self.verticalLayout_2.addWidget(self.radioCOM)
self.radioCN = QRadioButton(self.loginGroupBox)
- self.radioCN.setObjectName(u"radioCN")
+ self.radioCN.setObjectName("radioCN")
self.radioCN.setChecked(True)
self.verticalLayout_2.addWidget(self.radioCN)
self.radioJP = QRadioButton(self.loginGroupBox)
- self.radioJP.setObjectName(u"radioJP")
+ self.radioJP.setObjectName("radioJP")
self.verticalLayout_2.addWidget(self.radioJP)
self.radioDE = QRadioButton(self.loginGroupBox)
- self.radioDE.setObjectName(u"radioDE")
+ self.radioDE.setObjectName("radioDE")
self.verticalLayout_2.addWidget(self.radioDE)
self.radioUK = QRadioButton(self.loginGroupBox)
- self.radioUK.setObjectName(u"radioUK")
+ self.radioUK.setObjectName("radioUK")
self.verticalLayout_2.addWidget(self.radioUK)
self.horizontalLayout_2.addLayout(self.verticalLayout_2)
self.loginButton = QPushButton(self.loginGroupBox)
- self.loginButton.setObjectName(u"loginButton")
+ self.loginButton.setObjectName("loginButton")
self.loginButton.setMaximumSize(QSize(80, 16777215))
self.horizontalLayout_2.addWidget(self.loginButton)
-
self.verticalLayout.addWidget(self.loginGroupBox)
self.cookiesGroupBox = QGroupBox(self.settingsBox)
- self.cookiesGroupBox.setObjectName(u"cookiesGroupBox")
+ self.cookiesGroupBox.setObjectName("cookiesGroupBox")
self.verticalLayout_3 = QVBoxLayout(self.cookiesGroupBox)
- self.verticalLayout_3.setObjectName(u"verticalLayout_3")
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
self.cookieTextEdit = QPlainTextEdit(self.cookiesGroupBox)
- self.cookieTextEdit.setObjectName(u"cookieTextEdit")
+ self.cookieTextEdit.setObjectName("cookieTextEdit")
self.cookieTextEdit.setEnabled(False)
self.verticalLayout_3.addWidget(self.cookieTextEdit)
self.horizontalLayout_3 = QHBoxLayout()
- self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
+ self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.radioFromInput = QRadioButton(self.cookiesGroupBox)
- self.radioFromInput.setObjectName(u"radioFromInput")
+ self.radioFromInput.setObjectName("radioFromInput")
self.horizontalLayout_3.addWidget(self.radioFromInput)
self.radioFromBrowser = QRadioButton(self.cookiesGroupBox)
- self.radioFromBrowser.setObjectName(u"radioFromBrowser")
+ self.radioFromBrowser.setObjectName("radioFromBrowser")
self.radioFromBrowser.setChecked(True)
self.horizontalLayout_3.addWidget(self.radioFromBrowser)
-
self.verticalLayout_3.addLayout(self.horizontalLayout_3)
-
self.verticalLayout.addWidget(self.cookiesGroupBox)
self.groupBox = QGroupBox(self.settingsBox)
- self.groupBox.setObjectName(u"groupBox")
+ self.groupBox.setObjectName("groupBox")
self.verticalLayout_4 = QVBoxLayout(self.groupBox)
- self.verticalLayout_4.setObjectName(u"verticalLayout_4")
+ self.verticalLayout_4.setObjectName("verticalLayout_4")
self.csrfEdit = QLineEdit(self.groupBox)
- self.csrfEdit.setObjectName(u"csrfEdit")
+ self.csrfEdit.setObjectName("csrfEdit")
self.verticalLayout_4.addWidget(self.csrfEdit)
-
self.verticalLayout.addWidget(self.groupBox)
self.horizontalLayout_5 = QHBoxLayout()
- self.horizontalLayout_5.setObjectName(u"horizontalLayout_5")
+ self.horizontalLayout_5.setObjectName("horizontalLayout_5")
self.label = QLabel(self.settingsBox)
- self.label.setObjectName(u"label")
+ self.label.setObjectName("label")
self.horizontalLayout_5.addWidget(self.label)
self.cutLengthSpin = QSpinBox(self.settingsBox)
- self.cutLengthSpin.setObjectName(u"cutLengthSpin")
+ self.cutLengthSpin.setObjectName("cutLengthSpin")
self.cutLengthSpin.setMaximum(999)
self.cutLengthSpin.setValue(100)
self.horizontalLayout_5.addWidget(self.cutLengthSpin)
-
self.verticalLayout.addLayout(self.horizontalLayout_5)
-
self.rightLayout.addWidget(self.settingsBox, 0, Qt.AlignTop)
self.copyrightBox = QGroupBox(MainDialog)
- self.copyrightBox.setObjectName(u"copyrightBox")
+ self.copyrightBox.setObjectName("copyrightBox")
self.verticalLayout_6 = QVBoxLayout(self.copyrightBox)
- self.verticalLayout_6.setObjectName(u"verticalLayout_6")
+ self.verticalLayout_6.setObjectName("verticalLayout_6")
self.label_6 = QLabel(self.copyrightBox)
- self.label_6.setObjectName(u"label_6")
+ self.label_6.setObjectName("label_6")
self.verticalLayout_6.addWidget(self.label_6)
self.label_3 = QLabel(self.copyrightBox)
- self.label_3.setObjectName(u"label_3")
+ self.label_3.setObjectName("label_3")
self.label_3.setTextFormat(Qt.MarkdownText)
self.verticalLayout_6.addWidget(self.label_3)
self.label_4 = QLabel(self.copyrightBox)
- self.label_4.setObjectName(u"label_4")
+ self.label_4.setObjectName("label_4")
self.label_4.setTextFormat(Qt.MarkdownText)
self.verticalLayout_6.addWidget(self.label_4)
self.label_5 = QLabel(self.copyrightBox)
- self.label_5.setObjectName(u"label_5")
+ self.label_5.setObjectName("label_5")
self.verticalLayout_6.addWidget(self.label_5)
-
self.rightLayout.addWidget(self.copyrightBox, 0, Qt.AlignTop)
-
self.horizontalLayout.addLayout(self.rightLayout)
self.horizontalLayout.setStretch(0, 1)
@@ -274,44 +302,127 @@ class Ui_MainDialog(object):
self.retranslateUi(MainDialog)
QMetaObject.connectSlotsByName(MainDialog)
+
# setupUi
def retranslateUi(self, MainDialog):
- MainDialog.setWindowTitle(QCoreApplication.translate("MainDialog", u"Kindle \u4e0b\u8f7d\u52a9\u624b", None))
- self.listBox.setTitle(QCoreApplication.translate("MainDialog", u"\u4e0b\u8f7d\u5217\u8868", None))
- self.radioEBOK.setText(QCoreApplication.translate("MainDialog", u"\u7535\u5b50\u4e66", None))
- self.radioPDO.setText(QCoreApplication.translate("MainDialog", u"\u4e2a\u4eba\u6587\u6863", None))
- self.fetchButton.setText(QCoreApplication.translate("MainDialog", u"\u83b7\u53d6\u4e0b\u8f7d\u5217\u8868", None))
- self.label_2.setText(QCoreApplication.translate("MainDialog", u"\u76ee\u6807\u6587\u4ef6\u5939", None))
- self.outDirEdit.setText(QCoreApplication.translate("MainDialog", u"DOWNLOADS", None))
- self.browseButton.setText(QCoreApplication.translate("MainDialog", u"\u6d4f\u89c8...", None))
- self.downloadButton.setText(QCoreApplication.translate("MainDialog", u"\u4e0b\u8f7d\u5168\u90e8", None))
- self.selectedButton.setText(QCoreApplication.translate("MainDialog", u"Download Selected", None))
- self.dedrmCkb.setText(QCoreApplication.translate("MainDialog", u"DeDRM", None))
- self.groupBox_2.setTitle(QCoreApplication.translate("MainDialog", u"\u8f93\u51fa", None))
- self.logBrowser.setHtml(QCoreApplication.translate("MainDialog", u"\n"
-"
\n"
-"
", None))
- self.settingsBox.setTitle(QCoreApplication.translate("MainDialog", u"\u8bbe\u7f6e", None))
+ MainDialog.setWindowTitle(
+ QCoreApplication.translate(
+ "MainDialog", "Kindle \u4e0b\u8f7d\u52a9\u624b", None
+ )
+ )
+ self.listBox.setTitle(
+ QCoreApplication.translate("MainDialog", "\u4e0b\u8f7d\u5217\u8868", None)
+ )
+ self.radioEBOK.setText(
+ QCoreApplication.translate("MainDialog", "\u7535\u5b50\u4e66", None)
+ )
+ self.radioPDO.setText(
+ QCoreApplication.translate("MainDialog", "\u4e2a\u4eba\u6587\u6863", None)
+ )
+ self.fetchButton.setText(
+ QCoreApplication.translate(
+ "MainDialog", "\u83b7\u53d6\u4e0b\u8f7d\u5217\u8868", None
+ )
+ )
+ self.label_2.setText(
+ QCoreApplication.translate(
+ "MainDialog", "\u76ee\u6807\u6587\u4ef6\u5939", None
+ )
+ )
+ self.outDirEdit.setText(
+ QCoreApplication.translate("MainDialog", "DOWNLOADS", None)
+ )
+ self.browseButton.setText(
+ QCoreApplication.translate("MainDialog", "\u6d4f\u89c8...", None)
+ )
+ self.downloadButton.setText(
+ QCoreApplication.translate("MainDialog", "\u4e0b\u8f7d\u5168\u90e8", None)
+ )
+ self.selectedButton.setText(
+ QCoreApplication.translate("MainDialog", "Download Selected", None)
+ )
+ self.dedrmCkb.setText(QCoreApplication.translate("MainDialog", "DeDRM", None))
+ self.groupBox_2.setTitle(
+ QCoreApplication.translate("MainDialog", "\u8f93\u51fa", None)
+ )
+ self.logBrowser.setHtml(
+ QCoreApplication.translate(
+ "MainDialog",
+ '\n'
+ '\n"
+ "
",
+ None,
+ )
+ )
+ self.settingsBox.setTitle(
+ QCoreApplication.translate("MainDialog", "\u8bbe\u7f6e", None)
+ )
self.loginGroupBox.setTitle("")
- self.radioCOM.setText(QCoreApplication.translate("MainDialog", u"\u7f8e\u4e9a(.com)", None))
- self.radioCN.setText(QCoreApplication.translate("MainDialog", u"\u4e2d\u4e9a(.cn)", None))
- self.radioJP.setText(QCoreApplication.translate("MainDialog", u"\u65e5\u4e9a(.jp)", None))
- self.radioDE.setText(QCoreApplication.translate("MainDialog", u"\u5fb7\u4e9a(.de)", None))
- self.radioUK.setText(QCoreApplication.translate("MainDialog", u"\u82F1\u4e9a(.uk)", None))
- self.loginButton.setText(QCoreApplication.translate("MainDialog", u"\u767b\u5f55", None))
- self.cookiesGroupBox.setTitle(QCoreApplication.translate("MainDialog", u"Cookies", None))
- self.radioFromInput.setText(QCoreApplication.translate("MainDialog", u"\u6765\u81ea\u8f93\u5165", None))
- self.radioFromBrowser.setText(QCoreApplication.translate("MainDialog", u"\u6765\u81ea\u6d4f\u89c8\u5668", None))
- self.groupBox.setTitle(QCoreApplication.translate("MainDialog", u"CSRF Token", None))
- self.label.setText(QCoreApplication.translate("MainDialog", u"\u6587\u4ef6\u540d\u622a\u65ad", None))
+ self.radioCOM.setText(
+ QCoreApplication.translate("MainDialog", "\u7f8e\u4e9a(.com)", None)
+ )
+ self.radioCN.setText(
+ QCoreApplication.translate("MainDialog", "\u4e2d\u4e9a(.cn)", None)
+ )
+ self.radioJP.setText(
+ QCoreApplication.translate("MainDialog", "\u65e5\u4e9a(.jp)", None)
+ )
+ self.radioDE.setText(
+ QCoreApplication.translate("MainDialog", "\u5fb7\u4e9a(.de)", None)
+ )
+ self.radioUK.setText(
+ QCoreApplication.translate("MainDialog", "\u82F1\u4e9a(.uk)", None)
+ )
+ self.loginButton.setText(
+ QCoreApplication.translate("MainDialog", "\u767b\u5f55", None)
+ )
+ self.cookiesGroupBox.setTitle(
+ QCoreApplication.translate("MainDialog", "Cookies", None)
+ )
+ self.radioFromInput.setText(
+ QCoreApplication.translate("MainDialog", "\u6765\u81ea\u8f93\u5165", None)
+ )
+ self.radioFromBrowser.setText(
+ QCoreApplication.translate(
+ "MainDialog", "\u6765\u81ea\u6d4f\u89c8\u5668", None
+ )
+ )
+ self.groupBox.setTitle(
+ QCoreApplication.translate("MainDialog", "CSRF Token", None)
+ )
+ self.label.setText(
+ QCoreApplication.translate(
+ "MainDialog", "\u6587\u4ef6\u540d\u622a\u65ad", None
+ )
+ )
self.copyrightBox.setTitle("")
- self.label_6.setText(QCoreApplication.translate("MainDialog", u"\u9690\u79c1\u58f0\u660e\uff1a\u6211\u4eec\u4e0d\u4f1a\u6536\u96c6\u4efb\u4f55\u7528\u6237\u4fe1\u606f\uff0c\u8bf7\u653e\u5fc3\u4f7f\u7528", None))
- self.label_3.setText(QCoreApplication.translate("MainDialog", u"Copyright 2022 \u00a9 [yihong0618](https://github.com/yihong0618) and [frostming](https://github.com/frostming)", None))
- self.label_4.setText(QCoreApplication.translate("MainDialog", u"GitHub: ", None))
- self.label_5.setText(QCoreApplication.translate("MainDialog", u"License: GPL V3", None))
- # retranslateUi
+ self.label_6.setText(
+ QCoreApplication.translate(
+ "MainDialog",
+ "\u9690\u79c1\u58f0\u660e\uff1a\u6211\u4eec\u4e0d\u4f1a\u6536\u96c6\u4efb\u4f55\u7528\u6237\u4fe1\u606f\uff0c\u8bf7\u653e\u5fc3\u4f7f\u7528",
+ None,
+ )
+ )
+ self.label_3.setText(
+ QCoreApplication.translate(
+ "MainDialog",
+ "Copyright 2022 \u00a9 [yihong0618](https://github.com/yihong0618) and [frostming](https://github.com/frostming)",
+ None,
+ )
+ )
+ self.label_4.setText(
+ QCoreApplication.translate(
+ "MainDialog",
+ "GitHub: ",
+ None,
+ )
+ )
+ self.label_5.setText(
+ QCoreApplication.translate("MainDialog", "License: GPL V3", None)
+ )
+ # retranslateUi
diff --git a/kindle_download_helper/cli.py b/kindle_download_helper/cli.py
index f8d7f93..10859db 100644
--- a/kindle_download_helper/cli.py
+++ b/kindle_download_helper/cli.py
@@ -9,6 +9,7 @@ from kindle_download_helper.config import (
DEFAULT_OUT_DIR,
DEFAULT_SESSION_FILE,
DEFAULT_OUT_DEDRM_DIR,
+ DEFAULT_OUT_EPUB_DIR,
)
logger = logging.getLogger("kindle")
@@ -18,82 +19,119 @@ logger.addHandler(fh)
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
+
# download selected books for cli
def download_selected_books(kindle, options):
# get all books and get the default device
- print('Getting all books, please wait...')
+ print("Getting all books, please wait...")
books = kindle.get_all_books(filetype=options.filetype)
device = kindle.find_device()
-
+
# print all books
for idx, book in enumerate(books):
- print('Index: ' + '{:>5d}'.format(idx+1) + ' | Title: ' + book['title'] + ' | asin: '+book['asin'])
-
+ print(
+ "Index: "
+ + "{:>5d}".format(idx + 1)
+ + " | Title: "
+ + book["title"]
+ + " | asin: "
+ + book["asin"]
+ )
+
# download loop
while True:
# get the indices of the books to download
- indices = input('Input the index of books you want to download, split by space (q to quit, l to list books).\n').split()
-
+ indices = input(
+ "Input the index of books you want to download, split by space (q to quit, l to list books).\n"
+ ).split()
+
# if input "q", quit
# if input "l", list all books again
- if indices[0] == 'q':
+ if indices[0] == "q":
break
- elif indices[0] == 'l':
+ elif indices[0] == "l":
for idx, book in enumerate(books):
- print('Index: ' + '{:>5d}'.format(idx+1) + ' | Title: ' + book['title'] + ' | asin: '+book['asin'])
+ print(
+ "Index: "
+ + "{:>5d}".format(idx + 1)
+ + " | Title: "
+ + book["title"]
+ + " | asin: "
+ + book["asin"]
+ )
continue
-
+
# decode the indices
downlist = []
flag = True
for idx in indices:
if idx.isnumeric() == False:
- if ':' in idx:
+ if ":" in idx:
# if is not a number, and ":" in it, then it is a range
# decode the range
- idx_begin, idx_end = [int(i) for i in idx.split(':')]
+ idx_begin, idx_end = [int(i) for i in idx.split(":")]
# append the range to downlist
- extend_list = [i for i in range(idx_begin-1, idx_end)]
+ extend_list = [i for i in range(idx_begin - 1, idx_end)]
downlist.extend(extend_list)
else:
# if is not a number, and no ":" in it, then it is an error
- print('Input error, please input numbers!!!')
+ print("Input error, please input numbers!!!")
flag = False
break
else:
# if is a number, then append it to downlist
- downlist.append(int(idx)-1)
+ downlist.append(int(idx) - 1)
if not flag:
continue
-
+
# remove the duplicate indices
downlist = list(set(downlist))
-
+
# check if the indices are valid
if max(downlist) >= len(books) or min(downlist) < 0:
- print('Input error, please input numbers between 1 and ' + str(len(books)) + '!!!')
+ print(
+ "Input error, please input numbers between 1 and "
+ + str(len(books))
+ + "!!!"
+ )
continue
-
+
# print the books to download
for idx in downlist:
- print('Index: ' + '{:>5d}'.format(idx+1) + ' | Title: ' + books[idx]['title'] + ' | asin: '+books[idx]['asin'])
- print('Downloading ' + str(len(downlist)) + ' books:')
-
+ print(
+ "Index: "
+ + "{:>5d}".format(idx + 1)
+ + " | Title: "
+ + books[idx]["title"]
+ + " | asin: "
+ + books[idx]["asin"]
+ )
+ print("Downloading " + str(len(downlist)) + " books:")
+
# ask if to continue
while True:
- flag = input('Continue? (y/n)')
- if flag == 'y' or flag == 'n':
+ flag = input("Continue? (y/n)")
+ if flag == "y" or flag == "n":
break
else:
- print('Input error, please input y or n')
- if flag == 'n':
+ print("Input error, please input y or n")
+ if flag == "n":
continue
-
+
# download the books
for i, idx in enumerate(downlist):
- print('Downloading ' + str(i+1) + '/' + str(len(downlist)) + ' ' + books[idx]['title'] + ' ...')
+ print(
+ "Downloading "
+ + str(i + 1)
+ + "/"
+ + str(len(downlist))
+ + " "
+ + books[idx]["title"]
+ + " ..."
+ )
kindle.download_one_book(books[idx], device, idx, filetype=options.filetype)
- print('Download finished.')
+ print("Download finished.")
+
def main():
logger.setLevel(os.environ.get("LOGGING_LEVEL", "INFO"))
@@ -165,6 +203,12 @@ def main():
default=DEFAULT_OUT_DEDRM_DIR,
help="dwonload output dedrm dir",
)
+ parser.add_argument(
+ "-oe",
+ "--outepubmdir",
+ default=DEFAULT_OUT_EPUB_DIR,
+ help="dwonload output epub dir",
+ )
parser.add_argument(
"-s",
"--session-file",
@@ -211,7 +255,7 @@ def main():
default="",
help="Download file for device with this serial number",
)
-
+
parser.add_argument(
"--mode",
dest="mode",
@@ -226,11 +270,16 @@ def main():
# for dedrm
if not os.path.exists(options.outdedrmdir):
os.makedirs(options.outdedrmdir)
+ # for epub
+ if not os.path.exists(options.outepubmdir):
+ os.makedirs(options.outepubmdir)
+
kindle = Kindle(
options.csrf_token,
options.domain,
options.outdir,
options.outdedrmdir,
+ options.outepubmdir,
options.cut_length,
session_file=options.session_file,
device_sn=options.device_sn,
@@ -265,7 +314,9 @@ def main():
# check the download mode
if options.mode == "all":
# download all books
- kindle.download_books(start_index=options.index - 1, filetype=options.filetype)
+ kindle.download_books(
+ start_index=options.index - 1, filetype=options.filetype
+ )
elif options.mode == "sel":
# download selected books
download_selected_books(kindle, options)
diff --git a/kindle_download_helper/config.py b/kindle_download_helper/config.py
index 634d876..23fe3e8 100644
--- a/kindle_download_helper/config.py
+++ b/kindle_download_helper/config.py
@@ -4,6 +4,7 @@ from kindle_download_helper.user_agents import USER_AGENTS
DEFAULT_OUT_DIR = "DOWNLOADS"
DEFAULT_OUT_DEDRM_DIR = "DEDRMS"
+DEFAULT_OUT_EPUB_DIR = "EPUB"
DEFAULT_SESSION_FILE = ".kindle_session"
KINDLE_HEADER = {
diff --git a/kindle_download_helper/dedrm/kgenpids.py b/kindle_download_helper/dedrm/kgenpids.py
index 5470c37..25b67f8 100644
--- a/kindle_download_helper/dedrm/kgenpids.py
+++ b/kindle_download_helper/dedrm/kgenpids.py
@@ -22,6 +22,7 @@ charMap1 = b"n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M"
charMap3 = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
charMap4 = b"ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
+
# crypto digestroutines
def MD5(message):
ctx = hashlib.md5()
diff --git a/kindle_download_helper/kindle.py b/kindle_download_helper/kindle.py
index ed175b6..11ada04 100644
--- a/kindle_download_helper/kindle.py
+++ b/kindle_download_helper/kindle.py
@@ -13,6 +13,8 @@ import re
import time
import urllib
import pathlib
+import shutil
+from mobi import extract
from http.cookies import SimpleCookie
import requests
@@ -23,8 +25,9 @@ from kindle_download_helper.dedrm import MobiBook, get_pid_list
from kindle_download_helper.config import (
KINDLE_URLS,
DEFAULT_OUT_DIR,
- DEFAULT_SESSION_FILE,
DEFAULT_OUT_DEDRM_DIR,
+ DEFAULT_OUT_EPUB_DIR,
+ DEFAULT_SESSION_FILE,
CONTENT_TYPES,
KINDLE_STAT_TEMPLATE,
)
@@ -48,6 +51,7 @@ logger.addHandler(fh)
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
+
class Kindle:
def __init__(
self,
@@ -55,6 +59,7 @@ class Kindle:
domain="cn",
out_dir=DEFAULT_OUT_DIR,
out_dedrm_dir=DEFAULT_OUT_DEDRM_DIR,
+ out_epub_dir=DEFAULT_OUT_EPUB_DIR,
cut_length=100,
session_file=DEFAULT_SESSION_FILE,
**kwargs,
@@ -64,6 +69,7 @@ class Kindle:
self.total_to_download = 0
self.out_dir = out_dir
self.out_dedrm_dir = out_dedrm_dir
+ self.out_epub_dir = out_epub_dir
self.dedrm = False
self.cut_length = cut_length
self.not_done = False
@@ -104,7 +110,7 @@ class Kindle:
if not self.session.cookies:
logger.debug("No cookie found, trying to load from browsers")
try:
- self.set_cookie(browser_cookie3.load(domain_name="amazon"))
+ self.set_cookie(browser_cookie3.load())
except:
print("not found browser_cookie3 here, you should use --cookie command")
@@ -120,11 +126,11 @@ class Kindle:
cookies_dict, cookiejar=None, overwrite=True
)
return cookiejar
-
+
def find_device(self):
devices = self.get_devices()
device_sn = self.device_sn
-
+
if isinstance(device_sn, str) and device_sn != "":
for device in devices:
if device["deviceSerialNumber"] == device_sn.strip():
@@ -364,7 +370,9 @@ class Kindle:
book_title = book.get("title", "")
# filter the brackets in the book title
- book_title = re.sub(r"(\([^)]*\)|\([^)]*\)|\【[^)]*\】|\[[^)]*\])", "", book_title)
+ book_title = re.sub(
+ r"(\([^)]*\)|\([^)]*\)|\【[^)]*\】|\[[^)]*\])", "", book_title
+ )
book_title = book_title.replace(" ", "")
if book.get("category", "") == "KindleEBook":
@@ -466,27 +474,28 @@ class Kindle:
out = os.path.join(self.out_dir, name)
out_dedrm = os.path.join(self.out_dedrm_dir, name)
+ out_epub = os.path.join(self.out_epub_dir, name.split(".")[0] + ".epub")
- #normally one owns no more than 9999 books
+ # normally one owns no more than 9999 books
count_digit_length = 4
size_length = 6
- size_in_mb = round(float(total_size) / (1024*1024), 3)
-
+ size_in_mb = round(float(total_size) / (1024 * 1024), 3)
+
logger.info(
f"[{index+1:>{count_digit_length}}/{self.total_to_download:>{count_digit_length}}][{size_in_mb:> {size_length}}Mb]Downloading {name}"
)
- #try if we can writ the file
- try :
+ # try if we can write the file
+ try:
pathlib.Path(out).touch()
except OSError as e:
- if e.errno == 36 : #means file name too long
+ if e.errno == 36: # means file name too long
name = self.trim_title_suffix(title) + extname
logger.info(f"Original filename too long, trim to {name}")
out = os.path.join(self.out_dir, name)
out_dedrm = os.path.join(self.out_dedrm_dir, name)
- else :
+ else:
logger.error(e)
with open(out, "wb") as f:
@@ -501,6 +510,14 @@ class Kindle:
totalpids = get_pid_list(md1, md2, [self.device_serial_number], [])
totalpids = list(set(totalpids))
mb.make_drm_file(totalpids, out_dedrm)
+ time.sleep(1)
+ # save to EPUB
+ epub_dir, epub_file = extract(out_dedrm)
+ print(epub_file)
+ shutil.copy2(epub_file, out_epub)
+ # delete it
+ shutil.rmtree(epub_dir)
+
except Exception as e:
logger.error("DeDRM failed for %s: %s", name, e)
pass
@@ -511,7 +528,7 @@ class Kindle:
def download_books(self, start_index=0, filetype="EBOK"):
# use default device
device = self.find_device()
-
+
books = self.get_all_books(filetype=filetype, start_index=start_index)
if start_index > 0:
print(f"resuming the download {start_index + 1}/{self.total_to_download}")
@@ -538,7 +555,11 @@ class Kindle:
with open(os.path.join(self.out_dir, "key.txt"), "w") as f:
f.write(f"Key is: {device['deviceSerialNumber']}")
- logger.info("the device serial number can also be found here: {0}".format(os.path.join(self.out_dir, "key.txt")))
+ logger.info(
+ "the device serial number can also be found here: {0}".format(
+ os.path.join(self.out_dir, "key.txt")
+ )
+ )
def trim_title_suffix(self, title):
return re.sub(r"(([^)]+)?|【[^】]+】?)", "", title)
diff --git a/requirements.txt b/requirements.txt
index 5df0bfb..193c809 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,4 @@
requests
browser-cookie3
+mobi
pywin32 ; sys_platform == 'win32'