├── .gitignore
├── EHentaiSearch.ini
├── EHentaiSearch.pro
├── EHentaiSearch.pro.user
├── README.md
├── ex.jpg
├── main.cpp
├── mainwindow.cpp
├── mainwindow.h
├── mainwindow.ui
├── resource.qrc
└── ui.png
/.gitignore:
--------------------------------------------------------------------------------
1 | *.db
2 | *.db-shm
3 | *.db-wal
--------------------------------------------------------------------------------
/EHentaiSearch.ini:
--------------------------------------------------------------------------------
1 | [thumb]
2 | enable=true
3 | password=awslawslawslawsl
4 | prefix=http://103.45.103.176:8088/picture/
5 | suffix=
6 | [database]
7 | path=eh.db
8 | limit=20
9 |
--------------------------------------------------------------------------------
/EHentaiSearch.pro:
--------------------------------------------------------------------------------
1 | #-------------------------------------------------
2 | #
3 | # Project created by QtCreator 2019-07-27T22:49:20
4 | #
5 | #-------------------------------------------------
6 |
7 | QT += core gui sql network
8 |
9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
10 |
11 | TARGET = EHentaiSearch
12 | TEMPLATE = app
13 |
14 | # The following define makes your compiler emit warnings if you use
15 | # any feature of Qt which has been marked as deprecated (the exact warnings
16 | # depend on your compiler). Please consult the documentation of the
17 | # deprecated API in order to know how to port your code away from it.
18 | DEFINES += QT_DEPRECATED_WARNINGS
19 |
20 | # You can also make your code fail to compile if you use deprecated APIs.
21 | # In order to do so, uncomment the following line.
22 | # You can also select to disable deprecated APIs only up to a certain version of Qt.
23 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
24 |
25 | CONFIG += c++11
26 |
27 | SOURCES += \
28 | main.cpp \
29 | mainwindow.cpp
30 |
31 | HEADERS += \
32 | mainwindow.h
33 |
34 | FORMS += \
35 | mainwindow.ui
36 |
37 | win32{
38 | QMAKE_CXXFLAGS += /utf-8
39 | }
40 | # Default rules for deployment.
41 | qnx: target.path = /tmp/$${TARGET}/bin
42 | else: unix:!android: target.path = /opt/$${TARGET}/bin
43 | !isEmpty(target.path): INSTALLS += target
44 |
45 | win32:CONFIG(release, debug|release): LIBS += -L$$PWD/../../../../vcpkg/installed/x86-windows/lib/ -lcryptopp-static
46 | else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/../../../../vcpkg/installed/x86-windows/debug/lib/ -lcryptopp-static
47 |
48 | INCLUDEPATH += $$PWD/../../../../vcpkg/installed/x86-windows/include
49 | DEPENDPATH += $$PWD/../../../../vcpkg/installed/x86-windows/include
50 |
51 | win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$PWD/../../../../vcpkg/installed/x86-windows/lib/cryptopp-static.lib
52 | else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$PWD/../../../../vcpkg/installed/x86-windows/debug/lib/cryptopp-static.lib
53 |
54 | RESOURCES += \
55 | resource.qrc
56 |
57 | DISTFILES += \
58 | EHentaiSearch.ini
59 |
--------------------------------------------------------------------------------
/EHentaiSearch.pro.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | EnvironmentId
7 | {59f19bb9-3b4e-4102-b975-bc071ced5f2f}
8 |
9 |
10 | ProjectExplorer.Project.ActiveTarget
11 | 0
12 |
13 |
14 | ProjectExplorer.Project.EditorSettings
15 |
16 | true
17 | false
18 | true
19 |
20 | Cpp
21 |
22 | CppGlobal
23 |
24 |
25 |
26 | QmlJS
27 |
28 | QmlJSGlobal
29 |
30 |
31 | 2
32 | UTF-8
33 | false
34 | 4
35 | false
36 | 80
37 | true
38 | true
39 | 1
40 | true
41 | false
42 | 0
43 | true
44 | true
45 | 0
46 | 8
47 | true
48 | 1
49 | true
50 | true
51 | true
52 | false
53 |
54 |
55 |
56 | ProjectExplorer.Project.PluginSettings
57 |
58 |
59 | -fno-delayed-template-parsing
60 |
61 | false
62 | {bf0843d8-06c8-4e6b-ac88-db71480b9e6d}
63 |
64 |
65 |
66 | ProjectExplorer.Project.Target.0
67 |
68 | Desktop Qt 5.13.0 MSVC2017 32bit
69 | Desktop Qt 5.13.0 MSVC2017 32bit
70 | qt.qt5.5130.win32_msvc2017_kit
71 | 1
72 | 0
73 | 0
74 |
75 | C:/Users/liaoh/Documents/build-EHentaiSearch-Desktop_Qt_5_13_0_MSVC2017_32bit
76 |
77 |
78 | true
79 | qmake
80 |
81 | QtProjectManager.QMakeBuildStep
82 | true
83 |
84 | false
85 | false
86 | false
87 |
88 |
89 | true
90 | Make
91 |
92 | Qt4ProjectManager.MakeStep
93 |
94 | false
95 |
96 |
97 | false
98 |
99 | 2
100 | Build
101 |
102 | ProjectExplorer.BuildSteps.Build
103 |
104 |
105 |
106 | true
107 | Make
108 |
109 | Qt4ProjectManager.MakeStep
110 |
111 | true
112 | clean
113 |
114 | false
115 |
116 | 1
117 | Clean
118 |
119 | ProjectExplorer.BuildSteps.Clean
120 |
121 | 2
122 | false
123 |
124 | Debug
125 | Debug
126 | Qt4ProjectManager.Qt4BuildConfiguration
127 | 2
128 | true
129 |
130 |
131 | C:/Users/liaoh/Documents/build-EHentaiSearch-Desktop_Qt_5_13_0_MSVC2017_32bit
132 |
133 |
134 | true
135 | qmake
136 |
137 | QtProjectManager.QMakeBuildStep
138 | false
139 |
140 | false
141 | false
142 | true
143 |
144 |
145 | true
146 | Make
147 |
148 | Qt4ProjectManager.MakeStep
149 |
150 | false
151 |
152 |
153 | false
154 |
155 | 2
156 | Build
157 |
158 | ProjectExplorer.BuildSteps.Build
159 |
160 |
161 |
162 | true
163 | Make
164 |
165 | Qt4ProjectManager.MakeStep
166 |
167 | true
168 | clean
169 |
170 | false
171 |
172 | 1
173 | Clean
174 |
175 | ProjectExplorer.BuildSteps.Clean
176 |
177 | 2
178 | false
179 |
180 | Release
181 | Release
182 | Qt4ProjectManager.Qt4BuildConfiguration
183 | 0
184 | true
185 |
186 | 2
187 |
188 |
189 | 0
190 | 部署
191 |
192 | ProjectExplorer.BuildSteps.Deploy
193 |
194 | 1
195 | Deploy Configuration
196 |
197 | ProjectExplorer.DefaultDeployConfiguration
198 |
199 | 1
200 |
201 |
202 | dwarf
203 |
204 | cpu-cycles
205 |
206 |
207 | 250
208 | -F
209 | true
210 | 4096
211 | false
212 | false
213 | 1000
214 |
215 | true
216 |
217 | false
218 | false
219 | false
220 | false
221 | true
222 | 0.01
223 | 10
224 | true
225 | kcachegrind
226 | 1
227 | 25
228 |
229 | 1
230 | true
231 | false
232 | true
233 | valgrind
234 |
235 | 0
236 | 1
237 | 2
238 | 3
239 | 4
240 | 5
241 | 6
242 | 7
243 | 8
244 | 9
245 | 10
246 | 11
247 | 12
248 | 13
249 | 14
250 |
251 | 2
252 |
253 | EHentaiSearch
254 |
255 | Qt4ProjectManager.Qt4RunConfiguration:C:/Users/liaoh/Documents/EHentaiSearch/EHentaiSearch.pro
256 |
257 | 3768
258 | false
259 | true
260 | true
261 | false
262 | false
263 | true
264 | true
265 | C:/Users/liaoh/Documents/build-EHentaiSearch-Desktop_Qt_5_13_0_MSVC2017_32bit/debug
266 | C:/Users/liaoh/Documents/build-EHentaiSearch-Desktop_Qt_5_13_0_MSVC2017_32bit
267 |
268 | 1
269 |
270 |
271 |
272 | ProjectExplorer.Project.Target.1
273 |
274 | Desktop Qt 5.13.0 MSVC2017 64bit
275 | Desktop Qt 5.13.0 MSVC2017 64bit
276 | qt.qt5.5130.win64_msvc2017_64_kit
277 | 0
278 | 0
279 | 0
280 |
281 | C:/Users/liaoh/Documents/build-EHentaiSearch-Desktop_Qt_5_13_0_MSVC2017_64bit-Debug
282 |
283 |
284 | true
285 | qmake
286 |
287 | QtProjectManager.QMakeBuildStep
288 | true
289 |
290 | false
291 | false
292 | false
293 |
294 |
295 | true
296 | Make
297 |
298 | Qt4ProjectManager.MakeStep
299 |
300 | false
301 |
302 |
303 | false
304 |
305 | 2
306 | Build
307 |
308 | ProjectExplorer.BuildSteps.Build
309 |
310 |
311 |
312 | true
313 | Make
314 |
315 | Qt4ProjectManager.MakeStep
316 |
317 | true
318 | clean
319 |
320 | false
321 |
322 | 1
323 | Clean
324 |
325 | ProjectExplorer.BuildSteps.Clean
326 |
327 | 2
328 | false
329 |
330 | Debug
331 | Debug
332 | Qt4ProjectManager.Qt4BuildConfiguration
333 | 2
334 | true
335 |
336 |
337 | C:/Users/liaoh/Documents/build-EHentaiSearch-Desktop_Qt_5_13_0_MSVC2017_64bit-Release
338 |
339 |
340 | true
341 | qmake
342 |
343 | QtProjectManager.QMakeBuildStep
344 | false
345 |
346 | false
347 | false
348 | true
349 |
350 |
351 | true
352 | Make
353 |
354 | Qt4ProjectManager.MakeStep
355 |
356 | false
357 |
358 |
359 | false
360 |
361 | 2
362 | Build
363 |
364 | ProjectExplorer.BuildSteps.Build
365 |
366 |
367 |
368 | true
369 | Make
370 |
371 | Qt4ProjectManager.MakeStep
372 |
373 | true
374 | clean
375 |
376 | false
377 |
378 | 1
379 | Clean
380 |
381 | ProjectExplorer.BuildSteps.Clean
382 |
383 | 2
384 | false
385 |
386 | Release
387 | Release
388 | Qt4ProjectManager.Qt4BuildConfiguration
389 | 0
390 | true
391 |
392 |
393 | C:/Users/liaoh/Documents/build-EHentaiSearch-Desktop_Qt_5_13_0_MSVC2017_64bit-Profile
394 |
395 |
396 | true
397 | qmake
398 |
399 | QtProjectManager.QMakeBuildStep
400 | true
401 |
402 | false
403 | true
404 | true
405 |
406 |
407 | true
408 | Make
409 |
410 | Qt4ProjectManager.MakeStep
411 |
412 | false
413 |
414 |
415 | false
416 |
417 | 2
418 | Build
419 |
420 | ProjectExplorer.BuildSteps.Build
421 |
422 |
423 |
424 | true
425 | Make
426 |
427 | Qt4ProjectManager.MakeStep
428 |
429 | true
430 | clean
431 |
432 | false
433 |
434 | 1
435 | Clean
436 |
437 | ProjectExplorer.BuildSteps.Clean
438 |
439 | 2
440 | false
441 |
442 | Profile
443 | Profile
444 | Qt4ProjectManager.Qt4BuildConfiguration
445 | 0
446 | true
447 |
448 | 3
449 |
450 |
451 | 0
452 | 部署
453 |
454 | ProjectExplorer.BuildSteps.Deploy
455 |
456 | 1
457 | Deploy Configuration
458 |
459 | ProjectExplorer.DefaultDeployConfiguration
460 |
461 | 1
462 |
463 |
464 | dwarf
465 |
466 | cpu-cycles
467 |
468 |
469 | 250
470 | -F
471 | true
472 | 4096
473 | false
474 | false
475 | 1000
476 |
477 | true
478 |
479 | false
480 | false
481 | false
482 | false
483 | true
484 | 0.01
485 | 10
486 | true
487 | kcachegrind
488 | 1
489 | 25
490 |
491 | 1
492 | true
493 | false
494 | true
495 | valgrind
496 |
497 | 0
498 | 1
499 | 2
500 | 3
501 | 4
502 | 5
503 | 6
504 | 7
505 | 8
506 | 9
507 | 10
508 | 11
509 | 12
510 | 13
511 | 14
512 |
513 | 2
514 |
515 | EHentaiSearch
516 |
517 | Qt4ProjectManager.Qt4RunConfiguration:C:/Users/liaoh/Documents/EHentaiSearch/EHentaiSearch.pro
518 |
519 | 3768
520 | false
521 | true
522 | true
523 | false
524 | false
525 | true
526 |
527 |
528 |
529 | 1
530 |
531 |
532 |
533 | ProjectExplorer.Project.TargetCount
534 | 2
535 |
536 |
537 | ProjectExplorer.Project.Updater.FileVersion
538 | 21
539 |
540 |
541 | Version
542 | 21
543 |
544 |
545 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # e站搜索工具(本地)
4 |
5 | 使用e站的数据库
6 |
7 | [数据库](https://pan.baidu.com/s/18xSrYlx29NkSYfby3ct01A) 密码 p0q1
8 |
9 | [程序](https://github.com/ex-rip/EHentaiSearch/releases/latest/download/release.7z)
10 |
11 | [QT依赖](https://github.com/ex-rip/EHentaiSearch/releases/download/1.0.3/depend.7z) 如果安装了QT并添加到PATH,就不需要这个包
12 |
13 | ## 使用
14 |
15 | 
16 |
17 | #### 标题
18 |
19 | 按标题搜索,包括英文标题或日文标题
20 |
21 | #### 标签
22 |
23 | 格式:类:值
24 |
25 | * id:符号 数值
26 | * id:>4
27 | * id:<=10
28 | * category:类别
29 | * rating:评分 同id
30 | * artist:艺术家
31 | * group:组织
32 | * parody:原作
33 | * language:语言
34 | * misc:杂项标签
35 | * female:女性
36 | * male:男性
37 | * character:人物
38 |
39 | ## ini配置
40 |
41 | ### thumb 封面
42 |
43 | #### enable
44 |
45 | 是否启用 true/false
46 |
47 | #### prefix suffix
48 |
49 | 封面链接的前缀、后缀
50 |
51 | #### password
52 |
53 | aes加密的密码
54 |
55 | aes加密使用__256bit__
56 |
57 | ### database 数据库
58 |
59 | #### path
60 |
61 | 数据库路径
62 |
63 | #### limit
64 |
65 | 每一页显示结果数
--------------------------------------------------------------------------------
/ex.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ex-rip/EHentaiSearch/422d43dfcb975384a0282895e102b15586095c9d/ex.jpg
--------------------------------------------------------------------------------
/main.cpp:
--------------------------------------------------------------------------------
1 | #include "mainwindow.h"
2 | #include
3 |
4 | int main(int argc, char *argv[]) {
5 | QApplication a(argc, argv);
6 | MainWindow w;
7 | w.show();
8 |
9 | return a.exec();
10 | }
11 |
--------------------------------------------------------------------------------
/mainwindow.cpp:
--------------------------------------------------------------------------------
1 | #include "mainwindow.h"
2 | #include "ui_mainwindow.h"
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | static QSettings* setting;
18 |
19 | static bool thumb_enable;
20 | static QString thumb_prefix;
21 | static QString thumb_suffix;
22 | static QByteArray thumb_aes_key;
23 |
24 | //const QString dbpath = R"(C:\Users\liaoh\Documents\EHentaiSearch\eh.db)";
25 | static QString dbpath;
26 | static int result_limit;
27 |
28 | QMap> parseTag(QString tags) {
29 | QMap> result;
30 | QRegularExpression re(R"!!!( *([^: ]+):(?:([^ ]+)|"([^"]+)$") *)!!!");
31 | int offset = 0;
32 | QRegularExpressionMatch match;
33 | while (true) {
34 | match = re.match(tags, offset);
35 | if (!match.hasMatch()) {
36 | break;
37 | }
38 | result[match.captured(1)].insert(match.captured(2));
39 | offset = match.capturedEnd(0);
40 | }
41 | QMap> ret;
42 | for (const QString& k: result.keys()) {
43 | ret[k].resize(result[k].size());
44 | std::copy(result[k].begin(), result[k].end(), ret[k].begin());
45 | std::sort(ret[k].begin(), ret[k].end());
46 | }
47 | return ret;
48 | }
49 |
50 | static QString bys[2] = {
51 | "posted",
52 | "rating"
53 | };
54 |
55 | static QString orders[2] = {
56 | " desc",
57 | "",
58 | };
59 |
60 | MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), reply(nullptr) {
61 | ui->setupUi(this);
62 | ordergroup = new QButtonGroup(this);
63 | ordergroup->addButton(ui->desc, 0);
64 | ordergroup->addButton(ui->asc, 1);
65 | ui->desc->setChecked(true);
66 | bygroup = new QButtonGroup(this);
67 | bygroup->addButton(ui->byposted, 0);
68 | bygroup->addButton(ui->byrating, 1);
69 | ui->byposted->setChecked(true);
70 | by = bys[0];
71 | order = orders[0];
72 | setting = new QSettings("EHentaiSearch.ini", QSettings::IniFormat);
73 | dbpath = setting->value("database/path", "eh.db").toString();
74 | result_limit = setting->value("database/limit", 20).toInt();
75 | thumb_enable = setting->value("thumb/enable", false).toBool();
76 | thumb_prefix = setting->value("thumb/prefix", "").toString();
77 | thumb_suffix = setting->value("thumb/suffix", "").toString();
78 | thumb_aes_key = setting->value("thumb/password", QByteArray("awslawslawslawsl")).toByteArray();
79 | database = QSqlDatabase::addDatabase("QSQLITE");
80 | database.setDatabaseName(dbpath);
81 | if (!database.open()) {
82 | QMessageBox::critical(this, "EHentaiSearch", "无法打开数据库");
83 | exit(0);
84 | }
85 | QLabel* update = new QLabel("构建于 " __DATE__ " " __TIME__ " 主页与帮助", ui->status);
86 | update->setOpenExternalLinks(true);
87 | ui->status->addPermanentWidget(update);
88 | query = QSqlQuery(database);
89 | connect(bygroup, QOverload::of(&QButtonGroup::buttonClicked), [&](int id) {
90 | by = bys[id];
91 | });
92 | connect(ordergroup, QOverload::of(&QButtonGroup::buttonClicked), [&](int id) {
93 | order = orders[id];
94 | });
95 | connect(ui->prev, &QPushButton::clicked, [&]() {
96 | int n = curpage;
97 | --n;
98 | if (n == 1) {
99 | ui->prev->setEnabled(false);
100 | }
101 | if (n < maxpage) {
102 | ui->next->setEnabled(true);
103 | }
104 | ui->page->setValue(n);
105 | movePage(n);
106 | });
107 | connect(ui->next, &QPushButton::clicked, [&]() {
108 | int n = curpage;
109 | ++n;
110 | if (n == maxpage) {
111 | ui->next->setEnabled(false);
112 | }
113 | if (n > 1) {
114 | ui->prev->setEnabled(true);
115 | }
116 | ui->page->setValue(n);
117 | movePage(n);
118 | });
119 | connect(ui->search, &QPushButton::clicked, [&]() {
120 | ui->result->disconnect();
121 | ui->page->disconnect();
122 | ui->result->clear();
123 | ui->status->showMessage("搜索中,请稍候。");
124 | ui->status->repaint();
125 | if (!query.exec("select count(id) from gallery " + genWhere() + ";")) {
126 | QMessageBox::critical(this, "EX R.I.P.", "搜索失败:" + query.lastError().text());
127 | exit(0);
128 | }
129 | query.next();
130 | int results = query.value(0).toInt();
131 | ui->status->showMessage("搜索出结果" + QString::asprintf("%d", results) + "条。搜索中,请稍候。");
132 | ui->status->repaint();
133 | if (results) {
134 | ui->page->setEnabled(results > result_limit);
135 | maxpage = (results - 1) / result_limit + 1;
136 | curpage = 1;
137 | ui->page->setRange(1, (results - 1) / result_limit + 1);
138 | ui->page->setValue(1);
139 | ui->prev->setEnabled(false);
140 | ui->next->setEnabled(maxpage > 1);
141 | connect(ui->page, &QSpinBox::editingFinished, [&]() {
142 | this->movePage(ui->page->value());
143 | });
144 | if (!query.exec("select * from gallery " + genWhere() + " order by " + by + order + QString::asprintf(" limit %d;", result_limit))) {
145 | QMessageBox::critical(this, "EX R.I.P.", "搜索失败:" + query.lastError().text());
146 | exit(0);
147 | }
148 | while (query.next()) {
149 | QString title;
150 | if (query.value(3).toString().length() > 0) {
151 | title = query.value(3).toString();
152 | } else {
153 | title = query.value(2).toString();
154 | }
155 | ui->result->addItem(title);
156 | }
157 | connect(ui->result, &QListWidget::currentRowChanged, this, &MainWindow::updateInfo);
158 | } else {
159 | maxpage = 1;
160 | curpage = 1;
161 | ui->page->setRange(1, 1);
162 | ui->page->setValue(1);
163 | ui->page->setEnabled(false);
164 | ui->prev->setEnabled(false);
165 | ui->next->setEnabled(false);
166 | }
167 | updateInfo(-1);
168 | message = QString::asprintf("结果共计%d条,%d页。", results, maxpage);
169 | ui->status->showMessage(message);
170 | });
171 | }
172 |
173 | MainWindow::~MainWindow() {
174 | delete ui;
175 | }
176 |
177 | QString MainWindow::genWhere() {
178 | auto tags = parseTag(ui->tag->text()
179 | .replace("/", "//")
180 | .replace("'", "''")
181 | .replace("[", "/[")
182 | .replace("]", "/]")
183 | .replace("%", "/%")
184 | .replace("&", "/&")
185 | .replace("_", "/_")
186 | .replace("(", "/(")
187 | .replace(")", "/)"));
188 | QList likes;
189 | for (const QString& k: {"artist", "group", "parody", "language", "character", "female", "male"}) {
190 | if (tags[k].size()) {
191 | QString rst = "%";
192 | for (const QString& v: tags[k]) {
193 | rst += v + "%";
194 | }
195 | likes.push_back(k + " like '" + rst + "' escape '/'");
196 | }
197 | }
198 | if (tags["misc"].size()) {
199 | QString rst = "%";
200 | for (const QString& v: tags["misc"]) {
201 | rst += v + "%";
202 | }
203 | likes.push_back("rest like '" + rst + "' escape '/'");
204 | }
205 | if (tags["category"].size()) {
206 | QList categories;
207 | for (QString category: tags["category"]) {
208 | category = category.toLower();
209 | category[0] = category[0].toUpper();
210 | categories.push_back("category = '" + category + "'");
211 | }
212 | likes.push_back("( " + categories.join(" or ") + " )");
213 | }
214 | if (tags["rating"].size()) {
215 | QList ratingreq;
216 | for (QString rating: tags["rating"]) {
217 | ratingreq.push_back("rating " + rating);
218 | }
219 | likes.push_back("( " + ratingreq.join(" and ") + " )");
220 | }
221 | if (tags["id"].size()) {
222 | QList idreq;
223 | for (QString id: tags["id"]) {
224 | idreq.push_back("id " + id);
225 | }
226 | likes.push_back("( " + idreq.join(" and ") + " )");
227 | }
228 | QString tag = likes.join(" and ");
229 | QString tit = "";
230 | if (ui->query->text().length()) {
231 | QString s = ui->query->text()
232 | .replace("/", "//")
233 | .replace("'", "''")
234 | .replace("[", "/[")
235 | .replace("]", "/]")
236 | .replace("%", "/%")
237 | .replace("&", "/&")
238 | .replace("_", "/_")
239 | .replace("(", "/(")
240 | .replace(")", "/)");
241 | tit = "title like '%" + s + "%' escape '/' or title_jpn like '%" + s + "%' escape '/'";
242 | }
243 | return tag.length() ? (tit.length() ? " where ( " + tit + " ) and ( " + tag + " )" : " where " + tag) : (tit.length() ? " where " + tit : "");
244 | }
245 |
246 | QByteArray decryption(QByteArray data) {
247 | int len = data.length();
248 | int blocks = len >> 4;
249 | unsigned char* ptr = reinterpret_cast(data.data());
250 | CryptoPP::AES::Decryption aesDecryption(reinterpret_cast(thumb_aes_key.data()), 16);
251 | while (blocks--) {
252 | aesDecryption.ProcessBlock(ptr);
253 | ptr += 16;
254 | }
255 | data.chop(*--ptr);
256 | return data;
257 | }
258 |
259 | void MainWindow::updateInfo(int r) {
260 | auto div = [](const QString& raw) -> QList {
261 | if (raw.length() == 0) {
262 | return QList();
263 | } else {
264 | auto rst = raw.mid(2, raw.length() - 4).split("', '");
265 | std::sort(rst.begin(), rst.end());
266 | return std::move(rst);
267 | }
268 | };
269 | if (reply) {
270 | reply->disconnect();
271 | reply->abort();
272 | reply->deleteLater();
273 | reply = nullptr;
274 | }
275 | if (r == -1) {
276 | ui->id->setText("0");
277 | ui->title->setText("EX R.I.P.");
278 | ui->titlejp->setText("EX R.I.P.");
279 | ui->category->setText("Misc");
280 | ui->uploader->setText("nekosu");
281 | ui->posted->setText("2019/7/26");
282 | ui->rating->setText("5.00");
283 | ui->artist->setText("Tenboro");
284 | ui->group->setText("Sad Panda");
285 | ui->parody->setText("");
286 | ui->language->setText("chinese translated");
287 | ui->misc->setText("");
288 | ui->female->clear();
289 | ui->male->clear();
290 | ui->character->clear();
291 | ui->urlF->setText("e-hentai");
292 | ui->urlB->setText("exhentai");
293 | ui->thumb->setPixmap(QPixmap(":/ex.jpg"));
294 | } else {
295 | query.seek(r);
296 | ui->id->setText(query.value(0).toString());
297 | ui->title->setText(query.value(2).toString());
298 | ui->title->setCursorPosition(0);
299 | ui->titlejp->setText(query.value(3).toString());
300 | ui->titlejp->setCursorPosition(0);
301 | ui->category->setText(query.value(4).toString());
302 | ui->uploader->setText(query.value(5).toString());
303 | QDate tm = QDateTime().fromTime_t(query.value(6).toString().toULong()).date();
304 | ui->posted->setText(QString::asprintf("%d/%d/%d", tm.year(), tm.month(), tm.day()));
305 | ui->rating->setText(query.value(14).toString());
306 | ui->artist->setText(div(query.value(15).toString()).join(", "));
307 | ui->group->setText(div(query.value(16).toString()).join(", "));
308 | ui->parody->setText(div(query.value(17).toString()).join(", "));
309 | QList lang = div(query.value(21).toString());
310 | if (lang.length() == 0) {
311 | ui->language->setText("unknown");
312 | } else if (lang.length() == 1) {
313 | ui->language->setText(lang[0]);
314 | } else {
315 | ui->language->setText(lang[lang[0] == "translated"] + " translated");
316 | }
317 | ui->misc->setText(div(query.value(23).toString()).join(", "));
318 | ui->character->clear();
319 | ui->character->addItems(div(query.value(18).toString()));
320 | ui->female->clear();
321 | ui->female->addItems(div(query.value(19).toString()));
322 | ui->male->clear();
323 | ui->male->addItems(div(query.value(20).toString()));
324 | ui->urlF->setText("e-hentai");
325 | ui->urlB->setText("exhentai");
326 | if (thumb_enable) {
327 | int id = query.value(0).toString().toInt();
328 | if (!QDir("cache").exists()) {
329 | QDir().mkdir("cache");
330 | }
331 | QString cache = QString("cache") + QDir::separator() + QString::asprintf("%d", id);
332 | if (!QFile::exists(cache)) {
333 | ui->thumb->setPixmap(QPixmap(":/ex.jpg"));
334 | QString url = thumb_prefix + QString::asprintf("%d", id) + thumb_suffix;
335 | reply = manager.get(QNetworkRequest(QUrl(url)));
336 | ui->status->showMessage(message + "正在下载封面。");
337 | ui->status->repaint();
338 | connect(reply, &QNetworkReply::finished, [&]() {
339 | auto err = reply->error();
340 | if (err) {
341 | QMessageBox::warning(this, "EHentaiSearch", "无法加载图片 " + reply->errorString());
342 | } else {
343 | QByteArray data = reply->readAll();
344 | reply->disconnect();
345 | reply->deleteLater();
346 | reply = nullptr;
347 | QString cache = QString("cache") + QDir::separator() + QString::asprintf("%d", query.value(0).toString().toInt());
348 | QFile file(cache);
349 | file.open(QIODevice::WriteOnly);
350 | file.write(data);
351 | file.close();
352 | updateThumb(data);
353 | }
354 | ui->status->showMessage(message);
355 | ui->status->repaint();
356 | });
357 | } else {
358 | QFile file(cache);
359 | file.open(QIODevice::ReadOnly);
360 | updateThumb(file.readAll());
361 | file.close();
362 | }
363 | }
364 | }
365 | }
366 |
367 | void MainWindow::updateThumb(const QByteArray& data) {
368 | QPixmap pixmap;
369 | pixmap.loadFromData(decryption(data));
370 | int w = pixmap.width(), h = pixmap.height();
371 | if (w >= h && w > 400) {
372 | pixmap.scaledToWidth(400);
373 | } else if (h > w && h > 400) {
374 | pixmap.scaledToHeight(400);
375 | }
376 | ui->thumb->setPixmap(pixmap);
377 | }
378 |
379 | void MainWindow::movePage(int p) {
380 | curpage = p;
381 | ui->result->disconnect();
382 | ui->result->clear();
383 | if (!query.exec("select * from gallery " + genWhere() + " order by " + by + order + QString::asprintf(" limit %d offset ", result_limit) + QString::asprintf("%d", (p - 1) * result_limit) + ";")) {
384 | QMessageBox::critical(this, "EX R.I.P.", "搜索失败:" + query.lastError().text());
385 | exit(0);
386 | }
387 | while (query.next()) {
388 | QString title;
389 | if (query.value(3).toString().length() > 0) {
390 | title = query.value(3).toString();
391 | } else {
392 | title = query.value(2).toString();
393 | }
394 | ui->result->addItem(title);
395 | }
396 | connect(ui->result, &QListWidget::currentRowChanged, this, &MainWindow::updateInfo);
397 | updateInfo(-1);
398 | }
399 |
--------------------------------------------------------------------------------
/mainwindow.h:
--------------------------------------------------------------------------------
1 | #ifndef MAINWINDOW_H
2 | #define MAINWINDOW_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | namespace Ui {
12 | class MainWindow;
13 | }
14 |
15 | class MainWindow : public QMainWindow {
16 | Q_OBJECT
17 |
18 | public:
19 | explicit MainWindow(QWidget *parent = nullptr);
20 | ~MainWindow();
21 |
22 | void updateInfo(int r);
23 | void movePage(int p);
24 | QString genWhere();
25 | void updateThumb(const QByteArray& data);
26 |
27 | private:
28 | Ui::MainWindow *ui;
29 | int curpage;
30 | int maxpage;
31 | QSqlDatabase database;
32 | QSqlQuery query;
33 | QNetworkAccessManager manager;
34 | QNetworkReply* reply;
35 | QString message;
36 | QString order;
37 | QString by;
38 | QButtonGroup* ordergroup;
39 | QButtonGroup* bygroup;
40 | };
41 |
42 | #endif // MAINWINDOW_H
43 |
--------------------------------------------------------------------------------
/mainwindow.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 | EHentaiSearch
7 |
8 |
9 |
10 | -
11 |
12 |
-
13 |
14 |
-
15 |
16 |
17 | 标题
18 |
19 |
20 |
21 | -
22 |
23 |
24 | -
25 |
26 |
27 | 标签
28 |
29 |
30 |
31 | -
32 |
33 |
34 |
35 |
36 | -
37 |
38 |
-
39 |
40 |
41 | 时间
42 |
43 |
44 | false
45 |
46 |
47 |
48 | -
49 |
50 |
51 | 评分
52 |
53 |
54 | false
55 |
56 |
57 |
58 |
59 |
60 | -
61 |
62 |
-
63 |
64 |
65 | 高->低
66 |
67 |
68 | false
69 |
70 |
71 |
72 | -
73 |
74 |
75 | 低->高
76 |
77 |
78 | false
79 |
80 |
81 |
82 |
83 |
84 | -
85 |
86 |
-
87 |
88 |
89 | 搜索
90 |
91 |
92 |
93 | -
94 |
95 |
-
96 |
97 |
98 | false
99 |
100 |
101 | <
102 |
103 |
104 |
105 | -
106 |
107 |
108 | false
109 |
110 |
111 |
112 | 0
113 | 0
114 |
115 |
116 |
117 |
118 | 50
119 | 0
120 |
121 |
122 |
123 | QAbstractSpinBox::NoButtons
124 |
125 |
126 | 1
127 |
128 |
129 | 1
130 |
131 |
132 |
133 | -
134 |
135 |
136 | false
137 |
138 |
139 | >
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 | -
150 |
151 |
152 | Qt::Horizontal
153 |
154 |
155 |
156 | Qt::ScrollBarAlwaysOn
157 |
158 |
159 | QAbstractItemView::NoEditTriggers
160 |
161 |
162 |
163 |
164 |
165 | 5
166 |
167 |
168 | 0
169 |
170 |
171 | 0
172 |
173 |
-
174 |
175 |
-
176 |
177 |
178 | 0
179 |
180 |
181 | true
182 |
183 |
184 |
185 | -
186 |
187 |
188 | 编号
189 |
190 |
191 |
192 | -
193 |
194 |
195 | 英文标题
196 |
197 |
198 |
199 | -
200 |
201 |
202 | EX R.I.P.
203 |
204 |
205 | true
206 |
207 |
208 |
209 | -
210 |
211 |
212 | 日文标题
213 |
214 |
215 |
216 | -
217 |
218 |
219 | EX R.I.P.
220 |
221 |
222 | true
223 |
224 |
225 |
226 |
227 |
228 | -
229 |
230 |
231 | 0
232 |
233 |
-
234 |
235 |
-
236 |
237 |
-
238 |
239 |
240 | 类别
241 |
242 |
243 |
244 | -
245 |
246 |
247 | Misc
248 |
249 |
250 |
251 | -
252 |
253 |
254 | 上传者
255 |
256 |
257 |
258 | -
259 |
260 |
261 | nekosu
262 |
263 |
264 |
265 | -
266 |
267 |
268 | 上传时间
269 |
270 |
271 |
272 | -
273 |
274 |
275 | 2019/7/26
276 |
277 |
278 |
279 | -
280 |
281 |
282 | 评分
283 |
284 |
285 |
286 | -
287 |
288 |
289 | 5.00
290 |
291 |
292 |
293 | -
294 |
295 |
296 | 艺术家
297 |
298 |
299 |
300 | -
301 |
302 |
303 | Tenboro
304 |
305 |
306 | true
307 |
308 |
309 |
310 | -
311 |
312 |
313 | 组织
314 |
315 |
316 |
317 | -
318 |
319 |
320 | Sad Panda
321 |
322 |
323 | true
324 |
325 |
326 |
327 | -
328 |
329 |
330 | 原作
331 |
332 |
333 |
334 | -
335 |
336 |
337 |
338 |
339 |
340 | true
341 |
342 |
343 |
344 | -
345 |
346 |
347 | 语言
348 |
349 |
350 |
351 | -
352 |
353 |
354 | chinese translated
355 |
356 |
357 | true
358 |
359 |
360 |
361 | -
362 |
363 |
364 | 杂项标签
365 |
366 |
367 |
368 | -
369 |
370 |
371 |
372 |
373 |
374 | true
375 |
376 |
377 |
378 | -
379 |
380 |
381 | 链接
382 |
383 |
384 |
385 | -
386 |
387 |
-
388 |
389 |
390 | <a href="https://e-hentai.org">e-hentai</a>
391 |
392 |
393 | true
394 |
395 |
396 |
397 | -
398 |
399 |
400 | <a href="https://exhentai.org">exhentai</a>
401 |
402 |
403 | true
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 | -
412 |
413 |
-
414 |
415 |
416 | 女性
417 |
418 |
419 |
420 | 1
421 |
422 |
423 | 1
424 |
425 |
426 | 1
427 |
428 |
429 | 1
430 |
431 |
-
432 |
433 |
434 | QAbstractItemView::NoEditTriggers
435 |
436 |
437 |
438 |
439 |
440 |
441 | -
442 |
443 |
444 | 男性
445 |
446 |
447 |
448 | 1
449 |
450 |
451 | 1
452 |
453 |
454 | 1
455 |
456 |
457 | 1
458 |
459 |
-
460 |
461 |
462 | QAbstractItemView::NoEditTriggers
463 |
464 |
465 |
466 |
467 |
468 |
469 | -
470 |
471 |
472 | 角色
473 |
474 |
475 |
476 | 1
477 |
478 |
479 | 1
480 |
481 |
482 | 1
483 |
484 |
485 | 1
486 |
487 |
-
488 |
489 |
490 | QAbstractItemView::NoEditTriggers
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 | -
502 |
503 |
504 |
505 | 0
506 | 0
507 |
508 |
509 |
510 | :/ex.jpg
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
--------------------------------------------------------------------------------
/resource.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | ex.jpg
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ex-rip/EHentaiSearch/422d43dfcb975384a0282895e102b15586095c9d/ui.png
--------------------------------------------------------------------------------