更新项目依赖库版本

This commit is contained in:
JoeanAmier
2024-06-29 10:12:04 +08:00
parent e7c4d6daee
commit b87132e364
22 changed files with 257 additions and 89 deletions

16
Dockerfile Normal file
View File

@@ -0,0 +1,16 @@
FROM python:3.12.4-slim
LABEL name="XHS-Downloader" version="2.1 Beta" authors="JoeanAmier"
COPY locale /locale
COPY source /source
COPY static /static
COPY LICENSE /LICENSE
COPY main.py /main.py
COPY README.md /README.md
COPY README_EN.md /README_EN.md
COPY requirements.txt /requirements.txt
RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
CMD ["python", "main.py"]

View File

@@ -40,6 +40,7 @@
<li>✅ 提取搜索结果作品链接</li>
<li>✅ 提取搜索结果用户链接</li>
</ul>
<p>⭐ XHS-Downloader 开发计划及进度可前往 <a href="https://github.com/users/JoeanAmier/projects/5">Projects</a> 查阅</p>
<h1>📸 程序截图</h1>
<p><b>🎥 点击图片观看演示视频</b></p>
<a href="https://www.bilibili.com/video/BV1PJ4m1Y7Jt/"><img src="static/screenshot/程序运行截图CN1.png" alt=""></a>
@@ -83,7 +84,8 @@
<p><b>启动:</b>运行命令:<code>python .\main.py server</code></p>
<p><b>关闭:</b>按下 <code>Ctrl</code> + <code>C</code> 关闭服务器</p>
<p><b>请求接口:</b><code>/xhs/</code></p>
<p><b>请求类型</b><code>POST</code></p>
<p><b>请求方法</b><code>POST</code></p>
<p><b>请求格式:</b><code>JSON</code></p>
<p><b>请求参数:</b></p>
<table>
<thead>
@@ -109,8 +111,8 @@
</tr>
<tr>
<td align="center">index</td>
<td align="center">str</td>
<td align="center">下载指定序号的图片文件,仅对图文作品生效;多个序号之间使用空格分隔;<code>download</code> 参数设置为 <code>false</code> 时不生效</td>
<td align="center">list[int]</td>
<td align="center">下载指定序号的图片文件,仅对图文作品生效;<code>download</code> 参数设置为 <code>false</code> 时不生效</td>
<td align="center">null</td>
</tr>
<tr>
@@ -124,11 +126,17 @@
<p><b>代码示例:</b></p>
<pre>
def api_demo():
server = "http://127.0.0.1:8080"
server = "http://127.0.0.1:8000/xhs/"
data = {
"url": "https://www.xiaohongshu.com/explore/123456789",
"download": True,
"index": [
3,
6,
9,
],
}
response = requests.post(server, data=data)
response = requests.post(server, json=data)
print(response.json())
</pre>
<h1>🕹 用户脚本</h1>
@@ -152,6 +160,8 @@ async def example():
work_path = "D:\\" # 作品数据/文件保存根路径,默认值:项目根路径
folder_name = "Download" # 作品文件储存文件夹名称自动创建默认值Download
name_format = "作品标题 作品描述"
sec_ch_ua = "" # 请求头 Sec-Ch-Ua
sec_ch_ua_platform = "" # 请求头 Sec-Ch-Ua-Platform
user_agent = "" # User-Agent
cookie = "" # 小红书网页版 Cookie无需登录必需参数登录状态对数据采集有影响
proxy = None # 网络代理
@@ -166,6 +176,8 @@ async def example():
async with XHS(work_path=work_path,
folder_name=folder_name,
name_format=name_format,
sec_ch_ua=sec_ch_ua,
sec_ch_ua_platform=sec_ch_ua_platform,
user_agent=user_agent,
cookie=cookie,
proxy=proxy,
@@ -216,10 +228,22 @@ async def example():
<td align="center"><code>发布时间 作者昵称 作品标题</code></td>
</tr>
<tr>
<td align="center">sec_ch_ua</td>
<td align="center">str</td>
<td align="center">浏览器请求头 Sec-Ch-Ua</td>
<td align="center">内置 Chrome Sec-Ch-Ua</td>
</tr>
<tr>
<td align="center">sec_ch_ua_platform</td>
<td align="center">str</td>
<td align="center">浏览器请求头 Sec-Ch-Ua-Platform</td>
<td align="center">内置 Chrome Sec-Ch-Ua-Platform</td>
</tr>
<tr>
<td align="center">user_agent</td>
<td align="center">str</td>
<td align="center">浏览器 User-Agent</td>
<td align="center">内置 chrome user-agent</td>
<td align="center">浏览器 User Agent</td>
<td align="center">内置 Chrome User Agent</td>
</tr>
<tr>
<td align="center">cookie</td>
@@ -295,6 +319,8 @@ async def example():
</tr>
</tbody>
</table>
<p><b>其他说明:<code>sec_ch_ua</code><code>sec_ch_ua_platform</code><code>user_agent</code>参数获取示例,仅当程序获取数据失败时需要自行设置!</b></p>
<img src="static/screenshot/请求头示例图.png" alt="">
<h1>🌐 Cookie</h1>
<ol>
<li>打开浏览器(可选无痕模式启动),访问 <code>https://www.xiaohongshu.com/explore</code></li>
@@ -361,6 +387,7 @@ async def example():
* https://github.com/encode/httpx/
* https://github.com/tiangolo/fastapi
* https://github.com/textualize/textual/
* https://textual.textualize.io/
* https://aiosqlite.omnilib.dev/en/stable/
* https://click.palletsprojects.com/en/8.1.x/

Binary file not shown.

View File

@@ -271,7 +271,13 @@ msgstr "Web API server has been shut down!"
msgid "服务器主机及端口: {0}"
msgstr "Server host and port: {0}"
msgid "内置 Chrome User-Agent"
msgid "内置 Chrome Sec-Ch-Ua"
msgstr "Built in Chrome Sec-Ch-Ua"
msgid "内置 Chrome Sec-Ch-Ua-Platform"
msgstr "Built in Chrome Sec-Ch-Ua-Platform"
msgid "内置 Chrome User Agent"
msgstr "Built in Chrome User Agent"
msgid "proxy 参数 {0} 设置错误,程序将不会使用代理"

View File

@@ -271,7 +271,13 @@ msgstr ""
msgid "服务器主机及端口: {0}"
msgstr ""
msgid "内置 Chrome User-Agent"
msgid "内置 Chrome Sec-Ch-Ua"
msgstr ""
msgid "内置 Chrome Sec-Ch-Ua-Platform"
msgstr ""
msgid "内置 Chrome User Agent"
msgstr ""
msgid "proxy 参数 {0} 设置错误,程序将不会使用代理"

27
main.py
View File

@@ -1,4 +1,6 @@
from asyncio import run
from asyncio.exceptions import CancelledError
from contextlib import suppress
from sys import argv
from source import Settings
@@ -17,6 +19,8 @@ async def example():
work_path = "D:\\" # 作品数据/文件保存根路径,默认值:项目根路径
folder_name = "Download" # 作品文件储存文件夹名称自动创建默认值Download
name_format = "作品标题 作品描述"
sec_ch_ua = "" # 请求头 Sec-Ch-Ua
sec_ch_ua_platform = "" # 请求头 Sec-Ch-Ua-Platform
user_agent = "" # User-Agent
cookie = "" # 小红书网页版 Cookie无需登录必需参数登录状态对数据采集有影响
proxy = None # 网络代理
@@ -31,6 +35,8 @@ async def example():
async with XHS(work_path=work_path,
folder_name=folder_name,
name_format=name_format,
sec_ch_ua=sec_ch_ua,
sec_ch_ua_platform=sec_ch_ua_platform,
user_agent=user_agent,
cookie=cookie,
proxy=proxy,
@@ -55,16 +61,19 @@ async def app():
await xhs.run_async()
async def server():
async def server(host="127.0.0.1", port=8000, log_level="info", ):
async with XHS(**Settings().run()) as xhs:
await xhs.run_server()
await xhs.run_server(host, port, log_level, )
if __name__ == '__main__':
if len(argv) == 1:
run(app())
elif argv[1] == "server":
print("该模式重构中!")
# run(server())
else:
cli()
with suppress(
KeyboardInterrupt,
CancelledError,
):
if len(argv) == 1:
run(app())
elif argv[1] == "server":
run(server())
else:
cli()

View File

@@ -1,10 +1,10 @@
textual>=0.47.1
pyperclip>=1.8.2
lxml>=5.1.0
textual>=0.70.0
pyperclip>=1.9.0
lxml>=5.2.2
PyYAML>=6.0.1
aiosqlite>=0.20.0
click>=8.1.7
browser_cookie3>=0.19.1
httpx>=0.27.0
fastapi>=0.110.0
uvicorn>=0.24.0
fastapi>=0.111.0
uvicorn>=0.30.1

View File

@@ -123,6 +123,9 @@ class CLI:
("--work_path", "-wp", "str", _("作品数据 / 文件保存根路径")),
("--folder_name", "-fn", "str", _("作品文件储存文件夹名称")),
("--name_format", "-nf", "str", _("作品文件名称格式")),
("--sec_ch_ua", "-su", "str", _("Sec-Ch-Ua")),
("--sec_ch_ua_platform", "-sp", "str", _("User-Agent")),
("--user_agent", "-ua", "str", _("Sec-Ch-Ua-Platform")),
("--cookie", "-ck", "str", _("小红书网页版 Cookie无需登录")),
("--proxy", "-p", "str", _("网络代理")),
("--timeout", "-t", "int", _("请求数据超时限制,单位:秒")),
@@ -163,6 +166,9 @@ class CLI:
)
@option("--folder_name", "-fn", )
@option("--name_format", "-nf", )
@option("--sec_ch_ua", "-su", )
@option("--sec_ch_ua_platform", "-sp", )
@option("--user_agent", "-ua", )
@option("--cookie", "-ck", )
@option("--proxy", "-p", )
@option("--timeout", "-t", type=int, )

View File

@@ -21,15 +21,15 @@ __all__ = ["About"]
class About(Screen):
BINDINGS = [
Binding(
key="q",
key="Q",
action="quit",
description="退出程序/Quit"),
Binding(
key="u",
action="check_update_about",
key="U",
action="check_update",
description="检查更新/Update"),
Binding(
key="b",
key="B",
action="index",
description="返回首页/Back"),
]
@@ -53,3 +53,12 @@ class About(Screen):
def on_mount(self) -> None:
self.title = PROJECT
async def action_quit(self) -> None:
await self.app.action_quit()
async def action_index(self):
await self.app.push_screen("index")
async def action_check_update(self):
await self.app.run_action("update_and_return")

View File

@@ -14,7 +14,6 @@ from source.module import logging
from .about import About
from .index import Index
from .loading import Loading
from .monitor import Monitor
from .record import Record
from .setting import Setting
from .update import Update
@@ -78,15 +77,6 @@ class XHSDownloader(App):
await self.push_screen("setting", save_settings)
async def action_about(self):
await self.push_screen("about")
async def action_index(self):
await self.push_screen("index")
async def action_record(self):
await self.push_screen("record")
async def refresh_screen(self):
self.pop_screen()
await self.close_database()
@@ -117,13 +107,10 @@ class XHSDownloader(App):
async def action_check_update(self):
await self.push_screen(Update(self.APP, self.message), callback=self.update_result)
async def action_check_update_about(self):
async def action_update_and_return(self):
await self.push_screen("index")
await self.action_check_update()
async def action_monitor(self):
await self.push_screen(Monitor(self.APP, self.message))
async def close_database(self):
await self.APP.id_recorder.cursor.close()
await self.APP.id_recorder.database.close()

View File

@@ -27,18 +27,19 @@ from source.module import (
REPOSITORY,
GENERAL,
)
from .monitor import Monitor
__all__ = ["Index"]
class Index(Screen):
BINDINGS = [
Binding(key="q", action="quit", description="退出程序/Quit"),
Binding(key="u", action="check_update", description="检查更新/Update"),
Binding(key="s", action="settings", description="程序设置/Settings"),
Binding(key="r", action="record", description="下载记录/Record"),
Binding(key="m", action="monitor", description="开启监听/Monitor"),
Binding(key="a", action="about", description="关于项目/About"),
Binding(key="Q", action="quit", description="退出程序/Quit"),
Binding(key="U", action="update", description="检查更新/Update"),
Binding(key="S", action="settings", description="程序设置/Settings"),
Binding(key="R", action="record", description="下载记录/Record"),
Binding(key="M", action="monitor", description="开启监听/Monitor"),
Binding(key="A", action="about", description="关于项目/About"),
]
def __init__(self, app: XHS, message: Callable[[str], str]):
@@ -73,7 +74,7 @@ class Index(Screen):
Button(self.message("清空输入框"), id="reset"),
),
)
yield RichLog(markup=True, wrap=True)
yield RichLog(markup=True, )
yield Footer()
def on_mount(self) -> None:
@@ -86,7 +87,8 @@ class Index(Screen):
f"\n{
">" *
50}",
style=MASTER), scroll_end=False)
style=MASTER), scroll_end=False,
)
self.xhs.manager.print_proxy_tip(log=self.tip, )
@on(Button.Pressed, "#deal")
@@ -114,3 +116,21 @@ class Index(Screen):
self.tip.write(Text(self.message("下载小红书作品文件失败"), style=ERROR))
self.tip.write(Text(">" * 50, style=GENERAL))
self.app.pop_screen()
async def action_quit(self) -> None:
await self.app.action_quit()
async def action_update(self) -> None:
await self.app.run_action("check_update")
async def action_settings(self):
await self.app.run_action("settings")
async def action_monitor(self):
await self.app.push_screen(Monitor(self.xhs, self.message))
async def action_about(self):
await self.app.push_screen("about")
async def action_record(self):
await self.app.push_screen("record")

