mirror of
https://github.com/ThePhaseless/Byparr.git
synced 2026-09-24 22:03:30 +01:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ead42863d | ||
|
|
89e9a176c7 | ||
|
|
d5c163f621 | ||
|
|
6694d45e74 | ||
|
|
b23b814e43 | ||
|
|
9353daaf1a | ||
|
|
5a35117299 | ||
|
|
ee8aeed9b1 | ||
|
|
c188d4fe85 | ||
|
|
4ecfc98c81 | ||
|
|
38f344a71c | ||
|
|
815faddcf2 | ||
|
|
999ca88a19 | ||
|
|
dd35795ffa | ||
|
|
147902be2c | ||
|
|
7e9c2ba97d | ||
|
|
9fc9af3ba9 | ||
|
|
55526308f6 | ||
|
|
cdb3223a87 | ||
|
|
329235ba5e | ||
|
|
5031d1e554 | ||
|
|
090b5487e4 | ||
|
|
c608bbc46e | ||
|
|
46eff073b7 | ||
|
|
a347b7046b | ||
|
|
4658d04d2e | ||
|
|
b6aa1a8c58 | ||
|
|
5bff9e1bc1 | ||
|
|
dbd16882de | ||
|
|
adc98e8cea | ||
|
|
8e7d007c6f | ||
|
|
51c1577d7c | ||
|
|
98f9480157 | ||
|
|
2be9fa55a9 | ||
|
|
6520444d4e | ||
|
|
693d67bcfd | ||
|
|
b217778a79 | ||
|
|
3b18dfe522 | ||
|
|
ee5b7fe027 | ||
|
|
d2678643c0 | ||
|
|
343fdca493 | ||
|
|
5a8edf13ab | ||
|
|
1fa6b66f3a | ||
|
|
fe797ba2ac | ||
|
|
409c0f9d8c | ||
|
|
d22e780219 | ||
|
|
d4f722d121 | ||
|
|
38b3407b15 | ||
|
|
81959df037 | ||
|
|
05f9f44c79 | ||
|
|
08cfd95d6a | ||
|
|
f60ecbf2ee | ||
|
|
630389aebe | ||
|
|
603cd0f9a7 | ||
|
|
c75c79a2ad | ||
|
|
f3fe419eae | ||
|
|
e1a2e77036 | ||
|
|
1bdd65499d | ||
|
|
e15d1facf2 | ||
|
|
38d5b7dd21 | ||
|
|
96d96c32a6 | ||
|
|
c9967c85aa | ||
|
|
ed86a79c4d | ||
|
|
d6b2036956 | ||
|
|
dd74f80c18 | ||
|
|
2d9e415eb0 | ||
|
|
a328e52471 | ||
|
|
e3d1cdfb3e | ||
|
|
ae12dc05e4 | ||
|
|
d5a1bed3c1 | ||
|
|
3830149ba0 | ||
|
|
7a9a64a4b2 | ||
|
|
16efa73f48 | ||
|
|
a5d6a27b2c | ||
|
|
20ab7491e1 | ||
|
|
9aa041a022 | ||
|
|
725339f316 | ||
|
|
fed8e5d0a6 | ||
|
|
e612d82c2c |
@@ -1,4 +0,0 @@
|
||||
FROM ghcr.io/astral-sh/uv:debian
|
||||
ENV UV_LINK_MODE=copy
|
||||
|
||||
RUN apt update && apt upgrade -y && apt install -y chromium chromium-driver xvfb git
|
||||
@@ -3,36 +3,16 @@
|
||||
{
|
||||
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile"
|
||||
"dockerfile": "../Dockerfile",
|
||||
"target": "devcontainer"
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"charliermarsh.ruff",
|
||||
"-ms-python.autopep8",
|
||||
"-ms-python.vscode-pylance"
|
||||
],
|
||||
"extensions": ["charliermarsh.ruff", "ms-python.python"],
|
||||
"settings": {
|
||||
"python.venvPath": "./venv"
|
||||
}
|
||||
}
|
||||
},
|
||||
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||
// "features": {},
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
// "postCreateCommand": "pip3 install --user -r requirements.txt",
|
||||
// Configure tool-specific properties.
|
||||
// "customizations": {},
|
||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||
// "remoteUser": "root"
|
||||
"name": "Byparr Dev Container",
|
||||
"runArgs": [
|
||||
"-p",
|
||||
"8181:8191"
|
||||
],
|
||||
"features": {
|
||||
"ghcr.io/devcontainers-extra/features/act:1": {},
|
||||
"ghcr.io/devcontainers/features/desktop-lite:1": {}
|
||||
}
|
||||
}
|
||||
"postCreateCommand": "uv sync --group test && cd .venv/lib/*/site-packages/seleniumbase/drivers && rm -f uc_driver && ln -s /usr/bin/chromedriver uc_driver"
|
||||
}
|
||||
|
||||
-164
@@ -1,164 +0,0 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
||||
.pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
.extentions/
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
.gitignore
|
||||
@@ -11,12 +11,13 @@ body:
|
||||
- label: Read Loop Warning on Readme
|
||||
- label: Done the troubleshooting from Readme
|
||||
- label: Checked if such issue already exists
|
||||
- label: Checked other websites with Cloudflare Turnstile
|
||||
|
||||
- type: input
|
||||
id: docker-host
|
||||
attributes:
|
||||
label: OS
|
||||
placeholder: e.g. Docker, Ubuntu Linux
|
||||
placeholder: e.g. Docker, WSL2, Ubuntu, Windows
|
||||
|
||||
- type: input
|
||||
id: platform-model
|
||||
@@ -41,8 +42,30 @@ body:
|
||||
label: Describe the bug
|
||||
description: A clear and concise description of what the bug is.
|
||||
placeholder: "Example: The application crashes when trying to load a new file"
|
||||
|
||||
- type: textarea
|
||||
id: config
|
||||
attributes:
|
||||
label: Docker Compose config
|
||||
placeholder: |
|
||||
```yaml
|
||||
services:
|
||||
...
|
||||
```
|
||||
- type: textarea
|
||||
id: prowlarr-logs
|
||||
attributes:
|
||||
label: Prowlarr Logs
|
||||
placeholder: |
|
||||
```
|
||||
put logs between 3x `
|
||||
```
|
||||
|
||||
- type: textarea
|
||||
id: byparr-logs
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add any other context about the problem here.
|
||||
label: Byparr Logs
|
||||
placeholder: |
|
||||
```
|
||||
put logs between 3x `
|
||||
```
|
||||
|
||||
@@ -108,7 +108,9 @@ jobs:
|
||||
platforms: ${{ matrix.platform }}
|
||||
cache-from: type=gha,scope=${{ matrix.platform }}
|
||||
cache-to: type=gha,mode=max,scope=${{ matrix.platform }}
|
||||
build-args: GITHUB_BUILD=true,VERSION=${{ github.ref_type == 'tag' && github.ref_name || github.sha }}
|
||||
build-args: |
|
||||
GITHUB_BUILD=true
|
||||
VERSION=${{ github.ref_type == 'tag' && github.ref_name || github.sha }}
|
||||
|
||||
merge-and-push:
|
||||
needs: build
|
||||
|
||||
Vendored
+14
-18
@@ -1,19 +1,15 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"console": "integratedTerminal",
|
||||
"env": {
|
||||
"LOG_LEVEL": "DEBUG"
|
||||
},
|
||||
"justMyCode": false,
|
||||
"name": "Python Debugger: Main",
|
||||
"program": "main.py",
|
||||
"request": "launch",
|
||||
"type": "debugpy"
|
||||
},
|
||||
],
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0"
|
||||
}
|
||||
"configurations": [
|
||||
{
|
||||
"console": "integratedTerminal",
|
||||
"env": {
|
||||
"LOG_LEVEL": "DEBUG"
|
||||
},
|
||||
"justMyCode": false,
|
||||
"name": "Python Debugger: Main",
|
||||
"program": "main.py",
|
||||
"request": "launch",
|
||||
"type": "debugpy"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+20
-13
@@ -1,40 +1,47 @@
|
||||
FROM debian:bullseye-slim AS base
|
||||
FROM debian:bookworm-slim AS base
|
||||
ENV HOME=/root
|
||||
|
||||
ARG GITHUB_BUILD=false \
|
||||
VERSION
|
||||
|
||||
ENV HOME=/root \
|
||||
GITHUB_BUILD=${GITHUB_BUILD}\
|
||||
ENV GITHUB_BUILD=${GITHUB_BUILD}\
|
||||
VERSION=${VERSION}\
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
# prevents python creating .pyc files
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
DISPLAY=:0
|
||||
ENV PATH="${HOME}/.local/bin:$PATH"
|
||||
DISPLAY=:0\
|
||||
PATH="${HOME}/.local/bin:$PATH"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends --no-install-suggests xauth xvfb scrot curl chromium chromium-driver ca-certificates
|
||||
apt-get install -y --no-install-recommends --no-install-suggests xauth xvfb scrot curl chromium chromium-driver ca-certificates tini
|
||||
|
||||
ADD https://astral.sh/uv/install.sh install.sh
|
||||
RUN sh install.sh
|
||||
RUN sh install.sh && uv --version
|
||||
|
||||
|
||||
FROM base AS devcontainer
|
||||
RUN apt install -y git && apt upgrade -y
|
||||
ENV UV_LINK_MODE=copy
|
||||
ENTRYPOINT [ "sleep", "infinity" ]
|
||||
|
||||
|
||||
FROM base AS app
|
||||
COPY pyproject.toml uv.lock ./
|
||||
RUN --mount=type=cache,target=${HOME}/.cache/uv uv sync
|
||||
|
||||
# SeleniumBase does not come with an arm64 chromedriver binary
|
||||
RUN cd .venv/lib/*/site-packages/seleniumbase/drivers && ln -s /usr/bin/chromedriver uc_driver
|
||||
|
||||
RUN cd .venv/lib/*/site-packages/seleniumbase/drivers && rm -f uc_driver && ln -s /usr/bin/chromedriver uc_driver
|
||||
COPY . .
|
||||
|
||||
FROM base AS test
|
||||
|
||||
FROM app AS test
|
||||
RUN --mount=type=cache,target=${HOME}/.cache/uv uv sync --group test
|
||||
RUN ./test.sh
|
||||
|
||||
FROM base
|
||||
|
||||
FROM app
|
||||
EXPOSE 8191
|
||||
HEALTHCHECK --interval=15m --timeout=30s --start-period=5s --retries=3 CMD [ "curl", "http://localhost:8191/health" ]
|
||||
CMD ["./cmd.sh"]
|
||||
ENTRYPOINT ["/usr/bin/tini", "--", "uv", "run", "main.py"]
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
An alternative to [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr) as a drop-in replacement, built with [seleniumbase](https://seleniumbase.io/) and [FastAPI](https://fastapi.tiangolo.com).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Due to recent challenge changes, this software does not guarantee that the Cloudflare challenge will be bypassed. Cloudflare likely requires valid network traffic originating from the user’s public IP address to mark a connection as legitimate. While this tool may bypass the initial browser check, it does not ensure that requests will consistently pass Cloudflare's validation. More testing and data are required to understand how Cloudflare identifies connections and requests as valid. Invalid requests will result in Byparr's looping and eventually time-outing.
|
||||
> This software does not **guarantee** (only greatly increases the chance) that any challenge will be bypassed. While this tool passes the initial browser check, Cloudflare and other captcha providers likely require valid network traffic originating from the user’s public IP address to mark a connection as legitimate. If any website does not pass the challenge, please run troubleshooting steps and check if other websites work before you create an GitHub issue.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Support for NAS devices (like Synology) is minimal. Please report issues, but do not expect it to be fixed quickly. The only ARM device I have is a free Ampere Oracle VM, so I can only test ARM support on that. See [#22](https://github.com/ThePhaseless/Byparr/issues/22) and [#3](https://github.com/ThePhaseless/Byparr/issues/3)
|
||||
@@ -33,11 +33,15 @@ An alternative to [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr) a
|
||||
|
||||
## Options
|
||||
|
||||
| Env | Default | Description |
|
||||
| -------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `USE_XVFB` | `SeleniumBase default` | Use virtual desktop with Xvfb. (Linux only) (Can cause performance hog [#14](https://github.com/ThePhaseless/Byparr/issues/14)) |
|
||||
| `USE_HEADLESS` | `SeleniumBase default` | Use headless chromium. |
|
||||
| `PROXY` | None | Proxy to use. (format: `username:password@host:port`) |
|
||||
| Environment Variable | Default | Description |
|
||||
| -------------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `USE_HEADLESS` | `SeleniumBase default` | Use headless chromium. |
|
||||
| `PROXY` | None | Proxy to use in format: `protocol://username:password@host:port`. [SOCKS5 with authentication is not supported by Chrome](https://stackoverflow.com/questions/75602916/connection-to-private-proxy-socks5-with-chrome-webrequest-onauthrequired-and), see `compose.yaml` file for a workaround |
|
||||
|
||||
## Proxy Recommendation
|
||||
|
||||
Recently I've partnered with a *new in town* proxy service - ProxyBase - to offer affordable proxy services that seems to work seamlessly with Byparr! Using my affiliate code `byparr` (case sensitive!) when signing up will not only get you access to their cost-effective (**$0.69/GB with occasional promotions**) proxy network but will also help support the continued development of this project. ProxyBase's proxies can significantly improve your success rate when bypassing anti-bot challenges. [Check out ProxyBase](https://client.proxybase.org/signup?ref=byparr
|
||||
) and enhance your Byparr experience!
|
||||
|
||||
## Tags
|
||||
|
||||
@@ -53,18 +57,18 @@ See `compose.yaml`
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
docker run -p 8191:8191 ghcr.io/thephaseless/byparr:latest
|
||||
docker run --shm-size=2gb -p 8191:8191 ghcr.io/thephaseless/byparr:latest
|
||||
```
|
||||
|
||||
### Local
|
||||
|
||||
```bash
|
||||
uv sync && ./cmd.sh
|
||||
uv sync && uv run main.py
|
||||
```
|
||||
|
||||
## Need help with / TODO
|
||||
|
||||
- [x] Slimming container (only ~650 MB now!)
|
||||
- [x] Slimming container (only ~1.11 GB now!)
|
||||
- [x] Add more anti-bot challenges
|
||||
- [x] Add doc strings
|
||||
- [x] Implement versioning
|
||||
|
||||
+20
-6
@@ -1,12 +1,26 @@
|
||||
services:
|
||||
byparr:
|
||||
image: ghcr.io/thephaseless/byparr
|
||||
image: ghcr.io/thephaseless/byparr:latest
|
||||
restart: unless-stopped
|
||||
shm_size: 2gb
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
environment:
|
||||
- LOG_LEVEL=INFO
|
||||
# # Uncomment below to use pproxy
|
||||
# environment:
|
||||
# - PROXY=http://pproxy:8080
|
||||
volumes:
|
||||
- ./screenshots:/app/screenshots # For screenshots when exception occurs
|
||||
ports:
|
||||
- "8191:8191"
|
||||
- ./screenshots:/app/screenshots # (optional) For screenshots when exception occurs
|
||||
# # Uncomment below to use byparr outside of internal network
|
||||
# ports:
|
||||
# - "8191:8191"
|
||||
# # Uncomment the following lines to enable pproxy for socks5 proxy
|
||||
# pproxy:
|
||||
# tty: true # Required for pproxy to work
|
||||
# container_name: pproxy
|
||||
# restart: unless-stopped
|
||||
# image: mosajjal/pproxy:latest
|
||||
# command:
|
||||
# - -vv
|
||||
# - -l http://:8080
|
||||
# - -r socks5://host:port#username:password # Fill in your proxy credentials and address
|
||||
|
||||
+6
-7
@@ -8,23 +8,22 @@ version = "0.1.0"
|
||||
description = "API for getting cookies for Cloudflare challenges"
|
||||
readme = "README.md"
|
||||
dependencies = [
|
||||
"fastapi[standard]==0.115.11",
|
||||
"fastapi[standard]==0.115.12",
|
||||
"pyautogui==0.9.54",
|
||||
"pydantic==2.10.6",
|
||||
"seleniumbase==4.35.7",
|
||||
"uvicorn==0.34.0",
|
||||
"pydantic==2.11.5",
|
||||
"seleniumbase==4.39.2",
|
||||
"uvicorn==0.34.3",
|
||||
]
|
||||
urls = { repository = "https://github.com/ThePhaseless/Byparr" }
|
||||
|
||||
[dependency-groups]
|
||||
test = [
|
||||
"httpx==0.28.1",
|
||||
"pytest-asyncio==0.25.3",
|
||||
"pytest-asyncio==1.0.0",
|
||||
"pytest-progress==1.3.0",
|
||||
"pytest-retry==1.7.0",
|
||||
"pytest-xdist==3.6.1",
|
||||
]
|
||||
dev = ["deptry==0.23.0", "ruff==0.11.0"]
|
||||
dev = ["deptry==0.23.0", "ruff==0.11.13"]
|
||||
|
||||
[tool.deptry.per_rule_ignores]
|
||||
DEP002 = ["pyautogui"]
|
||||
|
||||
Executable → Regular
+16
-5
@@ -8,12 +8,12 @@ from sbase import BaseCase
|
||||
|
||||
from src.consts import CHALLENGE_TITLES
|
||||
from src.models import (
|
||||
HealthcheckResponse,
|
||||
LinkRequest,
|
||||
LinkResponse,
|
||||
Solution,
|
||||
)
|
||||
|
||||
from .utils import get_sb, logger, save_screenshot
|
||||
from src.utils import get_sb, logger, save_screenshot
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -41,13 +41,14 @@ def health_check(sb: SeleniumDep):
|
||||
detail="Health check failed",
|
||||
)
|
||||
|
||||
return {"status": "ok"}
|
||||
return HealthcheckResponse(user_agent=sb.get_user_agent())
|
||||
|
||||
|
||||
@router.post("/v1")
|
||||
def read_item(request: LinkRequest, sb: SeleniumDep) -> LinkResponse:
|
||||
"""Handle POST requests."""
|
||||
start_time = int(time.time() * 1000)
|
||||
request.url = request.url.replace('"', "").strip()
|
||||
sb.uc_open_with_reconnect(request.url)
|
||||
logger.debug(f"Got webpage: {request.url}")
|
||||
source_bs = sb.get_beautiful_soup()
|
||||
@@ -61,13 +62,23 @@ def read_item(request: LinkRequest, sb: SeleniumDep) -> LinkResponse:
|
||||
save_screenshot(sb)
|
||||
raise HTTPException(status_code=500, detail="Could not bypass challenge")
|
||||
|
||||
cookies = sb.get_cookies()
|
||||
for cookie in cookies:
|
||||
name = cookie["name"]
|
||||
value = cookie["value"]
|
||||
cookie["size"] = len(f"{name}={value}".encode())
|
||||
|
||||
cookie["session"] = False
|
||||
if "expiry" in cookie:
|
||||
cookie["expires"] = cookie["expiry"]
|
||||
|
||||
return LinkResponse(
|
||||
message="Success",
|
||||
solution=Solution(
|
||||
userAgent=sb.get_user_agent(),
|
||||
user_agent=sb.get_user_agent(),
|
||||
url=sb.get_current_url(),
|
||||
status=200,
|
||||
cookies=sb.get_cookies(),
|
||||
cookies=cookies,
|
||||
headers={},
|
||||
response=str(sb.get_beautiful_soup()),
|
||||
),
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ from src.utils import logger
|
||||
class LogRequest(BaseHTTPMiddleware):
|
||||
async def dispatch(self, request, call_next):
|
||||
"""Log requests."""
|
||||
if request.url.path != "/v1":
|
||||
if request.url.path != "/v1" or request.method != "POST":
|
||||
return await call_next(request)
|
||||
|
||||
start_time = time.perf_counter()
|
||||
|
||||
+19
-27
@@ -1,11 +1,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from http import HTTPStatus
|
||||
from http.client import INTERNAL_SERVER_ERROR
|
||||
from typing import Any
|
||||
|
||||
from fastapi import Body
|
||||
from pydantic import BaseModel, Field
|
||||
from pydantic.alias_generators import to_camel
|
||||
|
||||
from src import consts
|
||||
|
||||
@@ -19,37 +19,30 @@ class LinkRequest(BaseModel):
|
||||
max_timeout: int = Field(default=60)
|
||||
|
||||
|
||||
class HealthcheckResponse(BaseModel):
|
||||
model_config = {"alias_generator": to_camel, "populate_by_name": True}
|
||||
msg: str = "Byparr is ready!"
|
||||
version: str = consts.VERSION
|
||||
user_agent: str
|
||||
|
||||
|
||||
class Solution(BaseModel):
|
||||
model_config = {"alias_generator": to_camel, "populate_by_name": True}
|
||||
url: str
|
||||
status: int
|
||||
cookies: list
|
||||
userAgent: str # noqa: N815 # Ignore to preserve compatibility
|
||||
headers: dict[str, Any]
|
||||
response: str
|
||||
|
||||
@classmethod
|
||||
def invalid(cls, url: str):
|
||||
"""
|
||||
Return an empty Solution with default values.
|
||||
|
||||
Useful for returning an error response.
|
||||
"""
|
||||
return cls(
|
||||
url=url,
|
||||
status=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||
cookies=[],
|
||||
userAgent="",
|
||||
headers={},
|
||||
response="",
|
||||
)
|
||||
cookies: list = []
|
||||
user_agent: str = ""
|
||||
headers: dict[str, Any] = {}
|
||||
response: str = ""
|
||||
|
||||
|
||||
class LinkResponse(BaseModel):
|
||||
model_config = {"alias_generator": to_camel, "populate_by_name": True}
|
||||
status: str = "ok"
|
||||
message: str
|
||||
solution: Solution
|
||||
start_timestamp: int = Field(alias="startTimestamp", default_factory=lambda: int(time.time() * 1000))
|
||||
end_timestamp: int = Field(alias="endTimestamp", default_factory=lambda: int(time.time() * 1000))
|
||||
start_timestamp: int
|
||||
end_timestamp: int = Field(default_factory=lambda: int(time.time() * 1000))
|
||||
version: str = consts.VERSION
|
||||
|
||||
@classmethod
|
||||
@@ -62,7 +55,6 @@ class LinkResponse(BaseModel):
|
||||
return cls(
|
||||
status="error",
|
||||
message="Invalid request",
|
||||
solution=Solution.invalid(url),
|
||||
solution=Solution(url=url, status=INTERNAL_SERVER_ERROR),
|
||||
start_timestamp=int(time.time() * 1000),
|
||||
end_timestamp=int(time.time() * 1000),
|
||||
)
|
||||
)
|
||||
|
||||
+12
-5
@@ -1,7 +1,8 @@
|
||||
import logging
|
||||
from time import gmtime, strftime
|
||||
|
||||
from fastapi import Header
|
||||
from fastapi import Header, HTTPException
|
||||
from httpx import codes
|
||||
from sbase import SB, BaseCase
|
||||
|
||||
from src.consts import LOG_LEVEL, PROXY, USE_HEADLESS
|
||||
@@ -14,18 +15,24 @@ if len(logger.handlers) == 0:
|
||||
|
||||
def get_sb(
|
||||
proxy: str | None = Header(
|
||||
default=None,
|
||||
examples=["username:password@host:port"],
|
||||
description="Override default proxy from env",
|
||||
default=PROXY,
|
||||
examples=["protocol://username:password@host:port"],
|
||||
description="Override default proxy address",
|
||||
),
|
||||
):
|
||||
"""Get SeleniumBase instance."""
|
||||
if proxy and proxy.startswith("socks5://") and "@" in proxy:
|
||||
raise HTTPException(
|
||||
status_code=codes.BAD_REQUEST,
|
||||
detail="SOCKS5 proxy with authentication is not supported. Check README for more info.",
|
||||
)
|
||||
|
||||
with SB(
|
||||
uc=True,
|
||||
headless=USE_HEADLESS,
|
||||
locale_code="en",
|
||||
ad_block=True,
|
||||
proxy=proxy or PROXY,
|
||||
proxy=proxy,
|
||||
) as sb:
|
||||
yield sb
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ test_websites = [
|
||||
"https://extratorrent.st/",
|
||||
"https://idope.se/",
|
||||
"https://speed.cd/login",
|
||||
'https://www.yggtorrent.top/engine/search?do=search&order=desc&sort=publish_date&name="UNESCAPED"+"DOUBLEQUOTES"&category=2145'
|
||||
]
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user