feat: epub

This commit is contained in:
yihong0618
2023-03-10 08:50:56 +08:00
parent ef0058c49d
commit 50e3ed0eeb
8 changed files with 354 additions and 160 deletions

3
.gitignore vendored
View File

@@ -132,4 +132,5 @@ dmypy.json
.kindle_session .kindle_session
# remove MacOS .DS_Store # remove MacOS .DS_Store
.DS_Store .DS_Store
EPUB/

View File

@@ -1813,10 +1813,17 @@ qt_resource_struct = b"\
\x00\x00\x01\x81Cj\xfeG\ \x00\x00\x01\x81Cj\xfeG\
" "
def qInitResources(): 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(): 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() qInitResources()

View File

@@ -8,265 +8,293 @@
## WARNING! All changes made in this file will be lost when recompiling UI file! ## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################ ################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, from PySide6.QtCore import (
QMetaObject, QObject, QPoint, QRect, QCoreApplication,
QSize, QTime, QUrl, Qt) QDate,
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, QDateTime,
QFont, QFontDatabase, QGradient, QIcon, QLocale,
QImage, QKeySequence, QLinearGradient, QPainter, QMetaObject,
QPalette, QPixmap, QRadialGradient, QTransform) QObject,
from PySide6.QtWidgets import (QAbstractItemView, QApplication, QCheckBox, QDialog, QGroupBox, QPoint,
QHBoxLayout, QHeaderView, QLabel, QLineEdit, QRect,
QPlainTextEdit, QPushButton, QRadioButton, QSizePolicy, QSize,
QSpinBox, QTableView, QTextBrowser, QVBoxLayout, QTime,
QWidget) 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 import gui.icon_rc as icon_rc
class Ui_MainDialog(object): class Ui_MainDialog(object):
def setupUi(self, MainDialog): def setupUi(self, MainDialog):
if not MainDialog.objectName(): if not MainDialog.objectName():
MainDialog.setObjectName(u"MainDialog") MainDialog.setObjectName("MainDialog")
MainDialog.resize(1190, 872) MainDialog.resize(1190, 872)
icon = QIcon() 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) MainDialog.setWindowIcon(icon)
self.horizontalLayout = QHBoxLayout(MainDialog) self.horizontalLayout = QHBoxLayout(MainDialog)
self.horizontalLayout.setSpacing(10) self.horizontalLayout.setSpacing(10)
self.horizontalLayout.setObjectName(u"horizontalLayout") self.horizontalLayout.setObjectName("horizontalLayout")
self.leftLayout = QVBoxLayout() self.leftLayout = QVBoxLayout()
self.leftLayout.setObjectName(u"leftLayout") self.leftLayout.setObjectName("leftLayout")
self.listBox = QGroupBox(MainDialog) self.listBox = QGroupBox(MainDialog)
self.listBox.setObjectName(u"listBox") self.listBox.setObjectName("listBox")
self.verticalLayout_5 = QVBoxLayout(self.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 = QWidget(self.listBox)
self.widget.setObjectName(u"widget") self.widget.setObjectName("widget")
self.widget.setMinimumSize(QSize(0, 0)) self.widget.setMinimumSize(QSize(0, 0))
self.widget.setBaseSize(QSize(0, 0)) self.widget.setBaseSize(QSize(0, 0))
self.horizontalLayout_6 = QHBoxLayout(self.widget) 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.horizontalLayout_6.setContentsMargins(0, 0, 0, 0)
self.radioEBOK = QRadioButton(self.widget) self.radioEBOK = QRadioButton(self.widget)
self.radioEBOK.setObjectName(u"radioEBOK") self.radioEBOK.setObjectName("radioEBOK")
self.radioEBOK.setChecked(True) self.radioEBOK.setChecked(True)
self.horizontalLayout_6.addWidget(self.radioEBOK) self.horizontalLayout_6.addWidget(self.radioEBOK)
self.radioPDO = QRadioButton(self.widget) self.radioPDO = QRadioButton(self.widget)
self.radioPDO.setObjectName(u"radioPDO") self.radioPDO.setObjectName("radioPDO")
self.horizontalLayout_6.addWidget(self.radioPDO) self.horizontalLayout_6.addWidget(self.radioPDO)
self.fetchButton = QPushButton(self.widget) self.fetchButton = QPushButton(self.widget)
self.fetchButton.setObjectName(u"fetchButton") self.fetchButton.setObjectName("fetchButton")
self.horizontalLayout_6.addWidget(self.fetchButton) self.horizontalLayout_6.addWidget(self.fetchButton)
self.verticalLayout_5.addWidget(self.widget, 0, Qt.AlignRight) self.verticalLayout_5.addWidget(self.widget, 0, Qt.AlignRight)
self.bookView = QTableView(self.listBox) self.bookView = QTableView(self.listBox)
self.bookView.setObjectName(u"bookView") self.bookView.setObjectName("bookView")
self.bookView.setSelectionBehavior(QAbstractItemView.SelectRows) self.bookView.setSelectionBehavior(QAbstractItemView.SelectRows)
self.verticalLayout_5.addWidget(self.bookView) self.verticalLayout_5.addWidget(self.bookView)
self.horizontalLayout_4 = QHBoxLayout() 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 = QLabel(self.listBox)
self.label_2.setObjectName(u"label_2") self.label_2.setObjectName("label_2")
self.horizontalLayout_4.addWidget(self.label_2) self.horizontalLayout_4.addWidget(self.label_2)
self.outDirEdit = QLineEdit(self.listBox) self.outDirEdit = QLineEdit(self.listBox)
self.outDirEdit.setObjectName(u"outDirEdit") self.outDirEdit.setObjectName("outDirEdit")
self.horizontalLayout_4.addWidget(self.outDirEdit) self.horizontalLayout_4.addWidget(self.outDirEdit)
self.browseButton = QPushButton(self.listBox) self.browseButton = QPushButton(self.listBox)
self.browseButton.setObjectName(u"browseButton") self.browseButton.setObjectName("browseButton")
self.horizontalLayout_4.addWidget(self.browseButton) self.horizontalLayout_4.addWidget(self.browseButton)
self.downloadButton = QPushButton(self.listBox) self.downloadButton = QPushButton(self.listBox)
self.downloadButton.setObjectName(u"downloadButton") self.downloadButton.setObjectName("downloadButton")
self.horizontalLayout_4.addWidget(self.downloadButton) self.horizontalLayout_4.addWidget(self.downloadButton)
self.selectedButton = QPushButton(self.listBox) self.selectedButton = QPushButton(self.listBox)
self.selectedButton.setObjectName(u"selectedButton") self.selectedButton.setObjectName("selectedButton")
self.horizontalLayout_4.addWidget(self.selectedButton) self.horizontalLayout_4.addWidget(self.selectedButton)
self.dedrmCkb = QCheckBox(self.listBox) self.dedrmCkb = QCheckBox(self.listBox)
self.dedrmCkb.setObjectName(u"dedrmCkb") self.dedrmCkb.setObjectName("dedrmCkb")
self.horizontalLayout_4.addWidget(self.dedrmCkb) self.horizontalLayout_4.addWidget(self.dedrmCkb)
self.verticalLayout_5.addLayout(self.horizontalLayout_4) self.verticalLayout_5.addLayout(self.horizontalLayout_4)
self.leftLayout.addWidget(self.listBox) self.leftLayout.addWidget(self.listBox)
self.groupBox_2 = QGroupBox(MainDialog) 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.groupBox_2.setMaximumSize(QSize(16777215, 200))
self.verticalLayout_7 = QVBoxLayout(self.groupBox_2) 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 = QTextBrowser(self.groupBox_2)
self.logBrowser.setObjectName(u"logBrowser") self.logBrowser.setObjectName("logBrowser")
self.verticalLayout_7.addWidget(self.logBrowser) self.verticalLayout_7.addWidget(self.logBrowser)
self.leftLayout.addWidget(self.groupBox_2) self.leftLayout.addWidget(self.groupBox_2)
self.horizontalLayout.addLayout(self.leftLayout) self.horizontalLayout.addLayout(self.leftLayout)
self.rightLayout = QVBoxLayout() self.rightLayout = QVBoxLayout()
self.rightLayout.setSpacing(6) self.rightLayout.setSpacing(6)
self.rightLayout.setObjectName(u"rightLayout") self.rightLayout.setObjectName("rightLayout")
self.settingsBox = QGroupBox(MainDialog) self.settingsBox = QGroupBox(MainDialog)
self.settingsBox.setObjectName(u"settingsBox") self.settingsBox.setObjectName("settingsBox")
self.settingsBox.setMaximumSize(QSize(400, 16777215)) self.settingsBox.setMaximumSize(QSize(400, 16777215))
self.verticalLayout = QVBoxLayout(self.settingsBox) self.verticalLayout = QVBoxLayout(self.settingsBox)
self.verticalLayout.setObjectName(u"verticalLayout") self.verticalLayout.setObjectName("verticalLayout")
self.loginGroupBox = QGroupBox(self.settingsBox) self.loginGroupBox = QGroupBox(self.settingsBox)
self.loginGroupBox.setObjectName(u"loginGroupBox") self.loginGroupBox.setObjectName("loginGroupBox")
self.horizontalLayout_2 = QHBoxLayout(self.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 = QVBoxLayout()
self.verticalLayout_2.setObjectName(u"verticalLayout_2") self.verticalLayout_2.setObjectName("verticalLayout_2")
self.radioCOM = QRadioButton(self.loginGroupBox) self.radioCOM = QRadioButton(self.loginGroupBox)
self.radioCOM.setObjectName(u"radioCOM") self.radioCOM.setObjectName("radioCOM")
self.verticalLayout_2.addWidget(self.radioCOM) self.verticalLayout_2.addWidget(self.radioCOM)
self.radioCN = QRadioButton(self.loginGroupBox) self.radioCN = QRadioButton(self.loginGroupBox)
self.radioCN.setObjectName(u"radioCN") self.radioCN.setObjectName("radioCN")
self.radioCN.setChecked(True) self.radioCN.setChecked(True)
self.verticalLayout_2.addWidget(self.radioCN) self.verticalLayout_2.addWidget(self.radioCN)
self.radioJP = QRadioButton(self.loginGroupBox) self.radioJP = QRadioButton(self.loginGroupBox)
self.radioJP.setObjectName(u"radioJP") self.radioJP.setObjectName("radioJP")
self.verticalLayout_2.addWidget(self.radioJP) self.verticalLayout_2.addWidget(self.radioJP)
self.radioDE = QRadioButton(self.loginGroupBox) self.radioDE = QRadioButton(self.loginGroupBox)
self.radioDE.setObjectName(u"radioDE") self.radioDE.setObjectName("radioDE")
self.verticalLayout_2.addWidget(self.radioDE) self.verticalLayout_2.addWidget(self.radioDE)
self.radioUK = QRadioButton(self.loginGroupBox) self.radioUK = QRadioButton(self.loginGroupBox)
self.radioUK.setObjectName(u"radioUK") self.radioUK.setObjectName("radioUK")
self.verticalLayout_2.addWidget(self.radioUK) self.verticalLayout_2.addWidget(self.radioUK)
self.horizontalLayout_2.addLayout(self.verticalLayout_2) self.horizontalLayout_2.addLayout(self.verticalLayout_2)
self.loginButton = QPushButton(self.loginGroupBox) self.loginButton = QPushButton(self.loginGroupBox)
self.loginButton.setObjectName(u"loginButton") self.loginButton.setObjectName("loginButton")
self.loginButton.setMaximumSize(QSize(80, 16777215)) self.loginButton.setMaximumSize(QSize(80, 16777215))
self.horizontalLayout_2.addWidget(self.loginButton) self.horizontalLayout_2.addWidget(self.loginButton)
self.verticalLayout.addWidget(self.loginGroupBox) self.verticalLayout.addWidget(self.loginGroupBox)
self.cookiesGroupBox = QGroupBox(self.settingsBox) self.cookiesGroupBox = QGroupBox(self.settingsBox)
self.cookiesGroupBox.setObjectName(u"cookiesGroupBox") self.cookiesGroupBox.setObjectName("cookiesGroupBox")
self.verticalLayout_3 = QVBoxLayout(self.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 = QPlainTextEdit(self.cookiesGroupBox)
self.cookieTextEdit.setObjectName(u"cookieTextEdit") self.cookieTextEdit.setObjectName("cookieTextEdit")
self.cookieTextEdit.setEnabled(False) self.cookieTextEdit.setEnabled(False)
self.verticalLayout_3.addWidget(self.cookieTextEdit) self.verticalLayout_3.addWidget(self.cookieTextEdit)
self.horizontalLayout_3 = QHBoxLayout() self.horizontalLayout_3 = QHBoxLayout()
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.radioFromInput = QRadioButton(self.cookiesGroupBox) self.radioFromInput = QRadioButton(self.cookiesGroupBox)
self.radioFromInput.setObjectName(u"radioFromInput") self.radioFromInput.setObjectName("radioFromInput")
self.horizontalLayout_3.addWidget(self.radioFromInput) self.horizontalLayout_3.addWidget(self.radioFromInput)
self.radioFromBrowser = QRadioButton(self.cookiesGroupBox) self.radioFromBrowser = QRadioButton(self.cookiesGroupBox)
self.radioFromBrowser.setObjectName(u"radioFromBrowser") self.radioFromBrowser.setObjectName("radioFromBrowser")
self.radioFromBrowser.setChecked(True) self.radioFromBrowser.setChecked(True)
self.horizontalLayout_3.addWidget(self.radioFromBrowser) self.horizontalLayout_3.addWidget(self.radioFromBrowser)
self.verticalLayout_3.addLayout(self.horizontalLayout_3) self.verticalLayout_3.addLayout(self.horizontalLayout_3)
self.verticalLayout.addWidget(self.cookiesGroupBox) self.verticalLayout.addWidget(self.cookiesGroupBox)
self.groupBox = QGroupBox(self.settingsBox) self.groupBox = QGroupBox(self.settingsBox)
self.groupBox.setObjectName(u"groupBox") self.groupBox.setObjectName("groupBox")
self.verticalLayout_4 = QVBoxLayout(self.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 = QLineEdit(self.groupBox)
self.csrfEdit.setObjectName(u"csrfEdit") self.csrfEdit.setObjectName("csrfEdit")
self.verticalLayout_4.addWidget(self.csrfEdit) self.verticalLayout_4.addWidget(self.csrfEdit)
self.verticalLayout.addWidget(self.groupBox) self.verticalLayout.addWidget(self.groupBox)
self.horizontalLayout_5 = QHBoxLayout() self.horizontalLayout_5 = QHBoxLayout()
self.horizontalLayout_5.setObjectName(u"horizontalLayout_5") self.horizontalLayout_5.setObjectName("horizontalLayout_5")
self.label = QLabel(self.settingsBox) self.label = QLabel(self.settingsBox)
self.label.setObjectName(u"label") self.label.setObjectName("label")
self.horizontalLayout_5.addWidget(self.label) self.horizontalLayout_5.addWidget(self.label)
self.cutLengthSpin = QSpinBox(self.settingsBox) self.cutLengthSpin = QSpinBox(self.settingsBox)
self.cutLengthSpin.setObjectName(u"cutLengthSpin") self.cutLengthSpin.setObjectName("cutLengthSpin")
self.cutLengthSpin.setMaximum(999) self.cutLengthSpin.setMaximum(999)
self.cutLengthSpin.setValue(100) self.cutLengthSpin.setValue(100)
self.horizontalLayout_5.addWidget(self.cutLengthSpin) self.horizontalLayout_5.addWidget(self.cutLengthSpin)
self.verticalLayout.addLayout(self.horizontalLayout_5) self.verticalLayout.addLayout(self.horizontalLayout_5)
self.rightLayout.addWidget(self.settingsBox, 0, Qt.AlignTop) self.rightLayout.addWidget(self.settingsBox, 0, Qt.AlignTop)
self.copyrightBox = QGroupBox(MainDialog) self.copyrightBox = QGroupBox(MainDialog)
self.copyrightBox.setObjectName(u"copyrightBox") self.copyrightBox.setObjectName("copyrightBox")
self.verticalLayout_6 = QVBoxLayout(self.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 = QLabel(self.copyrightBox)
self.label_6.setObjectName(u"label_6") self.label_6.setObjectName("label_6")
self.verticalLayout_6.addWidget(self.label_6) self.verticalLayout_6.addWidget(self.label_6)
self.label_3 = QLabel(self.copyrightBox) 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.label_3.setTextFormat(Qt.MarkdownText)
self.verticalLayout_6.addWidget(self.label_3) self.verticalLayout_6.addWidget(self.label_3)
self.label_4 = QLabel(self.copyrightBox) 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.label_4.setTextFormat(Qt.MarkdownText)
self.verticalLayout_6.addWidget(self.label_4) self.verticalLayout_6.addWidget(self.label_4)
self.label_5 = QLabel(self.copyrightBox) 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.verticalLayout_6.addWidget(self.label_5)
self.rightLayout.addWidget(self.copyrightBox, 0, Qt.AlignTop) self.rightLayout.addWidget(self.copyrightBox, 0, Qt.AlignTop)
self.horizontalLayout.addLayout(self.rightLayout) self.horizontalLayout.addLayout(self.rightLayout)
self.horizontalLayout.setStretch(0, 1) self.horizontalLayout.setStretch(0, 1)
@@ -274,44 +302,127 @@ class Ui_MainDialog(object):
self.retranslateUi(MainDialog) self.retranslateUi(MainDialog)
QMetaObject.connectSlotsByName(MainDialog) QMetaObject.connectSlotsByName(MainDialog)
# setupUi # setupUi
def retranslateUi(self, MainDialog): def retranslateUi(self, MainDialog):
MainDialog.setWindowTitle(QCoreApplication.translate("MainDialog", u"Kindle \u4e0b\u8f7d\u52a9\u624b", None)) MainDialog.setWindowTitle(
self.listBox.setTitle(QCoreApplication.translate("MainDialog", u"\u4e0b\u8f7d\u5217\u8868", None)) QCoreApplication.translate(
self.radioEBOK.setText(QCoreApplication.translate("MainDialog", u"\u7535\u5b50\u4e66", None)) "MainDialog", "Kindle \u4e0b\u8f7d\u52a9\u624b", 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.listBox.setTitle(
self.outDirEdit.setText(QCoreApplication.translate("MainDialog", u"DOWNLOADS", None)) QCoreApplication.translate("MainDialog", "\u4e0b\u8f7d\u5217\u8868", None)
self.browseButton.setText(QCoreApplication.translate("MainDialog", u"\u6d4f\u89c8...", None)) )
self.downloadButton.setText(QCoreApplication.translate("MainDialog", u"\u4e0b\u8f7d\u5168\u90e8", None)) self.radioEBOK.setText(
self.selectedButton.setText(QCoreApplication.translate("MainDialog", u"Download Selected", None)) QCoreApplication.translate("MainDialog", "\u7535\u5b50\u4e66", None)
self.dedrmCkb.setText(QCoreApplication.translate("MainDialog", u"DeDRM", None)) )
self.groupBox_2.setTitle(QCoreApplication.translate("MainDialog", u"\u8f93\u51fa", None)) self.radioPDO.setText(
self.logBrowser.setHtml(QCoreApplication.translate("MainDialog", u"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n" QCoreApplication.translate("MainDialog", "\u4e2a\u4eba\u6587\u6863", None)
"<html><head><meta name=\"qrichtext\" content=\"1\" /><meta charset=\"utf-8\" /><style type=\"text/css\">\n" )
"p, li { white-space: pre-wrap; }\n" self.fetchButton.setText(
"hr { height: 1px; border-width: 0; }\n" QCoreApplication.translate(
"</style></head><body style=\" font-family:'.AppleSystemUIFont'; font-size:13pt; font-weight:400; font-style:normal;\">\n" "MainDialog", "\u83b7\u53d6\u4e0b\u8f7d\u5217\u8868", None
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Microsoft YaHei UI'; font-size:9pt;\"><br /></p></body></html>", None)) )
self.settingsBox.setTitle(QCoreApplication.translate("MainDialog", u"\u8bbe\u7f6e", 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",
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">\n'
'<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css">\n'
"p, li { white-space: pre-wrap; }\n"
"hr { height: 1px; border-width: 0; }\n"
"</style></head><body style=\" font-family:'.AppleSystemUIFont'; font-size:13pt; font-weight:400; font-style:normal;\">\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Microsoft YaHei UI'; font-size:9pt;\"><br /></p></body></html>",
None,
)
)
self.settingsBox.setTitle(
QCoreApplication.translate("MainDialog", "\u8bbe\u7f6e", None)
)
self.loginGroupBox.setTitle("") self.loginGroupBox.setTitle("")
self.radioCOM.setText(QCoreApplication.translate("MainDialog", u"\u7f8e\u4e9a(.com)", None)) self.radioCOM.setText(
self.radioCN.setText(QCoreApplication.translate("MainDialog", u"\u4e2d\u4e9a(.cn)", None)) QCoreApplication.translate("MainDialog", "\u7f8e\u4e9a(.com)", None)
self.radioJP.setText(QCoreApplication.translate("MainDialog", u"\u65e5\u4e9a(.jp)", None)) )
self.radioDE.setText(QCoreApplication.translate("MainDialog", u"\u5fb7\u4e9a(.de)", None)) self.radioCN.setText(
self.radioUK.setText(QCoreApplication.translate("MainDialog", u"\u82F1\u4e9a(.uk)", None)) QCoreApplication.translate("MainDialog", "\u4e2d\u4e9a(.cn)", None)
self.loginButton.setText(QCoreApplication.translate("MainDialog", u"\u767b\u5f55", None)) )
self.cookiesGroupBox.setTitle(QCoreApplication.translate("MainDialog", u"Cookies", None)) self.radioJP.setText(
self.radioFromInput.setText(QCoreApplication.translate("MainDialog", u"\u6765\u81ea\u8f93\u5165", None)) QCoreApplication.translate("MainDialog", "\u65e5\u4e9a(.jp)", 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.radioDE.setText(
self.label.setText(QCoreApplication.translate("MainDialog", u"\u6587\u4ef6\u540d\u622a\u65ad", None)) 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.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_6.setText(
self.label_3.setText(QCoreApplication.translate("MainDialog", u"Copyright 2022 \u00a9 [yihong0618](https://github.com/yihong0618) and [frostming](https://github.com/frostming)", None)) QCoreApplication.translate(
self.label_4.setText(QCoreApplication.translate("MainDialog", u"GitHub: <https://github.com/yihong0618/Kindle_download_helper>", None)) "MainDialog",
self.label_5.setText(QCoreApplication.translate("MainDialog", u"License: GPL V3", None)) "\u9690\u79c1\u58f0\u660e\uff1a\u6211\u4eec\u4e0d\u4f1a\u6536\u96c6\u4efb\u4f55\u7528\u6237\u4fe1\u606f\uff0c\u8bf7\u653e\u5fc3\u4f7f\u7528",
# retranslateUi 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: <https://github.com/yihong0618/Kindle_download_helper>",
None,
)
)
self.label_5.setText(
QCoreApplication.translate("MainDialog", "License: GPL V3", None)
)
# retranslateUi

View File

@@ -9,6 +9,7 @@ from kindle_download_helper.config import (
DEFAULT_OUT_DIR, DEFAULT_OUT_DIR,
DEFAULT_SESSION_FILE, DEFAULT_SESSION_FILE,
DEFAULT_OUT_DEDRM_DIR, DEFAULT_OUT_DEDRM_DIR,
DEFAULT_OUT_EPUB_DIR,
) )
logger = logging.getLogger("kindle") logger = logging.getLogger("kindle")
@@ -18,82 +19,119 @@ logger.addHandler(fh)
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# download selected books for cli # download selected books for cli
def download_selected_books(kindle, options): def download_selected_books(kindle, options):
# get all books and get the default device # 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) books = kindle.get_all_books(filetype=options.filetype)
device = kindle.find_device() device = kindle.find_device()
# print all books # print all books
for idx, book in enumerate(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 # download loop
while True: while True:
# get the indices of the books to download # 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 "q", quit
# if input "l", list all books again # if input "l", list all books again
if indices[0] == 'q': if indices[0] == "q":
break break
elif indices[0] == 'l': elif indices[0] == "l":
for idx, book in enumerate(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"]
)
continue continue
# decode the indices # decode the indices
downlist = [] downlist = []
flag = True flag = True
for idx in indices: for idx in indices:
if idx.isnumeric() == False: if idx.isnumeric() == False:
if ':' in idx: if ":" in idx:
# if is not a number, and ":" in it, then it is a range # if is not a number, and ":" in it, then it is a range
# decode the 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 # 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) downlist.extend(extend_list)
else: else:
# if is not a number, and no ":" in it, then it is an error # 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 flag = False
break break
else: else:
# if is a number, then append it to downlist # if is a number, then append it to downlist
downlist.append(int(idx)-1) downlist.append(int(idx) - 1)
if not flag: if not flag:
continue continue
# remove the duplicate indices # remove the duplicate indices
downlist = list(set(downlist)) downlist = list(set(downlist))
# check if the indices are valid # check if the indices are valid
if max(downlist) >= len(books) or min(downlist) < 0: 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 continue
# print the books to download # print the books to download
for idx in downlist: for idx in downlist:
print('Index: ' + '{:>5d}'.format(idx+1) + ' | Title: ' + books[idx]['title'] + ' | asin: '+books[idx]['asin']) print(
print('Downloading ' + str(len(downlist)) + ' books:') "Index: "
+ "{:>5d}".format(idx + 1)
+ " | Title: "
+ books[idx]["title"]
+ " | asin: "
+ books[idx]["asin"]
)
print("Downloading " + str(len(downlist)) + " books:")
# ask if to continue # ask if to continue
while True: while True:
flag = input('Continue? (y/n)') flag = input("Continue? (y/n)")
if flag == 'y' or flag == 'n': if flag == "y" or flag == "n":
break break
else: else:
print('Input error, please input y or n') print("Input error, please input y or n")
if flag == 'n': if flag == "n":
continue continue
# download the books # download the books
for i, idx in enumerate(downlist): 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) kindle.download_one_book(books[idx], device, idx, filetype=options.filetype)
print('Download finished.') print("Download finished.")
def main(): def main():
logger.setLevel(os.environ.get("LOGGING_LEVEL", "INFO")) logger.setLevel(os.environ.get("LOGGING_LEVEL", "INFO"))
@@ -165,6 +203,12 @@ def main():
default=DEFAULT_OUT_DEDRM_DIR, default=DEFAULT_OUT_DEDRM_DIR,
help="dwonload output 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( parser.add_argument(
"-s", "-s",
"--session-file", "--session-file",
@@ -211,7 +255,7 @@ def main():
default="", default="",
help="Download file for device with this serial number", help="Download file for device with this serial number",
) )
parser.add_argument( parser.add_argument(
"--mode", "--mode",
dest="mode", dest="mode",
@@ -226,11 +270,16 @@ def main():
# for dedrm # for dedrm
if not os.path.exists(options.outdedrmdir): if not os.path.exists(options.outdedrmdir):
os.makedirs(options.outdedrmdir) os.makedirs(options.outdedrmdir)
# for epub
if not os.path.exists(options.outepubmdir):
os.makedirs(options.outepubmdir)
kindle = Kindle( kindle = Kindle(
options.csrf_token, options.csrf_token,
options.domain, options.domain,
options.outdir, options.outdir,
options.outdedrmdir, options.outdedrmdir,
options.outepubmdir,
options.cut_length, options.cut_length,
session_file=options.session_file, session_file=options.session_file,
device_sn=options.device_sn, device_sn=options.device_sn,
@@ -265,7 +314,9 @@ def main():
# check the download mode # check the download mode
if options.mode == "all": if options.mode == "all":
# download all books # 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": elif options.mode == "sel":
# download selected books # download selected books
download_selected_books(kindle, options) download_selected_books(kindle, options)

View File

@@ -4,6 +4,7 @@ from kindle_download_helper.user_agents import USER_AGENTS
DEFAULT_OUT_DIR = "DOWNLOADS" DEFAULT_OUT_DIR = "DOWNLOADS"
DEFAULT_OUT_DEDRM_DIR = "DEDRMS" DEFAULT_OUT_DEDRM_DIR = "DEDRMS"
DEFAULT_OUT_EPUB_DIR = "EPUB"
DEFAULT_SESSION_FILE = ".kindle_session" DEFAULT_SESSION_FILE = ".kindle_session"
KINDLE_HEADER = { KINDLE_HEADER = {

View File

@@ -22,6 +22,7 @@ charMap1 = b"n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M"
charMap3 = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" charMap3 = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
charMap4 = b"ABCDEFGHIJKLMNPQRSTUVWXYZ123456789" charMap4 = b"ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
# crypto digestroutines # crypto digestroutines
def MD5(message): def MD5(message):
ctx = hashlib.md5() ctx = hashlib.md5()

View File

@@ -13,6 +13,8 @@ import re
import time import time
import urllib import urllib
import pathlib import pathlib
import shutil
from mobi import extract
from http.cookies import SimpleCookie from http.cookies import SimpleCookie
import requests import requests
@@ -23,8 +25,9 @@ from kindle_download_helper.dedrm import MobiBook, get_pid_list
from kindle_download_helper.config import ( from kindle_download_helper.config import (
KINDLE_URLS, KINDLE_URLS,
DEFAULT_OUT_DIR, DEFAULT_OUT_DIR,
DEFAULT_SESSION_FILE,
DEFAULT_OUT_DEDRM_DIR, DEFAULT_OUT_DEDRM_DIR,
DEFAULT_OUT_EPUB_DIR,
DEFAULT_SESSION_FILE,
CONTENT_TYPES, CONTENT_TYPES,
KINDLE_STAT_TEMPLATE, KINDLE_STAT_TEMPLATE,
) )
@@ -48,6 +51,7 @@ logger.addHandler(fh)
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class Kindle: class Kindle:
def __init__( def __init__(
self, self,
@@ -55,6 +59,7 @@ class Kindle:
domain="cn", domain="cn",
out_dir=DEFAULT_OUT_DIR, out_dir=DEFAULT_OUT_DIR,
out_dedrm_dir=DEFAULT_OUT_DEDRM_DIR, out_dedrm_dir=DEFAULT_OUT_DEDRM_DIR,
out_epub_dir=DEFAULT_OUT_EPUB_DIR,
cut_length=100, cut_length=100,
session_file=DEFAULT_SESSION_FILE, session_file=DEFAULT_SESSION_FILE,
**kwargs, **kwargs,
@@ -64,6 +69,7 @@ class Kindle:
self.total_to_download = 0 self.total_to_download = 0
self.out_dir = out_dir self.out_dir = out_dir
self.out_dedrm_dir = out_dedrm_dir self.out_dedrm_dir = out_dedrm_dir
self.out_epub_dir = out_epub_dir
self.dedrm = False self.dedrm = False
self.cut_length = cut_length self.cut_length = cut_length
self.not_done = False self.not_done = False
@@ -104,7 +110,7 @@ class Kindle:
if not self.session.cookies: if not self.session.cookies:
logger.debug("No cookie found, trying to load from browsers") logger.debug("No cookie found, trying to load from browsers")
try: try:
self.set_cookie(browser_cookie3.load(domain_name="amazon")) self.set_cookie(browser_cookie3.load())
except: except:
print("not found browser_cookie3 here, you should use --cookie command") print("not found browser_cookie3 here, you should use --cookie command")
@@ -120,11 +126,11 @@ class Kindle:
cookies_dict, cookiejar=None, overwrite=True cookies_dict, cookiejar=None, overwrite=True
) )
return cookiejar return cookiejar
def find_device(self): def find_device(self):
devices = self.get_devices() devices = self.get_devices()
device_sn = self.device_sn device_sn = self.device_sn
if isinstance(device_sn, str) and device_sn != "": if isinstance(device_sn, str) and device_sn != "":
for device in devices: for device in devices:
if device["deviceSerialNumber"] == device_sn.strip(): if device["deviceSerialNumber"] == device_sn.strip():
@@ -364,7 +370,9 @@ class Kindle:
book_title = book.get("title", "") book_title = book.get("title", "")
# filter the brackets in the book 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(" ", "") book_title = book_title.replace(" ", "")
if book.get("category", "") == "KindleEBook": if book.get("category", "") == "KindleEBook":
@@ -466,27 +474,28 @@ class Kindle:
out = os.path.join(self.out_dir, name) out = os.path.join(self.out_dir, name)
out_dedrm = os.path.join(self.out_dedrm_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 count_digit_length = 4
size_length = 6 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( logger.info(
f"[{index+1:>{count_digit_length}}/{self.total_to_download:>{count_digit_length}}][{size_in_mb:> {size_length}}Mb]Downloading {name}" 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 if we can write the file
try : try:
pathlib.Path(out).touch() pathlib.Path(out).touch()
except OSError as e: 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 name = self.trim_title_suffix(title) + extname
logger.info(f"Original filename too long, trim to {name}") logger.info(f"Original filename too long, trim to {name}")
out = os.path.join(self.out_dir, name) out = os.path.join(self.out_dir, name)
out_dedrm = os.path.join(self.out_dedrm_dir, name) out_dedrm = os.path.join(self.out_dedrm_dir, name)
else : else:
logger.error(e) logger.error(e)
with open(out, "wb") as f: with open(out, "wb") as f:
@@ -501,6 +510,14 @@ class Kindle:
totalpids = get_pid_list(md1, md2, [self.device_serial_number], []) totalpids = get_pid_list(md1, md2, [self.device_serial_number], [])
totalpids = list(set(totalpids)) totalpids = list(set(totalpids))
mb.make_drm_file(totalpids, out_dedrm) 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: except Exception as e:
logger.error("DeDRM failed for %s: %s", name, e) logger.error("DeDRM failed for %s: %s", name, e)
pass pass
@@ -511,7 +528,7 @@ class Kindle:
def download_books(self, start_index=0, filetype="EBOK"): def download_books(self, start_index=0, filetype="EBOK"):
# use default device # use default device
device = self.find_device() device = self.find_device()
books = self.get_all_books(filetype=filetype, start_index=start_index) books = self.get_all_books(filetype=filetype, start_index=start_index)
if start_index > 0: if start_index > 0:
print(f"resuming the download {start_index + 1}/{self.total_to_download}") 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: with open(os.path.join(self.out_dir, "key.txt"), "w") as f:
f.write(f"Key is: {device['deviceSerialNumber']}") 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): def trim_title_suffix(self, title):
return re.sub(r"([^]+?|【[^】]+】?)", "", title) return re.sub(r"([^]+?|【[^】]+】?)", "", title)

View File

@@ -1,3 +1,4 @@
requests requests
browser-cookie3 browser-cookie3
mobi
pywin32 ; sys_platform == 'win32' pywin32 ; sys_platform == 'win32'