View File

@@ -24,8 +24,8 @@ __all__ = ["Monitor"]
class Monitor(Screen):
BINDINGS = [
Binding(key="q", action="quit", description="退出程序/Quit"),
Binding(key="c", action="close", description="关闭监听/Close"),
Binding(key="Q", action="quit", description="退出程序/Quit"),
Binding(key="C", action="close", description="关闭监听/Close"),
]
def __init__(self, app: XHS, message: Callable[[str], str]):
@@ -60,3 +60,7 @@ class Monitor(Screen):
def action_close(self):
self.xhs.stop_monitor()
self.app.pop_screen()
async def action_quit(self) -> None:
self.action_close()
await self.app.action_quit()

View File

@@ -15,7 +15,6 @@ __all__ = ["Record"]
class Record(ModalScreen):
def __init__(self, app: XHS, message: Callable[[str], str]):
super().__init__()
self.xhs = app

View File

@@ -19,8 +19,8 @@ __all__ = ["Setting"]
class Setting(Screen):
BINDINGS = [
Binding(key="q", action="quit", description="退出程序/Quit"),
Binding(key="b", action="index", description="返回首页/Back"),
Binding(key="Q", action="quit", description="退出程序/Quit"),
Binding(key="B", action="index", description="返回首页/Back"),
]
def __init__(self, data: dict, message: Callable[[str], str]):
@@ -39,8 +39,15 @@ class Setting(Screen):
Label(self.message("作品文件名称格式"), classes="params", ),
Input(self.data["name_format"], placeholder=self.message("发布时间 作者昵称 作品标题"), valid_empty=True,
id="name_format", ),
Label(self.message("Sec-Ch-Ua"), classes="params", ),
Input(self.data["sec_ch_ua"], placeholder=self.message("内置 Chrome Sec-Ch-Ua"), valid_empty=True,
id="sec_ch_ua", ),
Label(self.message("Sec-Ch-Ua-Platform"), classes="params", ),
Input(self.data["sec_ch_ua_platform"], placeholder=self.message("内置 Chrome Sec-Ch-Ua-Platform"),
valid_empty=True,
id="sec_ch_ua_platform", ),
Label(self.message("User-Agent"), classes="params", ),
Input(self.data["user_agent"], placeholder=self.message("内置 Chrome User-Agent"), valid_empty=True,
Input(self.data["user_agent"], placeholder=self.message("内置 Chrome User Agent"), valid_empty=True,
id="user_agent", ),
Label(self.message("小红书网页版 Cookie"), classes="params", ),
Input(placeholder=self.__check_cookie(), valid_empty=True, id="cookie", ),
@@ -101,6 +108,8 @@ class Setting(Screen):
"work_path": self.query_one("#work_path").value,
"folder_name": self.query_one("#folder_name").value,
"name_format": self.query_one("#name_format").value,
"sec_ch_ua": self.query_one("#sec_ch_ua").value,
"sec_ch_ua_platform": self.query_one("#sec_ch_ua_platform").value,
"user_agent": self.query_one("#user_agent").value,
"cookie": self.query_one("#cookie").value or self.data["cookie"],
"proxy": self.query_one("#proxy").value or None,
@@ -120,3 +129,9 @@ class Setting(Screen):
@on(Button.Pressed, "#abandon")
def reset(self):
self.dismiss(self.data)
async def action_quit(self) -> None:
await self.app.action_quit()
async def action_index(self):
await self.app.push_screen("index")

View File

@@ -1,4 +1,3 @@
# from asyncio import CancelledError
from asyncio import Event
from asyncio import Queue
from asyncio import QueueEmpty
@@ -10,13 +9,19 @@ from re import compile
from typing import Callable
from urllib.parse import urlparse
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
# from aiohttp import web
from pyperclip import paste
from uvicorn import Config
from uvicorn import Server
from source.expansion import BrowserCookie
from source.expansion import Converter
from source.expansion import Namespace
from source.module import DataRecorder
from source.module import ExtractData
from source.module import ExtractParams
from source.module import IDRecorder
from source.module import Manager
from source.module import (
@@ -24,7 +29,9 @@ from source.module import (
ERROR,
WARNING,
MASTER,
# REPOSITORY,
REPOSITORY,
VERSION_MAJOR,
VERSION_MINOR,
)
from source.module import Translate
from source.module import logging
@@ -53,6 +60,8 @@ class XHS:
work_path="",
folder_name="Download",
name_format="发布时间 作者昵称 作品标题",
sec_ch_ua: str = "",
sec_ch_ua_platform: str = "",
user_agent: str = None,
cookie: str = None,
proxy: str | dict = None,
@@ -80,6 +89,8 @@ class XHS:
folder_name,
name_format,
chunk,
sec_ch_ua,
sec_ch_ua_platform,
user_agent,
self.read_browser_cookie(read_cookie) or cookie,
proxy,
@@ -108,6 +119,7 @@ class XHS:
self.event = Event()
# self.runner = self.init_server()
# self.site = None
self.server = None
def __extract_image(self, container: dict, data: Namespace):
container["下载地址"], container["动图地址"] = self.image.get_image_link(
@@ -339,3 +351,46 @@ class XHS:
# async def close_server(self, log=None, ):
# await self.runner.cleanup()
# logging(log, self.message("Web API 服务器已关闭!"))
async def run_server(self, host="127.0.0.1", port=8000, log_level="info", ):
self.server = FastAPI(
title="XHS-Downloader",
version=f"{VERSION_MAJOR}.{VERSION_MINOR}")
self.setup_routes()
config = Config(
self.server,
host=host,
port=port,
log_level=log_level,
)
server = Server(config)
await server.serve()
def setup_routes(self):
@self.server.get("/")
async def index():
return RedirectResponse(url=REPOSITORY)
@self.server.post("/xhs/", response_model=ExtractData, )
async def handle(extract: ExtractParams):
url = await self.__extract_links(extract.url, None)
if not url:
msg = self.message("提取小红书作品链接失败")
data = None
else:
if data := await self.__deal_extract(
url[0],
extract.download,
extract.index,
None,
None,
not extract.skip,
):
msg = self.message("获取小红书作品数据成功")
else:
msg = self.message("获取小红书作品数据失败")
data = None
return ExtractData(
message=msg,
url=url[0] if url else extract.url,
data=data)

View File

@@ -1,5 +1,9 @@
from .extend import Account
from .manager import Manager
from .model import (
ExtractData,
ExtractParams,
)
from .recorder import DataRecorder
from .recorder import IDRecorder
from .settings import Settings
@@ -22,38 +26,11 @@ from .static import (
HEADERS,
PROJECT,
USERAGENT,
SEC_CH_UA,
SEC_CH_UA_PLATFORM,
)
from .tools import (
retry,
logging,
)
from .translator import Translate
__all__ = [
"Account",
"Settings",
"IDRecorder",
"Manager",
"VERSION_MAJOR",
"VERSION_MINOR",
"VERSION_BETA",
"ROOT",
"REPOSITORY",
"LICENCE",
"RELEASES",
"MASTER",
"PROMPT",
"GENERAL",
"PROGRESS",
"ERROR",
"WARNING",
"INFO",
"USERSCRIPT",
"HEADERS",
"retry",
"logging",
"PROJECT",
"Translate",
"DataRecorder",
"USERAGENT",
]

View File

@@ -11,6 +11,8 @@ from httpx import TimeoutException
from httpx import get
from .static import HEADERS
from .static import SEC_CH_UA
from .static import SEC_CH_UA_PLATFORM
from .static import USERAGENT
from .static import WARNING
from .tools import logging
@@ -48,6 +50,8 @@ class Manager:
folder: str,
name_format: str,
chunk: int,
sec_ch_ua: str,
sec_ch_ua_platform: str,
user_agent: str,
cookie: str,
proxy: str | dict,
@@ -68,7 +72,11 @@ class Manager:
self.path = self.__check_path(path)
self.folder = self.__check_folder(folder)
self.message = transition
self.blank_headers = HEADERS | {"User-Agent": user_agent or USERAGENT}
self.blank_headers = HEADERS | {
"User-Agent": user_agent or USERAGENT,
"Sec-Ch-Ua": sec_ch_ua or SEC_CH_UA,
"Sec-Ch-Ua-Platform": sec_ch_ua_platform or SEC_CH_UA_PLATFORM,
}
self.headers = self.blank_headers | {"Cookie": cookie}
self.retry = retry
self.chunk = chunk
@@ -83,11 +91,13 @@ class Manager:
headers=self.headers | {
"Referer": "https://www.xiaohongshu.com/explore", },
timeout=timeout,
verify=False,
**self.proxy,
)
self.download_client = AsyncClient(
headers=self.blank_headers,
timeout=timeout,
verify=False,
**self.proxy,
)
self.image_download = self.check_bool(image_download, True)

14
source/module/model.py Normal file
View File

@@ -0,0 +1,14 @@
from pydantic import BaseModel
class ExtractParams(BaseModel):
url: str
download: bool = False
index: list = None
skip: bool = False
class ExtractData(BaseModel):
message: str
url: str
data: dict | None

View File

@@ -4,6 +4,8 @@ from pathlib import Path
from platform import system
from .static import ROOT
from .static import SEC_CH_UA
from .static import SEC_CH_UA_PLATFORM
from .static import USERAGENT
__all__ = ['Settings']
@@ -14,6 +16,8 @@ class Settings:
"work_path": "",
"folder_name": "Download",
"name_format": "发布时间 作者昵称 作品标题",
"sec_ch_ua": SEC_CH_UA,
"sec_ch_ua_platform": SEC_CH_UA_PLATFORM,
"user_agent": USERAGENT,
"cookie": "",
"proxy": None,

View File

@@ -19,6 +19,8 @@ __all__ = [
"HEADERS",
"PROJECT",
"USERAGENT",
"SEC_CH_UA",
"SEC_CH_UA_PLATFORM",
]
VERSION_MAJOR = 2
@@ -37,6 +39,8 @@ USERSCRIPT = "https://raw.githubusercontent.com/JoeanAmier/XHS-Downloader/master
USERAGENT = (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 "
"Safari/537.36")
SEC_CH_UA = "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google Chrome\";v=\"126\""
SEC_CH_UA_PLATFORM = "\"Windows\""
HEADERS = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,"
@@ -46,9 +50,9 @@ HEADERS = {
"Cookie": "",
"Dnt": "1",
# "Priority": "u=0, i",
# "Sec-Ch-Ua": "\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google Chrome\";v=\"126\"",
"Sec-Ch-Ua": SEC_CH_UA,
"Sec-Ch-Ua-Mobile": "?0",
# "Sec-Ch-Ua-Platform": "\"Windows\"",
"Sec-Ch-Ua-Platform": SEC_CH_UA_PLATFORM,
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB