donghua-cli 3.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- donghua_cli-3.1.0/.claude/settings.local.json +29 -0
- donghua_cli-3.1.0/.github/workflows/ci.yml +38 -0
- donghua_cli-3.1.0/.github/workflows/publish.yml +64 -0
- donghua_cli-3.1.0/.gitignore +47 -0
- donghua_cli-3.1.0/CHANGELOG.md +58 -0
- donghua_cli-3.1.0/LICENSE +21 -0
- donghua_cli-3.1.0/PKG-INFO +222 -0
- donghua_cli-3.1.0/README.md +195 -0
- donghua_cli-3.1.0/docs/assets/banner.png +0 -0
- donghua_cli-3.1.0/docs/assets/banner.svg +77 -0
- donghua_cli-3.1.0/docs/assets/banner.txt +10 -0
- donghua_cli-3.1.0/docs/assets/episode-selection.png +0 -0
- donghua_cli-3.1.0/docs/assets/episode-selection.svg +120 -0
- donghua_cli-3.1.0/docs/assets/features.txt +36 -0
- donghua_cli-3.1.0/docs/assets/playback-controls.png +0 -0
- donghua_cli-3.1.0/docs/assets/playback-controls.svg +91 -0
- donghua_cli-3.1.0/docs/assets/social-banner.png +0 -0
- donghua_cli-3.1.0/docs/assets/social-banner.svg +88 -0
- donghua_cli-3.1.0/docs/future_ideas.md +53 -0
- donghua_cli-3.1.0/docs/index.html +4101 -0
- donghua_cli-3.1.0/packaging/aur/PKGBUILD +29 -0
- donghua_cli-3.1.0/packaging/desktop/donghua-cli.desktop +10 -0
- donghua_cli-3.1.0/packaging/desktop/donghua.bat +10 -0
- donghua_cli-3.1.0/packaging/desktop/install-desktop.sh +9 -0
- donghua_cli-3.1.0/packaging/homebrew/donghua-cli.rb +21 -0
- donghua_cli-3.1.0/packaging/pyinstaller/donghua.spec +74 -0
- donghua_cli-3.1.0/pyproject.toml +46 -0
- donghua_cli-3.1.0/scripts/install.ps1 +89 -0
- donghua_cli-3.1.0/scripts/install.sh +95 -0
- donghua_cli-3.1.0/src/donghua_cli/__init__.py +3 -0
- donghua_cli-3.1.0/src/donghua_cli/__main__.py +5 -0
- donghua_cli-3.1.0/src/donghua_cli/app.py +325 -0
- donghua_cli-3.1.0/src/donghua_cli/banner_frames.json.gz +0 -0
- donghua_cli-3.1.0/src/donghua_cli/cache.py +167 -0
- donghua_cli-3.1.0/src/donghua_cli/cli.py +131 -0
- donghua_cli-3.1.0/src/donghua_cli/config.py +139 -0
- donghua_cli-3.1.0/src/donghua_cli/extractor.py +124 -0
- donghua_cli-3.1.0/src/donghua_cli/player.py +161 -0
- donghua_cli-3.1.0/src/donghua_cli/scraper.py +209 -0
- donghua_cli-3.1.0/src/donghua_cli/sources/__init__.py +33 -0
- donghua_cli-3.1.0/src/donghua_cli/sources/animexin.py +95 -0
- donghua_cli-3.1.0/src/donghua_cli/sources/base.py +63 -0
- donghua_cli-3.1.0/src/donghua_cli/sources/hdonghua.py +85 -0
- donghua_cli-3.1.0/src/donghua_cli/sources/lmanime.py +95 -0
- donghua_cli-3.1.0/src/donghua_cli/sources/luciferdonghua.py +78 -0
- donghua_cli-3.1.0/src/donghua_cli/sources/misterdonghua.py +86 -0
- donghua_cli-3.1.0/src/donghua_cli/theme.py +415 -0
- donghua_cli-3.1.0/src/donghua_cli/tui.py +780 -0
- donghua_cli-3.1.0/src/donghua_cli/ui.py +220 -0
- donghua_cli-3.1.0/src/donghua_cli/utils.py +125 -0
- donghua_cli-3.1.0/tests/__init__.py +0 -0
- donghua_cli-3.1.0/tests/test_extractor.py +56 -0
- donghua_cli-3.1.0/tests/test_scraper.py +84 -0
- donghua_cli-3.1.0/tests/test_sources.py +39 -0
- donghua_cli-3.1.0/tests/test_utils.py +60 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(python3:*)",
|
|
5
|
+
"Bash(\"/tmp/readme_part1.html\" << 'HTMLEOF'\n<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Donghua CLI | Imperial Documentation</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600;700;900&family=Inter:wght@300;400;500;600;700&family=Fira+Code:wght@400;500&display=swap\" rel=\"stylesheet\">\n <style>\n /* ══════════════════════════════════════════════════════════════\n IMPERIAL PALACE DESIGN SYSTEM\n Jade & Gold | Premium Donghua CLI Documentation\n ══════════════════════════════════════════════════════════════ */\n\n :root {\n --bg-deep: #060d0b;\n --bg: #0a1512;\n --bg-elevated: #0e1c18;\n --surface: #132822;\n --surface-hover: #1a3530;\n --jade: #00a86b;\n --jade-light: #2ed89a;\n --jade-dark: #006e46;\n --jade-glow: rgba\\(0, 168, 107, 0.25\\);\n --jade-subtle: rgba\\(0, 168, 107, 0.08\\);\n --gold: #d4af37;\n --gold-light: #f0d060;\n --gold-dark: #a88a2a;\n --gold-glow: rgba\\(212, 175, 55, 0.25\\);\n --gold-subtle: rgba\\(212, 175, 55, 0.08\\);\n --imperial-red: #c41e3a;\n --imperial-red-glow: rgba\\(196, 30, 58, 0.2\\);\n --text-primary: #f5f0e1;\n --text-secondary: #b8c5a6;\n --text-muted: #7a8a6e;\n --glass: rgba\\(19, 40, 34, 0.85\\);\n --glass-light: rgba\\(26, 53, 48, 0.7\\);\n --border-gold: rgba\\(212, 175, 55, 0.2\\);\n --border-jade: rgba\\(0, 168, 107, 0.15\\);\n --border-strong: rgba\\(212, 175, 55, 0.4\\);\n --shadow-gold: 0 4px 30px rgba\\(212, 175, 55, 0.1\\);\n --shadow-jade: 0 4px 30px rgba\\(0, 168, 107, 0.1\\);\n --shadow-deep: 0 8px 40px rgba\\(0, 0, 0, 0.4\\);\n }\n\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n\n html {\n scroll-behavior: smooth;\n }\n\n body {\n background-color: var\\(--bg\\);\n color: var\\(--text-primary\\);\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n line-height: 1.7;\n overflow-x: hidden;\n min-height: 100vh;\n }\n\n /* ── IMPERIAL BACKGROUND PATTERN ── */\n body::before {\n content: '';\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background:\n radial-gradient\\(ellipse 800px 600px at 20% 10%, rgba\\(0, 168, 107, 0.04\\) 0%, transparent 70%\\),\n radial-gradient\\(ellipse 600px 800px at 80% 50%, rgba\\(212, 175, 55, 0.03\\) 0%, transparent 70%\\),\n radial-gradient\\(ellipse 900px 400px at 50% 90%, rgba\\(0, 168, 107, 0.03\\) 0%, transparent 70%\\);\n pointer-events: none;\n z-index: 0;\n }\n\n /* ── ORNAMENTAL BORDERS \\(Left & Right Palace Pillars\\) ── */\n body::after {\n content: '';\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background:\n linear-gradient\\(to right, rgba\\(212, 175, 55, 0.06\\) 0px, transparent 2px, transparent calc\\(100% - 2px\\), rgba\\(212, 175, 55, 0.06\\) 100%\\);\n pointer-events: none;\n z-index: 0;\n }\n\n ::selection {\n background: rgba\\(212, 175, 55, 0.3\\);\n color: var\\(--text-primary\\);\n }\n\n h1, h2, h3, h4 {\n font-family: 'Cinzel', 'Georgia', serif;\n font-weight: 700;\n letter-spacing: 0.02em;\n }\n\n a {\n color: var\\(--jade-light\\);\n text-decoration: none;\n transition: color 0.3s ease;\n }\n\n a:hover {\n color: var\\(--gold-light\\);\n }\n\n .container {\n max-width: 1000px;\n margin: 0 auto;\n padding: 0 2rem;\n position: relative;\n z-index: 1;\n }\n\n /* ══════════════════════════════════════════════════════════════\n IMPERIAL HEADER\n ══════════════════════════════════════════════════════════════ */\n header {\n width: 100%;\n margin-bottom: 0;\n position: relative;\n }\n\n .hero-img-wrapper {\n position: relative;\n overflow: hidden;\n }\n\n .hero-img-wrapper::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 200px;\n background: linear-gradient\\(to bottom, transparent, var\\(--bg\\)\\);\n pointer-events: none;\n }\n\n .hero-img {\n width: 100%;\n display: block;\n }\n\n .hero-content {\n text-align: center;\n padding: 3rem 1rem 5rem;\n position: relative;\n }\n\n /* Imperial Seal Ring */\n .imperial-seal {\n width: 90px;\n height: 90px;\n margin: 0 auto 2rem;\n border: 2px solid var\\(--gold\\);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 2.2rem;\n background: radial-gradient\\(circle, rgba\\(212, 175, 55, 0.1\\) 0%, transparent 70%\\);\n box-shadow: 0 0 40px rgba\\(212, 175, 55, 0.15\\), inset 0 0 20px rgba\\(212, 175, 55, 0.05\\);\n animation: sealPulse 4s ease-in-out infinite;\n }\n\n @keyframes sealPulse {\n 0%, 100% { box-shadow: 0 0 40px rgba\\(212, 175, 55, 0.15\\), inset 0 0 20px rgba\\(212, 175, 55, 0.05\\); }\n 50% { box-shadow: 0 0 60px rgba\\(212, 175, 55, 0.25\\), inset 0 0 30px rgba\\(212, 175, 55, 0.1\\); }\n }\n\n .hero-content h1 {\n font-size: clamp\\(2.5rem, 6vw, 4.5rem\\);\n background: linear-gradient\\(135deg, var\\(--gold-light\\) 0%, var\\(--gold\\) 30%, var\\(--jade\\) 70%, var\\(--jade-light\\) 100%\\);\n -webkit-background-clip: text;\n background-clip: text;\n -webkit-text-fill-color: transparent;\n margin-bottom: 0.5rem;\n letter-spacing: 0.05em;\n font-weight: 900;\n line-height: 1.1;\n }\n\n .hero-subtitle {\n font-family: 'Cinzel', serif;\n font-size: clamp\\(0.85rem, 2vw, 1.1rem\\);\n color: var\\(--gold\\);\n letter-spacing: 0.3em;\n text-transform: uppercase;\n margin-bottom: 1rem;\n opacity: 0.8;\n }\n\n .hero-desc {\n font-size: 1.1rem;\n color: var\\(--text-secondary\\);\n max-width: 550px;\n margin: 0 auto 1.5rem;\n font-weight: 300;\n }\n\n .hero-author {\n font-size: 0.9rem;\n color: var\\(--text-muted\\);\n margin-bottom: 2rem;\n }\n\n .hero-author strong {\n color: var\\(--gold\\);\n font-weight: 600;\n }\n\n /* Badges */\n .badges {\n display: flex;\n justify-content: center;\n gap: 0.75rem;\n flex-wrap: wrap;\n }\n\n .badge {\n padding: 0.4rem 1rem;\n border-radius: 2rem;\n font-size: 0.8rem;\n font-weight: 600;\n letter-spacing: 0.03em;\n border: 1px solid transparent;\n transition: transform 0.3s ease, box-shadow 0.3s ease;\n }\n\n .badge:hover {\n transform: translateY\\(-2px\\);\n }\n\n .badge-gold {\n background: linear-gradient\\(135deg, var\\(--gold-dark\\), var\\(--gold\\)\\);\n color: var\\(--bg-deep\\);\n box-shadow: 0 2px 15px rgba\\(212, 175, 55, 0.3\\);\n }\n\n .badge-jade {\n background: linear-gradient\\(135deg, var\\(--jade-dark\\), var\\(--jade\\)\\);\n color: white;\n box-shadow: 0 2px 15px rgba\\(0, 168, 107, 0.3\\);\n }\n\n .badge-red {\n background: linear-gradient\\(135deg, #8b1528, var\\(--imperial-red\\)\\);\n color: white;\n box-shadow: 0 2px 15px var\\(--imperial-red-glow\\);\n }\n\n .badge-outline {\n background: transparent;\n color: var\\(--text-secondary\\);\n border: 1px solid var\\(--border-gold\\);\n }\n\n /* ── ORNAMENTAL DIVIDER ── */\n .imperial-divider {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 1rem;\n padding: 2rem 0;\n opacity: 0.5;\n }\n\n .imperial-divider .line {\n flex: 1;\n max-width: 120px;\n height: 1px;\n background: linear-gradient\\(90deg, transparent, var\\(--gold\\), transparent\\);\n }\n\n .imperial-divider .ornament {\n color: var\\(--gold\\);\n font-size: 0.9rem;\n }\n\n /* ══════════════════════════════════════════════════════════════\n NAVIGATION\n ══════════════════════════════════════════════════════════════ */\n .nav-bar {\n position: sticky;\n top: 0;\n z-index: 100;\n background: rgba\\(6, 13, 11, 0.92\\);\n backdrop-filter: blur\\(20px\\);\n -webkit-backdrop-filter: blur\\(20px\\);\n border-bottom: 1px solid var\\(--border-gold\\);\n padding: 0;\n }\n\n .nav-inner {\n max-width: 1000px;\n margin: 0 auto;\n padding: 0 2rem;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.25rem;\n overflow-x: auto;\n scrollbar-width: none;\n }\n\n .nav-inner::-webkit-scrollbar { display: none; }\n\n .nav-link {\n padding: 1rem 1.25rem;\n font-size: 0.8rem;\n font-weight: 500;\n color: var\\(--text-muted\\);\n text-transform: uppercase;\n letter-spacing: 0.1em;\n white-space: nowrap;\n transition: color 0.3s ease;\n position: relative;\n font-family: 'Cinzel', serif;\n }\n\n .nav-link:hover {\n color: var\\(--gold\\);\n }\n\n .nav-link::after {\n content: '';\n position: absolute;\n bottom: 0;\n left: 50%;\n transform: translateX\\(-50%\\);\n width: 0;\n height: 2px;\n background: var\\(--gold\\);\n transition: width 0.3s ease;\n }\n\n .nav-link:hover::after {\n width: 60%;\n }\n\n /* ══════════════════════════════════════════════════════════════\n SECTIONS\n ══════════════════════════════════════════════════════════════ */\n section {\n padding: 5rem 0;\n position: relative;\n }\n\n .section-header {\n text-align: center;\n margin-bottom: 3.5rem;\n }\n\n .section-label {\n font-family: 'Cinzel', serif;\n font-size: 0.75rem;\n letter-spacing: 0.35em;\n text-transform: uppercase;\n color: var\\(--gold\\);\n margin-bottom: 0.75rem;\n display: block;\n }\n\n .section-title {\n font-size: clamp\\(2rem, 4vw, 2.8rem\\);\n background: linear-gradient\\(135deg, var\\(--text-primary\\) 0%, var\\(--gold\\) 100%\\);\n -webkit-background-clip: text;\n background-clip: text;\n -webkit-text-fill-color: transparent;\n margin-bottom: 0.75rem;\n }\n\n .section-subtitle {\n font-size: 1.05rem;\n color: var\\(--text-secondary\\);\n max-width: 600px;\n margin: 0 auto;\n font-weight: 300;\n }\n\n /* Ornamental underline for section titles */\n .section-header::after {\n content: '';\n display: block;\n width: 80px;\n height: 2px;\n margin: 1.5rem auto 0;\n background: linear-gradient\\(90deg, var\\(--gold\\), var\\(--jade\\), var\\(--gold\\)\\);\n border-radius: 1px;\n box-shadow: 0 0 10px var\\(--gold-glow\\);\n }\n\n /* ── CARDS ── */\n .grid {\n display: grid;\n grid-template-columns: repeat\\(auto-fit, minmax\\(250px, 1fr\\)\\);\n gap: 1.5rem;\n }\n\n .card {\n background: linear-gradient\\(160deg, var\\(--surface\\) 0%, rgba\\(0, 168, 107, 0.05\\) 100%\\);\n padding: 2rem;\n border-radius: 1rem;\n border: 1px solid var\\(--border-gold\\);\n position: relative;\n overflow: hidden;\n transition: transform 0.4s ease, border-color 0.4s ease, box-shadow 0.4s ease;\n }\n\n .card::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 2px;\n background: linear-gradient\\(90deg, transparent, var\\(--gold\\), var\\(--jade\\), var\\(--gold\\), transparent\\);\n }\n\n .card::after {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: radial-gradient\\(ellipse at top right, rgba\\(212, 175, 55, 0.03\\) 0%, transparent 70%\\);\n pointer-events: none;\n }\n\n .card:hover {\n transform: translateY\\(-4px\\);\n border-color: rgba\\(212, 175, 55, 0.4\\);\n box-shadow: var\\(--shadow-gold\\);\n }\n\n .card-icon {\n font-size: 1.8rem;\n margin-bottom: 1rem;\n display: block;\n }\n\n .card h3 {\n font-size: 1.15rem;\n color: var\\(--text-primary\\);\n margin-bottom: 0.75rem;\n }\n\n .card p {\n color: var\\(--text-secondary\\);\n font-size: 0.92rem;\n line-height: 1.6;\n }\n\n /* ══════════════════════════════════════════════════════════════\n IMPERIAL DECREE \\(Alert Callout\\)\n ══════════════════════════════════════════════════════════════ */\n .decree {\n background: linear-gradient\\(135deg, rgba\\(196, 30, 58, 0.08\\) 0%, rgba\\(212, 175, 55, 0.05\\) 100%\\);\n border: 1px solid rgba\\(196, 30, 58, 0.25\\);\n border-left: 4px solid var\\(--imperial-red\\);\n border-radius: 0.75rem;\n padding: 1.75rem 2rem;\n margin: 2rem 0 3rem;\n position: relative;\n }\n\n .decree-title {\n font-family: 'Cinzel', serif;\n color: var\\(--imperial-red\\);\n font-size: 0.95rem;\n font-weight: 700;\n letter-spacing: 0.05em;\n margin-bottom: 0.5rem;\n }\n\n .decree p {\n color: var\\(--text-secondary\\);\n font-size: 0.93rem;\n }\n\n /* ══════════════════════════════════════════════════════════════\n PREREQUISITES\n ══════════════════════════════════════════════════════════════ */\n .prereq-grid {\n display: grid;\n grid-template-columns: repeat\\(auto-fit, minmax\\(200px, 1fr\\)\\);\n gap: 1.25rem;\n }\n\n .prereq-item {\n background: var\\(--surface\\);\n border: 1px solid var\\(--border-jade\\);\n border-radius: 0.75rem;\n padding: 1.5rem;\n text-align: center;\n transition: border-color 0.3s ease, box-shadow 0.3s ease;\n }\n\n .prereq-item:hover {\n border-color: var\\(--jade\\);\n box-shadow: var\\(--shadow-jade\\);\n }\n\n .prereq-icon {\n font-size: 2rem;\n margin-bottom: 0.75rem;\n display: block;\n }\n\n .prereq-name {\n font-family: 'Cinzel', serif;\n font-weight: 600;\n font-size: 0.95rem;\n color: var\\(--text-primary\\);\n margin-bottom: 0.25rem;\n }\n\n .prereq-desc {\n font-size: 0.8rem;\n color: var\\(--text-muted\\);\n }\n\n /* ══════════════════════════════════════════════════════════════\n INSTALLATION TABS\n ══════════════════════════════════════════════════════════════ */\n .os-tabs {\n display: flex;\n justify-content: center;\n gap: 0.5rem;\n margin-bottom: 3rem;\n flex-wrap: wrap;\n }\n\n .os-tab {\n padding: 0.75rem 1.5rem;\n border-radius: 0.75rem;\n background: var\\(--surface\\);\n border: 1px solid var\\(--border-gold\\);\n color: var\\(--text-secondary\\);\n font-family: 'Cinzel', serif;\n font-size: 0.85rem;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.3s ease;\n letter-spacing: 0.03em;\n position: relative;\n overflow: hidden;\n }\n\n .os-tab::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: linear-gradient\\(135deg, rgba\\(212, 175, 55, 0.1\\), rgba\\(0, 168, 107, 0.1\\)\\);\n opacity: 0;\n transition: opacity 0.3s ease;\n }\n\n .os-tab:hover::before,\n .os-tab.active::before {\n opacity: 1;\n }\n\n .os-tab.active {\n border-color: var\\(--gold\\);\n color: var\\(--gold\\);\n box-shadow: 0 0 20px rgba\\(212, 175, 55, 0.15\\);\n }\n\n .os-tab .tab-icon {\n margin-right: 0.5rem;\n }\n\n .os-panel {\n display: none;\n animation: panelFade 0.5s ease;\n }\n\n .os-panel.active {\n display: block;\n }\n\n @keyframes panelFade {\n from { opacity: 0; transform: translateY\\(10px\\); }\n to { opacity: 1; transform: translateY\\(0\\); }\n }\n\n /* ── OS PANEL LAYOUT ── */\n .install-layout {\n display: grid;\n grid-template-columns: 1fr;\n gap: 2rem;\n }\n\n .install-header {\n display: flex;\n align-items: center;\n gap: 1rem;\n margin-bottom: 1.5rem;\n }\n\n .install-header .os-icon {\n width: 50px;\n height: 50px;\n border-radius: 0.75rem;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1.5rem;\n border: 1px solid var\\(--border-gold\\);\n background: var\\(--surface\\);\n }\n\n .install-header h3 {\n font-size: 1.5rem;\n color: var\\(--text-primary\\);\n }\n\n .install-header .os-version {\n font-size: 0.8rem;\n color: var\\(--text-muted\\);\n font-family: 'Inter', sans-serif;\n font-weight: 400;\n }\n\n /* ── STEPS ── */\n .step {\n position: relative;\n padding-left: 3.5rem;\n margin-bottom: 2.5rem;\n }\n\n .step::before {\n content: '';\n position: absolute;\n left: 17px;\n top: 40px;\n bottom: -2.5rem;\n width: 1px;\n background: linear-gradient\\(to bottom, var\\(--gold\\), transparent\\);\n }\n\n .step:last-child::before {\n display: none;\n }\n\n .step-number {\n position: absolute;\n left: 0;\n top: 0;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n background: linear-gradient\\(135deg, var\\(--gold-dark\\), var\\(--gold\\)\\);\n color: var\\(--bg-deep\\);\n display: flex;\n align-items: center;\n justify-content: center;\n font-family: 'Cinzel', serif;\n font-weight: 700;\n font-size: 0.85rem;\n box-shadow: 0 0 15px rgba\\(212, 175, 55, 0.3\\);\n }\n\n .step-title {\n font-family: 'Cinzel', serif;\n font-size: 1.05rem;\n color: var\\(--text-primary\\);\n font-weight: 600;\n margin-bottom: 0.5rem;\n }\n\n .step-desc {\n font-size: 0.9rem;\n color: var\\(--text-secondary\\);\n margin-bottom: 1rem;\n line-height: 1.6;\n }\n\n /* ── CODE BLOCKS ── */\n .code-block {\n background: linear-gradient\\(160deg, #050a08 0%, #0e1c18 50%, #0a1410 100%\\);\n padding: 1.5rem;\n border-radius: 0.75rem;\n position: relative;\n overflow-x: auto;\n border: 1px solid var\\(--border-gold\\);\n box-shadow: inset 0 2px 15px rgba\\(0, 0, 0, 0.4\\), var\\(--shadow-gold\\);\n }\n\n .code-block::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 1px;\n background: linear-gradient\\(90deg, transparent, var\\(--gold\\), var\\(--jade\\), var\\(--gold\\), transparent\\);\n }\n\n .code-label {\n position: absolute;\n top: 0.6rem;\n right: 0.75rem;\n font-family: 'Cinzel', serif;\n font-size: 0.65rem;\n letter-spacing: 0.15em;\n text-transform: uppercase;\n color: var\\(--gold\\);\n opacity: 0.4;\n padding: 0.15rem 0.5rem;\n border: 1px solid rgba\\(212, 175, 55, 0.15\\);\n border-radius: 0.25rem;\n }\n\n .code-block pre {\n font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;\n font-size: 0.85rem;\n color: var\\(--text-primary\\);\n line-height: 1.8;\n white-space: pre;\n overflow-x: auto;\n }\n\n .code-block .comment {\n color: var\\(--text-muted\\);\n }\n\n .code-block .cmd {\n color: var\\(--jade-light\\);\n }\n\n .code-block .flag {\n color: var\\(--gold\\);\n }\n\n /* Copy button */\n .copy-btn {\n position: absolute;\n bottom: 0.6rem;\n right: 0.75rem;\n background: rgba\\(212, 175, 55, 0.1\\);\n border: 1px solid rgba\\(212, 175, 55, 0.2\\);\n color: var\\(--gold\\);\n padding: 0.3rem 0.7rem;\n border-radius: 0.4rem;\n font-size: 0.7rem;\n cursor: pointer;\n font-family: 'Inter', sans-serif;\n transition: all 0.3s ease;\n }\n\n .copy-btn:hover {\n background: rgba\\(212, 175, 55, 0.2\\);\n border-color: var\\(--gold\\);\n }\n\n /* ── INFO/TIP CALLOUTS ── */\n .tip {\n background: linear-gradient\\(135deg, var\\(--gold-subtle\\) 0%, var\\(--jade-subtle\\) 100%\\);\n border: 1px solid var\\(--border-gold\\);\n border-left: 3px solid var\\(--gold\\);\n border-radius: 0.75rem;\n padding: 1.5rem 1.75rem;\n margin: 1.5rem 0;\n position: relative;\n }\n\n .tip-title {\n font-family: 'Cinzel', serif;\n color: var\\(--gold\\);\n font-size: 0.9rem;\n font-weight: 700;\n margin-bottom: 0.5rem;\n letter-spacing: 0.03em;\n }\n\n .tip p, .tip li {\n color: var\\(--text-secondary\\);\n font-size: 0.9rem;\n }\n\n .tip.tip-jade {\n border-left-color: var\\(--jade\\);\n }\n\n .tip.tip-jade .tip-title {\n color: var\\(--jade-light\\);\n }\n\n /* ── VERIFICATION BOX ── */\n .verify-box {\n background: linear-gradient\\(135deg, rgba\\(0, 168, 107, 0.08\\), rgba\\(0, 168, 107, 0.03\\)\\);\n border: 1px solid rgba\\(0, 168, 107, 0.2\\);\n border-radius: 0.75rem;\n padding: 1.25rem 1.5rem;\n margin-top: 1rem;\n }\n\n .verify-title {\n font-family: 'Cinzel', serif;\n color: var\\(--jade-light\\);\n font-size: 0.85rem;\n font-weight: 600;\n margin-bottom: 0.5rem;\n }\n\n .verify-box code {\n display: block;\n margin-top: 0.5rem;\n }\n\n /* ══════════════════════════════════════════════════════════════\n USAGE TABLE\n ══════════════════════════════════════════════════════════════ */\n .commands-table {\n width: 100%;\n border-collapse: separate;\n border-spacing: 0;\n margin: 2rem 0;\n border-radius: 1rem;\n overflow: hidden;\n border: 1px solid var\\(--border-gold\\);\n box-shadow: var\\(--shadow-deep\\);\n }\n\n .commands-table thead {\n background: linear-gradient\\(135deg, rgba\\(212, 175, 55, 0.1\\), rgba\\(0, 168, 107, 0.05\\)\\);\n }\n\n .commands-table th {\n text-align: left;\n padding: 1.1rem 1.5rem;\n font-family: 'Cinzel', serif;\n color: var\\(--gold\\);\n font-size: 0.8rem;\n text-transform: uppercase;\n letter-spacing: 0.1em;\n font-weight: 600;\n border-bottom: 2px solid var\\(--border-strong\\);\n }\n\n .commands-table td {\n padding: 1rem 1.5rem;\n border-bottom: 1px solid var\\(--border-gold\\);\n font-size: 0.9rem;\n color: var\\(--text-secondary\\);\n background: rgba\\(19, 40, 34, 0.5\\);\n transition: background 0.3s ease;\n }\n\n .commands-table tr:last-child td {\n border-bottom: none;\n }\n\n .commands-table tr:hover td {\n background: rgba\\(212, 175, 55, 0.04\\);\n }\n\n code {\n background: linear-gradient\\(135deg, rgba\\(212, 175, 55, 0.12\\) 0%, rgba\\(0, 168, 107, 0.12\\) 100%\\);\n padding: 0.2rem 0.5rem;\n border-radius: 0.3rem;\n font-family: 'Fira Code', monospace;\n color: var\\(--gold\\);\n border: 1px solid rgba\\(212, 175, 55, 0.2\\);\n font-size: 0.85em;\n font-weight: 500;\n }\n\n /* ══════════════════════════════════════════════════════════════\n TROUBLESHOOTING\n ══════════════════════════════════════════════════════════════ */\n .trouble-card {\n background: var\\(--surface\\);\n border: 1px solid var\\(--border-gold\\);\n border-radius: 1rem;\n overflow: hidden;\n margin-bottom: 1.25rem;\n transition: border-color 0.3s ease;\n }\n\n .trouble-card:hover {\n border-color: rgba\\(212, 175, 55, 0.4\\);\n }\n\n .trouble-header {\n padding: 1.25rem 1.75rem;\n display: flex;\n align-items: center;\n gap: 1rem;\n cursor: pointer;\n transition: background 0.3s ease;\n }\n\n .trouble-header:hover {\n background: rgba\\(212, 175, 55, 0.04\\);\n }\n\n .trouble-icon {\n width: 36px;\n height: 36px;\n border-radius: 0.5rem;\n background: linear-gradient\\(135deg, rgba\\(196, 30, 58, 0.15\\), rgba\\(212, 175, 55, 0.1\\)\\);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1rem;\n flex-shrink: 0;\n }\n\n .trouble-question {\n font-family: 'Cinzel', serif;\n font-size: 1rem;\n color: var\\(--text-primary\\);\n font-weight: 600;\n }\n\n .trouble-answer {\n padding: 0 1.75rem 1.5rem;\n padding-left: calc\\(1.75rem + 36px + 1rem\\);\n color: var\\(--text-secondary\\);\n font-size: 0.9rem;\n line-height: 1.7;\n }\n\n /* ══════════════════════════════════════════════════════════════\n FOOTER\n ══════════════════════════════════════════════════════════════ */\n footer {\n margin-top: 0;\n position: relative;\n }\n\n .footer-img-wrapper {\n position: relative;\n overflow: hidden;\n }\n\n .footer-img-wrapper::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 200px;\n background: linear-gradient\\(to top, transparent, var\\(--bg\\)\\);\n pointer-events: none;\n z-index: 1;\n }\n\n .footer-img {\n width: 100%;\n display: block;\n }\n\n .footer-content {\n text-align: center;\n padding: 4rem 2rem;\n background: linear-gradient\\(to bottom, var\\(--bg-elevated\\), var\\(--bg-deep\\)\\);\n border-top: 1px solid var\\(--border-gold\\);\n }\n\n .footer-brand {\n font-family: 'Cinzel', serif;\n font-size: 1.3rem;\n color: var\\(--gold\\);\n margin-bottom: 0.5rem;\n }\n\n .footer-text {\n color: var\\(--text-muted\\);\n font-size: 0.9rem;\n margin-bottom: 0.25rem;\n }\n\n .footer-copy {\n color: var\\(--text-muted\\);\n font-size: 0.8rem;\n opacity: 0.5;\n margin-top: 1.5rem;\n }\n\n /* ══════════════════════════════════════════════════════════════\n ANIMATIONS\n ══════════════════════════════════════════════════════════════ */\n .reveal {\n opacity: 0;\n transform: translateY\\(25px\\);\n transition: opacity 0.8s ease, transform 0.8s ease;\n }\n\n .reveal.visible {\n opacity: 1;\n transform: translateY\\(0\\);\n }\n\n /* ── RESPONSIVE ── */\n @media \\(max-width: 768px\\) {\n .container { padding: 0 1.25rem; }\n section { padding: 3.5rem 0; }\n .step { padding-left: 3rem; }\n .install-header h3 { font-size: 1.2rem; }\n .commands-table th, .commands-table td { padding: 0.75rem 1rem; font-size: 0.82rem; }\n .os-tabs { gap: 0.35rem; }\n .os-tab { padding: 0.6rem 1rem; font-size: 0.78rem; }\n .trouble-answer { padding-left: 1.75rem; }\n }\n\n @media \\(max-width: 480px\\) {\n .hero-content { padding: 2rem 0.5rem 3rem; }\n .imperial-seal { width: 70px; height: 70px; font-size: 1.8rem; }\n .code-block { padding: 1.25rem; }\n .code-block pre { font-size: 0.78rem; }\n }\n </style>\n</head>\n\n<body>\n\n <!-- ═══ HEADER ═══ -->\n <header>\n <div class=\"hero-img-wrapper\">\nHTMLEOF)",
|
|
6
|
+
"Bash(\"/tmp/readme_part2.html\" << 'HTMLEOF'\n alt=\"Donghua CLI\" class=\"hero-img\">\n </div>\n <div class=\"hero-content\">\n <div class=\"imperial-seal\">⚔</div>\n <h1>Donghua CLI</h1>\n <div class=\"hero-subtitle\">Imperial Terminal for Chinese Animation</div>\n <p class=\"hero-desc\">Stream and download Donghua from your terminal with a Wuxia-forged engine. Multi-source, multi-platform, multi-quality.</p>\n <p class=\"hero-author\">Forged by <strong>Thanukamax</strong></p>\n <div class=\"badges\">\n <span class=\"badge badge-gold\">Python 3.8+</span>\n <span class=\"badge badge-jade\">MIT License</span>\n <span class=\"badge badge-red\">v2.0</span>\n <span class=\"badge badge-outline\">Linux • Windows • Android • iOS</span>\n </div>\n </div>\n </header>\n\n <!-- ═══ NAVIGATION ═══ -->\n <nav class=\"nav-bar\">\n <div class=\"nav-inner\">\n <a href=\"#features\" class=\"nav-link\">Features</a>\n <a href=\"#prerequisites\" class=\"nav-link\">Prerequisites</a>\n <a href=\"#installation\" class=\"nav-link\">Installation</a>\n <a href=\"#usage\" class=\"nav-link\">Usage</a>\n <a href=\"#troubleshooting\" class=\"nav-link\">Troubleshooting</a>\n </div>\n </nav>\n\n <main class=\"container\">\n\n <!-- ═══ IMPERIAL DECREE ═══ -->\n <div class=\"decree reveal\">\n <div class=\"decree-title\">Imperial Decree: Series Availability</div>\n <p>Ancient tomes and first seasons may be lost to the realms, as source domains preserve only recent chronicles. The imperial scribes are working on realm aggregation techniques to restore these lost cultivation manuals in future updates.</p>\n </div>\n\n <!-- ═══════════ FEATURES ═══════════ -->\n <section id=\"features\">\n <div class=\"section-header reveal\">\n <span class=\"section-label\">Core Abilities</span>\n <h2 class=\"section-title\">Features</h2>\n <p class=\"section-subtitle\">Everything you need to stream and download Donghua from the terminal</p>\n </div>\n <div class=\"grid reveal\">\n <div class=\"card\">\n <span class=\"card-icon\">🌐</span>\n <h3>Multi-Source Scraping</h3>\n <p>Searches LuciferDonghua and AnimeXin simultaneously, giving you the widest library of Chinese animation available.</p>\n </div>\n <div class=\"card\">\n <span class=\"card-icon\">⚡</span>\n <h3>Instant Streaming</h3>\n <p>MPV-powered playback with yt-dlp backend. LRU caching and episode preloading for zero-wait binge sessions.</p>\n </div>\n <div class=\"card\">\n <span class=\"card-icon\">⬇</span>\n <h3>Fast Downloads</h3>\n <p>Save episodes locally to ~/Videos/Donghua for offline cultivation. Select quality from 360p to 1080p.</p>\n </div>\n <div class=\"card\">\n <span class=\"card-icon\">📱</span>\n <h3>Cross-Platform</h3>\n <p>Runs on Linux, Windows, Android \\(Termux\\), and iOS \\(iSH\\). Optimized scripts for each platform.</p>\n </div>\n <div class=\"card\">\n <span class=\"card-icon\">🔎</span>\n <h3>Deep Iframe Scanning</h3>\n <p>Advanced stream extraction that digs through nested iframes, base64 obfuscation, and Dailymotion embeds.</p>\n </div>\n <div class=\"card\">\n <span class=\"card-icon\">🎨</span>\n <h3>Quality Control</h3>\n <p>Choose 360p for mobile data, 720p for balanced, or 1080p for the full cultivation experience.</p>\n </div>\n </div>\n </section>\n\n <div class=\"imperial-divider\"><span class=\"line\"></span><span class=\"ornament\">♦</span><span class=\"line\"></span></div>\n\n <!-- ═══════════ PREREQUISITES ═══════════ -->\n <section id=\"prerequisites\">\n <div class=\"section-header reveal\">\n <span class=\"section-label\">Before You Begin</span>\n <h2 class=\"section-title\">Prerequisites</h2>\n <p class=\"section-subtitle\">Gather these tools before entering the cultivation grounds</p>\n </div>\n <div class=\"prereq-grid reveal\">\n <div class=\"prereq-item\">\n <span class=\"prereq-icon\">🐍</span>\n <div class=\"prereq-name\">Python 3.8+</div>\n <div class=\"prereq-desc\">Core runtime</div>\n </div>\n <div class=\"prereq-item\">\n <span class=\"prereq-icon\">📦</span>\n <div class=\"prereq-name\">pip</div>\n <div class=\"prereq-desc\">Package installer</div>\n </div>\n <div class=\"prereq-item\">\n <span class=\"prereq-icon\">🎬</span>\n <div class=\"prereq-name\">mpv / VLC</div>\n <div class=\"prereq-desc\">Video player</div>\n </div>\n <div class=\"prereq-item\">\n <span class=\"prereq-icon\">📹</span>\n <div class=\"prereq-name\">ffmpeg</div>\n <div class=\"prereq-desc\">Media processing \\(Linux\\)</div>\n </div>\n </div>\n <div class=\"tip tip-jade reveal\" style=\"margin-top: 2rem;\">\n <div class=\"tip-title\">Python Packages \\(Installed Automatically\\)</div>\n <p><code>requests</code> — HTTP requests • <code>beautifulsoup4</code> — HTML parsing • <code>yt-dlp</code> — Video extraction</p>\n </div>\n </section>\n\n <div class=\"imperial-divider\"><span class=\"line\"></span><span class=\"ornament\">♦</span><span class=\"line\"></span></div>\n\n <!-- ═══════════ INSTALLATION ═══════════ -->\n <section id=\"installation\">\n <div class=\"section-header reveal\">\n <span class=\"section-label\">Enter the Realm</span>\n <h2 class=\"section-title\">Installation</h2>\n <p class=\"section-subtitle\">Select your platform and follow the sacred scrolls</p>\n </div>\n\n <!-- OS TABS -->\n <div class=\"os-tabs reveal\">\n <button class=\"os-tab active\" onclick=\"showPanel\\('linux'\\)\"><span class=\"tab-icon\">🐧</span> Linux</button>\n <button class=\"os-tab\" onclick=\"showPanel\\('windows'\\)\"><span class=\"tab-icon\">💻</span> Windows</button>\n <button class=\"os-tab\" onclick=\"showPanel\\('android'\\)\"><span class=\"tab-icon\">📱</span> Android</button>\n <button class=\"os-tab\" onclick=\"showPanel\\('ios'\\)\"><span class=\"tab-icon\">🍎</span> iOS</button>\n </div>\n\n <!-- ─── LINUX PANEL ─── -->\n <div id=\"panel-linux\" class=\"os-panel active\">\n <div class=\"install-header\">\n <div class=\"os-icon\">🐧</div>\n <div>\n <h3>Linux</h3>\n <span class=\"os-version\">Ubuntu / Debian / Linux Mint / Fedora / Arch</span>\n </div>\n </div>\n <div class=\"install-layout\">\n <div class=\"step\">\n <div class=\"step-number\">1</div>\n <div class=\"step-title\">Install System Dependencies</div>\n <div class=\"step-desc\">Install Python, the MPV video player, and ffmpeg for media processing. Open your terminal with <code>Ctrl+Alt+T</code>.</div>\n <div class=\"code-block\">\n <span class=\"code-label\">Terminal</span>\n <pre><span class=\"comment\"># Debian / Ubuntu / Mint</span>\n<span class=\"cmd\">sudo apt update</span> <span class=\"flag\">&&</span> <span class=\"cmd\">sudo apt install</span> <span class=\"flag\">python3 python3-pip mpv ffmpeg -y</span>\n\n<span class=\"comment\"># Fedora</span>\n<span class=\"cmd\">sudo dnf install</span> <span class=\"flag\">python3 python3-pip mpv ffmpeg -y</span>\n\n<span class=\"comment\"># Arch Linux</span>\n<span class=\"cmd\">sudo pacman -S</span> <span class=\"flag\">python python-pip mpv ffmpeg</span></pre>\n <button class=\"copy-btn\" onclick=\"copyCode\\(this\\)\">Copy</button>\n </div>\n </div>\n\n <div class=\"step\">\n <div class=\"step-number\">2</div>\n <div class=\"step-title\">Install Python Dependencies</div>\n <div class=\"step-desc\">Install the required Python packages that power the stream extraction engine.</div>\n <div class=\"code-block\">\n <span class=\"code-label\">Terminal</span>\n <pre><span class=\"cmd\">pip3 install</span> <span class=\"flag\">requests beautifulsoup4 yt-dlp</span></pre>\n <button class=\"copy-btn\" onclick=\"copyCode\\(this\\)\">Copy</button>\n </div>\n </div>\n\n <div class=\"step\">\n <div class=\"step-number\">3</div>\n <div class=\"step-title\">Run the Installer</div>\n <div class=\"step-desc\">The installer detects your shell \\(bash/zsh\\), backs up your config, and adds the <code>dhua</code> command aliases.</div>\n <div class=\"code-block\">\n <span class=\"code-label\">Terminal</span>\n <pre><span class=\"cmd\">chmod +x</span> <span class=\"flag\">install.sh</span> <span class=\"flag\">&&</span> <span class=\"cmd\">./install.sh</span></pre>\n <button class=\"copy-btn\" onclick=\"copyCode\\(this\\)\">Copy</button>\n </div>\n </div>\n\n <div class=\"step\">\n <div class=\"step-number\">4</div>\n <div class=\"step-title\">Reload & Verify</div>\n <div class=\"step-desc\">Reload your shell configuration, then verify the installation.</div>\n <div class=\"code-block\">\n <span class=\"code-label\">Terminal</span>\n <pre><span class=\"cmd\">source</span> <span class=\"flag\">~/.bashrc</span> <span class=\"comment\"># or ~/.zshrc if you use Zsh</span></pre>\n <button class=\"copy-btn\" onclick=\"copyCode\\(this\\)\">Copy</button>\n </div>\n <div class=\"verify-box\">\n <div class=\"verify-title\">Verify Installation</div>\n <p style=\"color: var\\(--text-secondary\\); font-size: 0.88rem;\">Run <code>dhua \"test\"</code> — if the Wuxia-themed interface appears, the installation succeeded.</p>\n </div>\n </div>\n </div>\n\n <div class=\"tip reveal\">\n <div class=\"tip-title\">Manual Run \\(Without Installer\\)</div>\n <p>You can always run directly: <code>python3 dhua.py \"Series Name\"</code></p>\n </div>\n </div>\n\n <!-- ─── WINDOWS PANEL ─── -->\n <div id=\"panel-windows\" class=\"os-panel\">\n <div class=\"install-header\">\n <div class=\"os-icon\">💻</div>\n <div>\n <h3>Windows</h3>\n <span class=\"os-version\">Windows 10 / 11 • PowerShell</span>\n </div>\n </div>\n <div class=\"install-layout\">\n <div class=\"step\">\n <div class=\"step-number\">1</div>\n <div class=\"step-title\">Install Python</div>\n <div class=\"step-desc\">Download and install Python from <a href=\"https://python.org\" target=\"_blank\">python.org</a>. During installation, check the box that says <strong>\"Add Python to PATH\"</strong> — this is essential.</div>\n </div>\n\n <div class=\"step\">\n <div class=\"step-number\">2</div>\n <div class=\"step-title\">Install MPV Player</div>\n <div class=\"step-desc\">MPV is the recommended video player. Install it via winget or download from <a href=\"https://mpv.io\" target=\"_blank\">mpv.io</a>.</div>\n <div class=\"code-block\">\n <span class=\"code-label\">PowerShell</span>\n <pre><span class=\"cmd\">winget install</span> <span class=\"flag\">mpv</span></pre>\n <button class=\"copy-btn\" onclick=\"copyCode\\(this\\)\">Copy</button>\n </div>\n </div>\n\n <div class=\"step\">\n <div class=\"step-number\">3</div>\n <div class=\"step-title\">Allow Script Execution</div>\n <div class=\"step-desc\">Right-click the Start button, select <strong>PowerShell</strong> \\(or Terminal\\), and allow local script execution.</div>\n <div class=\"code-block\">\n <span class=\"code-label\">PowerShell</span>\n <pre><span class=\"cmd\">Set-ExecutionPolicy</span> <span class=\"flag\">Bypass -Scope CurrentUser</span></pre>\n <button class=\"copy-btn\" onclick=\"copyCode\\(this\\)\">Copy</button>\n </div>\n </div>\n\n <div class=\"step\">\n <div class=\"step-number\">4</div>\n <div class=\"step-title\">Run the Installer</div>\n <div class=\"step-desc\">Navigate to the Donghua CLI folder and run the PowerShell installer. It will install Python dependencies, detect MPV, and set up the <code>dhua</code> commands in your PowerShell profile.</div>\n <div class=\"code-block\">\n <span class=\"code-label\">PowerShell</span>\n <pre><span class=\"cmd\">.\\\\install.ps1</span></pre>\n <button class=\"copy-btn\" onclick=\"copyCode\\(this\\)\">Copy</button>\n </div>\n </div>\n\n <div class=\"step\">\n <div class=\"step-number\">5</div>\n <div class=\"step-title\">Restart & Verify</div>\n <div class=\"step-desc\">Close and reopen PowerShell to load the new profile, then verify.</div>\n <div class=\"verify-box\">\n <div class=\"verify-title\">Verify Installation</div>\n <p style=\"color: var\\(--text-secondary\\); font-size: 0.88rem;\">Run <code>dhua \"test\"</code> in a new PowerShell window — you should see the Donghua CLI interface.</p>\n </div>\n </div>\n </div>\n\n <div class=\"tip reveal\">\n <div class=\"tip-title\">What the Installer Does</div>\n <p>The PowerShell installer: \\(1\\) auto-detects your Python command \\(<code>python</code>, <code>python3</code>, or <code>py</code>\\), \\(2\\) installs requests, beautifulsoup4 & yt-dlp via pip, \\(3\\) checks for MPV, and \\(4\\) adds function aliases to your PowerShell profile.</p>\n </div>\n </div>\n\n <!-- ─── ANDROID PANEL ─── -->\n <div id=\"panel-android\" class=\"os-panel\">\n <div class=\"install-header\">\n <div class=\"os-icon\">📱</div>\n <div>\n <h3>Android</h3>\n <span class=\"os-version\">Termux • Android 7+</span>\n </div>\n </div>\n <div class=\"install-layout\">\n <div class=\"step\">\n <div class=\"step-number\">1</div>\n <div class=\"step-title\">Install Termux</div>\n <div class=\"step-desc\">Download <strong>Termux</strong> from <a href=\"https://f-droid.org\" target=\"_blank\">F-Droid</a> \\(not the Play Store — that version is outdated and broken\\).</div>\n </div>\n\n <div class=\"step\">\n <div class=\"step-number\">2</div>\n <div class=\"step-title\">Update & Install Packages</div>\n <div class=\"step-desc\">Open Termux and update all packages, then install Python and the required libraries.</div>\n <div class=\"code-block\">\n <span class=\"code-label\">Termux</span>\n <pre><span class=\"comment\"># Update Termux packages</span>\n<span class=\"cmd\">pkg update</span> <span class=\"flag\">&&</span> <span class=\"cmd\">pkg upgrade</span> <span class=\"flag\">-y</span>\n\n<span class=\"comment\"># Install Python and libraries</span>\n<span class=\"cmd\">pkg install</span> <span class=\"flag\">python python-pip requests beautifulsoup4 -y</span></pre>\n <button class=\"copy-btn\" onclick=\"copyCode\\(this\\)\">Copy</button>\n </div>\n </div>\n\n <div class=\"step\">\n <div class=\"step-number\">3</div>\n <div class=\"step-title\">Install yt-dlp</div>\n <div class=\"step-desc\">Install the video extraction backend.</div>\n <div class=\"code-block\">\n <span class=\"code-label\">Termux</span>\n <pre><span class=\"cmd\">pip install</span> <span class=\"flag\">yt-dlp</span></pre>\n <button class=\"copy-btn\" onclick=\"copyCode\\(this\\)\">Copy</button>\n </div>\n </div>\n\n <div class=\"step\">\n <div class=\"step-number\">4</div>\n <div class=\"step-title\">Get the Android Script</div>\n <div class=\"step-desc\">Download the Android-optimized <code>donghua.py</code> script to your Termux environment.</div>\n <div class=\"code-block\">\n <span class=\"code-label\">Termux</span>\n <pre><span class=\"comment\"># Copy donghua.py into Termux, then:</span>\n<span class=\"cmd\">chmod +x</span> <span class=\"flag\">donghua.py</span></pre>\n <button class=\"copy-btn\" onclick=\"copyCode\\(this\\)\">Copy</button>\n </div>\n </div>\n\n <div class=\"step\">\n <div class=\"step-number\">5</div>\n <div class=\"step-title\">Run & Enjoy</div>\n <div class=\"step-desc\">Launch the script. It auto-detects your installed video player \\(MPV, VLC, or MX Player\\).</div>\n <div class=\"code-block\">\n <span class=\"code-label\">Termux</span>\n <pre><span class=\"cmd\">python</span> <span class=\"flag\">donghua.py</span></pre>\n <button class=\"copy-btn\" onclick=\"copyCode\\(this\\)\">Copy</button>\n </div>\n <div class=\"verify-box\">\n <div class=\"verify-title\">Verify Installation</div>\n <p style=\"color: var\\(--text-secondary\\); font-size: 0.88rem;\">The Donghua CLI menu should appear. Search for any series to confirm streams are working.</p>\n </div>\n </div>\n </div>\n\n <div class=\"tip tip-jade reveal\">\n <div class=\"tip-title\">Android-Specific Features</div>\n <p>\n • Defaults to 360p to save mobile data<br>\n • Deep iframe scanning finds hidden streams<br>\n • Dailymotion embed auto-detection<br>\n • Auto-launches MPV, VLC, MX Player, or any installed video app\n </p>\n </div>\n </div>\n\n <!-- ─── iOS PANEL ─── -->\n <div id=\"panel-ios\" class=\"os-panel\">\n <div class=\"install-header\">\n <div class=\"os-icon\">🍎</div>\n <div>\n <h3>iOS</h3>\n <span class=\"os-version\">iSH Shell • iPhone / iPad</span>\n </div>\n </div>\n <div class=\"install-layout\">\n <div class=\"step\">\n <div class=\"step-number\">1</div>\n <div class=\"step-title\">Install iSH Shell</div>\n <div class=\"step-desc\">Download <strong>iSH Shell</strong> from the <a href=\"https://apps.apple.com/app/ish-shell/id1436902243\" target=\"_blank\">App Store</a>. This gives you a Linux-like terminal on iOS.</div>\n </div>\n\n <div class=\"step\">\n <div class=\"step-number\">2</div>\n <div class=\"step-title\">Install VLC</div>\n <div class=\"step-desc\">Install <strong>VLC for Mobile</strong> from the App Store. This is the video player that will handle stream playback.</div>\n </div>\n\n <div class=\"step\">\n <div class=\"step-number\">3</div>\n <div class=\"step-title\">Update & Install Python</div>\n <div class=\"step-desc\">Open iSH and install Python with the Alpine package manager.</div>\n <div class=\"code-block\">\n <span class=\"code-label\">iSH</span>\n <pre><span class=\"cmd\">apk update</span> <span class=\"flag\">&&</span> <span class=\"cmd\">apk upgrade</span>\n<span class=\"cmd\">apk add</span> <span class=\"flag\">python3 py3-pip</span></pre>\n <button class=\"copy-btn\" onclick=\"copyCode\\(this\\)\">Copy</button>\n </div>\n </div>\n\n <div class=\"step\">\n <div class=\"step-number\">4</div>\n <div class=\"step-title\">Install Dependencies</div>\n <div class=\"step-desc\">Install the Python packages required by Donghua CLI.</div>\n <div class=\"code-block\">\n <span class=\"code-label\">iSH</span>\n <pre><span class=\"cmd\">pip3 install</span> <span class=\"flag\">requests beautifulsoup4 yt-dlp</span></pre>\n <button class=\"copy-btn\" onclick=\"copyCode\\(this\\)\">Copy</button>\n </div>\n </div>\n\n <div class=\"step\">\n <div class=\"step-number\">5</div>\n <div class=\"step-title\">Get the Script & Run</div>\n <div class=\"step-desc\">Copy <code>donghua.py</code> into iSH and launch it. Streams will open as clickable links that launch VLC.</div>\n <div class=\"code-block\">\n <span class=\"code-label\">iSH</span>\n <pre><span class=\"cmd\">python3</span> <span class=\"flag\">donghua.py</span></pre>\n <button class=\"copy-btn\" onclick=\"copyCode\\(this\\)\">Copy</button>\n </div>\n <div class=\"verify-box\">\n <div class=\"verify-title\">Verify Installation</div>\n <p style=\"color: var\\(--text-secondary\\); font-size: 0.88rem;\">Search for any series. Tap a stream link to open it in VLC.</p>\n </div>\n </div>\n </div>\n\n <div class=\"tip reveal\">\n <div class=\"tip-title\">iOS Notes</div>\n <p>\n • VLC is required for video playback on iOS<br>\n • Stream URLs appear as clickable links in the terminal<br>\n • iSH uses Alpine Linux under the hood, so <code>apk</code> is the package manager\n </p>\n </div>\n </div>\n\n <!-- Update Notes -->\n <div class=\"tip reveal\" style=\"margin-top: 3rem;\">\n <div class=\"tip-title\">v2.0 — Recent Updates</div>\n <p>\n • <strong>Python 3.14 compatibility</strong> — Fixed subprocess handling<br>\n • <strong>Deep iframe scanning</strong> — Extracts streams from hidden/nested players<br>\n • <strong>Dailymotion detection</strong> — Automatically finds Dailymotion embeds<br>\n • <strong>Android optimization</strong> — Mobile-friendly with 360p default<br>\n • <strong>Multi-player support</strong> — Auto-launches MPV, VLC, or MX Player on Android\n </p>\n </div>\n </section>\n\n <div class=\"imperial-divider\"><span class=\"line\"></span><span class=\"ornament\">♦</span><span class=\"line\"></span></div>\n\n <!-- ═══════════ USAGE ═══════════ -->\n <section id=\"usage\">\n <div class=\"section-header reveal\">\n <span class=\"section-label\">Command Scrolls</span>\n <h2 class=\"section-title\">Usage</h2>\n <p class=\"section-subtitle\">Master these commands to navigate the cultivation grounds</p>\n </div>\n\n <!-- Desktop Commands -->\n <h3 style=\"font-size: 1.3rem; margin-bottom: 1.5rem; color: var\\(--text-primary\\);\" class=\"reveal\">Desktop Commands \\(Linux & Windows\\)</h3>\n\n <table class=\"commands-table reveal\">\n <thead>\n <tr>\n <th>Command</th>\n <th>Description</th>\n <th>Example</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td><code>dhua</code></td>\n <td>Interactive search & stream \\(default quality\\)</td>\n <td><code>dhua \"Soul Land\"</code></td>\n </tr>\n <tr>\n <td><code>dhua360</code></td>\n <td>Stream in 360p \\(low bandwidth\\)</td>\n <td><code>dhua360 \"Perfect World\"</code></td>\n </tr>\n <tr>\n <td><code>dhua480</code></td>\n <td>Stream in 480p \\(balanced\\)</td>\n <td><code>dhua480 \"Martial Peak\"</code></td>\n </tr>\n <tr>\n <td><code>dhua720</code></td>\n <td>Stream in 720p HD</td>\n <td><code>dhua720 \"Btth\"</code></td>\n </tr>\n <tr>\n <td><code>dhua1080</code></td>\n <td>Stream in 1080p Full HD</td>\n <td><code>dhua1080 \"Swallowed Star\"</code></td>\n </tr>\n <tr>\n <td><code>dhuadl</code></td>\n <td>Download episodes to ~/Videos/Donghua</td>\n <td><code>dhuadl \"Martial Peak\"</code></td>\n </tr>\n </tbody>\n </table>\n\n <!-- Mobile Commands -->\n <h3 style=\"font-size: 1.3rem; margin-top: 3rem; margin-bottom: 1.5rem; color: var\\(--text-primary\\);\" class=\"reveal\">Mobile Commands \\(Android & iOS\\)</h3>\n\n <div class=\"grid reveal\" style=\"grid-template-columns: 1fr 1fr;\">\n <div class=\"card\" style=\"border-color: rgba\\(16, 185, 129, 0.3\\);\">\n <h3 style=\"font-size: 1.05rem;\">Android / Termux</h3>\n <p><code>python donghua.py</code></p>\n <p style=\"margin-top: 0.75rem; font-size: 0.85rem;\">Interactive menu with 360p default. Auto-detects installed video players.</p>\n </div>\n <div class=\"card\" style=\"border-color: rgba\\(79, 70, 229, 0.3\\);\">\n <h3 style=\"font-size: 1.05rem;\">iOS / iSH</h3>\n <p><code>python3 donghua.py</code></p>\n <p style=\"margin-top: 0.75rem; font-size: 0.85rem;\">Interactive menu with VLC integration. Clickable stream links.</p>\n </div>\n </div>\n\n <!-- Pro Tips -->\n <h3 style=\"font-size: 1.3rem; margin-top: 3rem; margin-bottom: 1.5rem; color: var\\(--text-primary\\);\" class=\"reveal\">Cultivation Tips</h3>\n\n <div class=\"grid reveal\">\n <div class=\"card\">\n <span class=\"card-icon\">💡</span>\n <h3>Series Selection</h3>\n <p>When multiple results appear, type the number and press Enter to select.</p>\n </div>\n <div class=\"card\">\n <span class=\"card-icon\">📺</span>\n <h3>Episode Ranges</h3>\n <p>Play multiple episodes: type <code>1-5</code> for a range or <code>1,3,7</code> for specific episodes.</p>\n </div>\n <div class=\"card\">\n <span class=\"card-icon\">⚡</span>\n <h3>Stream Caching</h3>\n <p>Previously played streams are cached. Replay any episode instantly without re-extracting.</p>\n </div>\n <div class=\"card\">\n <span class=\"card-icon\">🔄</span>\n <h3>Episode Preloading</h3>\n <p>The next episode's stream is preloaded in the background while you watch. Zero wait between episodes.</p>\n </div>\n </div>\n </section>\n\n <div class=\"imperial-divider\"><span class=\"line\"></span><span class=\"ornament\">♦</span><span class=\"line\"></span></div>\n\n <!-- ═══════════ TROUBLESHOOTING ═══════════ -->\n <section id=\"troubleshooting\">\n <div class=\"section-header reveal\">\n <span class=\"section-label\">Scroll of Remedies</span>\n <h2 class=\"section-title\">Troubleshooting</h2>\n <p class=\"section-subtitle\">Common issues and their solutions</p>\n </div>\n\n <div class=\"reveal\">\n <div class=\"trouble-card\">\n <div class=\"trouble-header\">\n <div class=\"trouble-icon\">❌</div>\n <div class=\"trouble-question\">\"dhua: command not found\"</div>\n </div>\n <div class=\"trouble-answer\">\n Your shell hasn't loaded the new aliases yet. Close your terminal completely and reopen it, or run <code>source ~/.bashrc</code> \\(or <code>source ~/.zshrc</code>\\). On Windows, restart PowerShell. You can also run directly with <code>python3 dhua.py \"query\"</code>.\n </div>\n </div>\n\n <div class=\"trouble-card\">\n <div class=\"trouble-header\">\n <div class=\"trouble-icon\">🚫</div>\n <div class=\"trouble-question\">\"Video won't play\" or \"No stream found\"</div>\n </div>\n <div class=\"trouble-answer\">\n The source site may be down or blocking your region. Try a different series or wait a few minutes. Make sure <code>mpv</code> is installed and in your PATH. On Android, ensure you have a video player app installed \\(MPV, VLC, or MX Player\\).\n </div>\n </div>\n\n <div class=\"trouble-card\">\n <div class=\"trouble-header\">\n <div class=\"trouble-icon\">🐍</div>\n <div class=\"trouble-question\">\"ModuleNotFoundError\" for requests or bs4</div>\n </div>\n <div class=\"trouble-answer\">\n The Python dependencies aren't installed. Run <code>pip3 install requests beautifulsoup4 yt-dlp</code>. If you have multiple Python versions, make sure you're using the right pip \\(try <code>python3 -m pip install</code> instead\\).\n </div>\n </div>\n\n <div class=\"trouble-card\">\n <div class=\"trouble-header\">\n <div class=\"trouble-icon\">🔒</div>\n <div class=\"trouble-question\">PowerShell: \"execution of scripts is disabled\"</div>\n </div>\n <div class=\"trouble-answer\">\n Run <code>Set-ExecutionPolicy Bypass -Scope CurrentUser</code> in PowerShell before running the installer. This allows local scripts to execute on your machine.\n </div>\n </div>\n\n <div class=\"trouble-card\">\n <div class=\"trouble-header\">\n <div class=\"trouble-icon\">📱</div>\n <div class=\"trouble-question\">Android: video player doesn't launch</div>\n </div>\n <div class=\"trouble-answer\">\n Make sure you have a video player installed \\(MPV for Android, VLC, or MX Player\\). The script auto-detects installed players. If no player is found, it will print the stream URL so you can copy and paste it into any browser or player.\n </div>\n </div>\n\n <div class=\"trouble-card\">\n <div class=\"trouble-header\">\n <div class=\"trouble-icon\">🍎</div>\n <div class=\"trouble-question\">iOS: streams not opening in VLC</div>\n </div>\n <div class=\"trouble-answer\">\n Ensure VLC for Mobile is installed from the App Store. In iSH, stream URLs appear as clickable links in the terminal. Tap the link to open it in VLC. If links aren't clickable, long-press to copy the URL and paste it into VLC's \"Open Network Stream\" option.\n </div>\n </div>\n </div>\n </section>\n\n </main>\n\n <!-- ═══ FOOTER ═══ -->\n <footer>\n <div class=\"footer-img-wrapper\">\nHTMLEOF)",
|
|
7
|
+
"Bash(\"/tmp/readme_part3.html\" << 'HTMLEOF'\n alt=\"Donghua CLI\" class=\"footer-img\">\n </div>\n <div class=\"footer-content\">\n <div class=\"imperial-divider\" style=\"padding: 1rem 0 2rem;\"><span class=\"line\"></span><span class=\"ornament\">♦</span><span class=\"line\"></span></div>\n <div class=\"footer-brand\">Donghua CLI</div>\n <p class=\"footer-text\">Developed with passion by <strong style=\"color: var\\(--gold\\);\">Thanukamax</strong></p>\n <p class=\"footer-text\">Built for the Donghua community. Enjoy the cultivation.</p>\n <p class=\"footer-copy\">© 2026 Donghua CLI • All Rights Reserved</p>\n </div>\n </footer>\n\n <script>\n // ══ TAB SWITCHING ══\n function showPanel\\(os\\) {\n document.querySelectorAll\\('.os-panel'\\).forEach\\(p => p.classList.remove\\('active'\\)\\);\n document.querySelectorAll\\('.os-tab'\\).forEach\\(t => t.classList.remove\\('active'\\)\\);\n document.getElementById\\('panel-' + os\\).classList.add\\('active'\\);\n event.currentTarget.classList.add\\('active'\\);\n }\n\n // ══ COPY TO CLIPBOARD ══\n function copyCode\\(btn\\) {\n const pre = btn.parentElement.querySelector\\('pre'\\);\n const text = pre.innerText;\n navigator.clipboard.writeText\\(text\\).then\\(\\(\\) => {\n btn.textContent = 'Copied!';\n btn.style.color = '#2ed89a';\n btn.style.borderColor = '#2ed89a';\n setTimeout\\(\\(\\) => {\n btn.textContent = 'Copy';\n btn.style.color = '';\n btn.style.borderColor = '';\n }, 2000\\);\n }\\);\n }\n\n // ══ SCROLL REVEAL ANIMATION ══\n const revealObserver = new IntersectionObserver\\(\\(entries\\) => {\n entries.forEach\\(entry => {\n if \\(entry.isIntersecting\\) {\n entry.target.classList.add\\('visible'\\);\n }\n }\\);\n }, { threshold: 0.08, rootMargin: '0px 0px -40px 0px' }\\);\n\n document.querySelectorAll\\('.reveal'\\).forEach\\(el => revealObserver.observe\\(el\\)\\);\n\n // ══ ACTIVE NAV HIGHLIGHT ══\n const sections = document.querySelectorAll\\('section[id]'\\);\n const navLinks = document.querySelectorAll\\('.nav-link'\\);\n\n window.addEventListener\\('scroll', \\(\\) => {\n let current = '';\n sections.forEach\\(section => {\n const top = section.offsetTop - 120;\n if \\(window.scrollY >= top\\) {\n current = section.getAttribute\\('id'\\);\n }\n }\\);\n navLinks.forEach\\(link => {\n link.style.color = '';\n if \\(link.getAttribute\\('href'\\) === '#' + current\\) {\n link.style.color = 'var\\(--gold\\)';\n }\n }\\);\n }\\);\n </script>\n\n</body>\n</html>\nHTMLEOF)",
|
|
8
|
+
"Bash(awk:*)",
|
|
9
|
+
"Bash(gpg:*)",
|
|
10
|
+
"Bash(sudo tee:*)",
|
|
11
|
+
"Bash(curl:*)",
|
|
12
|
+
"Bash(chmod:*)",
|
|
13
|
+
"Bash(~/fix-github-desktop-auth.sh:*)",
|
|
14
|
+
"Bash(__NEW_LINE_ff6b0ec1ef9f5895__ chmod +x ~/fix-github-desktop-auth.sh)",
|
|
15
|
+
"Bash(~/fix-github-desktop-auth.sh)",
|
|
16
|
+
"Bash(git init:*)",
|
|
17
|
+
"Bash(git branch:*)",
|
|
18
|
+
"Bash(git config:*)",
|
|
19
|
+
"Bash(git add:*)",
|
|
20
|
+
"Bash(git commit:*)",
|
|
21
|
+
"Bash(gh:*)",
|
|
22
|
+
"Bash(git push:*)",
|
|
23
|
+
"Bash(git rm:*)",
|
|
24
|
+
"Bash(xdg-open:*)",
|
|
25
|
+
"Bash(convert:*)",
|
|
26
|
+
"Bash(ls:*)"
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint-and-test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: |
|
|
26
|
+
python -m pip install --upgrade pip
|
|
27
|
+
pip install -e ".[dev]"
|
|
28
|
+
|
|
29
|
+
- name: Lint with ruff
|
|
30
|
+
run: ruff check src/
|
|
31
|
+
|
|
32
|
+
- name: Type check with pyright
|
|
33
|
+
run: pyright src/
|
|
34
|
+
continue-on-error: true # non-blocking until types are fully clean
|
|
35
|
+
|
|
36
|
+
- name: Run tests
|
|
37
|
+
run: pytest tests/ -v --tb=short
|
|
38
|
+
continue-on-error: true # tests being added incrementally
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build-and-publish:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Set up Python
|
|
19
|
+
uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.12"
|
|
22
|
+
|
|
23
|
+
- name: Install build tools
|
|
24
|
+
run: pip install build twine
|
|
25
|
+
|
|
26
|
+
- name: Build package
|
|
27
|
+
run: python -m build
|
|
28
|
+
|
|
29
|
+
- name: Publish to PyPI
|
|
30
|
+
env:
|
|
31
|
+
TWINE_USERNAME: __token__
|
|
32
|
+
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
|
|
33
|
+
run: twine upload dist/*
|
|
34
|
+
|
|
35
|
+
- name: Create GitHub Release
|
|
36
|
+
uses: softprops/action-gh-release@v2
|
|
37
|
+
with:
|
|
38
|
+
files: dist/*
|
|
39
|
+
generate_release_notes: true
|
|
40
|
+
|
|
41
|
+
build-windows-exe:
|
|
42
|
+
runs-on: windows-latest
|
|
43
|
+
needs: build-and-publish
|
|
44
|
+
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
|
+
|
|
48
|
+
- name: Set up Python
|
|
49
|
+
uses: actions/setup-python@v5
|
|
50
|
+
with:
|
|
51
|
+
python-version: "3.12"
|
|
52
|
+
|
|
53
|
+
- name: Install dependencies
|
|
54
|
+
run: |
|
|
55
|
+
pip install -e .
|
|
56
|
+
pip install pyinstaller
|
|
57
|
+
|
|
58
|
+
- name: Build Windows executable
|
|
59
|
+
run: pyinstaller packaging/pyinstaller/donghua.spec --noconfirm
|
|
60
|
+
|
|
61
|
+
- name: Upload exe to release
|
|
62
|
+
uses: softprops/action-gh-release@v2
|
|
63
|
+
with:
|
|
64
|
+
files: dist/donghua.exe
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Virtual Environment
|
|
7
|
+
.venv/
|
|
8
|
+
venv/
|
|
9
|
+
ENV/
|
|
10
|
+
env/
|
|
11
|
+
|
|
12
|
+
# Cache directories
|
|
13
|
+
.cache/
|
|
14
|
+
*.cache
|
|
15
|
+
|
|
16
|
+
# IDE
|
|
17
|
+
.vscode/
|
|
18
|
+
.idea/
|
|
19
|
+
*.swp
|
|
20
|
+
*.swo
|
|
21
|
+
|
|
22
|
+
# OS
|
|
23
|
+
.DS_Store
|
|
24
|
+
Thumbs.db
|
|
25
|
+
|
|
26
|
+
# Donghua CLI specific
|
|
27
|
+
.donghua/
|
|
28
|
+
stream_cache.json
|
|
29
|
+
episode_cache.json
|
|
30
|
+
|
|
31
|
+
# Logs
|
|
32
|
+
*.log
|
|
33
|
+
debug_log.txt
|
|
34
|
+
|
|
35
|
+
# Debug / dev scripts
|
|
36
|
+
debug.py
|
|
37
|
+
logs.py
|
|
38
|
+
|
|
39
|
+
# Distribution / packaging
|
|
40
|
+
dist/
|
|
41
|
+
build/
|
|
42
|
+
*.egg-info/
|
|
43
|
+
*.egg
|
|
44
|
+
|
|
45
|
+
# Misc
|
|
46
|
+
embed_images.py
|
|
47
|
+
create_screenshots.sh
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Donghua CLI will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [3.1.0] - 2026-04-10
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Movie support** -- movies now show up in search results and episode lists. Fixed keyword filter that rejected movie parts (PT-01, Part 01) across all 5 source plugins.
|
|
9
|
+
- **Movie/Series type tags** -- search results display `[MOVIE]` or `[SERIES]` badges so movies and series are visually distinct.
|
|
10
|
+
- **Desktop shortcuts** -- Linux `.desktop` file, Windows `.bat` launcher, and macOS app shortcut for quick access.
|
|
11
|
+
- **Updated installers** -- `install.sh` and `install.ps1` rewritten for v3 modular `pip install` workflow with quality aliases.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- Episode number extraction now handles `PT-XX` and `Part XX` formats used by movie pages.
|
|
15
|
+
- All 5 source plugins (LuciferDonghua, AnimeXin, MisterDonghua, H-Donghua, LMAnime) now accept movie part keywords.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- Homebrew formula and AUR PKGBUILD updated to 3.1.0.
|
|
19
|
+
- PyInstaller spec updated with all current modules.
|
|
20
|
+
- CI publish workflow updated for cleaner release flow.
|
|
21
|
+
|
|
22
|
+
## [3.0.0] - 2026-04-10
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- **Source plugin system** -- each source is its own module. Adding a new site = one file.
|
|
26
|
+
- **Unified global search** -- searches all sources concurrently, merges results by fuzzy title match. No more picking a source upfront.
|
|
27
|
+
- **Automatic server fallback** -- when stream extraction fails on one source, silently tries the next. Episodes show which servers they're available on.
|
|
28
|
+
- **Rich terminal UI** -- replaced ~300 lines of raw ANSI codes with Rich panels, tables, and styled output. Wuxia theme preserved.
|
|
29
|
+
- **httpx + selectolax** -- replaced requests + BeautifulSoup for ~5-10x faster HTTP and HTML parsing. Connection pooling via shared client.
|
|
30
|
+
- **Adaptive preloader** -- tracks navigation patterns. Sequential watchers get 3-5 episodes preloaded ahead; jumpy users get reduced lookahead.
|
|
31
|
+
- **Concurrent async search** -- `asyncio.gather` searches all sources in parallel.
|
|
32
|
+
- **Episode multi-source merging** -- episodes matched by number across sources, each storing URLs from every available server.
|
|
33
|
+
- **pyproject.toml** -- proper Python packaging with hatchling. Installable via `pipx install donghua-cli`.
|
|
34
|
+
- **Dual entry points** -- both `donghua` and `dhua` commands registered.
|
|
35
|
+
- **Unit tests** -- 43 tests covering utils, scraper logic, extraction patterns, and data structures.
|
|
36
|
+
- **CI/CD** -- GitHub Actions for lint/test on push, PyPI publish on tag, Windows exe on release.
|
|
37
|
+
- **Packaging** -- Homebrew formula, AUR PKGBUILD, PyInstaller spec for Windows binary.
|
|
38
|
+
- **Config file support** -- `~/.config/donghua-cli/config.toml` for persistent preferences.
|
|
39
|
+
- **CHANGELOG.md** -- you're reading it.
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
- **Modular architecture** -- 2 monolithic scripts (1550 + 500 lines) replaced by 12 focused modules in `src/donghua_cli/`.
|
|
43
|
+
- **No more source selection** -- sources are now "servers" that work automatically behind the scenes.
|
|
44
|
+
- **Merged Termux support** -- Android/Termux is auto-detected (no separate script needed).
|
|
45
|
+
- **Dependencies** -- `requests` -> `httpx`, `beautifulsoup4` -> `selectolax`, added `rich`.
|
|
46
|
+
- **Cache format** -- now stores (stream_url, source_key) pairs. Backward-compatible with old format.
|
|
47
|
+
|
|
48
|
+
### Removed
|
|
49
|
+
- `dhua.py` and `donghua.py` monolithic scripts (preserved in git history).
|
|
50
|
+
- `--source` / `-s` CLI flag (sources are automatic now).
|
|
51
|
+
- Manual ANSI escape code theme system.
|
|
52
|
+
|
|
53
|
+
## [2.0.0] - 2025
|
|
54
|
+
|
|
55
|
+
- Initial public release with dual-script architecture.
|
|
56
|
+
- LuciferDonghua + AnimeXin sources.
|
|
57
|
+
- Wuxia-themed terminal UI with ANSI codes.
|
|
58
|
+
- LRU cache, background preloading, yt-dlp fallback.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Thanukamax
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|