diff --git a/README.md b/README.md
index 974c9f6..68ce041 100644
--- a/README.md
+++ b/README.md
@@ -57,6 +57,19 @@
下载本项目最新的源码或 Releases 发布的源码至本地
运行 main.py 即可使用
+🕹 用户脚本
+如果您的浏览器安装了 Tampermonkey 浏览器扩展程序,可以添加 用户脚本,无需下载即可体验!
+使用 XHS-Downloader 用户脚本批量获取作品链接,搭配 XHS-Downloader 程序可以实现批量下载无水印作品文件!
+脚本功能
+
+- 下载无水印作品文件
+- 提取发现页面作品链接
+- 提取账号发布作品链接
+- 提取账号收藏作品链接
+- 提取账号点赞作品链接
+
+脚本截图
+
💻 二次开发
如果有其他需求,可以根据 main.py 的注释提示进行代码调用或修改!
@@ -193,9 +206,6 @@ async with XHS(work_path=work_path,
-🕹 用户脚本
-如果您的浏览器安装了 Tampermonkey 浏览器扩展程序,可以 点击获取 用户脚本,无需下载即可体验!
-可以使用 XHS-Downloader 用户脚本批量获取账号作品链接,搭配 XHS-Downloader 程序实现批量下载账号作品文件!
♥️ 支持项目
如果 XHS-Downloader 对您有帮助,请考虑为它点个 Star ⭐,感谢您的支持!
diff --git a/source/TUI/__init__.py b/source/TUI/__init__.py
index e91bd0f..7aed1a2 100644
--- a/source/TUI/__init__.py
+++ b/source/TUI/__init__.py
@@ -1,3 +1,3 @@
-from .index import XHSDownloader
+from .app import XHSDownloader
__all__ = ['XHSDownloader']
diff --git a/source/TUI/app.py b/source/TUI/app.py
new file mode 100644
index 0000000..d3d28ff
--- /dev/null
+++ b/source/TUI/app.py
@@ -0,0 +1,37 @@
+from textual.app import App
+
+from source.application import XHS
+from source.module import (
+ ROOT,
+)
+from source.module import Settings
+from source.translator import Chinese
+from source.translator import LANGUAGE
+from .index import Index
+from .setting import Setting
+
+__all__ = ["XHSDownloader"]
+
+
+class XHSDownloader(App):
+ def __init__(self):
+ super().__init__()
+ self.settings = Settings(ROOT)
+ self.parameter = self.settings.run()
+ self.prompt = LANGUAGE.get(self.parameter["language"], Chinese)
+ self.APP = XHS(**self.parameter, language_object=self.prompt)
+
+ async def __aenter__(self):
+ await self.APP.__aenter__()
+ return self
+
+ async def __aexit__(self, exc_type, exc_value, traceback):
+ await self.APP.__aexit__(exc_type, exc_value, traceback)
+
+ async def on_mount(self) -> None:
+ self.install_screen(Setting(), name="setting")
+ self.install_screen(Index(self.APP, self.prompt), name="index")
+ await self.push_screen("index")
+
+ async def action_settings(self):
+ await self.push_screen("setting")
diff --git a/source/TUI/index.py b/source/TUI/index.py
index 3a76075..6424676 100644
--- a/source/TUI/index.py
+++ b/source/TUI/index.py
@@ -1,13 +1,15 @@
from webbrowser import open
+# from asyncio import sleep
from pyperclip import paste
from rich.text import Text
-from textual.app import App
+from textual import on
from textual.app import ComposeResult
from textual.binding import Binding
from textual.containers import Center
from textual.containers import HorizontalScroll
from textual.containers import ScrollableContainer
+from textual.screen import Screen
from textual.widgets import Button
from textual.widgets import Footer
from textual.widgets import Header
@@ -17,12 +19,11 @@ from textual.widgets import ProgressBar
from textual.widgets import RichLog
from source.application import XHS
-from source.module import Settings
+from source.module import ROOT
from source.module import (
VERSION_MAJOR,
VERSION_MINOR,
VERSION_BETA,
- ROOT,
PROMPT,
MASTER,
ERROR,
@@ -34,11 +35,9 @@ from source.module import (
GENERAL,
USERSCRIPT,
)
-from source.translator import Chinese
-from source.translator import LANGUAGE
-from .setting import Setting
+from source.translator import (English, Chinese)
-__all__ = ["XHSDownloader"]
+__all__ = ["Index"]
def show_state(function):
@@ -53,7 +52,7 @@ def show_state(function):
return inner
-class XHSDownloader(App):
+class Index(Screen):
CSS_PATH = ROOT.joinpath(
"static/css/index.tcss")
BINDINGS = [
@@ -61,28 +60,18 @@ class XHSDownloader(App):
# ("d", "toggle_dark", "切换主题"),
Binding(key="u", action="check_update", description="检查更新"),
Binding(key="m", action="user_script", description="获取脚本"),
- # Binding(key="l", action="choose_language", description="切换语言"),
- # Binding(key="s", action="settings", description="程序设置"),
+ Binding(key="s", action="settings", description="程序设置"),
]
- def __init__(self):
+ def __init__(self, app: XHS, language: Chinese | English):
super().__init__()
- settings = Settings(ROOT).run()
- self.prompt = LANGUAGE.get(settings["language"], Chinese)
- self.APP = XHS(**settings, language_object=self.prompt)
+ self.app_ = app
+ self.prompt = language
self.url = None
self.tip = None
self.bar = None
- self.setting = None
self.disclaimer = True
- async def __aenter__(self):
- await self.APP.__aenter__()
- return self
-
- async def __aexit__(self, exc_type, exc_value, traceback):
- await self.APP.__aexit__(exc_type, exc_value, traceback)
-
def compose(self) -> ComposeResult:
yield Header()
yield ScrollableContainer(Label(Text(f"{self.prompt.open_source_protocol}{LICENCE}", style=MASTER)),
@@ -96,7 +85,6 @@ class XHSDownloader(App):
HorizontalScroll(Button(self.prompt.download_button, id="deal"),
Button(self.prompt.paste_button, id="paste"),
Button(self.prompt.reset_button, id="reset"), ),
- id="index",
)
with Center():
yield ProgressBar(total=None, show_percentage=False, show_eta=False)
@@ -106,8 +94,6 @@ class XHSDownloader(App):
def on_mount(self) -> None:
self.title = f"XHS-Downloader V{VERSION_MAJOR}.{
VERSION_MINOR}{" Beta" if VERSION_BETA else ""}"
-
- def on_ready(self) -> None:
self.url = self.query_one(Input)
self.tip = self.query_one(RichLog)
self.bar = self.query_one(ProgressBar)
@@ -118,20 +104,26 @@ class XHSDownloader(App):
self.tip.clear()
self.disclaimer = False
- async def on_button_pressed(self, event: Button.Pressed) -> None:
- if event.button.id == "deal":
- await self.deal()
- elif event.button.id == "reset":
- self.query_one(Input).value = ""
- elif event.button.id == "paste":
- self.query_one(Input).value = paste()
+ @on(Button.Pressed, "#deal")
+ async def deal_button(self):
+ await self.deal()
+
+ @on(Button.Pressed, "#reset")
+ def reset_button(self):
+ self.query_one(Input).value = ""
+
+ @on(Button.Pressed, "#paste")
+ def paste_button(self):
+ self.query_one(Input).value = paste()
@show_state
async def deal(self):
+ # TODO: 处理过程中,进度条异常卡顿,待排查!
+ # await sleep(2)
if not self.url.value:
self.tip.write(Text(self.prompt.invalid_link, style=WARNING))
return
- if any(await self.APP.extract(self.url.value, True, log=self.tip)):
+ if any(await self.app_.extract(self.url.value, True, log=self.tip)):
self.url.value = ""
else:
self.tip.write(Text(self.prompt.download_failure, style=ERROR))
@@ -143,7 +135,7 @@ class XHSDownloader(App):
self.prompt.check_update_notification,
style=WARNING))
try:
- url = await self.APP.html.request_url(RELEASES, False, self.tip)
+ url = await self.app_.html.request_url(RELEASES, False, self.tip)
latest_major, latest_minor = map(
int, url.split("/")[-1].split(".", 1))
if latest_major > VERSION_MAJOR or latest_minor > VERSION_MINOR:
@@ -176,14 +168,3 @@ class XHSDownloader(App):
@staticmethod
def action_user_script():
open(USERSCRIPT)
-
- def action_choose_language(self):
- pass
-
- def action_settings(self):
- if self.setting:
- self.setting.remove()
- self.setting = None
- else:
- self.setting = Setting()
- self.query_one("#index").mount(self.setting)
diff --git a/source/TUI/server.py b/source/TUI/server.py
new file mode 100644
index 0000000..e69de29
diff --git a/source/TUI/setting.py b/source/TUI/setting.py
index 83e8b7d..ffbc064 100644
--- a/source/TUI/setting.py
+++ b/source/TUI/setting.py
@@ -1,16 +1,23 @@
from textual.app import ComposeResult
+from textual.binding import Binding
+from textual.screen import Screen
+from textual.widgets import Footer
+from textual.widgets import Header
from textual.widgets import Label
-from textual.widgets import Static
from source.module import ROOT
__all__ = ["Setting"]
-class Setting(Static):
+class Setting(Screen):
CSS_PATH = ROOT.joinpath(
"static/css/setting.tcss")
+ BINDINGS = [
+ Binding(key="q", action="quit", description="退出程序"),
+ ]
def compose(self) -> ComposeResult:
- """Create child widgets for the app."""
+ yield Header()
yield Label("我是设置页")
+ yield Footer()
diff --git a/source/application/Downloader.py b/source/application/Downloader.py
index dfadfbb..01ac379 100644
--- a/source/application/Downloader.py
+++ b/source/application/Downloader.py
@@ -68,7 +68,7 @@ class Download:
except ClientError as error:
self.manager.delete(temp)
# self.__create_progress(bar, None)
- logging(log, error, ERROR)
+ logging(log, str(error), ERROR)
logging(log, self.prompt.download_error(name), ERROR)
return False
diff --git a/source/application/Html.py b/source/application/Html.py
index 2f77e70..e99829b 100644
--- a/source/application/Html.py
+++ b/source/application/Html.py
@@ -29,7 +29,7 @@ class Html:
) as response:
return await response.text() if content else str(response.url)
except ClientError as error:
- logging(log, error, ERROR)
+ logging(log, str(error), ERROR)
logging(log, self.prompt.request_error(url), ERROR)
return ""
diff --git a/static/用户脚本截图.png b/static/用户脚本截图.png
new file mode 100644
index 0000000..3654c5e
Binary files /dev/null and b/static/用户脚本截图.png differ