├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ ├── build_macos.yml
│ └── build_on_dispatch.yml
├── .gitignore
├── Compile_UI.bat
├── MarkdownView.py
├── OKP.ui
├── OKPLogic.py
├── OKPUI.py
├── ProcessWindow.py
├── README.md
├── WarningDialog.py
├── WarningDialog.ui
├── WebHelper.py
├── image
├── Console.jpg
├── Home01.jpg
├── ProfileManager01.jpg
├── ProfileManager02.jpg
├── ProfileManager03.jpg
├── Proxy.jpg
├── acgnx.jpg
└── login.jpg
├── main.py
├── make.bat
├── requirements.txt
├── test.py
├── venv
└── Lib
│ └── site-packages
│ ├── html2bbcode-2.3.3-py3.11.egg-info
│ ├── PKG-INFO
│ ├── SOURCES.txt
│ ├── dependency_links.txt
│ ├── installed-files.txt
│ └── top_level.txt
│ ├── html2bbcode
│ ├── __init__.py
│ ├── data
│ │ └── defaults.conf
│ └── parser.py
│ ├── html2phpbbcode-0.1.4.dist-info
│ ├── INSTALLER
│ ├── METADATA
│ ├── RECORD
│ ├── REQUESTED
│ ├── WHEEL
│ └── top_level.txt
│ └── html2phpbbcode
│ ├── __init__.py
│ ├── data
│ └── defaults.conf
│ ├── parser.py
│ └── validators.py
└── versionfile.rc
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Environment:**
27 | - OS: [e.g. iOS]
28 | - Version [e.g. 22]
29 |
30 | **Additional context**
31 | Add any other context about the problem here.
32 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/build_macos.yml:
--------------------------------------------------------------------------------
1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
3 |
4 | name: Build MacOS Package
5 |
6 | on:
7 | workflow_dispatch:
8 | #branches: [ "master" ]
9 |
10 | permissions:
11 | contents: read
12 |
13 | jobs:
14 | Build_MacOS:
15 | runs-on: macos-latest
16 | steps:
17 | - uses: actions/checkout@v3
18 | - name: Set up Python 3.11
19 | uses: actions/setup-python@v3
20 | with:
21 | python-version: "3.11"
22 | - name: Install dependencies
23 | run: |
24 | python -m venv venv
25 | source ./venv/bin/activate
26 | python -m pip install --upgrade pip
27 | pip install -r requirements.txt
28 | - name: Build with Pyinstaller
29 | run: |
30 | source ./venv/bin/activate
31 | pyinstaller --onefile --noconsole main.py -p ./venv/Lib/site-packages --collect-all html2phpbbcode --osx-bundle-identifier com.AmusementClub.Publish.OKPGUI -n OKPGUI.app
32 | # - name: Sign binary
33 | # env:
34 | # WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
35 | # WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
36 | # run: |
37 | # New-Item -ItemType directory -Path certificate
38 | # Set-Content -Path certificate/tempCert.txt -Value $env:WINDOWS_CERTIFICATE
39 | # certutil -decode certificate/tempCert.txt certificate/certificate.pfx
40 | # Remove-Item -path certificate -include tempCert.txt
41 | # & "C:/Program Files (x86)/Windows Kits/10/bin/10.0.17763.0/x86/signtool.exe" sign /v /f certificate/certificate.pfx /p $env:WINDOWS_CERTIFICATE_PASSWORD /t http://timestamp.digicert.com/ /fd SHA256 dist/OKPGUI.exe
42 | - name: Upload artifacts
43 | uses: actions/upload-artifact@v3
44 | with:
45 | name: MacOS-package
46 | path: dist/OKPGUI.app
47 |
48 |
--------------------------------------------------------------------------------
/.github/workflows/build_on_dispatch.yml:
--------------------------------------------------------------------------------
1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
3 |
4 | name: Build Package
5 |
6 | on:
7 | workflow_dispatch:
8 | #branches: [ "master" ]
9 |
10 | permissions:
11 | contents: read
12 |
13 | jobs:
14 | build:
15 |
16 | runs-on: windows-latest
17 |
18 | steps:
19 | - uses: actions/checkout@v3
20 | - name: Set up Python 3.11
21 | uses: actions/setup-python@v3
22 | with:
23 | python-version: "3.11"
24 | - name: Install dependencies
25 | run: |
26 | python -m venv venv
27 | & ./venv/Scripts/Activate.ps1
28 | python -m pip install --upgrade pip
29 | pip install -r requirements.txt
30 | - name: Build with Pyinstaller
31 | run: |
32 | & ./venv/Scripts/Activate.ps1
33 | pyinstaller --onefile --noconsole main.py --collect-all html2phpbbcode --version-file versionfile.rc -n OKPGUI.exe
34 | - name: Sign binary
35 | env:
36 | WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
37 | WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
38 | run: |
39 | New-Item -ItemType directory -Path certificate
40 | Set-Content -Path certificate/tempCert.txt -Value $env:WINDOWS_CERTIFICATE
41 | certutil -decode certificate/tempCert.txt certificate/certificate.pfx
42 | Remove-Item -path certificate -include tempCert.txt
43 | & "C:/Program Files (x86)/Windows Kits/10/bin/10.0.17763.0/x86/signtool.exe" sign /v /f certificate/certificate.pfx /p $env:WINDOWS_CERTIFICATE_PASSWORD /t http://timestamp.digicert.com/ /fd SHA256 dist/OKPGUI.exe
44 | - name: Upload artifacts
45 | uses: actions/upload-artifact@v3
46 | with:
47 | name: windows-package
48 | path: dist/OKPGUI.exe
49 |
50 |
51 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #################
2 | ## Eclipse
3 | #################
4 |
5 | *.pydevproject
6 | .project
7 | .metadata
8 | bin/
9 | tmp/
10 | *.tmp
11 | *.bak
12 | *.swp
13 | *~.nib
14 | local.properties
15 | .classpath
16 | .settings/
17 | .loadpath
18 |
19 | # External tool builders
20 | .externalToolBuilders/
21 |
22 | # Locally stored "Eclipse launch configurations"
23 | *.launch
24 |
25 | # CDT-specific
26 | .cproject
27 |
28 | # PDT-specific
29 | .buildpath
30 |
31 |
32 | #################
33 | ## Visual Studio
34 | #################
35 |
36 | ## Ignore Visual Studio temporary files, build results, and
37 | ## files generated by popular Visual Studio add-ons.
38 |
39 | # User-specific files
40 | *.suo
41 | *.user
42 | *.sln.docstates
43 |
44 | # Build results
45 |
46 | [Dd]ebug/
47 | [Rr]elease/
48 | x64/
49 | build/
50 | [Bb]in/
51 | [Oo]bj/
52 |
53 | # MSTest test Results
54 | [Tt]est[Rr]esult*/
55 | [Bb]uild[Ll]og.*
56 |
57 | *_i.c
58 | *_p.c
59 | *.ilk
60 | *.meta
61 | *.obj
62 | *.pch
63 | *.pdb
64 | *.pgc
65 | *.pgd
66 | *.rsp
67 | *.sbr
68 | *.tlb
69 | *.tli
70 | *.tlh
71 | *.tmp
72 | *.tmp_proj
73 | *.log
74 | *.vspscc
75 | *.vssscc
76 | .builds
77 | *.pidb
78 | *.log
79 | *.scc
80 |
81 | # Visual C++ cache files
82 | ipch/
83 | *.aps
84 | *.ncb
85 | *.opensdf
86 | *.sdf
87 | *.cachefile
88 |
89 | # Visual Studio profiler
90 | *.psess
91 | *.vsp
92 | *.vspx
93 |
94 | # Guidance Automation Toolkit
95 | *.gpState
96 |
97 | # ReSharper is a .NET coding add-in
98 | _ReSharper*/
99 | *.[Rr]e[Ss]harper
100 |
101 | # TeamCity is a build add-in
102 | _TeamCity*
103 |
104 | # DotCover is a Code Coverage Tool
105 | *.dotCover
106 |
107 | # NCrunch
108 | *.ncrunch*
109 | .*crunch*.local.xml
110 |
111 | # Installshield output folder
112 | [Ee]xpress/
113 |
114 | # DocProject is a documentation generator add-in
115 | DocProject/buildhelp/
116 | DocProject/Help/*.HxT
117 | DocProject/Help/*.HxC
118 | DocProject/Help/*.hhc
119 | DocProject/Help/*.hhk
120 | DocProject/Help/*.hhp
121 | DocProject/Help/Html2
122 | DocProject/Help/html
123 |
124 | # Click-Once directory
125 | publish/
126 |
127 | # Publish Web Output
128 | *.Publish.xml
129 | *.pubxml
130 |
131 | # NuGet Packages Directory
132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
133 | #packages/
134 |
135 | # Windows Azure Build Output
136 | csx
137 | *.build.csdef
138 |
139 | # Windows Store app package directory
140 | AppPackages/
141 |
142 | # Others
143 | sql/
144 | *.Cache
145 | ClientBin/
146 | [Ss]tyle[Cc]op.*
147 | ~$*
148 | *~
149 | *.dbmdl
150 | *.[Pp]ublish.xml
151 | *.pfx
152 | *.publishsettings
153 |
154 | # RIA/Silverlight projects
155 | Generated_Code/
156 |
157 | # Backup & report files from converting an old project file to a newer
158 | # Visual Studio version. Backup files are not needed, because we have git ;-)
159 | _UpgradeReport_Files/
160 | Backup*/
161 | UpgradeLog*.XML
162 | UpgradeLog*.htm
163 |
164 | # SQL Server files
165 | App_Data/*.mdf
166 | App_Data/*.ldf
167 |
168 | #############
169 | ## Windows detritus
170 | #############
171 |
172 | # Windows image file caches
173 | Thumbs.db
174 | ehthumbs.db
175 |
176 | # Folder config file
177 | Desktop.ini
178 |
179 | # Recycle Bin used on file shares
180 | $RECYCLE.BIN/
181 |
182 | # Mac crap
183 | .DS_Store
184 |
185 |
186 | #############
187 | ## Python
188 | #############
189 |
190 | *.py[co]
191 |
192 | # Packages
193 | *.egg
194 | *.egg-info
195 | dist/
196 | build/
197 | eggs/
198 | parts/
199 | var/
200 | sdist/
201 | develop-eggs/
202 | .installed.cfg
203 | venv/*
204 | !venv/Lib/
205 | venv/Lib/site-packages/*
206 | !venv/Lib/site-packages/HTMLParser.py
207 | !venv/Lib/site-packages/markupbase.py
208 | !venv/lib/site-packages/html2bbcode/
209 | !venv/lib/site-packages/html2bbcode-2.3.3-py3.11.egg-info/
210 | !venv/lib/site-packages/html2phpbbcode/
211 | !venv/lib/site-packages/html2phpbbcode-0.1.4.dist-info/
212 |
213 | # Installer logs
214 | pip-log.txt
215 |
216 | # Unit test / coverage reports
217 | .coverage
218 | .tox
219 |
220 | #Translations
221 | *.mo
222 |
223 | #Mr Developer
224 | .mr.developer.cfg
225 |
226 | #OKP.core
227 | config/
228 | OKP.Core.exe
229 | OKP.Core.pdb
230 |
231 | template.toml
232 | cookies.txt
233 | *.spec
234 | log*.txt
235 |
236 | okpgui_config.yml
237 | okpgui_profile.yml
238 |
239 | Traceback_*
240 | *.crt
--------------------------------------------------------------------------------
/Compile_UI.bat:
--------------------------------------------------------------------------------
1 | pyuic6 -x OKP.ui -o OKPUI.py
2 | pyuic6 -x WarningDialog.ui -o WarningDialog.py
--------------------------------------------------------------------------------
/MarkdownView.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | from PyQt6.QtCore import QUrl, QByteArray, QSize
4 | from PyQt6.QtWebEngineWidgets import QWebEngineView
5 | from PyQt6.QtWebEngineCore import QWebEngineProfile
6 | from PyQt6.QtWidgets import QApplication, QTextEdit, QPushButton, QToolBar, QMainWindow, QDialog, QWidget, QVBoxLayout
7 | from PyQt6.QtNetwork import QNetworkCookie, QNetworkCookieJar
8 | from PyQt6.QtGui import QAction
9 | from urllib import parse
10 |
11 | class MarkdownViewWindow(QWidget):
12 | def __init__(self, html, parentWindow, *args, **kwargs):
13 | super(QWidget, self).__init__(*args, **kwargs)
14 |
15 | self.resize(1200, 1080)
16 | self.parentWindow = parentWindow
17 | self.setWindowTitle("Preview")
18 |
19 | self.browser = QWebEngineView()
20 | self.browser.setHtml(html)
21 |
22 | vbox = QVBoxLayout(self)
23 | vbox.addWidget(self.browser)
24 | self.setLayout(vbox)
25 |
--------------------------------------------------------------------------------
/OKP.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 609
10 | 950
11 |
12 |
13 |
14 |
15 | 0
16 | 0
17 |
18 |
19 |
20 |
21 | 600
22 | 840
23 |
24 |
25 |
26 |
27 | Microsoft YaHei UI
28 | 12
29 |
30 |
31 |
32 | OKPGUI by AmusementClub
33 |
34 |
35 |
36 |
37 | 0
38 | 0
39 |
40 |
41 |
42 | -
43 |
44 |
45 |
46 | 0
47 | 0
48 |
49 |
50 |
51 |
52 | 594
53 | 800
54 |
55 |
56 |
57 |
58 | 12
59 | PreferDefault
60 |
61 |
62 |
63 |
64 |
65 |
66 | 0
67 |
68 |
69 |
70 |
71 | 0
72 | 0
73 |
74 |
75 |
76 |
77 | 12
78 |
79 |
80 |
81 | 主页
82 |
83 |
84 |
-
85 |
86 |
87 |
88 | 0
89 | 0
90 |
91 |
92 |
93 | false
94 |
95 |
96 | color: rgb(255, 0, 30);
97 | font: 12pt "Microsoft YaHei UI";
98 |
99 |
100 | 删除模板
101 |
102 |
103 | false
104 |
105 |
106 |
107 | -
108 |
109 |
110 | Qt::Horizontal
111 |
112 |
113 |
114 | 40
115 | 20
116 |
117 |
118 |
119 |
120 | -
121 |
122 |
123 |
124 | 0
125 | 0
126 |
127 |
128 |
129 |
130 | 9
131 |
132 |
133 |
134 | color:rgb(75, 75, 75)
135 |
136 |
137 |
138 | 请使用 Markdown 编写
139 |
140 |
141 |
142 | -
143 |
144 |
145 |
146 | 0
147 | 0
148 |
149 |
150 |
151 |
152 | -
153 |
154 |
155 | QFormLayout::ExpandingFieldsGrow
156 |
157 |
-
158 |
159 |
160 |
161 | 0
162 | 0
163 |
164 |
165 |
166 | 集数匹配
167 |
168 |
169 |
170 | -
171 |
172 |
173 |
174 | 0
175 | 0
176 |
177 |
178 |
179 |
180 | 0
181 | 30
182 |
183 |
184 |
185 |
186 | 10
187 |
188 |
189 |
190 | <html><head/><body><p>测试</p></body></html>
191 |
192 |
193 | <html><head/><body><p>测试</p></body></html>
194 |
195 |
196 | 在文件名集数部分使用 <ep> 替换,分辨率用 <res> 替换
197 |
198 |
199 |
200 | -
201 |
202 |
203 |
204 | 0
205 | 0
206 |
207 |
208 |
209 | 标题匹配
210 |
211 |
212 |
213 | -
214 |
215 |
216 |
217 | 0
218 | 0
219 |
220 |
221 |
222 |
223 | 0
224 | 30
225 |
226 |
227 |
228 |
229 | 10
230 |
231 |
232 |
233 |
234 | -
235 |
236 |
237 |
238 | 0
239 | 0
240 |
241 |
242 |
243 | 标题
244 |
245 |
246 |
247 | -
248 |
249 |
250 |
251 | 0
252 | 0
253 |
254 |
255 |
256 |
257 | 0
258 | 30
259 |
260 |
261 |
262 |
263 | 10
264 |
265 |
266 |
267 |
268 | -
269 |
270 |
271 |
272 | 0
273 | 0
274 |
275 |
276 |
277 | 海报链接
278 |
279 |
280 |
281 | -
282 |
283 |
284 |
285 | 0
286 | 0
287 |
288 |
289 |
290 |
291 | 0
292 | 30
293 |
294 |
295 |
296 |
297 | 10
298 |
299 |
300 |
301 | For dmhy.org
302 |
303 |
304 |
305 | -
306 |
307 |
308 |
309 | 0
310 | 0
311 |
312 |
313 |
314 | 关于
315 |
316 |
317 |
318 | -
319 |
320 |
321 |
322 | 0
323 | 0
324 |
325 |
326 |
327 |
328 | 0
329 | 30
330 |
331 |
332 |
333 |
334 | 10
335 |
336 |
337 |
338 | For nyaa.si
339 |
340 |
341 |
342 | -
343 |
344 |
345 |
346 | 0
347 | 0
348 |
349 |
350 |
351 |
352 | 0
353 | 30
354 |
355 |
356 |
357 |
358 | 10
359 |
360 |
361 |
362 | 输入标签,以英文逗号分隔,可用标签可参考 “如何使用 tags?”
363 |
364 |
365 |
366 |
367 |
368 |
369 | -
370 |
371 |
372 |
373 | 0
374 | 0
375 |
376 |
377 |
378 | Tags
379 |
380 |
381 |
382 |
383 |
384 | -
385 |
386 |
-
387 |
388 |
389 |
390 | 0
391 | 0
392 |
393 |
394 |
395 | 模板名称
396 |
397 |
398 |
399 | -
400 |
401 |
402 |
403 | 0
404 | 0
405 |
406 |
407 |
408 |
409 | -
410 |
411 |
412 |
413 | 0
414 | 0
415 |
416 |
417 |
418 | 选择模板
419 |
420 |
421 |
422 | -
423 |
424 |
425 |
426 | 0
427 | 0
428 |
429 |
430 |
431 | <html><head/><body><p>第一次使用请选择「新模板」</p></body></html>
432 |
433 |
-
434 |
435 |
436 |
437 |
438 |
439 |
440 | -
441 |
442 |
443 |
444 | 0
445 | 0
446 |
447 |
448 |
449 | 种子文件
450 |
451 |
452 |
453 | -
454 |
455 |
456 |
457 | 0
458 | 0
459 |
460 |
461 |
462 |
463 | 0
464 | 30
465 |
466 |
467 |
468 |
469 | 10
470 |
471 |
472 |
473 | true
474 |
475 |
476 | 可直接 .torrent 文件拖放到此处
477 |
478 |
479 |
480 |
481 |
482 | -
483 |
484 |
485 |
486 | 0
487 | 0
488 |
489 |
490 |
491 | 保存模板
492 |
493 |
494 |
495 | -
496 |
497 |
498 |
499 | 0
500 | 0
501 |
502 |
503 |
504 | 内容
505 |
506 |
507 |
508 | -
509 |
510 |
511 | Qt::Horizontal
512 |
513 |
514 |
515 | 40
516 | 20
517 |
518 |
519 |
520 |
521 | -
522 |
523 |
524 |
525 | 0
526 | 0
527 |
528 |
529 |
530 | 浏览
531 |
532 |
533 |
534 | -
535 |
536 |
537 |
538 | 0
539 | 150
540 |
541 |
542 |
543 | true
544 |
545 |
546 | true
547 |
548 |
549 |
550 | Files
551 |
552 |
553 |
554 |
555 | Size
556 |
557 |
558 |
559 |
560 | -
561 |
562 |
563 |
564 | 0
565 | 0
566 |
567 |
568 |
569 |
570 | 20
571 |
572 |
573 |
574 | One Key Publish!
575 |
576 |
577 |
578 | -
579 |
580 |
581 | QFormLayout::ExpandingFieldsGrow
582 |
583 |
-
584 |
585 |
586 |
587 | 0
588 | 0
589 |
590 |
591 |
592 | 选择身份
593 |
594 |
595 |
596 | -
597 |
598 |
599 |
600 | 0
601 | 0
602 |
603 |
604 |
605 |
606 | -
607 |
608 |
609 |
610 | 0
611 | 0
612 |
613 |
614 |
615 | dmhy
616 |
617 |
618 |
619 | -
620 |
621 |
622 |
623 | 0
624 | 0
625 |
626 |
627 |
628 | bangumi
629 |
630 |
631 |
632 | -
633 |
634 |
635 |
636 | 0
637 | 0
638 |
639 |
640 |
641 | nyaa
642 |
643 |
644 |
645 | -
646 |
647 |
648 |
649 | 0
650 | 0
651 |
652 |
653 |
654 | acg.rip
655 |
656 |
657 |
658 | -
659 |
660 |
661 |
662 | 0
663 | 0
664 |
665 |
666 |
667 | acgnx_asia
668 |
669 |
670 |
671 | -
672 |
673 |
674 |
675 | 0
676 | 0
677 |
678 |
679 |
680 | acgnx_global
681 |
682 |
683 |
684 |
685 |
686 | -
687 |
688 |
689 |
690 | 0
691 | 0
692 |
693 |
694 |
695 | 预览
696 |
697 |
698 |
699 | -
700 |
701 |
702 | <html><head/><body><p><a href="https://github.com/AmusementClub/OKP/wiki/TagsConvert"><span style=" text-decoration: underline; color:#0000ff;">如何使用 tags?</span></a></p></body></html>
703 |
704 |
705 | Qt::RichText
706 |
707 |
708 |
709 | -
710 |
711 |
712 | Qt::Horizontal
713 |
714 |
715 |
716 | 40
717 | 20
718 |
719 |
720 |
721 |
722 |
723 |
724 |
725 |
726 | 身份管理器
727 |
728 |
729 | -
730 |
731 |
-
732 |
733 |
734 | 登录发布网站
735 |
736 |
737 |
738 | -
739 |
740 |
741 | 发布组名称
742 |
743 |
744 |
745 | -
746 |
747 |
748 |
749 | 0
750 | 0
751 |
752 |
753 |
754 | dmhy
755 |
756 |
757 |
758 | -
759 |
760 |
761 | -
762 |
763 |
764 | nyaa
765 |
766 |
767 |
768 | -
769 |
770 |
771 | false
772 |
773 |
774 | false
775 |
776 |
777 |
778 | -
779 |
780 |
781 | acg.rip
782 |
783 |
784 |
785 | -
786 |
787 |
788 | -
789 |
790 |
791 | bangumi
792 |
793 |
794 |
795 | -
796 |
797 |
798 | -
799 |
800 |
801 | acgnx_asia UID
802 |
803 |
804 |
805 | -
806 |
807 |
808 | 填写 acgnx UID
809 |
810 |
811 |
812 | -
813 |
814 |
815 | 填写 acgnx asia API Token
816 |
817 |
818 |
819 | -
820 |
821 |
822 | acgnx_global UID
823 |
824 |
825 |
826 | -
827 |
828 |
829 | 填写 acgnx UID
830 |
831 |
832 |
833 | -
834 |
835 |
836 | 填写 acgnx global API Token
837 |
838 |
839 |
840 | -
841 |
842 |
843 | Token
844 |
845 |
846 |
847 | -
848 |
849 |
850 | Token
851 |
852 |
853 |
854 |
855 |
856 | -
857 |
858 |
859 | IBeamCursor
860 |
861 |
862 |
863 |
864 |
865 | QTextEdit::NoWrap
866 |
867 |
868 |
869 | -
870 |
871 |
872 | Cookies 文件:
873 |
874 |
875 |
876 | -
877 |
878 |
879 | 本页中的内容需要保存身份后才会生效。
880 |
881 |
882 |
883 | -
884 |
885 |
886 | color: rgb(255, 0, 30);
887 | font: 12pt "Microsoft YaHei UI";
888 |
889 |
890 | 删除身份
891 |
892 |
893 |
894 | -
895 |
896 |
897 | Qt::Horizontal
898 |
899 |
900 |
901 | 40
902 | 20
903 |
904 |
905 |
906 |
907 | -
908 |
909 |
910 | 保存身份
911 |
912 |
913 |
914 | -
915 |
916 |
-
917 |
918 |
919 | 选择身份
920 |
921 |
922 |
923 | -
924 |
925 |
926 | <html><head/><body><p>第一次使用请选择「新模板」</p></body></html>
927 |
928 |
-
929 |
930 | 新身份
931 |
932 |
933 |
934 |
935 | -
936 |
937 |
938 | 身份名称
939 |
940 |
941 |
942 | -
943 |
944 |
945 |
946 |
947 |
948 |
949 |
950 |
951 | 杂项
952 |
953 |
954 | -
955 |
956 |
957 | 代理类型
958 |
959 |
960 |
961 | -
962 |
963 |
-
964 |
965 | 不使用代理
966 |
967 |
968 | -
969 |
970 | HTTP
971 |
972 |
973 |
974 |
975 | -
976 |
977 |
978 | Host
979 |
980 |
981 |
982 | -
983 |
984 |
985 | http://127.0.0.1:7890
986 |
987 |
988 |
989 |
990 |
991 |
992 | -
993 |
994 |
995 | 应用
996 |
997 |
998 |
999 | -
1000 |
1001 |
1002 | true
1003 |
1004 |
1005 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
1006 | <html><head><meta name="qrichtext" content="1" /><style type="text/css">
1007 | p, li { white-space: pre-wrap; }
1008 | </style></head><body style=" font-family:'Microsoft YaHei UI'; font-size:12pt; font-weight:400; font-style:normal;">
1009 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">此软件为 <a href="https://github.com/AmusementClub/OKP"><span style=" text-decoration: underline; color:#0000ff;">OKP</span></a> 的 GUI,由<a href="https://github.com/AmusementClub"><span style=" text-decoration: underline; color:#0000ff;">娱乐部</span></a>制作,用于快速在多个 BT 资源站发布种子。</p>
1010 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">使用方法参见 GitHub 上的 <a href="https://github.com/AmusementClub/OKPGUI"><span style=" text-decoration: underline; color:#0000ff;">README</span></a>。</p>
1011 | <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;"><br /></p>
1012 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Version: 0.0.1 Alpha 内部测试版。</p>
1013 | <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;"><br /></p>
1014 | <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;"><br /></p>
1015 | <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;"><br /></p>
1016 | <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;"><br /></p>
1017 | <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;"><br /></p>
1018 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">作者:<a href="https://github.com/tastysugar"><span style=" text-decoration: underline; color:#0000ff;">tastySugar</span></a></p>
1019 | <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;"><br /></p>
1020 | <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;"><br /></p></body></html>
1021 |
1022 |
1023 | Qt::RichText
1024 |
1025 |
1026 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
1027 |
1028 |
1029 | true
1030 |
1031 |
1032 | Qt::TextBrowserInteraction
1033 |
1034 |
1035 |
1036 | -
1037 |
1038 |
1039 | 代理设置
1040 |
1041 |
1042 | Qt::AlignCenter
1043 |
1044 |
1045 |
1046 | -
1047 |
1048 |
1049 | true
1050 |
1051 |
1052 | 关于
1053 |
1054 |
1055 | Qt::AlignCenter
1056 |
1057 |
1058 |
1059 | -
1060 |
1061 |
1062 | Qt::Horizontal
1063 |
1064 |
1065 |
1066 | 40
1067 | 20
1068 |
1069 |
1070 |
1071 |
1072 | -
1073 |
1074 |
1075 | Qt::Horizontal
1076 |
1077 |
1078 |
1079 | 40
1080 | 20
1081 |
1082 |
1083 |
1084 |
1085 |
1086 |
1087 |
1088 |
1089 |
1090 |
1091 |
1092 |
1093 |
1094 |
1095 |
--------------------------------------------------------------------------------
/OKPLogic.py:
--------------------------------------------------------------------------------
1 | from PyQt6.QtCore import (
2 | QUrl,
3 | QProcess,
4 | Qt,
5 | QFileInfo,
6 | )
7 | from PyQt6.QtWidgets import (
8 | QApplication,
9 | QWidget,
10 | QMainWindow,
11 | QFileDialog,
12 | QDialog,
13 | QTreeWidgetItem,
14 | QFileIconProvider,
15 | QStyle,
16 | )
17 | import sys
18 | from OKPUI import Ui_MainWindow
19 | from WarningDialog import Ui_Dialog
20 | import yaml
21 | from pathlib import Path
22 | from WebHelper import WebEngineView
23 | import re
24 | import markdown
25 | from MarkdownView import MarkdownViewWindow
26 | import toml
27 | from html2phpbbcode.parser import HTML2PHPBBCode
28 | from collections import defaultdict
29 | import torrent_parser as tp
30 | from ProcessWindow import MyConsole
31 | import platform
32 |
33 | VERSION = "v0.1.7 Beta"
34 |
35 | CATEGORY = {
36 | 'Anime': ['Default', 'MV', 'TV', 'Movie', 'Collection', 'Raw', 'English'],
37 | 'Music': ['Default', 'Lossless', 'Lossy', 'ACG', 'Doujin', 'Pop'],
38 | 'Comic': ['Default', 'HongKong', 'Taiwan', 'Japanese', 'English'],
39 | 'Novel': ['Default', 'HongKong', 'Taiwan', 'Japanese', 'English'],
40 | 'Action': ['Default', 'Idol', 'TV', 'Movie', 'Tokusatsu', 'Show', 'Raw', 'English'],
41 | 'Picture': ['Default', 'Graphics', 'Photo'],
42 | 'Software': ['Default', 'App', 'Game']
43 | }
44 |
45 | TEMPLATE_CONFIG = Path("okpgui_config.yml")
46 | PROFILE_CONFIG = Path("okpgui_profile.yml")
47 |
48 | class OKPerror(Exception):
49 | pass
50 |
51 | class OKPMainWIndow(QMainWindow, Ui_MainWindow):
52 | def __init__(self, *args, **kwargs):
53 | QMainWindow.__init__(self, *args, **kwargs)
54 | self.setupUi(self)
55 | self.setupUi2()
56 | if not Path("OKP.Core.exe").exists():
57 | self.warning("找不到 OKP.Core.exe,请将本程序复制到 OKP.Core.exe 同目录下。")
58 | sys.exit(1)
59 |
60 | def setupUi2(self):
61 | # set title
62 | self.setWindowTitle("OKPGUI by AmusementClub " + VERSION)
63 |
64 | self.textAboutProgram.setText(f"""
65 |
66 |
69 | 此软件为 OKP 的 GUI,由娱乐部制作,用于快速在多个 BT 资源站发布种子。
70 | 使用方法参见 GitHub 上的 README。
71 |
72 | Version: {VERSION}
73 |
74 | 作者:tastySugar
75 |
76 | """)
77 |
78 | # Select torrent
79 | self.buttonBrowse.clicked.connect(self.selectTorrentFile)
80 |
81 | self.HomeTab.setAcceptDrops(True)
82 | self.tab.currentChanged.connect(self.changeTabHandler)
83 | # self.textTorrentPath.setAcceptDrops(True)
84 | self.HomeTab.dragEnterEvent = self.onDragEnterEvent
85 | self.HomeTab.dropEvent = self.onDropEvent
86 | self.HomeTab.dragLeaveEvent = self.onDragLeaveEvent
87 | self.textTorrentPath.textChanged.connect(self.loadTorrent)
88 |
89 |
90 | # Select template
91 | self.reloadProfile()
92 |
93 | self.reloadTemplate()
94 | self.updateTemplate()
95 | self.selectCookiesChangeHandler(self.menuSelectCookies.currentText())
96 |
97 | self.loadProxy()
98 |
99 | # Save / Delete template
100 | self.buttonSaveTemplate.clicked.connect(self.saveTemplate)
101 | self.buttonDeleteTemplate.clicked.connect(self.deleteTemplate)
102 |
103 |
104 | # preview markdown
105 | self.buttonPreviewMarkdown.clicked.connect(self.previewMarkdown)
106 | #self.textDescription.setMarkdown(self.textDescription.toPlainText())
107 |
108 | self.textEpPattern.textEdited.connect(self.setTitleText)
109 | self.textTitlePattern.textEdited.connect(self.setTitleText)
110 |
111 | self.menuSelectCookies.currentTextChanged.connect(self.selectCookiesChangeHandler)
112 |
113 | self.fileTree.setColumnWidth(0,450)
114 |
115 | # tab 2 login
116 | self.buttonDmhyLogin.clicked.connect(self.loginWebsite("https://share.dmhy.org/user/login"))
117 | self.buttonNyaaLogin.clicked.connect(self.loginWebsite("https://nyaa.si/login"))
118 | self.buttonAcgripLogin.clicked.connect(self.loginWebsite("https://acg.rip/users/sign_in"))
119 | self.buttonBangumiLogin.clicked.connect(self.loginWebsite("https://bangumi.moe/"))
120 |
121 | self.textNyaaName.setDisabled(True)
122 |
123 |
124 | self.buttonSaveProfile.clicked.connect(self.saveProfile)
125 | self.buttonDeleteProfile.clicked.connect(self.deleteProfile)
126 |
127 | self.menuProxyType.currentTextChanged.connect(self.onProxySelection)
128 | self.onProxySelection()
129 |
130 | self.buttonSaveProxy.clicked.connect(self.saveProxy)
131 |
132 | self.textAcgnxasiaToken.textEdited.connect(self.applyAcgnxasiaAPIToken)
133 | self.textAcgnxglobalToken.textEdited.connect(self.applyAcgnxglobalAPIToken)
134 | self.textCookies.textChanged.connect(self.onCookiesChange)
135 |
136 | # publish button
137 | self.buttonOKP.clicked.connect(self.publishRun)
138 |
139 | def changeTabHandler(self, event):
140 | if event == 1:
141 | self.reloadProfile()
142 | if event == 2:
143 | self.loadProxy()
144 |
145 | def onDragEnterEvent(self, event):
146 | if event.mimeData().hasUrls():
147 | event.accept()
148 | self.textTorrentPath.setPlaceholderText("请在此释放鼠标")
149 | else:
150 | event.ignore()
151 |
152 | def onDropEvent(self, event):
153 | self.textTorrentPath.setPlaceholderText("可直接 .torrent 文件拖放到此处")
154 | files = [u.toLocalFile() for u in event.mimeData().urls()]
155 | self.textTorrentPath.setText(files[0])
156 |
157 | def onDragLeaveEvent(self, evet):
158 | self.textTorrentPath.setPlaceholderText("可直接 .torrent 文件拖放到此处")
159 |
160 | def loadTorrent(self):
161 |
162 | def sizeof_fmt(num, suffix="B"):
163 | if num == -1:
164 | return ""
165 |
166 | for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]:
167 | if abs(num) < 1024.0:
168 | return f"{num:3.1f} {unit}{suffix}"
169 | num /= 1024.0
170 | return f"{num:.1f} Yi{suffix}"
171 |
172 | self.fileTree.clear()
173 | self.setTitleText()
174 |
175 | torrentPath = Path(self.textTorrentPath.text())
176 | try:
177 | data = tp.parse_torrent_file(torrentPath)
178 | except:
179 | return
180 |
181 |
182 | if 'files' not in data['info']:
183 | # One file torrent
184 | root = QTreeWidgetItem(self.fileTree)
185 | root.setText(0, Path(data['info']['name']).stem)
186 | root.setText(1, sizeof_fmt(data['info']['length']))
187 | file_info = QFileInfo(str(data['info']['name']))
188 | file_icon_provider = QFileIconProvider()
189 | root.setIcon(0, file_icon_provider.icon(file_info))
190 |
191 | else:
192 | # Multi file torrent
193 | data = data['info']['files']
194 | folder_icon = QApplication.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)
195 |
196 | root = QTreeWidgetItem(self.fileTree)
197 | root.setText(0, torrentPath.stem)
198 | root.setIcon(0, folder_icon)
199 | root.setExpanded(True)
200 | self.fileTree.insertTopLevelItem(0, root)
201 |
202 | d = {str(x['path']):x['length'] for x in data}
203 | d["[]"] = root
204 |
205 | longetstPath = 0
206 | for file in data:
207 | if len(file['path']) > longetstPath:
208 | longetstPath = len(file['path'])
209 |
210 | nodes = dict() # path: length, if length = -1 then it is a dir
211 |
212 | for x in range(longetstPath + 1):
213 | for path, length in d.items():
214 | path = eval(path)
215 | if len(path) > x:
216 | nodes[str(path[:x])] = -1
217 | else:
218 | nodes[str(path)] = length
219 |
220 |
221 | sorted_nodes = sorted(nodes, key=lambda x: len(eval(x)))
222 |
223 | for n in sorted_nodes:
224 | if n == "[]":
225 | continue
226 | item = QTreeWidgetItem(nodes[f'{eval(n)[:-1]}'])
227 | item.setText(0, eval(n)[-1])
228 | item.setText(1, sizeof_fmt(nodes[n]))
229 | if nodes[n] == -1:
230 | item.setIcon(0, folder_icon)
231 | else:
232 | file_info = QFileInfo(eval(n)[-1])
233 | file_icon_provider = QFileIconProvider()
234 | item.setIcon(0, file_icon_provider.icon(file_info))
235 | nodes[n] = item
236 |
237 | self.fileTree.sortByColumn(1, Qt.SortOrder.AscendingOrder)
238 | self.fileTree.setAlternatingRowColors(True)
239 |
240 |
241 |
242 |
243 |
244 | def selectTorrentFile(self):
245 | fname = QFileDialog.getOpenFileName(self, 'Open file', 'c:\\',"Torrent file v1 (*.torrent)")[0]
246 | self.textTorrentPath.setText(fname)
247 |
248 |
249 |
250 |
251 |
252 | def loadConfig(self):
253 | path = Path(TEMPLATE_CONFIG)
254 | if not path.exists():
255 | with open(TEMPLATE_CONFIG, "w", encoding='utf-8') as f:
256 | f.write('''
257 | lastUsed: 新模板
258 | proxy: http://127.0.0.1:7890
259 | proxyType: 不使用代理
260 | template:
261 | 新模板:
262 | about:
263 | checkAcgnxasia: false
264 | checkAcgnxglobal: false
265 | checkAcgrip: false
266 | checkDmhy: false
267 | checkNyaa: false
268 | description: ""
269 | epPattern: ""
270 | poster: ""
271 | profile: ""
272 | tags: "Anime"
273 | titlePattern: ""
274 | title: ""
275 | ''')
276 |
277 | with open(path, "r", encoding="utf-8") as f:
278 | self.conf = yaml.safe_load(f)
279 |
280 | def loadProxy(self):
281 | conf = defaultdict(str, self.conf)
282 | self.menuProxyType.setCurrentText(conf['proxyType'])
283 | self.textProxyHost.setText(conf['proxy'])
284 |
285 | def saveProxy(self):
286 | self.conf['proxyType'] = self.menuProxyType.currentText()
287 | self.conf['proxy'] = self.textProxyHost.text()
288 | with open(TEMPLATE_CONFIG, "w", encoding='utf-8') as file:
289 | yaml.safe_dump(self.conf, file, encoding='utf-8',allow_unicode=True)
290 |
291 |
292 | def loginWebsite(self, url):
293 | def login():
294 | self.webview = WebEngineView(url=QUrl(url),parentWindow=self)
295 | self.webview.show()
296 |
297 | return login
298 |
299 | def getCookies(self):
300 | return self.textCookies.toPlainText()
301 |
302 | def setCookies(self, cookies:str):
303 | self.textCookies.setText(cookies)
304 |
305 | def addCookies(self, cookies:str):
306 | c = self.textCookies.toPlainText()
307 | cookies = re.sub(r"https://\.", "https://", cookies)
308 | if c == "":
309 | if len(cookies) > 0 and cookies[-1] != "\n":
310 | cookies += "\n"
311 | self.textCookies.setText(cookies)
312 | else:
313 | c += cookies
314 | c = re.sub(r"\n\n", "", c)
315 | if c[-1] != "\n":
316 | c += "\n"
317 | self.textCookies.setText(c)
318 |
319 | def setUserAgent(self, ua:str):
320 | if not re.search(r"^user-agent:", self.textCookies.toPlainText()):
321 | self.textCookies.setText(f"user-agent:\t{ua}\n" + self.textCookies.toPlainText())
322 | else:
323 | self.textCookies.setText(
324 | re.sub(r"^user-agent:.*\n", f"user-agent:\t{ua}\n", self.textCookies.toPlainText())
325 | )
326 |
327 | def updateTemplate(self):
328 | selected = self.menuTemplateSelection.currentText()
329 | if selected == "创建新模板":
330 | self.textTemplateName.setText("新模板")
331 | self.textEpPattern.clear()
332 | self.textTitlePattern.clear()
333 | self.textTitle.clear()
334 | self.textPoster.clear()
335 | self.textAbout.clear()
336 | self.textTags.setText("Anime")
337 | self.textDescription.clear()
338 | self.menuSelectCookies.setCurrentIndex(0)
339 |
340 |
341 | elif selected not in self.conf['template']:
342 | return
343 | else:
344 |
345 | conf = defaultdict(str, self.conf['template'][selected])
346 | self.textTemplateName.setText(selected)
347 | self.textEpPattern.setText(conf['epPattern'])
348 | self.textTitlePattern.setText(conf['titlePattern'])
349 | self.setTitleText()
350 | self.textPoster.setText(conf['poster'])
351 | self.textAbout.setText(conf['about'])
352 | self.textDescription.setText(conf['description'])
353 | self.reloadMenuSelectCookies()
354 | self.textTags.setText(conf['tags'])
355 | self.textTitle.setText(conf['title'])
356 | self.setTitleText()
357 | self.conf['template'][selected] = dict(conf)
358 |
359 | conf = defaultdict(bool, self.conf['template'][selected])
360 | self.checkboxDmhyPublish.setChecked(conf['checkDmhy'])
361 | self.checkboxNyaaPublish.setChecked(conf['checkNyaa'])
362 | self.checkboxBangumiPublish.setChecked(conf['checkBangumi'])
363 | self.checkboxAcgripPublish.setChecked(conf['checkAcgrip'])
364 | self.checkboxAcgnxasiaPublish.setChecked(conf['checkAcgnxasia'])
365 | self.checkboxAcgnxglobalPublish.setChecked(conf['checkAcgnxglobal'])
366 | self.conf['template'][selected] = dict(conf)
367 |
368 | def setTitleText(self):
369 | # set title based on patterns, set to "" when no pattern set
370 | filename = Path(self.textTorrentPath.text()).name
371 | epPattern = self.textEpPattern.text()
372 | titlePattern = self.textTitlePattern.text()
373 |
374 | if epPattern == "" or titlePattern == "":
375 | return
376 |
377 | replaces = re.findall(r"<\w+>", epPattern)
378 | epPattern = re.escape(epPattern)
379 | epPattern = re.sub(r"<", r"(?P<", epPattern)
380 | epPattern = re.sub(r">", r">.+)", epPattern)
381 |
382 | try:
383 | m = re.search(epPattern, filename)
384 | except re.error:
385 | return
386 |
387 | if not m:
388 | return
389 |
390 | title = titlePattern
391 | for i in replaces:
392 | title = re.sub(i, m[f'{re.sub("<|>", "", i)}'], title)
393 |
394 | self.textTitle.setText(title)
395 |
396 | def selectCookiesChangeHandler(self, event):
397 | if event == "":
398 | return
399 |
400 | cookies = self.profile['profiles'][event]['cookies']
401 |
402 |
403 | if cookies is None or not re.search(r"https:\/\/share\.dmhy\.org", cookies):
404 | self.checkboxDmhyPublish.setChecked(False)
405 | self.checkboxDmhyPublish.setCheckable(False)
406 | else:
407 | self.checkboxDmhyPublish.setCheckable(True)
408 |
409 | if cookies is None or not re.search(r"https:\/\/nyaa\.si", cookies):
410 | self.checkboxNyaaPublish.setChecked(False)
411 | self.checkboxNyaaPublish.setCheckable(False)
412 | else:
413 | self.checkboxNyaaPublish.setCheckable(True)
414 |
415 | if cookies is None or not re.search(r"https:\/\/acg\.rip", cookies):
416 | self.checkboxAcgripPublish.setChecked(False)
417 | self.checkboxAcgripPublish.setCheckable(False)
418 | else:
419 | self.checkboxAcgripPublish.setCheckable(True)
420 |
421 | if cookies is None or not re.search(r"https:\/\/bangumi\.moe", cookies):
422 | self.checkboxBangumiPublish.setChecked(False)
423 | self.checkboxBangumiPublish.setCheckable(False)
424 | else:
425 | self.checkboxBangumiPublish.setCheckable(True)
426 |
427 | if cookies is None or not re.search(r"https:\/\/share\.acgnx\.se", cookies):
428 | self.checkboxAcgnxasiaPublish.setChecked(False)
429 | self.checkboxAcgnxasiaPublish.setCheckable(False)
430 | else:
431 | self.checkboxAcgnxasiaPublish.setCheckable(True)
432 |
433 | if cookies is None or not re.search(r"https:\/\/www\.acgnx\.se", cookies):
434 | self.checkboxAcgnxglobalPublish.setChecked(False)
435 | self.checkboxAcgnxglobalPublish.setCheckable(False)
436 | else:
437 | self.checkboxAcgnxglobalPublish.setCheckable(True)
438 |
439 |
440 | def reloadTemplate(self):
441 | self.loadConfig()
442 | templateList = list(self.conf['template'].keys())
443 | self.menuTemplateSelection.clear()
444 | self.menuTemplateSelection.addItems(templateList)
445 | self.menuTemplateSelection.addItem("创建新模板")
446 | self.menuTemplateSelection.currentTextChanged.connect(self.updateTemplate)
447 | try:
448 | self.menuTemplateSelection.setCurrentText(self.conf['lastUsed'])
449 | self.updateTemplate()
450 | except:
451 | pass
452 |
453 |
454 | def saveTemplate(self):
455 | templateName = self.textTemplateName.text()
456 |
457 | if templateName in ["", "创建新模板"]:
458 | self.warning(f"非法模板名\"{templateName}\",请换个名字。")
459 | return
460 |
461 | if templateName in self.conf['template']:
462 | if not self.warning(f"即将覆盖模板\"{templateName}\",是否确认?"):
463 | return
464 |
465 | self.conf['lastUsed'] = templateName
466 | self.conf['template'][templateName] = {
467 | 'epPattern': self.textEpPattern.text(),
468 | 'titlePattern': self.textTitlePattern.text(),
469 | 'poster': self.textPoster.text(),
470 | 'about': self.textAbout.text(),
471 | 'tags': self.textTags.text(),
472 | 'description': self.textDescription.toPlainText(),
473 | 'profile': self.menuSelectCookies.currentText(),
474 | 'checkDmhy': self.checkboxDmhyPublish.isChecked(),
475 | 'checkNyaa': self.checkboxNyaaPublish.isChecked(),
476 | 'checkBangumi': self.checkboxBangumiPublish.isChecked(),
477 | 'checkAcgrip': self.checkboxAcgripPublish.isChecked(),
478 | 'checkAcgnxasia': self.checkboxAcgnxasiaPublish.isChecked(),
479 | 'checkAcgnxglobal': self.checkboxAcgnxglobalPublish.isChecked(),
480 | 'title': self.textTitle.text()
481 | }
482 |
483 | with open(TEMPLATE_CONFIG, "w", encoding='utf-8') as file:
484 | yaml.safe_dump(self.conf, file, encoding='utf-8',allow_unicode=True)
485 |
486 | self.reloadTemplate()
487 |
488 |
489 | def deleteTemplate(self):
490 | # todo: ask for confirmation
491 | if self.warning(f'正在删除"{self.menuTemplateSelection.currentText()}"模板,删除后将无法恢复,是否继续?'):
492 | self.conf['template'].pop(self.menuTemplateSelection.currentText())
493 | with open(TEMPLATE_CONFIG, "w", encoding='utf-8') as file:
494 | yaml.safe_dump(self.conf, file, encoding='utf-8',allow_unicode=True)
495 |
496 | self.reloadTemplate()
497 |
498 | def loadProfile(self):
499 | path = Path(PROFILE_CONFIG)
500 | if not path.exists():
501 | with open(path, "w", encoding="utf-8") as f:
502 | f.write(
503 | '''
504 | lastUsed: 新身份
505 | profiles:
506 | 新身份:
507 | cookies:
508 | dmhyName:
509 | nyaaName:
510 | acgripName:
511 | bangumiName:
512 | acgnxasiaName:
513 | acgnxglobalName:
514 | '''
515 | )
516 | with open(path, "r", encoding="utf-8") as f:
517 | self.profile = yaml.safe_load(f)
518 |
519 | def reloadProfile(self):
520 | self.loadProfile()
521 | profileList = list(self.profile["profiles"].keys())
522 | self.menuProfileSelection.clear()
523 | self.menuProfileSelection.addItems(profileList)
524 | self.menuProfileSelection.addItem("创建新身份")
525 | self.updateProfile()
526 | self.menuProfileSelection.currentTextChanged.connect(self.updateProfile)
527 | try:
528 | self.menuProfileSelection.setCurrentText(self.profile["lastUsed"])
529 | self.updateProfile()
530 | except:
531 | pass
532 |
533 | def updateProfile(self):
534 |
535 | selected = self.menuProfileSelection.currentText()
536 |
537 | if selected == "创建新身份":
538 | # todo: warning
539 | self.textProfileName.setText("新身份")
540 | self.textDmhyName.clear()
541 | self.textNyaaName.clear()
542 | self.textAcgripName.clear()
543 | self.textBangumiName.clear()
544 | self.textAcgnxasiaName.clear()
545 | self.textAcgnxasiaToken.clear()
546 | self.textAcgnxglobalName.clear()
547 | self.textAcgnxglobalToken.clear()
548 | self.textCookies.clear()
549 | # self.menuProxyType.setCurrentIndex(0)
550 | # self.textProxyHost.setText("http://127.0.0.1:7890")
551 |
552 | elif selected not in self.profile["profiles"]:
553 | return
554 | else:
555 | # if 'proxyType' in self.profile: self.menuProxyType.setCurrentText(self.profile['proxyType'])
556 | # if 'proxy' in self.profile: self.textProxyHost.setText(self.profile['proxy'])
557 |
558 | prof = defaultdict(str, self.profile["profiles"][selected])
559 |
560 | self.textProfileName.setText(selected)
561 | self.textDmhyName.setText(prof['dmhyName'])
562 | self.textNyaaName.setText(prof['nyaaName'])
563 | self.textAcgripName.setText(prof['acgripName'])
564 | self.textBangumiName.setText(prof['bangumiName'])
565 | self.textAcgnxasiaName.setText(prof['acgnxasiaName'])
566 | self.textAcgnxglobalName.setText(prof['acgnxglobalName'])
567 | self.textCookies.setText(prof['cookies'])
568 |
569 |
570 | res = re.search(r'https:\/\/share.acgnx.se\ttoken=(?P.*)(\n|$)', str(prof['cookies']))
571 | if res:
572 | self.textAcgnxasiaToken.setText(res['token'])
573 | else:
574 | self.textAcgnxasiaToken.clear()
575 | res = re.search(r'https:\/\/www.acgnx.se\ttoken=(?P.*)(\n|$)', str(prof['cookies']))
576 | if res:
577 | self.textAcgnxglobalToken.setText(res['token'])
578 | else:
579 | self.textAcgnxglobalToken.clear()
580 |
581 | self.profile["profiles"][selected] = dict(prof)
582 |
583 |
584 | def saveProfile(self):
585 | profileName = self.textProfileName.text()
586 |
587 | if profileName in ["", "创建新身份"]:
588 | self.warning(f"非法身份名\"{profileName}\",请换个名字。")
589 | return
590 |
591 | if profileName in self.profile["profiles"]:
592 | if not self.warning(f"即将覆盖身份\"{profileName}\", 是否确认?"):
593 | return
594 |
595 | self.profile["lastUsed"] = self.textProfileName.text()
596 | self.profile["profiles"][self.textProfileName.text()] = {
597 | 'cookies': self.textCookies.toPlainText(),
598 | 'dmhyName': self.textDmhyName.text(),
599 | 'nyaaName': self.textNyaaName.text(),
600 | 'acgripName': self.textAcgripName.text(),
601 | 'bangumiName': self.textBangumiName.text(),
602 | 'acgnxasiaName': self.textAcgnxasiaName.text(),
603 | 'acgnxglobalName': self.textAcgnxglobalName.text(),
604 | }
605 |
606 | with open(PROFILE_CONFIG, "w", encoding='utf-8') as file:
607 | yaml.safe_dump(self.profile, file, encoding='utf-8',allow_unicode=True)
608 |
609 | self.reloadProfile()
610 | self.reloadMenuSelectCookies()
611 |
612 |
613 | def deleteProfile(self):
614 | if self.warning(f'正在删除"{self.menuProfileSelection.currentText()}"身份,删除后将无法恢复,是否继续?'):
615 | self.profile['profiles'].pop(self.menuProfileSelection.currentText())
616 | with open(PROFILE_CONFIG, "w", encoding='utf-8') as file:
617 | yaml.safe_dump(self.profile, file, encoding='utf-8',allow_unicode=True)
618 |
619 | self.reloadMenuSelectCookies()
620 | self.reloadProfile()
621 |
622 |
623 | def previewMarkdown(self):
624 | md = markdown.markdown(self.textDescription.toPlainText())
625 | #self.textDescription.setPlainText(md)
626 | self.markdownWindow = MarkdownViewWindow(html=md,parentWindow=self)
627 | self.markdownWindow.show()
628 |
629 | def warning(self, message):
630 | warning = WarningDialog()
631 | warning.label.setText(message)
632 | warning.show()
633 | return warning.exec()
634 |
635 | def reloadMenuSelectCookies(self):
636 | self.menuSelectCookies.clear()
637 | self.menuSelectCookies.addItems(self.profile['profiles'].keys())
638 | try: self.menuSelectCookies.setCurrentText(self.conf['template'][self.menuTemplateSelection.currentText()]['profile'])
639 | except: pass
640 |
641 | def onProxySelection(self):
642 | selected = self.menuProxyType.currentText()
643 | if selected == "不使用代理":
644 | self.textProxyHost.setDisabled(True)
645 | return
646 | if selected == "HTTP":
647 | self.textProxyHost.setEnabled(True)
648 | return
649 |
650 | def applyAcgnxasiaAPIToken(self):
651 | cookies = self.textCookies.toPlainText()
652 | new_string, n = re.subn(
653 | r"https:\/\/share.acgnx.se\ttoken=.*(\n|$)",
654 | f"https://share.acgnx.se\ttoken={self.textAcgnxasiaToken.text()}\n",
655 | cookies)
656 | if n != 0:
657 | self.textCookies.setText(new_string)
658 | else:
659 | if cookies and cookies[-1] != "\n": cookies += "\n"
660 | self.textCookies.setText(
661 | cookies + f"https://share.acgnx.se\ttoken={self.textAcgnxasiaToken.text()}\n"
662 | )
663 |
664 | def applyAcgnxglobalAPIToken(self):
665 | cookies = self.textCookies.toPlainText()
666 | new_string, n = re.subn(
667 | r"https:\/\/www.acgnx.se\ttoken=.*(\n|$)",
668 | f"https://www.acgnx.se\ttoken={self.textAcgnxglobalToken.text()}\n",
669 | cookies)
670 | if n != 0:
671 | self.textCookies.setText(new_string)
672 | else:
673 | if cookies and cookies[-1] != "\n": cookies += "\n"
674 | self.textCookies.setText(
675 | cookies + f"https://www.acgnx.se\ttoken={self.textAcgnxglobalToken.text()}\n"
676 | )
677 |
678 | def onCookiesChange(self):
679 | cookies = self.textCookies.toPlainText()
680 | m = re.search(r"https:\/\/share.acgnx.se\ttoken=(?P.*)(\n|$)", cookies)
681 | if m:
682 | self.textAcgnxasiaToken.setText(m['token'])
683 |
684 | m = re.search(r"https:\/\/www.acgnx.se\ttoken=(?P.*)(\n|$)", cookies)
685 | if m:
686 | self.textAcgnxglobalToken.setText(m['token'])
687 |
688 |
689 |
690 | def publishRun(self):
691 | # Sanity check
692 | path = self.textTorrentPath.text()
693 | if path == "":
694 | self.warning("种子文件不能为空。")
695 | return
696 |
697 | if not Path(path).exists():
698 | self.warning(f"无法找到种子文件'{path}'。")
699 | return
700 |
701 | if Path(path).suffix != ".torrent":
702 | self.warning(f"'{path}' 不是一个 .torrent 文件")
703 | return
704 |
705 | if self.textTitle.text() == "":
706 | self.warning("标题不能为空。")
707 | return
708 |
709 | if self.textDescription.toPlainText() == "":
710 | self.warning("内容不能为空。")
711 |
712 | # Generate template.toml
713 | tags = map(lambda x: x.strip() , self.textTags.text().split(","))
714 | intro_templates = []
715 |
716 | md = self.textDescription.toPlainText()
717 | html = markdown.markdown(md)
718 | parser = HTML2PHPBBCode()
719 | bbcode = parser.feed(html)
720 | proxy = self.conf["proxy"]
721 |
722 | cookies = self.profile['profiles'][self.menuSelectCookies.currentText()]['cookies']
723 | profile = self.profile['profiles'][self.menuSelectCookies.currentText()]
724 |
725 | if self.checkboxDmhyPublish.isChecked() and self.checkboxDmhyPublish.isCheckable():
726 | intro_templates.append(
727 | {
728 | 'site': 'dmhy',
729 | 'name': profile['dmhyName'],
730 | 'content': html,
731 | }
732 | )
733 |
734 | if self.checkboxNyaaPublish.isChecked() and self.checkboxNyaaPublish.isCheckable():
735 | intro_templates.append(
736 | {
737 | 'site': 'nyaa',
738 | 'name': profile['nyaaName'],
739 | 'content': md,
740 | }
741 | )
742 |
743 | if self.checkboxAcgripPublish.isChecked() and self.checkboxAcgripPublish.isCheckable():
744 | intro_templates.append(
745 | {
746 | 'site': 'acgrip',
747 | 'name': profile['acgripName'],
748 | 'content': bbcode,
749 | }
750 | )
751 |
752 | if self.checkboxBangumiPublish.isChecked() and self.checkboxBangumiPublish.isCheckable():
753 | intro_templates.append(
754 | {
755 | 'site': 'bangumi',
756 | 'name': profile['bangumiName'],
757 | 'content': html,
758 | }
759 | )
760 |
761 | if self.checkboxAcgnxasiaPublish.isChecked() and self.checkboxAcgnxasiaPublish.isCheckable():
762 | intro_templates.append(
763 | {
764 | 'site': 'acgnx_asia',
765 | 'name': profile['acgnxasiaName'],
766 | 'content': html,
767 | }
768 | )
769 |
770 | if self.checkboxAcgnxglobalPublish.isChecked() and self.checkboxAcgnxglobalPublish.isCheckable():
771 | intro_templates.append(
772 | {
773 | 'site': 'acgnx_global',
774 | 'name': profile['acgnxglobalName'],
775 | 'content': html,
776 | }
777 | )
778 |
779 | if self.conf['proxyType'] == "HTTP":
780 | for d in intro_templates:
781 | d['proxy'] = proxy
782 |
783 | template_conf = {
784 | 'display_name': self.textTitle.text(),
785 | 'poster': self.textPoster.text(),
786 | 'about': self.textAbout.text(),
787 | 'filename_regex': '',
788 | 'resolution_regex': '',
789 | 'tags': list(tags),
790 | 'intro_template': intro_templates
791 | }
792 |
793 | with open("template.toml", "w", encoding='utf-8') as f:
794 | toml.dump(template_conf, f)
795 |
796 | # Generate cookies.txt
797 | with open("cookies.txt", "w", encoding='utf-8') as f:
798 | f.write(self.profile['profiles'][self.menuSelectCookies.currentText()]['cookies'])
799 |
800 | self.console = MyConsole(self)
801 | self.console.onFinished(self.updateCookies)
802 | self.console.start("OKP.Core.exe", [
803 | self.textTorrentPath.text(),
804 | "-s", str(Path.cwd().joinpath("template.toml")),
805 | '--cookies', str(Path.cwd().joinpath("cookies.txt"))
806 | ])
807 | self.console.show()
808 |
809 |
810 |
811 |
812 | def updateCookies(self, int, exitStatus):
813 | if exitStatus == QProcess.ExitStatus.NormalExit:
814 | try:
815 | with open("cookies.txt", "r", encoding="utf-8") as f:
816 | newCookies = f.read()
817 |
818 | self.profile["profiles"][self.menuSelectCookies.currentText()]["cookies"] = newCookies
819 |
820 | with open(PROFILE_CONFIG, "w", encoding="utf-8") as file:
821 | yaml.safe_dump(self.profile, file, encoding='utf-8',allow_unicode=True)
822 |
823 | self.reloadProfile()
824 |
825 | except:
826 | return
827 |
828 |
829 |
830 | class WarningDialog(QDialog, Ui_Dialog):
831 | def __init__(self, *args, **kwargs):
832 | QDialog.__init__(self, *args, **kwargs)
833 | self.setupUi(self)
834 |
835 |
836 | if __name__ == '__main__':
837 | app = QApplication(sys.argv)
838 | if platform.system() != "Windows":
839 | app.setStyle('Fusion')
840 |
841 | window = OKPMainWIndow()
842 | window.show()
843 | sys.exit(app.exec())
--------------------------------------------------------------------------------
/OKPUI.py:
--------------------------------------------------------------------------------
1 | # Form implementation generated from reading ui file 'OKP.ui'
2 | #
3 | # Created by: PyQt6 UI code generator 6.4.2
4 | #
5 | # WARNING: Any manual changes made to this file will be lost when pyuic6 is
6 | # run again. Do not edit this file unless you know what you are doing.
7 |
8 |
9 | from PyQt6 import QtCore, QtGui, QtWidgets
10 |
11 |
12 | class Ui_MainWindow(object):
13 | def setupUi(self, MainWindow):
14 | MainWindow.setObjectName("MainWindow")
15 | MainWindow.resize(609, 950)
16 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
17 | sizePolicy.setHorizontalStretch(0)
18 | sizePolicy.setVerticalStretch(0)
19 | sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
20 | MainWindow.setSizePolicy(sizePolicy)
21 | MainWindow.setMinimumSize(QtCore.QSize(600, 840))
22 | font = QtGui.QFont()
23 | font.setFamily("Microsoft YaHei UI")
24 | font.setPointSize(12)
25 | MainWindow.setFont(font)
26 | self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
27 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
28 | sizePolicy.setHorizontalStretch(0)
29 | sizePolicy.setVerticalStretch(0)
30 | sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth())
31 | self.centralwidget.setSizePolicy(sizePolicy)
32 | self.centralwidget.setObjectName("centralwidget")
33 | self.gridLayout_2 = QtWidgets.QGridLayout(self.centralwidget)
34 | self.gridLayout_2.setObjectName("gridLayout_2")
35 | self.tab = QtWidgets.QTabWidget(parent=self.centralwidget)
36 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
37 | sizePolicy.setHorizontalStretch(0)
38 | sizePolicy.setVerticalStretch(0)
39 | sizePolicy.setHeightForWidth(self.tab.sizePolicy().hasHeightForWidth())
40 | self.tab.setSizePolicy(sizePolicy)
41 | self.tab.setMinimumSize(QtCore.QSize(594, 800))
42 | font = QtGui.QFont()
43 | font.setPointSize(12)
44 | font.setStyleStrategy(QtGui.QFont.StyleStrategy.PreferDefault)
45 | self.tab.setFont(font)
46 | self.tab.setStyleSheet("")
47 | self.tab.setObjectName("tab")
48 | self.HomeTab = QtWidgets.QWidget()
49 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
50 | sizePolicy.setHorizontalStretch(0)
51 | sizePolicy.setVerticalStretch(0)
52 | sizePolicy.setHeightForWidth(self.HomeTab.sizePolicy().hasHeightForWidth())
53 | self.HomeTab.setSizePolicy(sizePolicy)
54 | font = QtGui.QFont()
55 | font.setPointSize(12)
56 | self.HomeTab.setFont(font)
57 | self.HomeTab.setObjectName("HomeTab")
58 | self.gridLayout = QtWidgets.QGridLayout(self.HomeTab)
59 | self.gridLayout.setObjectName("gridLayout")
60 | self.buttonDeleteTemplate = QtWidgets.QPushButton(parent=self.HomeTab)
61 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
62 | sizePolicy.setHorizontalStretch(0)
63 | sizePolicy.setVerticalStretch(0)
64 | sizePolicy.setHeightForWidth(self.buttonDeleteTemplate.sizePolicy().hasHeightForWidth())
65 | self.buttonDeleteTemplate.setSizePolicy(sizePolicy)
66 | self.buttonDeleteTemplate.setAutoFillBackground(False)
67 | self.buttonDeleteTemplate.setStyleSheet("color: rgb(255, 0, 30);\n"
68 | "font: 12pt \"Microsoft YaHei UI\";")
69 | self.buttonDeleteTemplate.setFlat(False)
70 | self.buttonDeleteTemplate.setObjectName("buttonDeleteTemplate")
71 | self.gridLayout.addWidget(self.buttonDeleteTemplate, 1, 8, 1, 1)
72 | spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
73 | self.gridLayout.addItem(spacerItem, 5, 4, 1, 1)
74 | self.label_10 = QtWidgets.QLabel(parent=self.HomeTab)
75 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
76 | sizePolicy.setHorizontalStretch(0)
77 | sizePolicy.setVerticalStretch(0)
78 | sizePolicy.setHeightForWidth(self.label_10.sizePolicy().hasHeightForWidth())
79 | self.label_10.setSizePolicy(sizePolicy)
80 | font = QtGui.QFont()
81 | font.setPointSize(9)
82 | self.label_10.setFont(font)
83 | self.label_10.setStyleSheet("color:rgb(75, 75, 75)\n"
84 | "")
85 | self.label_10.setObjectName("label_10")
86 | self.gridLayout.addWidget(self.label_10, 6, 0, 1, 1)
87 | self.textDescription = QtWidgets.QTextEdit(parent=self.HomeTab)
88 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
89 | sizePolicy.setHorizontalStretch(0)
90 | sizePolicy.setVerticalStretch(0)
91 | sizePolicy.setHeightForWidth(self.textDescription.sizePolicy().hasHeightForWidth())
92 | self.textDescription.setSizePolicy(sizePolicy)
93 | self.textDescription.setObjectName("textDescription")
94 | self.gridLayout.addWidget(self.textDescription, 7, 0, 1, 9)
95 | self.formLayout = QtWidgets.QFormLayout()
96 | self.formLayout.setFieldGrowthPolicy(QtWidgets.QFormLayout.FieldGrowthPolicy.ExpandingFieldsGrow)
97 | self.formLayout.setObjectName("formLayout")
98 | self.label_4 = QtWidgets.QLabel(parent=self.HomeTab)
99 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
100 | sizePolicy.setHorizontalStretch(0)
101 | sizePolicy.setVerticalStretch(0)
102 | sizePolicy.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth())
103 | self.label_4.setSizePolicy(sizePolicy)
104 | self.label_4.setObjectName("label_4")
105 | self.formLayout.setWidget(0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_4)
106 | self.textEpPattern = QtWidgets.QLineEdit(parent=self.HomeTab)
107 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
108 | sizePolicy.setHorizontalStretch(0)
109 | sizePolicy.setVerticalStretch(0)
110 | sizePolicy.setHeightForWidth(self.textEpPattern.sizePolicy().hasHeightForWidth())
111 | self.textEpPattern.setSizePolicy(sizePolicy)
112 | self.textEpPattern.setMinimumSize(QtCore.QSize(0, 30))
113 | font = QtGui.QFont()
114 | font.setPointSize(10)
115 | self.textEpPattern.setFont(font)
116 | self.textEpPattern.setObjectName("textEpPattern")
117 | self.formLayout.setWidget(0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.textEpPattern)
118 | self.label_5 = QtWidgets.QLabel(parent=self.HomeTab)
119 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
120 | sizePolicy.setHorizontalStretch(0)
121 | sizePolicy.setVerticalStretch(0)
122 | sizePolicy.setHeightForWidth(self.label_5.sizePolicy().hasHeightForWidth())
123 | self.label_5.setSizePolicy(sizePolicy)
124 | self.label_5.setObjectName("label_5")
125 | self.formLayout.setWidget(2, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_5)
126 | self.textTitlePattern = QtWidgets.QLineEdit(parent=self.HomeTab)
127 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
128 | sizePolicy.setHorizontalStretch(0)
129 | sizePolicy.setVerticalStretch(0)
130 | sizePolicy.setHeightForWidth(self.textTitlePattern.sizePolicy().hasHeightForWidth())
131 | self.textTitlePattern.setSizePolicy(sizePolicy)
132 | self.textTitlePattern.setMinimumSize(QtCore.QSize(0, 30))
133 | font = QtGui.QFont()
134 | font.setPointSize(10)
135 | self.textTitlePattern.setFont(font)
136 | self.textTitlePattern.setObjectName("textTitlePattern")
137 | self.formLayout.setWidget(2, QtWidgets.QFormLayout.ItemRole.FieldRole, self.textTitlePattern)
138 | self.Lable = QtWidgets.QLabel(parent=self.HomeTab)
139 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
140 | sizePolicy.setHorizontalStretch(0)
141 | sizePolicy.setVerticalStretch(0)
142 | sizePolicy.setHeightForWidth(self.Lable.sizePolicy().hasHeightForWidth())
143 | self.Lable.setSizePolicy(sizePolicy)
144 | self.Lable.setObjectName("Lable")
145 | self.formLayout.setWidget(3, QtWidgets.QFormLayout.ItemRole.LabelRole, self.Lable)
146 | self.textTitle = QtWidgets.QLineEdit(parent=self.HomeTab)
147 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
148 | sizePolicy.setHorizontalStretch(0)
149 | sizePolicy.setVerticalStretch(0)
150 | sizePolicy.setHeightForWidth(self.textTitle.sizePolicy().hasHeightForWidth())
151 | self.textTitle.setSizePolicy(sizePolicy)
152 | self.textTitle.setMinimumSize(QtCore.QSize(0, 30))
153 | font = QtGui.QFont()
154 | font.setPointSize(10)
155 | self.textTitle.setFont(font)
156 | self.textTitle.setObjectName("textTitle")
157 | self.formLayout.setWidget(3, QtWidgets.QFormLayout.ItemRole.FieldRole, self.textTitle)
158 | self.label_7 = QtWidgets.QLabel(parent=self.HomeTab)
159 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
160 | sizePolicy.setHorizontalStretch(0)
161 | sizePolicy.setVerticalStretch(0)
162 | sizePolicy.setHeightForWidth(self.label_7.sizePolicy().hasHeightForWidth())
163 | self.label_7.setSizePolicy(sizePolicy)
164 | self.label_7.setObjectName("label_7")
165 | self.formLayout.setWidget(4, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_7)
166 | self.textPoster = QtWidgets.QLineEdit(parent=self.HomeTab)
167 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
168 | sizePolicy.setHorizontalStretch(0)
169 | sizePolicy.setVerticalStretch(0)
170 | sizePolicy.setHeightForWidth(self.textPoster.sizePolicy().hasHeightForWidth())
171 | self.textPoster.setSizePolicy(sizePolicy)
172 | self.textPoster.setMinimumSize(QtCore.QSize(0, 30))
173 | font = QtGui.QFont()
174 | font.setPointSize(10)
175 | self.textPoster.setFont(font)
176 | self.textPoster.setObjectName("textPoster")
177 | self.formLayout.setWidget(4, QtWidgets.QFormLayout.ItemRole.FieldRole, self.textPoster)
178 | self.label_8 = QtWidgets.QLabel(parent=self.HomeTab)
179 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
180 | sizePolicy.setHorizontalStretch(0)
181 | sizePolicy.setVerticalStretch(0)
182 | sizePolicy.setHeightForWidth(self.label_8.sizePolicy().hasHeightForWidth())
183 | self.label_8.setSizePolicy(sizePolicy)
184 | self.label_8.setObjectName("label_8")
185 | self.formLayout.setWidget(5, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_8)
186 | self.textAbout = QtWidgets.QLineEdit(parent=self.HomeTab)
187 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
188 | sizePolicy.setHorizontalStretch(0)
189 | sizePolicy.setVerticalStretch(0)
190 | sizePolicy.setHeightForWidth(self.textAbout.sizePolicy().hasHeightForWidth())
191 | self.textAbout.setSizePolicy(sizePolicy)
192 | self.textAbout.setMinimumSize(QtCore.QSize(0, 30))
193 | font = QtGui.QFont()
194 | font.setPointSize(10)
195 | self.textAbout.setFont(font)
196 | self.textAbout.setObjectName("textAbout")
197 | self.formLayout.setWidget(5, QtWidgets.QFormLayout.ItemRole.FieldRole, self.textAbout)
198 | self.textTags = QtWidgets.QLineEdit(parent=self.HomeTab)
199 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
200 | sizePolicy.setHorizontalStretch(0)
201 | sizePolicy.setVerticalStretch(0)
202 | sizePolicy.setHeightForWidth(self.textTags.sizePolicy().hasHeightForWidth())
203 | self.textTags.setSizePolicy(sizePolicy)
204 | self.textTags.setMinimumSize(QtCore.QSize(0, 30))
205 | font = QtGui.QFont()
206 | font.setPointSize(10)
207 | self.textTags.setFont(font)
208 | self.textTags.setPlaceholderText("")
209 | self.textTags.setObjectName("textTags")
210 | self.formLayout.setWidget(6, QtWidgets.QFormLayout.ItemRole.FieldRole, self.textTags)
211 | self.label_18 = QtWidgets.QLabel(parent=self.HomeTab)
212 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
213 | sizePolicy.setHorizontalStretch(0)
214 | sizePolicy.setVerticalStretch(0)
215 | sizePolicy.setHeightForWidth(self.label_18.sizePolicy().hasHeightForWidth())
216 | self.label_18.setSizePolicy(sizePolicy)
217 | self.label_18.setObjectName("label_18")
218 | self.formLayout.setWidget(6, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_18)
219 | self.gridLayout.addLayout(self.formLayout, 4, 0, 1, 9)
220 | self.formLayout_6 = QtWidgets.QFormLayout()
221 | self.formLayout_6.setObjectName("formLayout_6")
222 | self.label_2 = QtWidgets.QLabel(parent=self.HomeTab)
223 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
224 | sizePolicy.setHorizontalStretch(0)
225 | sizePolicy.setVerticalStretch(0)
226 | sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth())
227 | self.label_2.setSizePolicy(sizePolicy)
228 | self.label_2.setObjectName("label_2")
229 | self.formLayout_6.setWidget(2, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_2)
230 | self.textTemplateName = QtWidgets.QLineEdit(parent=self.HomeTab)
231 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
232 | sizePolicy.setHorizontalStretch(0)
233 | sizePolicy.setVerticalStretch(0)
234 | sizePolicy.setHeightForWidth(self.textTemplateName.sizePolicy().hasHeightForWidth())
235 | self.textTemplateName.setSizePolicy(sizePolicy)
236 | self.textTemplateName.setObjectName("textTemplateName")
237 | self.formLayout_6.setWidget(2, QtWidgets.QFormLayout.ItemRole.FieldRole, self.textTemplateName)
238 | self.label = QtWidgets.QLabel(parent=self.HomeTab)
239 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
240 | sizePolicy.setHorizontalStretch(0)
241 | sizePolicy.setVerticalStretch(0)
242 | sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
243 | self.label.setSizePolicy(sizePolicy)
244 | self.label.setObjectName("label")
245 | self.formLayout_6.setWidget(1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label)
246 | self.menuTemplateSelection = QtWidgets.QComboBox(parent=self.HomeTab)
247 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
248 | sizePolicy.setHorizontalStretch(0)
249 | sizePolicy.setVerticalStretch(0)
250 | sizePolicy.setHeightForWidth(self.menuTemplateSelection.sizePolicy().hasHeightForWidth())
251 | self.menuTemplateSelection.setSizePolicy(sizePolicy)
252 | self.menuTemplateSelection.setObjectName("menuTemplateSelection")
253 | self.menuTemplateSelection.addItem("")
254 | self.menuTemplateSelection.setItemText(0, "")
255 | self.formLayout_6.setWidget(1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.menuTemplateSelection)
256 | self.label_3 = QtWidgets.QLabel(parent=self.HomeTab)
257 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
258 | sizePolicy.setHorizontalStretch(0)
259 | sizePolicy.setVerticalStretch(0)
260 | sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth())
261 | self.label_3.setSizePolicy(sizePolicy)
262 | self.label_3.setObjectName("label_3")
263 | self.formLayout_6.setWidget(0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_3)
264 | self.textTorrentPath = QtWidgets.QLineEdit(parent=self.HomeTab)
265 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
266 | sizePolicy.setHorizontalStretch(0)
267 | sizePolicy.setVerticalStretch(0)
268 | sizePolicy.setHeightForWidth(self.textTorrentPath.sizePolicy().hasHeightForWidth())
269 | self.textTorrentPath.setSizePolicy(sizePolicy)
270 | self.textTorrentPath.setMinimumSize(QtCore.QSize(0, 30))
271 | font = QtGui.QFont()
272 | font.setPointSize(10)
273 | self.textTorrentPath.setFont(font)
274 | self.textTorrentPath.setReadOnly(True)
275 | self.textTorrentPath.setObjectName("textTorrentPath")
276 | self.formLayout_6.setWidget(0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.textTorrentPath)
277 | self.gridLayout.addLayout(self.formLayout_6, 0, 0, 4, 8)
278 | self.buttonSaveTemplate = QtWidgets.QPushButton(parent=self.HomeTab)
279 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
280 | sizePolicy.setHorizontalStretch(0)
281 | sizePolicy.setVerticalStretch(0)
282 | sizePolicy.setHeightForWidth(self.buttonSaveTemplate.sizePolicy().hasHeightForWidth())
283 | self.buttonSaveTemplate.setSizePolicy(sizePolicy)
284 | self.buttonSaveTemplate.setObjectName("buttonSaveTemplate")
285 | self.gridLayout.addWidget(self.buttonSaveTemplate, 2, 8, 1, 1)
286 | self.label_9 = QtWidgets.QLabel(parent=self.HomeTab)
287 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
288 | sizePolicy.setHorizontalStretch(0)
289 | sizePolicy.setVerticalStretch(0)
290 | sizePolicy.setHeightForWidth(self.label_9.sizePolicy().hasHeightForWidth())
291 | self.label_9.setSizePolicy(sizePolicy)
292 | self.label_9.setObjectName("label_9")
293 | self.gridLayout.addWidget(self.label_9, 5, 0, 1, 1)
294 | spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
295 | self.gridLayout.addItem(spacerItem1, 5, 3, 1, 1)
296 | self.label_22 = QtWidgets.QLabel(parent=self.HomeTab)
297 | self.label_22.setTextFormat(QtCore.Qt.TextFormat.RichText)
298 | self.label_22.setObjectName("label_22")
299 | self.gridLayout.addWidget(self.label_22, 5, 5, 1, 1)
300 | self.buttonPreviewMarkdown = QtWidgets.QPushButton(parent=self.HomeTab)
301 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
302 | sizePolicy.setHorizontalStretch(0)
303 | sizePolicy.setVerticalStretch(0)
304 | sizePolicy.setHeightForWidth(self.buttonPreviewMarkdown.sizePolicy().hasHeightForWidth())
305 | self.buttonPreviewMarkdown.setSizePolicy(sizePolicy)
306 | self.buttonPreviewMarkdown.setObjectName("buttonPreviewMarkdown")
307 | self.gridLayout.addWidget(self.buttonPreviewMarkdown, 5, 6, 1, 3)
308 | self.buttonBrowse = QtWidgets.QPushButton(parent=self.HomeTab)
309 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
310 | sizePolicy.setHorizontalStretch(0)
311 | sizePolicy.setVerticalStretch(0)
312 | sizePolicy.setHeightForWidth(self.buttonBrowse.sizePolicy().hasHeightForWidth())
313 | self.buttonBrowse.setSizePolicy(sizePolicy)
314 | self.buttonBrowse.setObjectName("buttonBrowse")
315 | self.gridLayout.addWidget(self.buttonBrowse, 0, 8, 1, 1)
316 | self.fileTree = QtWidgets.QTreeWidget(parent=self.HomeTab)
317 | self.fileTree.setMinimumSize(QtCore.QSize(0, 150))
318 | self.fileTree.setObjectName("fileTree")
319 | self.fileTree.header().setSortIndicatorShown(True)
320 | self.gridLayout.addWidget(self.fileTree, 9, 0, 1, 9)
321 | self.buttonOKP = QtWidgets.QPushButton(parent=self.HomeTab)
322 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
323 | sizePolicy.setHorizontalStretch(0)
324 | sizePolicy.setVerticalStretch(0)
325 | sizePolicy.setHeightForWidth(self.buttonOKP.sizePolicy().hasHeightForWidth())
326 | self.buttonOKP.setSizePolicy(sizePolicy)
327 | font = QtGui.QFont()
328 | font.setPointSize(20)
329 | self.buttonOKP.setFont(font)
330 | self.buttonOKP.setObjectName("buttonOKP")
331 | self.gridLayout.addWidget(self.buttonOKP, 10, 0, 1, 9)
332 | self.formLayout_2 = QtWidgets.QFormLayout()
333 | self.formLayout_2.setFieldGrowthPolicy(QtWidgets.QFormLayout.FieldGrowthPolicy.ExpandingFieldsGrow)
334 | self.formLayout_2.setObjectName("formLayout_2")
335 | self.label_12 = QtWidgets.QLabel(parent=self.HomeTab)
336 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
337 | sizePolicy.setHorizontalStretch(0)
338 | sizePolicy.setVerticalStretch(0)
339 | sizePolicy.setHeightForWidth(self.label_12.sizePolicy().hasHeightForWidth())
340 | self.label_12.setSizePolicy(sizePolicy)
341 | self.label_12.setObjectName("label_12")
342 | self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_12)
343 | self.menuSelectCookies = QtWidgets.QComboBox(parent=self.HomeTab)
344 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
345 | sizePolicy.setHorizontalStretch(0)
346 | sizePolicy.setVerticalStretch(0)
347 | sizePolicy.setHeightForWidth(self.menuSelectCookies.sizePolicy().hasHeightForWidth())
348 | self.menuSelectCookies.setSizePolicy(sizePolicy)
349 | self.menuSelectCookies.setObjectName("menuSelectCookies")
350 | self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.menuSelectCookies)
351 | self.checkboxDmhyPublish = QtWidgets.QCheckBox(parent=self.HomeTab)
352 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
353 | sizePolicy.setHorizontalStretch(0)
354 | sizePolicy.setVerticalStretch(0)
355 | sizePolicy.setHeightForWidth(self.checkboxDmhyPublish.sizePolicy().hasHeightForWidth())
356 | self.checkboxDmhyPublish.setSizePolicy(sizePolicy)
357 | self.checkboxDmhyPublish.setObjectName("checkboxDmhyPublish")
358 | self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.checkboxDmhyPublish)
359 | self.checkboxBangumiPublish = QtWidgets.QCheckBox(parent=self.HomeTab)
360 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
361 | sizePolicy.setHorizontalStretch(0)
362 | sizePolicy.setVerticalStretch(0)
363 | sizePolicy.setHeightForWidth(self.checkboxBangumiPublish.sizePolicy().hasHeightForWidth())
364 | self.checkboxBangumiPublish.setSizePolicy(sizePolicy)
365 | self.checkboxBangumiPublish.setObjectName("checkboxBangumiPublish")
366 | self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.checkboxBangumiPublish)
367 | self.checkboxNyaaPublish = QtWidgets.QCheckBox(parent=self.HomeTab)
368 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
369 | sizePolicy.setHorizontalStretch(0)
370 | sizePolicy.setVerticalStretch(0)
371 | sizePolicy.setHeightForWidth(self.checkboxNyaaPublish.sizePolicy().hasHeightForWidth())
372 | self.checkboxNyaaPublish.setSizePolicy(sizePolicy)
373 | self.checkboxNyaaPublish.setObjectName("checkboxNyaaPublish")
374 | self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.ItemRole.LabelRole, self.checkboxNyaaPublish)
375 | self.checkboxAcgripPublish = QtWidgets.QCheckBox(parent=self.HomeTab)
376 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
377 | sizePolicy.setHorizontalStretch(0)
378 | sizePolicy.setVerticalStretch(0)
379 | sizePolicy.setHeightForWidth(self.checkboxAcgripPublish.sizePolicy().hasHeightForWidth())
380 | self.checkboxAcgripPublish.setSizePolicy(sizePolicy)
381 | self.checkboxAcgripPublish.setObjectName("checkboxAcgripPublish")
382 | self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.ItemRole.FieldRole, self.checkboxAcgripPublish)
383 | self.checkboxAcgnxasiaPublish = QtWidgets.QCheckBox(parent=self.HomeTab)
384 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
385 | sizePolicy.setHorizontalStretch(0)
386 | sizePolicy.setVerticalStretch(0)
387 | sizePolicy.setHeightForWidth(self.checkboxAcgnxasiaPublish.sizePolicy().hasHeightForWidth())
388 | self.checkboxAcgnxasiaPublish.setSizePolicy(sizePolicy)
389 | self.checkboxAcgnxasiaPublish.setObjectName("checkboxAcgnxasiaPublish")
390 | self.formLayout_2.setWidget(3, QtWidgets.QFormLayout.ItemRole.LabelRole, self.checkboxAcgnxasiaPublish)
391 | self.checkboxAcgnxglobalPublish = QtWidgets.QCheckBox(parent=self.HomeTab)
392 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
393 | sizePolicy.setHorizontalStretch(0)
394 | sizePolicy.setVerticalStretch(0)
395 | sizePolicy.setHeightForWidth(self.checkboxAcgnxglobalPublish.sizePolicy().hasHeightForWidth())
396 | self.checkboxAcgnxglobalPublish.setSizePolicy(sizePolicy)
397 | self.checkboxAcgnxglobalPublish.setObjectName("checkboxAcgnxglobalPublish")
398 | self.formLayout_2.setWidget(3, QtWidgets.QFormLayout.ItemRole.FieldRole, self.checkboxAcgnxglobalPublish)
399 | self.gridLayout.addLayout(self.formLayout_2, 8, 0, 1, 9)
400 | self.tab.addTab(self.HomeTab, "")
401 | self.CookiesManagerTab = QtWidgets.QWidget()
402 | self.CookiesManagerTab.setObjectName("CookiesManagerTab")
403 | self.gridLayout_3 = QtWidgets.QGridLayout(self.CookiesManagerTab)
404 | self.gridLayout_3.setObjectName("gridLayout_3")
405 | self.formLayout_3 = QtWidgets.QFormLayout()
406 | self.formLayout_3.setObjectName("formLayout_3")
407 | self.label_16 = QtWidgets.QLabel(parent=self.CookiesManagerTab)
408 | self.label_16.setObjectName("label_16")
409 | self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_16)
410 | self.label_17 = QtWidgets.QLabel(parent=self.CookiesManagerTab)
411 | self.label_17.setObjectName("label_17")
412 | self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.label_17)
413 | self.buttonDmhyLogin = QtWidgets.QPushButton(parent=self.CookiesManagerTab)
414 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Fixed)
415 | sizePolicy.setHorizontalStretch(0)
416 | sizePolicy.setVerticalStretch(0)
417 | sizePolicy.setHeightForWidth(self.buttonDmhyLogin.sizePolicy().hasHeightForWidth())
418 | self.buttonDmhyLogin.setSizePolicy(sizePolicy)
419 | self.buttonDmhyLogin.setObjectName("buttonDmhyLogin")
420 | self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.buttonDmhyLogin)
421 | self.textDmhyName = QtWidgets.QLineEdit(parent=self.CookiesManagerTab)
422 | self.textDmhyName.setObjectName("textDmhyName")
423 | self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.textDmhyName)
424 | self.buttonNyaaLogin = QtWidgets.QPushButton(parent=self.CookiesManagerTab)
425 | self.buttonNyaaLogin.setObjectName("buttonNyaaLogin")
426 | self.formLayout_3.setWidget(2, QtWidgets.QFormLayout.ItemRole.LabelRole, self.buttonNyaaLogin)
427 | self.textNyaaName = QtWidgets.QLineEdit(parent=self.CookiesManagerTab)
428 | self.textNyaaName.setReadOnly(False)
429 | self.textNyaaName.setClearButtonEnabled(False)
430 | self.textNyaaName.setObjectName("textNyaaName")
431 | self.formLayout_3.setWidget(2, QtWidgets.QFormLayout.ItemRole.FieldRole, self.textNyaaName)
432 | self.buttonAcgripLogin = QtWidgets.QPushButton(parent=self.CookiesManagerTab)
433 | self.buttonAcgripLogin.setObjectName("buttonAcgripLogin")
434 | self.formLayout_3.setWidget(3, QtWidgets.QFormLayout.ItemRole.LabelRole, self.buttonAcgripLogin)
435 | self.textAcgripName = QtWidgets.QLineEdit(parent=self.CookiesManagerTab)
436 | self.textAcgripName.setObjectName("textAcgripName")
437 | self.formLayout_3.setWidget(3, QtWidgets.QFormLayout.ItemRole.FieldRole, self.textAcgripName)
438 | self.buttonBangumiLogin = QtWidgets.QPushButton(parent=self.CookiesManagerTab)
439 | self.buttonBangumiLogin.setObjectName("buttonBangumiLogin")
440 | self.formLayout_3.setWidget(4, QtWidgets.QFormLayout.ItemRole.LabelRole, self.buttonBangumiLogin)
441 | self.textBangumiName = QtWidgets.QLineEdit(parent=self.CookiesManagerTab)
442 | self.textBangumiName.setObjectName("textBangumiName")
443 | self.formLayout_3.setWidget(4, QtWidgets.QFormLayout.ItemRole.FieldRole, self.textBangumiName)
444 | self.acgnx_asiaLabel = QtWidgets.QLabel(parent=self.CookiesManagerTab)
445 | self.acgnx_asiaLabel.setObjectName("acgnx_asiaLabel")
446 | self.formLayout_3.setWidget(5, QtWidgets.QFormLayout.ItemRole.LabelRole, self.acgnx_asiaLabel)
447 | self.textAcgnxasiaName = QtWidgets.QLineEdit(parent=self.CookiesManagerTab)
448 | self.textAcgnxasiaName.setObjectName("textAcgnxasiaName")
449 | self.formLayout_3.setWidget(5, QtWidgets.QFormLayout.ItemRole.FieldRole, self.textAcgnxasiaName)
450 | self.textAcgnxasiaToken = QtWidgets.QLineEdit(parent=self.CookiesManagerTab)
451 | self.textAcgnxasiaToken.setObjectName("textAcgnxasiaToken")
452 | self.formLayout_3.setWidget(6, QtWidgets.QFormLayout.ItemRole.FieldRole, self.textAcgnxasiaToken)
453 | self.acgnx_globalLabel = QtWidgets.QLabel(parent=self.CookiesManagerTab)
454 | self.acgnx_globalLabel.setObjectName("acgnx_globalLabel")
455 | self.formLayout_3.setWidget(8, QtWidgets.QFormLayout.ItemRole.LabelRole, self.acgnx_globalLabel)
456 | self.textAcgnxglobalName = QtWidgets.QLineEdit(parent=self.CookiesManagerTab)
457 | self.textAcgnxglobalName.setObjectName("textAcgnxglobalName")
458 | self.formLayout_3.setWidget(8, QtWidgets.QFormLayout.ItemRole.FieldRole, self.textAcgnxglobalName)
459 | self.textAcgnxglobalToken = QtWidgets.QLineEdit(parent=self.CookiesManagerTab)
460 | self.textAcgnxglobalToken.setObjectName("textAcgnxglobalToken")
461 | self.formLayout_3.setWidget(9, QtWidgets.QFormLayout.ItemRole.FieldRole, self.textAcgnxglobalToken)
462 | self.label_11 = QtWidgets.QLabel(parent=self.CookiesManagerTab)
463 | self.label_11.setObjectName("label_11")
464 | self.formLayout_3.setWidget(6, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_11)
465 | self.label_19 = QtWidgets.QLabel(parent=self.CookiesManagerTab)
466 | self.label_19.setObjectName("label_19")
467 | self.formLayout_3.setWidget(9, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_19)
468 | self.gridLayout_3.addLayout(self.formLayout_3, 8, 0, 1, 4)
469 | self.textCookies = QtWidgets.QTextEdit(parent=self.CookiesManagerTab)
470 | self.textCookies.viewport().setProperty("cursor", QtGui.QCursor(QtCore.Qt.CursorShape.IBeamCursor))
471 | self.textCookies.setDocumentTitle("")
472 | self.textCookies.setLineWrapMode(QtWidgets.QTextEdit.LineWrapMode.NoWrap)
473 | self.textCookies.setObjectName("textCookies")
474 | self.gridLayout_3.addWidget(self.textCookies, 11, 0, 1, 4)
475 | self.label_14 = QtWidgets.QLabel(parent=self.CookiesManagerTab)
476 | self.label_14.setObjectName("label_14")
477 | self.gridLayout_3.addWidget(self.label_14, 10, 0, 1, 1)
478 | self.label_6 = QtWidgets.QLabel(parent=self.CookiesManagerTab)
479 | self.label_6.setObjectName("label_6")
480 | self.gridLayout_3.addWidget(self.label_6, 10, 1, 1, 3)
481 | self.buttonDeleteProfile = QtWidgets.QPushButton(parent=self.CookiesManagerTab)
482 | self.buttonDeleteProfile.setStyleSheet("color: rgb(255, 0, 30);\n"
483 | "font: 12pt \"Microsoft YaHei UI\";")
484 | self.buttonDeleteProfile.setObjectName("buttonDeleteProfile")
485 | self.gridLayout_3.addWidget(self.buttonDeleteProfile, 0, 3, 1, 1)
486 | spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
487 | self.gridLayout_3.addItem(spacerItem2, 9, 1, 1, 1)
488 | self.buttonSaveProfile = QtWidgets.QPushButton(parent=self.CookiesManagerTab)
489 | self.buttonSaveProfile.setObjectName("buttonSaveProfile")
490 | self.gridLayout_3.addWidget(self.buttonSaveProfile, 1, 3, 1, 1)
491 | self.formLayout_7 = QtWidgets.QFormLayout()
492 | self.formLayout_7.setObjectName("formLayout_7")
493 | self.label_13 = QtWidgets.QLabel(parent=self.CookiesManagerTab)
494 | self.label_13.setObjectName("label_13")
495 | self.formLayout_7.setWidget(1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_13)
496 | self.menuProfileSelection = QtWidgets.QComboBox(parent=self.CookiesManagerTab)
497 | self.menuProfileSelection.setObjectName("menuProfileSelection")
498 | self.menuProfileSelection.addItem("")
499 | self.formLayout_7.setWidget(1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.menuProfileSelection)
500 | self.label_15 = QtWidgets.QLabel(parent=self.CookiesManagerTab)
501 | self.label_15.setObjectName("label_15")
502 | self.formLayout_7.setWidget(2, QtWidgets.QFormLayout.ItemRole.LabelRole, self.label_15)
503 | self.textProfileName = QtWidgets.QLineEdit(parent=self.CookiesManagerTab)
504 | self.textProfileName.setObjectName("textProfileName")
505 | self.formLayout_7.setWidget(2, QtWidgets.QFormLayout.ItemRole.FieldRole, self.textProfileName)
506 | self.gridLayout_3.addLayout(self.formLayout_7, 0, 0, 2, 2)
507 | self.tab.addTab(self.CookiesManagerTab, "")
508 | self.ProxyTab = QtWidgets.QWidget()
509 | self.ProxyTab.setObjectName("ProxyTab")
510 | self.formLayout_4 = QtWidgets.QFormLayout(self.ProxyTab)
511 | self.formLayout_4.setObjectName("formLayout_4")
512 | self.Label = QtWidgets.QLabel(parent=self.ProxyTab)
513 | self.Label.setObjectName("Label")
514 | self.formLayout_4.setWidget(1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.Label)
515 | self.menuProxyType = QtWidgets.QComboBox(parent=self.ProxyTab)
516 | self.menuProxyType.setObjectName("menuProxyType")
517 | self.menuProxyType.addItem("")
518 | self.menuProxyType.addItem("")
519 | self.formLayout_4.setWidget(1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.menuProxyType)
520 | self.hostLabel = QtWidgets.QLabel(parent=self.ProxyTab)
521 | self.hostLabel.setObjectName("hostLabel")
522 | self.formLayout_4.setWidget(2, QtWidgets.QFormLayout.ItemRole.LabelRole, self.hostLabel)
523 | self.textProxyHost = QtWidgets.QLineEdit(parent=self.ProxyTab)
524 | self.textProxyHost.setPlaceholderText("")
525 | self.textProxyHost.setObjectName("textProxyHost")
526 | self.formLayout_4.setWidget(2, QtWidgets.QFormLayout.ItemRole.FieldRole, self.textProxyHost)
527 | self.buttonSaveProxy = QtWidgets.QPushButton(parent=self.ProxyTab)
528 | self.buttonSaveProxy.setObjectName("buttonSaveProxy")
529 | self.formLayout_4.setWidget(3, QtWidgets.QFormLayout.ItemRole.SpanningRole, self.buttonSaveProxy)
530 | self.textAboutProgram = QtWidgets.QLabel(parent=self.ProxyTab)
531 | self.textAboutProgram.setAutoFillBackground(True)
532 | self.textAboutProgram.setTextFormat(QtCore.Qt.TextFormat.RichText)
533 | self.textAboutProgram.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
534 | self.textAboutProgram.setOpenExternalLinks(True)
535 | self.textAboutProgram.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.TextBrowserInteraction)
536 | self.textAboutProgram.setObjectName("textAboutProgram")
537 | self.formLayout_4.setWidget(7, QtWidgets.QFormLayout.ItemRole.SpanningRole, self.textAboutProgram)
538 | self.label_20 = QtWidgets.QLabel(parent=self.ProxyTab)
539 | self.label_20.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
540 | self.label_20.setObjectName("label_20")
541 | self.formLayout_4.setWidget(0, QtWidgets.QFormLayout.ItemRole.SpanningRole, self.label_20)
542 | self.label_21 = QtWidgets.QLabel(parent=self.ProxyTab)
543 | self.label_21.setAutoFillBackground(True)
544 | self.label_21.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
545 | self.label_21.setObjectName("label_21")
546 | self.formLayout_4.setWidget(6, QtWidgets.QFormLayout.ItemRole.SpanningRole, self.label_21)
547 | spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
548 | self.formLayout_4.setItem(5, QtWidgets.QFormLayout.ItemRole.SpanningRole, spacerItem3)
549 | spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
550 | self.formLayout_4.setItem(4, QtWidgets.QFormLayout.ItemRole.SpanningRole, spacerItem4)
551 | self.tab.addTab(self.ProxyTab, "")
552 | self.gridLayout_2.addWidget(self.tab, 0, 0, 1, 1)
553 | MainWindow.setCentralWidget(self.centralwidget)
554 |
555 | self.retranslateUi(MainWindow)
556 | self.tab.setCurrentIndex(0)
557 | QtCore.QMetaObject.connectSlotsByName(MainWindow)
558 |
559 | def retranslateUi(self, MainWindow):
560 | _translate = QtCore.QCoreApplication.translate
561 | MainWindow.setWindowTitle(_translate("MainWindow", "OKPGUI by AmusementClub"))
562 | self.buttonDeleteTemplate.setText(_translate("MainWindow", "删除模板"))
563 | self.label_10.setText(_translate("MainWindow", "请使用 Markdown 编写"))
564 | self.label_4.setText(_translate("MainWindow", "集数匹配"))
565 | self.textEpPattern.setToolTip(_translate("MainWindow", "测试
"))
566 | self.textEpPattern.setWhatsThis(_translate("MainWindow", "测试
"))
567 | self.textEpPattern.setPlaceholderText(_translate("MainWindow", "在文件名集数部分使用 替换,分辨率用 替换"))
568 | self.label_5.setText(_translate("MainWindow", "标题匹配"))
569 | self.Lable.setText(_translate("MainWindow", "标题"))
570 | self.label_7.setText(_translate("MainWindow", "海报链接"))
571 | self.textPoster.setPlaceholderText(_translate("MainWindow", "For dmhy.org"))
572 | self.label_8.setText(_translate("MainWindow", "关于"))
573 | self.textAbout.setPlaceholderText(_translate("MainWindow", "For nyaa.si"))
574 | self.textTags.setToolTip(_translate("MainWindow", "输入标签,以英文逗号分隔,可用标签可参考 “如何使用 tags?”"))
575 | self.label_18.setText(_translate("MainWindow", "Tags"))
576 | self.label_2.setText(_translate("MainWindow", "模板名称"))
577 | self.label.setText(_translate("MainWindow", "选择模板"))
578 | self.menuTemplateSelection.setWhatsThis(_translate("MainWindow", "第一次使用请选择「新模板」
"))
579 | self.label_3.setText(_translate("MainWindow", "种子文件"))
580 | self.textTorrentPath.setPlaceholderText(_translate("MainWindow", "可直接 .torrent 文件拖放到此处"))
581 | self.buttonSaveTemplate.setText(_translate("MainWindow", "保存模板"))
582 | self.label_9.setText(_translate("MainWindow", "内容"))
583 | self.label_22.setText(_translate("MainWindow", "如何使用 tags?
"))
584 | self.buttonPreviewMarkdown.setText(_translate("MainWindow", "预览"))
585 | self.buttonBrowse.setText(_translate("MainWindow", "浏览"))
586 | self.fileTree.setSortingEnabled(True)
587 | self.fileTree.headerItem().setText(0, _translate("MainWindow", "Files"))
588 | self.fileTree.headerItem().setText(1, _translate("MainWindow", "Size"))
589 | self.buttonOKP.setText(_translate("MainWindow", "One Key Publish!"))
590 | self.label_12.setText(_translate("MainWindow", "选择身份"))
591 | self.checkboxDmhyPublish.setText(_translate("MainWindow", "dmhy"))
592 | self.checkboxBangumiPublish.setText(_translate("MainWindow", "bangumi"))
593 | self.checkboxNyaaPublish.setText(_translate("MainWindow", "nyaa"))
594 | self.checkboxAcgripPublish.setText(_translate("MainWindow", "acg.rip"))
595 | self.checkboxAcgnxasiaPublish.setText(_translate("MainWindow", "acgnx_asia"))
596 | self.checkboxAcgnxglobalPublish.setText(_translate("MainWindow", "acgnx_global"))
597 | self.tab.setTabText(self.tab.indexOf(self.HomeTab), _translate("MainWindow", "主页"))
598 | self.label_16.setText(_translate("MainWindow", "登录发布网站"))
599 | self.label_17.setText(_translate("MainWindow", "发布组名称"))
600 | self.buttonDmhyLogin.setText(_translate("MainWindow", "dmhy"))
601 | self.buttonNyaaLogin.setText(_translate("MainWindow", "nyaa"))
602 | self.buttonAcgripLogin.setText(_translate("MainWindow", "acg.rip"))
603 | self.buttonBangumiLogin.setText(_translate("MainWindow", "bangumi"))
604 | self.acgnx_asiaLabel.setText(_translate("MainWindow", "acgnx_asia UID"))
605 | self.textAcgnxasiaName.setPlaceholderText(_translate("MainWindow", "填写 acgnx UID"))
606 | self.textAcgnxasiaToken.setPlaceholderText(_translate("MainWindow", "填写 acgnx asia API Token"))
607 | self.acgnx_globalLabel.setText(_translate("MainWindow", "acgnx_global UID"))
608 | self.textAcgnxglobalName.setPlaceholderText(_translate("MainWindow", "填写 acgnx UID"))
609 | self.textAcgnxglobalToken.setPlaceholderText(_translate("MainWindow", "填写 acgnx global API Token"))
610 | self.label_11.setText(_translate("MainWindow", "Token"))
611 | self.label_19.setText(_translate("MainWindow", "Token"))
612 | self.label_14.setText(_translate("MainWindow", "Cookies 文件:"))
613 | self.label_6.setText(_translate("MainWindow", "本页中的内容需要保存身份后才会生效。"))
614 | self.buttonDeleteProfile.setText(_translate("MainWindow", "删除身份"))
615 | self.buttonSaveProfile.setText(_translate("MainWindow", "保存身份"))
616 | self.label_13.setText(_translate("MainWindow", "选择身份"))
617 | self.menuProfileSelection.setWhatsThis(_translate("MainWindow", "第一次使用请选择「新模板」
"))
618 | self.menuProfileSelection.setItemText(0, _translate("MainWindow", "新身份"))
619 | self.label_15.setText(_translate("MainWindow", "身份名称"))
620 | self.tab.setTabText(self.tab.indexOf(self.CookiesManagerTab), _translate("MainWindow", "身份管理器"))
621 | self.Label.setText(_translate("MainWindow", "代理类型"))
622 | self.menuProxyType.setItemText(0, _translate("MainWindow", "不使用代理"))
623 | self.menuProxyType.setItemText(1, _translate("MainWindow", "HTTP"))
624 | self.hostLabel.setText(_translate("MainWindow", "Host"))
625 | self.textProxyHost.setText(_translate("MainWindow", "http://127.0.0.1:7890"))
626 | self.buttonSaveProxy.setText(_translate("MainWindow", "应用"))
627 | self.textAboutProgram.setText(_translate("MainWindow", "\n"
628 | "\n"
631 | "此软件为 OKP 的 GUI,由娱乐部制作,用于快速在多个 BT 资源站发布种子。
\n"
632 | "使用方法参见 GitHub 上的 README。
\n"
633 | "
\n"
634 | "Version: 0.0.1 Alpha 内部测试版。
\n"
635 | "
\n"
636 | "
\n"
637 | "
\n"
638 | "
\n"
639 | "
\n"
640 | "作者:tastySugar
\n"
641 | "
\n"
642 | "
"))
643 | self.label_20.setText(_translate("MainWindow", "代理设置"))
644 | self.label_21.setText(_translate("MainWindow", "关于"))
645 | self.tab.setTabText(self.tab.indexOf(self.ProxyTab), _translate("MainWindow", "杂项"))
646 |
647 |
648 | if __name__ == "__main__":
649 | import sys
650 | app = QtWidgets.QApplication(sys.argv)
651 | MainWindow = QtWidgets.QMainWindow()
652 | ui = Ui_MainWindow()
653 | ui.setupUi(MainWindow)
654 | MainWindow.show()
655 | sys.exit(app.exec())
656 |
--------------------------------------------------------------------------------
/ProcessWindow.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | from PyQt6.QtCore import pyqtSignal, pyqtSlot, QProcess
4 | from PyQt6.QtGui import QTextCursor, QFont
5 | from PyQt6.QtWidgets import QApplication, QPlainTextEdit, QWidget, QVBoxLayout, QPushButton
6 | import locale
7 |
8 |
9 | class ProcessOutputReader(QProcess):
10 | produce_output = pyqtSignal(str)
11 |
12 | def __init__(self, parent=None):
13 | super().__init__(parent=parent)
14 |
15 | # merge stderr channel into stdout channel
16 | self.setProcessChannelMode(QProcess.ProcessChannelMode.MergedChannels)
17 |
18 |
19 | # only necessary when stderr channel isn't merged into stdout:
20 | # self._decoder_stderr = codec.makeDecoder()
21 |
22 | self.readyReadStandardOutput.connect(self._ready_read_standard_output)
23 | # only necessary when stderr channel isn't merged into stdout:
24 | # self.readyReadStandardError.connect(self._ready_read_standard_error)
25 |
26 | @pyqtSlot()
27 | def _ready_read_standard_output(self):
28 | raw_bytes = self.readAllStandardOutput()
29 | text = raw_bytes.data().decode(locale.getencoding())
30 | self.produce_output.emit(text)
31 |
32 |
33 |
34 | class MyConsoleWidget(QPlainTextEdit):
35 |
36 | def __init__(self, parent=None):
37 | super().__init__(parent=parent)
38 |
39 | self.setReadOnly(True)
40 | self.setMaximumBlockCount(10000) # limit console to 10000 lines
41 |
42 |
43 | self._cursor_output = self.textCursor()
44 |
45 | @pyqtSlot(str)
46 | def append_output(self, text):
47 | self._cursor_output.insertText(text)
48 | self.scroll_to_last_line()
49 |
50 | def scroll_to_last_line(self):
51 | cursor = self.textCursor()
52 | cursor.movePosition(QTextCursor.MoveOperation.End)
53 | cursor.movePosition(QTextCursor.MoveOperation.Up if cursor.atBlockStart() else
54 | QTextCursor.MoveOperation.StartOfLine)
55 | self.setTextCursor(cursor)
56 |
57 | class MyConsole(QWidget):
58 | def __init__(self, parentWindow, *args, **kwargs):
59 | super(QWidget, self).__init__(*args, **kwargs)
60 | self.resize(800, 600)
61 | self.vbox = QVBoxLayout(self)
62 |
63 | self.publishButton = QPushButton(self)
64 | self.publishButton.setText("确定")
65 | font = QFont()
66 | font.setPointSize(20)
67 | self.publishButton.setFont(font)
68 |
69 | self.publishButton.clicked.connect(self.onPublishButton)
70 | self.reader = ProcessOutputReader()
71 | self.consoleWidget = MyConsoleWidget()
72 |
73 | self.reader.produce_output.connect(self.consoleWidget.append_output)
74 |
75 | self.vbox.addWidget(self.consoleWidget)
76 | self.vbox.addWidget(self.publishButton)
77 |
78 | self.setWindowTitle("OKP 运行中…")
79 |
80 | #self.reader.start('python', ['test.py']) # start the process
81 |
82 | def onPublishButton(self):
83 | self.consoleWidget.append_output("\n")
84 | self.reader.write(b"\n")
85 |
86 | def start(self, *args, **kargs):
87 | self.reader.start(*args, **kargs)
88 |
89 | def onFinished(self, func):
90 | self.reader.finished.connect(func)
91 |
92 | def closeEvent(self, event):
93 | self.reader.terminate()
94 |
95 |
96 |
97 |
98 | if __name__ == "__main__":
99 | # create the application instance
100 | app = QApplication(sys.argv)
101 | # create a console and connect the process output reader to it
102 | console = MyConsole(None)
103 | console.start('python', ['test.py'])
104 |
105 | console.show()
106 | app.exec()
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OKPGUI
2 |
3 | 此软件为 [OKP](https://github.com/AmusementClub/OKP/) 的 GUI,由娱乐部制作,用于快速在多个 BT 资源站发布种子。
4 |
5 | ### 支持站点
6 |
7 | _以下排名无先后_
8 |
9 | | 站点 | 代号 |
10 | | --------------------------------------------- | ------------ |
11 | | [Nyaa](https://nyaa.si/) | nyaa |
12 | | [動漫花園](https://share.dmhy.org/) | dmhy |
13 | | [ACG.RIP](https://acg.rip/) | acgrip |
14 | | [末日動漫資源庫](https://share.acgnx.se/) | acgnx_asia |
15 | | [AcgnX Torrent Global](https://www.acgnx.se/) | acgnx_global |
16 | | [萌番组](https://bangumi.moe/) | bangumi |
17 |
18 | 注:
19 |
20 | 1. acgrip cookie 失效后会刷新,退出登录疑似会直接失效,ua 不同也会登录失败。
21 | 2. acgnx 站点登录可能会被 Cloudflare 风控,鉴于其站点会同步 nyaa、dmhy、acgrip 的种子,可以选择不使用其上传。
22 | 3. 萌番组暂不支持自定义 TAG,目前仅支持 _Team ID_ 和 setting 中 tags 映射的分类两个 TAG。
23 |
24 |
25 |
26 | ## 使用方法
27 |
28 | **请将 Cookies 视为你的账户密码并妥善保护,任何获取到 Cookies 文件(以及`okpgui_profile.yml`)的人都可以轻易登录你的账户。**
29 |
30 | ### 快速开始
31 |
32 | 1. 将程序`OKPGUI.exe`复制到`OKP.Core.exe`的同一个文件夹下,并打开程序。
33 | [点此下载](https://github.com/AmusementClub/OKP/releases)最新版 OKP,[点此下载](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) OKP 的依赖 .NET 6 Runtime。
34 | > 若你使用的是非 Windows 操作系统,请将 `OKP.Core` 重命名为 `OKP.Core.exe`。
35 |
36 | 2. 第一次使用时,请先使用`身份管理器`创建一个新的`身份`,一个`身份`中记载了你的登录发布站的 Cookies 和 API Token。
37 | 点击`身份管理器`进入身份管理器页面。
38 |
39 | 
40 |
41 | 3. 在`身份名称`一栏中给你的身份输入一个名字,例如说 `LoliHouse`。
42 |
43 | 4. 在下方的登录发布网站中,点击左边的网站按钮(例如 dmhy)可以打开网站的登录页面,打开后,输入用户名密码。在登录网站后点击`保存 Cookies`或者直接点击关闭。
44 | 
45 |
46 | 5. 此时,你可以看到`Cookies 文件`框中多出了一些 cookies 内容。如果你不理解你在做什么,请不要手动改动它。
47 |
48 | 6. 一些网站有多个发布身份,你需要在网站右边的发布组名称框中填入你想选用的发布身份,例如说 `LoliHouse`。
49 |
50 | 
51 |
52 | 7. 对于 [acgnx_asia](https://share.acgnx.se/) 和 [acgnx_global](https://www.acgnx.se/),请前往「发布资源 - 账户 API 设置」中,将 UID 和 API Token 填入对应的文本框中。注意,在填写后必须点击`保存身份`才会生效。
53 |
54 | 
55 |
56 | 
57 |
58 | 8. 在登录完所有需要的网站之后,点击`保存身份`,使身份信息得到保存。
59 |
60 | 9. 然后,我们可以点击`主页`回到主页,并创建一个新的模板。
61 |
62 | 10. 在一开始,我们还是在`模板名称`中给你的模板起个名字,例如说`Onimai`。
63 |
64 | 11. 点击`浏览`打开一个种子文件,或者也可以直接把种子文件拖放到窗口中。
65 |
66 | 12. 在`标题`一栏中填入发布标题,例如说`[SweetSub&LoliHouse] 不当哥哥了!/ Oniichan ha Oshimai! - 01 [WebRip 1080p HEVC-10bit AAC][简繁日内封字幕]`
67 |
68 | 13. 如果要发布在动漫花园,在`海报链接`一栏填入海报的 url,如果要发布在 nyaa.si,可以在`关于`里面填入 about 信息。
69 |
70 | 14. `Tags` 决定了资源在各个网站的分类,所有支持的标签和详细规则请参考 [OKP 的 wiki](https://github.com/AmusementClub/OKP/wiki/TagsConvert)。
71 | > 在填写时以逗号分隔开,例如在发布合集时填写 `Anime, Collection`(逗号后是否空格不会影响程序判断)。平时发布单集时填写 `Anime` 即可。
72 | > 注意: **这不是 bangumi.moe 的 tag 系统。**
73 |
74 | 15. `集数匹配`和`标题匹配`是选填项,其使用方法请[参考此处](#标题匹配)。
75 |
76 | 16. 在`内容`栏中使用 **markdown** 格式填写发布贴的详细内容,填写完毕后点击`预览`按钮可以预览其显示效果。
77 |
78 | > 暂不支持用户使用 html 或 bbcode
79 |
80 | 17. 在`选择身份`的选单中选择发布时使用的身份,在此例子中,我们选择刚才在身份管理器中创建的`LoliHouse`身份,如果需要其他的身份,请移步至身份管理器中创建。
81 |
82 | 18. 在下方需要发布的站点的复选框中打钩。
83 | > 如果发现某个网站不能打钩,说明选择的身份中并没有添加这个网站的信息(Cookies/API Token),请移步身份管理器中编辑身份。
84 |
85 | 
86 |
87 | 19. 发布时不需要保存模板,程序会根据目前文本框中的内容和选项来发布,也就是说,即使有模板,但每次发布前也可以手动微调发布标题或者是内容等。如果想要下次重复利用此发布模板,请别忘记了保存模板。
88 |
89 | 20. 点击`One Key Publish!`呼出 OKP 一键发布,在打开的控制台中,确认发布的标题和种子文件准确无误后,点击确定按钮即可发布。
90 |
91 | 
92 |
93 |
94 | ---
95 |
96 | ### 其他功能
97 |
98 | #### 1. 标题匹配
99 |
100 | 此功能是为了可以方便地根据种子文件名中的信息自动生成不同的发布标题。
101 |
102 |
103 | 例如说我们有一个种子文件,其名称为
104 |
105 | ```
106 | [SweetSub] Oniichan ha Oshimai! - 01 [WebRip][1080P][AVC 8bit][CHS].mp4.torrent
107 | ```
108 |
109 | 然后我们还有其不同分辨率,不同集数的版本,例如说:
110 |
111 | ```
112 | [SweetSub] Oniichan ha Oshimai! - 01 [WebRip][720P][AVC 8bit][CHS].mp4.torrent
113 | [SweetSub] Oniichan ha Oshimai! - 02 [WebRip][1080P][AVC 8bit][CHS].mp4.torrent
114 | ```
115 |
116 | 我们不想重复地手动更改发布标题,这样非常麻烦。标题匹配功能可以在种子文件中寻找需要的信息并填写到标题中。
117 |
118 | ##### 使用方法:
119 |
120 | 在集数匹配一栏中,把文件名中的重要信息用 `<>` 标签替换,并在其中填入字符来命名,例如说
121 |
122 | **集数匹配**:
123 | ```
124 | [SweetSub] Oniichan ha Oshimai! - [WebRip][P][AVC 8bit]
125 | ```
126 |
127 | 我们再在标题匹配中以 `<>` 标签替换相应的值
128 |
129 | **标题匹配**:
130 | ```
131 | [SweetSub][不当哥哥了!][Oniichan ha Oshimai!][][WebRip][P][AVC 8bit][简日双语][无修版]
132 | ```
133 |
134 | 如果我们此时添加一个种子文件
135 |
136 | 例如:
137 | ```
138 | [SweetSub] Oniichan ha Oshimai! - 01 [WebRip][720P][AVC 8bit][CHS].mp4.torrent
139 | ```
140 |
141 | 程序会根据种子文件的`文件名`和`集数匹配`中的字符串来确定 `` 的值为 `01`,`` 的值为 `720`
142 |
143 | 这些值会被自动代入到`标题匹配`的模板中,程序会自动生成发布标题:
144 |
145 | ```
146 | [SweetSub][不当哥哥了!][Oniichan ha Oshimai!][01][WebRip][720P][AVC 8bit][简日双语][无修版]
147 | ```
148 |
149 | 注意:
150 | 1. 虽然例子中只用了``和``,但你可以使用任意数量的`<>`标签,里面可以填写任意英文单词。
151 | 2. 因为没有设置转义,所以如果发布标题中使用了`<>`,则不可以使用标题匹配的功能。
152 |
153 |
154 | #### 2. 代理
155 |
156 | 为了方便对抗 GFW,程序内置了 HTTP 代理功能。
157 |
158 | 在`杂项`选项卡中选择代理类型 HTTP,并且在 Host 一栏中填入 HTTP 代理的地址,例如说 `http://127.0.0.1:7890`,然后点击`保存身份`,即可在访问网站时使用代理,同时,该代理也会直接传给 OKP.Core 所使用。
159 |
160 | 注意,代理的改动必须点击`应用`后才会生效。
161 |
162 | 
163 |
164 | ---
165 |
166 | ## 开发、编译此程序
167 |
168 |
169 |
170 | 请先下载代码或者 clone 此 repository 到本地,然后 cd 到根目录下,使用 python 创建一个虚拟环境,并安装依赖。
171 |
172 | ```
173 | python -m venv venv
174 | venv\Scripts\activate.bat
175 | pip3 install install setuptools==57.5.0 --upgrade
176 | pip3 install -r requirements.txt
177 | ```
178 |
179 | 如果使用非 cmd,请将第二步换成对应的脚本。
180 |
181 | 第三步是因为安装 html2phpbbcode 需要 use_2to3 支持。
182 |
183 | 运行 `main.py` 或者 `OKPLogic.py` 即可开始 debug。
184 |
185 | 如果需要改变 UI,请使用 [qt designer](https://build-system.fman.io/qt-designer-download) 打开 OKP.ui 或者其他 .ui 文件,在完成编辑后请调用 `Compile_UI.bat` 来将 .ui 文件编译为 .py 文件。
186 |
187 | 注意:绝对不要手动编辑编译后的 .py 文件,因为重新编译之后所有改动将会丢失。
188 |
189 | 在完成修改后可以调用 `make.bat` 将程序编译为 exe 文件。
190 |
191 | ----
192 |
193 | ## 常见问题
194 |
195 | 1. 为什么无法勾选某发布网站?
196 |
197 | 如果发现某个网站不能勾选,则说明选择的身份中并没有添加这个网站的信息(Cookies/API Token),请移步身份管理器中编辑身份。
198 |
199 | 2. 在设置好了 cookies 之后无法访问某个发布站。
200 |
201 | a. 请检查是否下载最新版的 OKP.Core。
202 | b. 尝试删除身份管理器中发布站对应的 cookies 信息,点击保存身份,然后重新登录。
203 |
204 | 3. 能否让用户自行提交发布站所需的 html, bbcode 发布内容?
205 |
206 | 作者认为 Less is more,简单的 Markdown 足以满足一般的排版需求,所以暂时不会加入提交 html, bbcode 的功能。
207 | 如有此功能的需求,可以使用 OKP.Core 的 CLI。
208 |
209 | 4. 有 Bug
210 |
211 | 去[此页面](https://github.com/AmusementClub/OKPGUI/issues/new/choose)创建一个 issue 并选择 Bug report,请尽量准确地描述要如何重现此 bug。
212 |
213 | 5. 想要新功能
214 |
215 | 去[此页面](https://github.com/AmusementClub/OKPGUI/issues/new/choose)创建一个 issue 并选择 Feature request。
--------------------------------------------------------------------------------
/WarningDialog.py:
--------------------------------------------------------------------------------
1 | # Form implementation generated from reading ui file 'WarningDialog.ui'
2 | #
3 | # Created by: PyQt6 UI code generator 6.4.2
4 | #
5 | # WARNING: Any manual changes made to this file will be lost when pyuic6 is
6 | # run again. Do not edit this file unless you know what you are doing.
7 |
8 |
9 | from PyQt6 import QtCore, QtGui, QtWidgets
10 |
11 |
12 | class Ui_Dialog(object):
13 | def setupUi(self, Dialog):
14 | Dialog.setObjectName("Dialog")
15 | Dialog.resize(300, 106)
16 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Expanding)
17 | sizePolicy.setHorizontalStretch(0)
18 | sizePolicy.setVerticalStretch(0)
19 | sizePolicy.setHeightForWidth(Dialog.sizePolicy().hasHeightForWidth())
20 | Dialog.setSizePolicy(sizePolicy)
21 | self.verticalLayout_2 = QtWidgets.QVBoxLayout(Dialog)
22 | self.verticalLayout_2.setObjectName("verticalLayout_2")
23 | self.label = QtWidgets.QLabel(parent=Dialog)
24 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
25 | sizePolicy.setHorizontalStretch(0)
26 | sizePolicy.setVerticalStretch(0)
27 | sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
28 | self.label.setSizePolicy(sizePolicy)
29 | font = QtGui.QFont()
30 | font.setFamily("Microsoft YaHei UI")
31 | font.setPointSize(15)
32 | font.setBold(True)
33 | font.setWeight(75)
34 | self.label.setFont(font)
35 | self.label.setTextFormat(QtCore.Qt.TextFormat.PlainText)
36 | self.label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
37 | self.label.setWordWrap(True)
38 | self.label.setObjectName("label")
39 | self.verticalLayout_2.addWidget(self.label)
40 | self.buttonBox = QtWidgets.QDialogButtonBox(parent=Dialog)
41 | self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal)
42 | self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok)
43 | self.buttonBox.setObjectName("buttonBox")
44 | self.verticalLayout_2.addWidget(self.buttonBox)
45 |
46 | self.retranslateUi(Dialog)
47 | self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
48 | self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
49 | QtCore.QMetaObject.connectSlotsByName(Dialog)
50 |
51 | def retranslateUi(self, Dialog):
52 | _translate = QtCore.QCoreApplication.translate
53 | Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
54 | self.label.setText(_translate("Dialog", "TextLabel"))
55 |
56 |
57 | if __name__ == "__main__":
58 | import sys
59 | app = QtWidgets.QApplication(sys.argv)
60 | Dialog = QtWidgets.QDialog()
61 | ui = Ui_Dialog()
62 | ui.setupUi(Dialog)
63 | Dialog.show()
64 | sys.exit(app.exec())
65 |
--------------------------------------------------------------------------------
/WarningDialog.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Dialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 300
10 | 106
11 |
12 |
13 |
14 |
15 | 0
16 | 0
17 |
18 |
19 |
20 | Dialog
21 |
22 |
23 | -
24 |
25 |
26 |
27 | 0
28 | 0
29 |
30 |
31 |
32 |
33 | Microsoft YaHei UI
34 | 15
35 | 75
36 | true
37 |
38 |
39 |
40 | TextLabel
41 |
42 |
43 | Qt::PlainText
44 |
45 |
46 | Qt::AlignCenter
47 |
48 |
49 | true
50 |
51 |
52 |
53 | -
54 |
55 |
56 | Qt::Horizontal
57 |
58 |
59 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | buttonBox
71 | accepted()
72 | Dialog
73 | accept()
74 |
75 |
76 | 248
77 | 254
78 |
79 |
80 | 157
81 | 274
82 |
83 |
84 |
85 |
86 | buttonBox
87 | rejected()
88 | Dialog
89 | reject()
90 |
91 |
92 | 316
93 | 260
94 |
95 |
96 | 286
97 | 274
98 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/WebHelper.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | from PyQt6.QtCore import QUrl, QByteArray, QSize, Qt
4 | from PyQt6.QtWebEngineWidgets import QWebEngineView
5 | from PyQt6.QtWebEngineCore import QWebEngineProfile
6 | from PyQt6.QtWidgets import QApplication, QTextEdit, QPushButton, QToolBar, QMainWindow, QDialog, QWidget, QVBoxLayout
7 | from PyQt6.QtNetwork import QNetworkCookie, QNetworkProxy
8 | from PyQt6.QtGui import QAction
9 | from urllib import parse
10 | import traceback
11 | import datetime
12 |
13 |
14 | def bytestostr(data):
15 | if isinstance(data, str):
16 | return data
17 | if isinstance(data, QByteArray):
18 | data = data.data()
19 | if isinstance(data, bytes):
20 | data = data.decode(errors='ignore')
21 | else:
22 | data = str(data)
23 | return data
24 |
25 | def cookiesToStr(cookie: QNetworkCookie):
26 | domain = cookie.domain()
27 | path = cookie.path()
28 | name = bytestostr(cookie.name().data())
29 | value = bytestostr(cookie.value().data())
30 |
31 | flags = []
32 | flags.append(f"{name}={value}")
33 | flags.append(f"domain={domain}")
34 | flags.append(f"path={path}")
35 |
36 | if cookie.isSecure(): flags.append("Secure")
37 | if cookie.isHttpOnly(): flags.append("HttpOnly")
38 | match cookie.sameSitePolicy():
39 | case QNetworkCookie.SameSite.Default:
40 | pass
41 | case QNetworkCookie.SameSite.Lax:
42 | flags.append("SameSite=Lax")
43 | case QNetworkCookie.SameSite.Strict:
44 | flags.append("SameSite=Strict")
45 | case QNetworkCookie.SameSite.None_:
46 | flags.append("SameSite=None")
47 |
48 | #now2 = datetime.datetime.fromisoformat(now.toString(Qt.DateFormat.ISODate))
49 | #strftime("%a, %d %b %Y %H:%M:%S %Z")
50 | if not cookie.isSessionCookie():
51 | time = cookie.expirationDate().toString(Qt.DateFormat.ISODate)
52 | time = datetime.datetime.fromisoformat(time).strftime("%a, %d %b %Y %H:%M:%S GMT")
53 | flags.append(f"expires={time}")
54 |
55 | flags = "; ".join(flags)
56 |
57 | return f"https://{domain}\t{flags}"
58 |
59 | def filterCookies(cookie: QNetworkCookie) -> bool:
60 | if cookie.domain() == "share.dmhy.org":
61 | if bytestostr(cookie.name().data()) in {"pass", "rsspass", "tid", "uname", "uid"}:
62 | return True
63 | if cookie.domain() == "nyaa.si":
64 | if bytestostr(cookie.name().data()) == "session":
65 | return True
66 | if cookie.domain() == "acg.rip":
67 | if bytestostr(cookie.name().data()) == "_kanako_session":
68 | return True
69 | if cookie.domain() == "bangumi.moe":
70 | if bytestostr(cookie.name().data()) in {"locale", "koa:sess", "koa:sess.sig"}:
71 | return True
72 | return False
73 |
74 |
75 | class WebEngineView(QWidget):
76 |
77 | def __init__(self, url, parentWindow, *args, **kwargs):
78 | super(QWidget, self).__init__(*args, **kwargs)
79 | QWebEngineProfile.defaultProfile().cookieStore().cookieAdded.connect(self.onCookieAdd)
80 | self.resize(1920, 600)
81 | self.parentWindow = parentWindow
82 | self.browser = QWebEngineView()
83 | self.browser.loadFinished.connect(self.onLoadFinished)
84 |
85 |
86 | vbox = QVBoxLayout(self)
87 |
88 |
89 | toolbar = QToolBar('toolbar')
90 | self.saveButton = QAction("保存 cookies", parent=self)
91 | backButton = QAction("后退", parent=self)
92 | refreshButton = QAction("刷新", parent=self)
93 |
94 | backButton.triggered.connect(self.browser.back)
95 | refreshButton.triggered.connect(self.browser.reload)
96 | self.saveButton.triggered.connect(self.saveCookies)
97 |
98 | toolbar.addAction(backButton)
99 | toolbar.addAction(refreshButton)
100 | toolbar.addAction(self.saveButton)
101 |
102 | self.cookies = []
103 | vbox.addWidget(toolbar)
104 | vbox.addWidget(self.browser)
105 | self.setLayout(vbox)
106 |
107 | if parentWindow.menuProxyType.currentText == "HTTP":
108 | parsed = parse.urlparse(self.parentWindow.profile['proxy'])
109 | self.proxy = QNetworkProxy(QNetworkProxy.ProxyType.HttpProxy, hostName=parsed.hostname, port=parsed.port)
110 | QNetworkProxy.setApplicationProxy(self.proxy)
111 |
112 |
113 | self.browser.load(url)
114 |
115 |
116 | def closeEvent(self, event):
117 | self.parentWindow.addCookies("\n".join(self.cookies))
118 | self.parentWindow.setUserAgent(self.browser.page().profile().httpUserAgent())
119 | self.parentWindow.saveProfile()
120 | super(WebEngineView, self).closeEvent(event)
121 |
122 | def onLoadFinished(self):
123 | pass
124 |
125 | def onCookieAdd(self, cookie:QNetworkCookie):
126 | if filterCookies(cookie):
127 | self.cookies.append(cookiesToStr(cookie))
128 |
129 |
130 | def saveCookies(self):
131 | self.close()
132 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/image/Console.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmusementClub/OKPGUI/368409a4f93294db1db3f30aa88056ccab0b3060/image/Console.jpg
--------------------------------------------------------------------------------
/image/Home01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmusementClub/OKPGUI/368409a4f93294db1db3f30aa88056ccab0b3060/image/Home01.jpg
--------------------------------------------------------------------------------
/image/ProfileManager01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmusementClub/OKPGUI/368409a4f93294db1db3f30aa88056ccab0b3060/image/ProfileManager01.jpg
--------------------------------------------------------------------------------
/image/ProfileManager02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmusementClub/OKPGUI/368409a4f93294db1db3f30aa88056ccab0b3060/image/ProfileManager02.jpg
--------------------------------------------------------------------------------
/image/ProfileManager03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmusementClub/OKPGUI/368409a4f93294db1db3f30aa88056ccab0b3060/image/ProfileManager03.jpg
--------------------------------------------------------------------------------
/image/Proxy.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmusementClub/OKPGUI/368409a4f93294db1db3f30aa88056ccab0b3060/image/Proxy.jpg
--------------------------------------------------------------------------------
/image/acgnx.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmusementClub/OKPGUI/368409a4f93294db1db3f30aa88056ccab0b3060/image/acgnx.jpg
--------------------------------------------------------------------------------
/image/login.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmusementClub/OKPGUI/368409a4f93294db1db3f30aa88056ccab0b3060/image/login.jpg
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | from PyQt6.QtWidgets import QApplication
4 | from OKPLogic import OKPMainWIndow
5 | import platform
6 |
7 |
8 | if __name__ == '__main__':
9 | app = QApplication(sys.argv)
10 | if platform.system() != "Windows":
11 | app.setStyle('Fusion')
12 |
13 | window = OKPMainWIndow()
14 | window.show()
15 | sys.exit(app.exec())
16 |
--------------------------------------------------------------------------------
/make.bat:
--------------------------------------------------------------------------------
1 | pyinstaller --onefile --noconsole main.py --collect-all html2phpbbcode --version-file versionfile.rc -n OKPGUI.exe
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmusementClub/OKPGUI/368409a4f93294db1db3f30aa88056ccab0b3060/requirements.txt
--------------------------------------------------------------------------------
/test.py:
--------------------------------------------------------------------------------
1 | input("do you want to write?")
2 |
3 | print("Nice")
--------------------------------------------------------------------------------
/venv/Lib/site-packages/html2bbcode-2.3.3-py3.11.egg-info/PKG-INFO:
--------------------------------------------------------------------------------
1 | Metadata-Version: 2.1
2 | Name: html2bbcode
3 | Version: 2.3.3
4 | Summary: HTML to BBCode converter
5 | Home-page: https://bitbucket.org/amigo/html2bbcode
6 | Author: Vladimir Korsun
7 | Author-email: korsun.vladimir@gmail.com
8 | License: BSD
9 | Platform: UNKNOWN
10 | Classifier: Topic :: Utilities
11 | Classifier: Topic :: Text Processing :: Markup :: HTML
12 | Classifier: License :: OSI Approved :: BSD License
13 | Classifier: Programming Language :: Python :: 2.7
14 | Classifier: Programming Language :: Python :: 3
15 |
16 | UNKNOWN
17 |
18 |
--------------------------------------------------------------------------------
/venv/Lib/site-packages/html2bbcode-2.3.3-py3.11.egg-info/SOURCES.txt:
--------------------------------------------------------------------------------
1 | README.rst
2 | setup.cfg
3 | setup.py
4 | html2bbcode/__init__.py
5 | html2bbcode/parser.py
6 | html2bbcode.egg-info/PKG-INFO
7 | html2bbcode.egg-info/SOURCES.txt
8 | html2bbcode.egg-info/dependency_links.txt
9 | html2bbcode.egg-info/top_level.txt
10 | html2bbcode/data/defaults.conf
11 | scripts/html2bbcode
--------------------------------------------------------------------------------
/venv/Lib/site-packages/html2bbcode-2.3.3-py3.11.egg-info/dependency_links.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/venv/Lib/site-packages/html2bbcode-2.3.3-py3.11.egg-info/installed-files.txt:
--------------------------------------------------------------------------------
1 | ..\..\..\Scripts\html2bbcode
2 | ..\html2bbcode\__init__.py
3 | ..\html2bbcode\__pycache__\__init__.cpython-311.pyc
4 | ..\html2bbcode\__pycache__\parser.cpython-311.pyc
5 | ..\html2bbcode\data\defaults.conf
6 | ..\html2bbcode\parser.py
7 | PKG-INFO
8 | SOURCES.txt
9 | dependency_links.txt
10 | top_level.txt
11 |
--------------------------------------------------------------------------------
/venv/Lib/site-packages/html2bbcode-2.3.3-py3.11.egg-info/top_level.txt:
--------------------------------------------------------------------------------
1 | html2bbcode
2 |
--------------------------------------------------------------------------------
/venv/Lib/site-packages/html2bbcode/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmusementClub/OKPGUI/368409a4f93294db1db3f30aa88056ccab0b3060/venv/Lib/site-packages/html2bbcode/__init__.py
--------------------------------------------------------------------------------
/venv/Lib/site-packages/html2bbcode/data/defaults.conf:
--------------------------------------------------------------------------------
1 | [a]
2 | start: [url=%(href)s]
3 | end: [/url]
4 |
5 | [img]
6 | start: [img]%(src)s[/img]
7 | end:
8 |
9 | [em]
10 | start: [i]
11 | end: [/i]
12 |
13 | [strong]
14 | start: [b]
15 | end: [/b]
16 |
17 | [del]
18 | start: [s]
19 | end: [/s]
20 |
21 | [ins]
22 | start: [u]
23 | end: [/u]
24 |
25 | [ul]
26 | start: [list]
27 | end: [/list]
28 |
29 | [li]
30 | start: [li]
31 | end: [/li]
32 |
33 | [blockquote]
34 | start: [quote]
35 | end: [/quote]
36 |
37 | [code]
38 | start: [code]
39 | end: [/code]
40 |
41 | [font]
42 | expand: color, face, size
43 | start:
44 | end:
45 |
46 | [color]
47 | start: [color=%(color)s]
48 | end: [/color]
49 |
50 | [size]
51 | start: [size=%(size)s]
52 | end: [/size]
53 |
54 | [face]
55 | start: [font=%(face)s]
56 | end: [/font]
57 |
58 | [br]
59 | start: \n
60 | end:
61 |
62 | [p]
63 | start: \n
64 | end: \n
65 |
66 | [h1]
67 | start: [h1]
68 | end: [/h1]
69 |
70 | [h2]
71 | start: [h2]
72 | end: [/h2]
73 |
74 | [h3]
75 | start: [h3]
76 | end: [/h3]
77 |
78 | [h4]
79 | start: [h4]
80 | end: [/h4]
81 |
--------------------------------------------------------------------------------
/venv/Lib/site-packages/html2bbcode/parser.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 |
3 | from configparser import RawConfigParser
4 | from html.parser import HTMLParser
5 | from collections import defaultdict
6 | from os.path import join, dirname
7 |
8 |
9 | class Attributes(dict):
10 | def __getitem__(self, name):
11 | try:
12 | return super(Attributes, self).__getitem__(name)
13 | except KeyError:
14 | return ''
15 |
16 |
17 | class ConfigParser(RawConfigParser, object):
18 | def get(self, section, option):
19 | value = super(ConfigParser, self).get(section, option)
20 | return value.replace('\\n', '\n')
21 |
22 |
23 | class HTML2BBCode(HTMLParser):
24 | """
25 | HTML to BBCode converter
26 |
27 | >>> parser = HTML2BBCode()
28 | >>> str(parser.feed(''))
29 | '[list][li]one[/li][li]two[/li][/list]'
30 |
31 | >>> str(parser.feed('Google'))
32 | '[url=http://google.com/]Google[/url]'
33 |
34 | >>> str(parser.feed('
'))
35 | '[img]http://www.google.com/images/logo.png[/img]'
36 |
37 | >>> str(parser.feed('EM test'))
38 | '[i]EM test[/i]'
39 |
40 | >>> str(parser.feed('Strong text'))
41 | '[b]Strong text[/b]'
42 |
43 | >>> str(parser.feed('a = 10;
'))
44 | '[code]a = 10;[/code]'
45 |
46 | >>> str(parser.feed('Beautiful is better than ugly.
'))
47 | '[quote]Beautiful is better than ugly.[/quote]'
48 |
49 | >>> str(parser.feed('Text decorations'))
50 | '[font=Arial]Text decorations[/font]'
51 |
52 | >>> str(parser.feed('Text decorations'))
53 | '[size=2]Text decorations[/size]'
54 |
55 | >>> str(parser.feed('Text decorations'))
56 | '[color=red]Text decorations[/color]'
57 |
58 | >>> str(parser.feed('Text decorations'))
59 | '[color=green][font=Arial][size=2]Text decorations[/size][/font][/color]'
60 |
61 | >>> str(parser.feed('Text
break'))
62 | 'Text\\nbreak'
63 |
64 | >>> str(parser.feed(' '))
65 | ' '
66 | """
67 |
68 | def __init__(self, config=None):
69 | HTMLParser.__init__(self)
70 | self.config = ConfigParser(allow_no_value=True)
71 | self.config.read(join(dirname(__file__), 'data/defaults.conf'))
72 | if config:
73 | self.config.read(config)
74 |
75 | def handle_starttag(self, tag, attrs):
76 | if self.config.has_section(tag):
77 | self.attrs[tag].append(dict(attrs))
78 | self.data.append(
79 | self.config.get(tag, 'start') % Attributes(attrs or {}))
80 | if self.config.has_option(tag, 'expand'):
81 | self.expand_starttags(tag)
82 |
83 | def handle_endtag(self, tag):
84 | if self.config.has_section(tag):
85 | self.data.append(self.config.get(tag, 'end'))
86 | if self.config.has_option(tag, 'expand'):
87 | self.expand_endtags(tag)
88 | self.attrs[tag].pop()
89 |
90 | def handle_data(self, data):
91 | self.data.append(data)
92 |
93 | def feed(self, data):
94 | self.data = []
95 | self.attrs = defaultdict(list)
96 | HTMLParser.feed(self, data)
97 | return ''.join(self.data)
98 |
99 | def expand_starttags(self, tag):
100 | for expand in self.get_expands(tag):
101 | if expand in self.attrs[tag][-1]:
102 | self.data.append(
103 | self.config.get(expand, 'start') % self.attrs[tag][-1])
104 |
105 | def expand_endtags(self, tag):
106 | for expand in reversed(self.get_expands(tag)):
107 | if expand in self.attrs[tag][-1]:
108 | self.data.append(
109 | self.config.get(expand, 'end') % self.attrs[tag][-1])
110 |
111 | def get_expands(self, tag):
112 | expands = self.config.get(tag, 'expand').split(',')
113 | return [x.strip() for x in expands]
114 |
115 | def handle_entityref(self, name):
116 | self.data.append('&{};'.format(name))
117 |
118 | def handle_charref(self, name):
119 | self.data.append('{};'.format(name))
120 |
121 |
122 | if __name__ == '__main__':
123 | import doctest
124 |
125 | doctest.testmod()
126 |
--------------------------------------------------------------------------------
/venv/Lib/site-packages/html2phpbbcode-0.1.4.dist-info/INSTALLER:
--------------------------------------------------------------------------------
1 | pip
2 |
--------------------------------------------------------------------------------
/venv/Lib/site-packages/html2phpbbcode-0.1.4.dist-info/METADATA:
--------------------------------------------------------------------------------
1 | Metadata-Version: 2.1
2 | Name: html2phpbbcode
3 | Version: 0.1.4
4 | Summary: HTML to phpBB-compatible BBCode converter
5 | Home-page: https://github.com/tdiam/html2phpbbcode
6 | Author: Theodoros Diamantidis
7 | Author-email: diamaltho@gmail.com
8 | License: BSD
9 | Platform: UNKNOWN
10 | Classifier: Topic :: Utilities
11 | Classifier: Topic :: Text Processing :: Markup :: HTML
12 | Classifier: Topic :: Software Development :: Libraries :: Python Modules
13 | Classifier: Development Status :: 2 - Pre-Alpha
14 | Classifier: Intended Audience :: Developers
15 | Classifier: Operating System :: OS Independent
16 | Classifier: License :: OSI Approved :: BSD License
17 | Classifier: Programming Language :: Python :: 3.5
18 | Classifier: Programming Language :: Python :: 3.6
19 | Classifier: Programming Language :: Python :: 3.7
20 | Description-Content-Type: text/markdown
21 | Requires-Dist: html2bbcode
22 | Requires-Dist: regex
23 |
24 | [](https://travis-ci.org/tdiam/html2phpbbcode)
25 | [](https://badge.fury.io/py/html2phpbbcode)
26 | [](https://opensource.org/licenses/BSD-3-Clause)
27 |
28 | # HTML2PHPBBCode
29 |
30 | HTML2PHPBBCode is a Python 3 package that can be used to parse HTML code and convert it to phpBB-compatible BBCode.
31 |
32 | ### Usage
33 |
34 | ```python
35 | >>> from html2phpbbcode.parser import HTML2PHPBBCode
36 | >>> parser = HTML2PHPBBCode()
37 | >>> parser.feed('')
38 | '[list][*]Hello[*]World[/list]'
39 | >>> parser.feed('- one
- two
')
40 | '[list=1][*]one[*]two[/list]'
41 | >>> parser.feed('Water.org')
42 | '[url=https://water.org]Water.org[/url]'
43 | >>> parser.feed('Mail Water.org')
44 | '[email=info@water.org]Mail Water.org[/email]'
45 | >>> parser.feed('Hello World. It's a wonderful world')
46 | "[b]Hello [i]World[/i]. It's a wonderful world[/b]"
47 | ```
48 |
49 | ### Acknowledgements
50 |
51 | HTML2PHPBBCode is based on the [html2bbcode](https://bitbucket.org/amigo/html2bbcode) package of [Vladimir Korsun](mailto:korsun.vladimir@gmail.com) which is available under the BSD License.
52 |
53 | The [regex](https://pypi.org/project/regex/) package by [Matthew Barnett](mailto:regex@mrabarnett.plus.com) is also used, available under the Python Software Foundation License.
54 |
55 | The code includes some regular expressions from the [phpBB](https://github.com/phpbb/area51-phpbb3) bulletin board software as well. Minor changes have been made for Python compatibility. phpBB code is available under [GNU GPL v2.0](https://opensource.org/licenses/gpl-2.0.php).
56 |
57 | ### Differences from html2bbcode
58 |
59 | This package differs from html2bbcode in the following:
60 | * The generated BBCode follows the syntax described in phpBB's [BBCode guide](https://www.phpbb.com/community/help/bbcode).
61 | * ``, ``, ``, ``, `` HTML tags are also supported.
62 | * ``'s `size` attribute handling has been changed so that it maps to reasonable BBCode size values.
63 | * If the `href` attribute of an `` link uses the `mailto:` protocol, then the `[email]` BBCode tag is used.
64 | * If the `href` attribute of an `` link is neither an email nor a valid http/https URL, the link is converted to plain-text in BBCode.
65 | * The parser removes excessive whitespace such as newlines between tags: `Hello
\nWorld
` *(TODO: Use the [W3C spec](https://www.w3.org/TR/css-text-3/) rules)*
66 |
67 | ### Installing
68 |
69 | The package is available at [PyPI](https://pypi.org/project/html2phpbbcode/) and can be installed with the following command:
70 |
71 | ```bash
72 | pip install html2phpbbcode
73 | ```
74 |
75 | Installing from source is also an option:
76 |
77 | ```bash
78 | python3 setup.py install
79 | ```
80 |
81 | ### Testing
82 |
83 | [pytest](https://pytest.org) is used for testing. Just run `pytest` in the project directory to execute the tests.
84 |
85 |
--------------------------------------------------------------------------------
/venv/Lib/site-packages/html2phpbbcode-0.1.4.dist-info/RECORD:
--------------------------------------------------------------------------------
1 | html2phpbbcode-0.1.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
2 | html2phpbbcode-0.1.4.dist-info/METADATA,sha256=Bt-rnf0v3oH2TZ5FNARoHtz2RxRbE2TW8m0DPfq_hEg,3831
3 | html2phpbbcode-0.1.4.dist-info/RECORD,,
4 | html2phpbbcode-0.1.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5 | html2phpbbcode-0.1.4.dist-info/WHEEL,sha256=NzFAKnL7g-U64xnS1s5e3mJnxKpOTeOtlXdFwS9yNXI,92
6 | html2phpbbcode-0.1.4.dist-info/top_level.txt,sha256=FaTLl0QBrhqKYJ2McC3bSMu-rorv6Xbns5RNDk7T6ZA,15
7 | html2phpbbcode/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8 | html2phpbbcode/__pycache__/__init__.cpython-311.pyc,,
9 | html2phpbbcode/__pycache__/parser.cpython-311.pyc,,
10 | html2phpbbcode/__pycache__/validators.cpython-311.pyc,,
11 | html2phpbbcode/data/defaults.conf,sha256=6YTd0ZbRZU1ZiMR8A5LIE2q9q8pWHy5aca_fJFvxJWg,834
12 | html2phpbbcode/parser.py,sha256=uN_oq8T5yrxsqzN9uvBnBu0knJkhR5b8cvUPIRe9_YU,3462
13 | html2phpbbcode/validators.py,sha256=_UrEvAlE60guvvzmjyJQ2fqs8jyM09HVOmnj0D3BMes,4251
14 |
--------------------------------------------------------------------------------
/venv/Lib/site-packages/html2phpbbcode-0.1.4.dist-info/REQUESTED:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmusementClub/OKPGUI/368409a4f93294db1db3f30aa88056ccab0b3060/venv/Lib/site-packages/html2phpbbcode-0.1.4.dist-info/REQUESTED
--------------------------------------------------------------------------------
/venv/Lib/site-packages/html2phpbbcode-0.1.4.dist-info/WHEEL:
--------------------------------------------------------------------------------
1 | Wheel-Version: 1.0
2 | Generator: bdist_wheel (0.31.1)
3 | Root-Is-Purelib: true
4 | Tag: py3-none-any
5 |
6 |
--------------------------------------------------------------------------------
/venv/Lib/site-packages/html2phpbbcode-0.1.4.dist-info/top_level.txt:
--------------------------------------------------------------------------------
1 | html2phpbbcode
2 |
--------------------------------------------------------------------------------
/venv/Lib/site-packages/html2phpbbcode/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AmusementClub/OKPGUI/368409a4f93294db1db3f30aa88056ccab0b3060/venv/Lib/site-packages/html2phpbbcode/__init__.py
--------------------------------------------------------------------------------
/venv/Lib/site-packages/html2phpbbcode/data/defaults.conf:
--------------------------------------------------------------------------------
1 | [a]
2 | start: [url=%(href)s]
3 | end: [/url]
4 |
5 | [email]
6 | start: [email=%(email)s]
7 | end: [/email]
8 |
9 | [img]
10 | start: [img]%(src)s[/img]
11 | end:
12 |
13 | [em]
14 | start: [i]
15 | end: [/i]
16 |
17 | [strong]
18 | start: [b]
19 | end: [/b]
20 |
21 | [i]
22 | start: [i]
23 | end: [/i]
24 |
25 | [b]
26 | start: [b]
27 | end: [/b]
28 |
29 | [del]
30 | start: [s]
31 | end: [/s]
32 |
33 | [s]
34 | start: [s]
35 | end: [/s]
36 |
37 | [ins]
38 | start: [u]
39 | end: [/u]
40 |
41 | [u]
42 | start: [u]
43 | end: [/u]
44 |
45 | [ul]
46 | start: [list]
47 | end: [/list]
48 |
49 | [ol]
50 | start: [list=1]
51 | end: [/list]
52 |
53 | [li]
54 | start: [*]
55 | end:
56 |
57 | [blockquote]
58 | start: [quote]
59 | end: [/quote]
60 |
61 | [code]
62 | start: [code]
63 | end: [/code]
64 |
65 | [font]
66 | expand: color, size
67 | start:
68 | end:
69 |
70 | [color]
71 | start: [color=%(color)s]
72 | end: [/color]
73 |
74 | [size]
75 | start: [size=%(size)s]
76 | end: [/size]
77 |
78 | [br]
79 | start: \n
80 | end:
81 |
82 | [p]
83 | start: \n
84 | end: \n
85 |
86 | [h1]
87 | start: [size=200]
88 | end: [/size]
89 |
90 | [h2]
91 | start: [size=167]
92 | end: [/size]
93 |
94 | [h3]
95 | start: [size=139]
96 | end: [/size]
97 |
98 | [h4]
99 | start: [size=116]
100 | end: [/size]
--------------------------------------------------------------------------------
/venv/Lib/site-packages/html2phpbbcode/parser.py:
--------------------------------------------------------------------------------
1 | from os.path import join, dirname
2 |
3 | from html2bbcode.parser import HTML2BBCode, Attributes
4 | from .validators import is_valid_url, is_valid_mail
5 |
6 | def is_mailto_url(url):
7 | return url.startswith("mailto:") and is_valid_mail(url[7:])
8 |
9 | class HTML2PHPBBCode(HTML2BBCode):
10 | def __init__(self, config=None):
11 | if config is None:
12 | config = join(dirname(__file__), "data/defaults.conf")
13 | super().__init__(config=config)
14 |
15 | def handle_starttag(self, tag, attrs):
16 | if self.config.has_section(tag):
17 | dct = dict(attrs)
18 |
19 | skip = False
20 | if tag == "font" and "size" in dct:
21 | sz = dct["size"]
22 | if sz.isdigit() and int(sz) >= 1 and int(sz) <= 7:
23 | """Size attribute of tag is a number between 1-7"""
24 | sz = int(100 * 1.15 ** (int(sz) - 3))
25 | else:
26 | sz = 100
27 | dct["size"] = str(sz)
28 | if tag == "a":
29 | if "href" not in dct:
30 | """Mark link as invalid so that handle_endtag knows to ignore it"""
31 | dct["!invalid"] = True
32 | skip = True
33 | else:
34 | url = dct["href"]
35 | if is_mailto_url(url):
36 | """Remove mailto: from URL"""
37 | attrs = {"email": url[7:]}
38 | """Mark link as mail so that handle_endtag knows how to close it"""
39 | dct["!mail"] = True
40 | self.data.append(
41 | self.config.get("email", "start") % Attributes(attrs or {})
42 | )
43 | skip = True
44 | elif not is_valid_url(url):
45 | dct["!invalid"] = True
46 | skip = True
47 |
48 | self.attrs[tag].append(dct)
49 | if not skip:
50 | self.data.append(
51 | self.config.get(tag, "start") % Attributes(attrs or {})
52 | )
53 | if self.config.has_option(tag, "expand"):
54 | self.expand_starttags(tag)
55 |
56 | def handle_endtag(self, tag):
57 | if self.config.has_section(tag):
58 | attrs = self.attrs[tag][-1]
59 |
60 | skip = False
61 | if tag == "a":
62 | if "!invalid" in attrs:
63 | skip = True
64 | if "!mail" in attrs:
65 | self.data.append(self.config.get("email", "end"))
66 | skip = True
67 |
68 | if not skip:
69 | self.data.append(self.config.get(tag, "end"))
70 | if self.config.has_option(tag, "expand"):
71 | self.expand_endtags(tag)
72 | self.attrs[tag].pop()
73 |
74 | def handle_data(self, data):
75 | """Remove excessive whitespace from text nodes
76 |
77 | TODO: Use the CSS Text Module Level 3 Working Draft rules for
78 | inline formatting contexts to process whitespace.
79 | Link: https://www.w3.org/TR/css-text-3/#white-space-processing"""
80 |
81 | """If there is leading whitespace, replace it with a single space"""
82 | left = data.lstrip()
83 | if left != data:
84 | data = " " + left
85 | """Same for trailing whitespace"""
86 | right = data.rstrip()
87 | if right != data:
88 | data = right + " "
89 | """Replace new lines with spaces"""
90 | data = data.replace("\n", " ")
91 | self.data.append(data)
92 |
--------------------------------------------------------------------------------
/venv/Lib/site-packages/html2phpbbcode/validators.py:
--------------------------------------------------------------------------------
1 | import regex
2 |
3 | """The following validators have been based on phpBB's URL and e-mail validators, which are available under GNU GPL v2.0 license.
4 |
5 | Link: https://github.com/phpbb/area51-phpbb3/blob/master/phpBB/includes/functions.php
6 |
7 | Copyright notice as found in phpBB:
8 |
9 | /**
10 | *
11 | * phpBB © Copyright phpBB Limited 2003-2016
12 | * http://www.phpbb.com
13 | *
14 | * phpBB is free software. You can redistribute it and/or modify it
15 | * under the terms of the GNU General Public License, version 2 (GPL-2.0)
16 | * as published by the Free Software Foundation.
17 | *
18 | * This program is distributed in the hope that it will be useful,
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 | * GNU General Public License for more details.
22 | *
23 | * A copy of the license can be viewed in the docs/LICENSE.txt file.
24 | * The same can be viewed at
25 | *
26 | */
27 |
28 | """
29 |
30 | url = [
31 | regex.compile(
32 | r"[a-z][a-z\d+\-.]*(?