threadstractormf 1.4.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.
- threadstractormf-1.4.0/.dockerignore +18 -0
- threadstractormf-1.4.0/.github/workflows/ci.yml +36 -0
- threadstractormf-1.4.0/.github/workflows/docker.yml +65 -0
- threadstractormf-1.4.0/.github/workflows/release.yml +134 -0
- threadstractormf-1.4.0/.gitignore +38 -0
- threadstractormf-1.4.0/Dockerfile +35 -0
- threadstractormf-1.4.0/LICENSE +674 -0
- threadstractormf-1.4.0/PKG-INFO +163 -0
- threadstractormf-1.4.0/README.md +126 -0
- threadstractormf-1.4.0/pyproject.toml +100 -0
- threadstractormf-1.4.0/scripts/sniff.py +179 -0
- threadstractormf-1.4.0/tests/conftest.py +161 -0
- threadstractormf-1.4.0/tests/test_adaptive_rate_limit.py +284 -0
- threadstractormf-1.4.0/tests/test_archive.py +557 -0
- threadstractormf-1.4.0/tests/test_auth.py +30 -0
- threadstractormf-1.4.0/tests/test_backend.py +155 -0
- threadstractormf-1.4.0/tests/test_brave_variants.py +127 -0
- threadstractormf-1.4.0/tests/test_cdn_allowlist.py +184 -0
- threadstractormf-1.4.0/tests/test_cookie_loading.py +302 -0
- threadstractormf-1.4.0/tests/test_directory_safety.py +202 -0
- threadstractormf-1.4.0/tests/test_discovery.py +981 -0
- threadstractormf-1.4.0/tests/test_download_retry.py +130 -0
- threadstractormf-1.4.0/tests/test_downloader.py +11 -0
- threadstractormf-1.4.0/tests/test_errors.py +264 -0
- threadstractormf-1.4.0/tests/test_extraction.py +245 -0
- threadstractormf-1.4.0/tests/test_fixtures.py +84 -0
- threadstractormf-1.4.0/tests/test_get_urls.py +251 -0
- threadstractormf-1.4.0/tests/test_impersonate.py +206 -0
- threadstractormf-1.4.0/tests/test_models.py +29 -0
- threadstractormf-1.4.0/tests/test_pagination.py +186 -0
- threadstractormf-1.4.0/tests/test_profile_fallback_cookies.py +301 -0
- threadstractormf-1.4.0/tests/test_rate_limit.py +61 -0
- threadstractormf-1.4.0/tests/test_retry_4xx.py +117 -0
- threadstractormf-1.4.0/tests/test_scrape_report.py +688 -0
- threadstractormf-1.4.0/tests/test_subfolders.py +134 -0
- threadstractormf-1.4.0/tests/test_user_id.py +227 -0
- threadstractormf-1.4.0/tests/test_video_url_fix.py +32 -0
- threadstractormf-1.4.0/tests/test_video_versions_fix.py +55 -0
- threadstractormf-1.4.0/threadstractormf/__init__.py +7 -0
- threadstractormf-1.4.0/threadstractormf/_backend.py +283 -0
- threadstractormf-1.4.0/threadstractormf/_discovery.py +445 -0
- threadstractormf-1.4.0/threadstractormf/_parsing.py +244 -0
- threadstractormf-1.4.0/threadstractormf/api.py +1269 -0
- threadstractormf-1.4.0/threadstractormf/archive.py +262 -0
- threadstractormf-1.4.0/threadstractormf/auth.py +319 -0
- threadstractormf-1.4.0/threadstractormf/cli.py +426 -0
- threadstractormf-1.4.0/threadstractormf/client.py +221 -0
- threadstractormf-1.4.0/threadstractormf/downloader.py +353 -0
- threadstractormf-1.4.0/threadstractormf/models.py +123 -0
- threadstractormf-1.4.0/threadstractormf/rate_limit.py +201 -0
- threadstractormf-1.4.0/threadstractormf/template.py +123 -0
- threadstractormf-1.4.0/uv.lock +1779 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
.git
|
|
2
|
+
.venv
|
|
3
|
+
__pycache__
|
|
4
|
+
*.pyc
|
|
5
|
+
*.pyo
|
|
6
|
+
*.pyd
|
|
7
|
+
.pytest_cache
|
|
8
|
+
.mypy_cache
|
|
9
|
+
.ruff_cache
|
|
10
|
+
tests
|
|
11
|
+
docs
|
|
12
|
+
# *.md excluded except README.md (needed by hatchling build)
|
|
13
|
+
.env
|
|
14
|
+
.env.*
|
|
15
|
+
cookies.txt
|
|
16
|
+
*cookies*.txt
|
|
17
|
+
scripts/sniff_log.json
|
|
18
|
+
scripts/*_requests*.json
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
name: Test (Python ${{ matrix.python-version }})
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.10", "3.12", "3.14"]
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Install uv
|
|
20
|
+
uses: astral-sh/setup-uv@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: "Sync dependencies (all extras)"
|
|
25
|
+
run: uv sync --all-extras
|
|
26
|
+
|
|
27
|
+
- name: Lint (ruff)
|
|
28
|
+
run: uv run ruff check .
|
|
29
|
+
|
|
30
|
+
# mypy pins python_version=3.10 (see [tool.mypy]) but runs on the matrix
|
|
31
|
+
# interpreter; the checks are version-independent for this codebase.
|
|
32
|
+
- name: Types (mypy)
|
|
33
|
+
run: uv run mypy threadstractormf scripts
|
|
34
|
+
|
|
35
|
+
- name: Tests (pytest)
|
|
36
|
+
run: uv run pytest -q
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
name: Docker
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
tags: ["v*"]
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
packages: write
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
build:
|
|
15
|
+
name: Build & push (${{ matrix.variant }})
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
strategy:
|
|
18
|
+
fail-fast: false
|
|
19
|
+
matrix:
|
|
20
|
+
variant: [slim, full]
|
|
21
|
+
steps:
|
|
22
|
+
- name: Checkout
|
|
23
|
+
uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- name: Set up QEMU
|
|
26
|
+
uses: docker/setup-qemu-action@v3
|
|
27
|
+
|
|
28
|
+
- name: Set up Docker Buildx
|
|
29
|
+
uses: docker/setup-buildx-action@v3
|
|
30
|
+
|
|
31
|
+
- name: Log in to GHCR
|
|
32
|
+
uses: docker/login-action@v3
|
|
33
|
+
with:
|
|
34
|
+
registry: ghcr.io
|
|
35
|
+
username: ${{ github.actor }}
|
|
36
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
37
|
+
|
|
38
|
+
- name: Docker metadata
|
|
39
|
+
id: meta
|
|
40
|
+
uses: docker/metadata-action@v5
|
|
41
|
+
with:
|
|
42
|
+
images: ghcr.io/extractorsmf/threadstractormf
|
|
43
|
+
tags: |
|
|
44
|
+
type=raw,value=${{ matrix.variant }}
|
|
45
|
+
type=raw,value=latest,enable=${{ matrix.variant == 'full' }}
|
|
46
|
+
type=semver,pattern={{version}},enable=${{ matrix.variant == 'full' }}
|
|
47
|
+
type=sha,prefix=sha-,enable=${{ matrix.variant == 'full' }}
|
|
48
|
+
|
|
49
|
+
- name: Build and push
|
|
50
|
+
uses: docker/build-push-action@v6
|
|
51
|
+
with:
|
|
52
|
+
context: .
|
|
53
|
+
file: ./Dockerfile
|
|
54
|
+
target: ${{ matrix.variant }}
|
|
55
|
+
platforms: linux/amd64,linux/arm64
|
|
56
|
+
push: true
|
|
57
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
58
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
59
|
+
build-args: |
|
|
60
|
+
VARIANT=${{ matrix.variant }}
|
|
61
|
+
|
|
62
|
+
- name: Smoke test (slim)
|
|
63
|
+
if: matrix.variant == 'slim'
|
|
64
|
+
run: |
|
|
65
|
+
docker run --rm ghcr.io/extractorsmf/threadstractormf:slim --version
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# python-semantic-release: analyzes conventional commits on main.
|
|
4
|
+
# feat -> minor | fix/perf -> patch | feat!/BREAKING CHANGE -> major
|
|
5
|
+
# If there are no new relevant commits, it does nothing (released=false).
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
branches: [main]
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: write # commit de version + tag + GitHub Release
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
release:
|
|
17
|
+
name: Semantic Release
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
outputs:
|
|
20
|
+
released: ${{ steps.out.outputs.released }}
|
|
21
|
+
tag: ${{ steps.out.outputs.tag }}
|
|
22
|
+
concurrency:
|
|
23
|
+
group: semantic-release
|
|
24
|
+
cancel-in-progress: false
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
with:
|
|
28
|
+
fetch-depth: 0 # PSR needs full history for commit analysis
|
|
29
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
30
|
+
|
|
31
|
+
- name: Install uv
|
|
32
|
+
uses: astral-sh/setup-uv@v5
|
|
33
|
+
|
|
34
|
+
- name: Sync dependencies
|
|
35
|
+
run: uv sync --all-extras
|
|
36
|
+
|
|
37
|
+
- name: Configure git (bot identity for the version commit)
|
|
38
|
+
run: |
|
|
39
|
+
git config user.name "semantic-release-bot"
|
|
40
|
+
git config user.email "semantic-release-bot@users.noreply.github.com"
|
|
41
|
+
|
|
42
|
+
# PSR invoked directly with explicit flags:
|
|
43
|
+
# --no-changelog : this project has NO CHANGELOG.md
|
|
44
|
+
# --push : pushes commit + tag
|
|
45
|
+
# --vcs-release : creates the GitHub Release with notes from commits
|
|
46
|
+
# NOTE: no --strict, because when the version was already released
|
|
47
|
+
# (e.g. first push with tag v1.0.0 at HEAD) PSR would exit with code 2.
|
|
48
|
+
# NOTE: PSR v9 reads the token from GH_TOKEN (not GITHUB_TOKEN)
|
|
49
|
+
- name: Semantic Release (bump + tag + release, no changelog)
|
|
50
|
+
id: release
|
|
51
|
+
run: |
|
|
52
|
+
uv run semantic-release \
|
|
53
|
+
version \
|
|
54
|
+
--no-changelog \
|
|
55
|
+
--commit --tag --push \
|
|
56
|
+
--vcs-release \
|
|
57
|
+
--skip-build
|
|
58
|
+
env:
|
|
59
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
60
|
+
|
|
61
|
+
# PSR CLI outputs: uses GITHUB_OUTPUT for the following steps
|
|
62
|
+
- name: Set outputs
|
|
63
|
+
if: always()
|
|
64
|
+
id: out
|
|
65
|
+
run: |
|
|
66
|
+
if git describe --exact-match --tags HEAD 2>/dev/null; then
|
|
67
|
+
echo "released=true" >> "$GITHUB_OUTPUT"
|
|
68
|
+
echo "tag=$(git describe --exact-match --tags HEAD)" >> "$GITHUB_OUTPUT"
|
|
69
|
+
else
|
|
70
|
+
echo "released=false" >> "$GITHUB_OUTPUT"
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
- name: Build distributions
|
|
74
|
+
if: steps.out.outputs.released == 'true'
|
|
75
|
+
run: uv build
|
|
76
|
+
|
|
77
|
+
- name: Upload distributions
|
|
78
|
+
if: steps.out.outputs.released == 'true'
|
|
79
|
+
uses: actions/upload-artifact@v4
|
|
80
|
+
with:
|
|
81
|
+
name: dist
|
|
82
|
+
path: dist/*
|
|
83
|
+
|
|
84
|
+
- name: Attach wheel + sdist to GitHub Release
|
|
85
|
+
if: steps.out.outputs.released == 'true'
|
|
86
|
+
uses: softprops/action-gh-release@v2
|
|
87
|
+
with:
|
|
88
|
+
tag_name: ${{ steps.out.outputs.tag }}
|
|
89
|
+
# explicit patterns: uv build drops a dist/.gitignore that a bare
|
|
90
|
+
# "dist/*" glob would upload as a bogus "default.gitignore" asset
|
|
91
|
+
files: |
|
|
92
|
+
dist/*.whl
|
|
93
|
+
dist/*.tar.gz
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
publish-pypi:
|
|
97
|
+
name: Publish to PyPI
|
|
98
|
+
needs: release
|
|
99
|
+
if: needs.release.outputs.released == 'true'
|
|
100
|
+
runs-on: ubuntu-latest
|
|
101
|
+
environment: pypi
|
|
102
|
+
permissions:
|
|
103
|
+
id-token: write
|
|
104
|
+
steps:
|
|
105
|
+
- name: Download distributions
|
|
106
|
+
uses: actions/download-artifact@v4
|
|
107
|
+
with:
|
|
108
|
+
name: dist
|
|
109
|
+
path: dist
|
|
110
|
+
- name: Publish to PyPI
|
|
111
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
112
|
+
with:
|
|
113
|
+
skip-existing: true
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
publish-testpypi:
|
|
117
|
+
name: Publish to TestPyPI
|
|
118
|
+
if: github.event_name == 'workflow_dispatch'
|
|
119
|
+
runs-on: ubuntu-latest
|
|
120
|
+
environment: testpypi
|
|
121
|
+
permissions:
|
|
122
|
+
id-token: write
|
|
123
|
+
steps:
|
|
124
|
+
- name: Checkout
|
|
125
|
+
uses: actions/checkout@v4
|
|
126
|
+
- name: Set up uv
|
|
127
|
+
uses: astral-sh/setup-uv@v5
|
|
128
|
+
- name: Build
|
|
129
|
+
run: uv build
|
|
130
|
+
- name: Publish to TestPyPI
|
|
131
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
132
|
+
with:
|
|
133
|
+
repository-url: https://test.pypi.org/legacy/
|
|
134
|
+
skip-existing: true
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
.venv/
|
|
7
|
+
venv/
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
.mypy_cache/
|
|
10
|
+
.ruff_cache/
|
|
11
|
+
*.egg-info/
|
|
12
|
+
dist/
|
|
13
|
+
build/
|
|
14
|
+
# cookies / secrets
|
|
15
|
+
cookies.txt
|
|
16
|
+
*cookies*.txt
|
|
17
|
+
# environment / secrets
|
|
18
|
+
.env
|
|
19
|
+
.env.*
|
|
20
|
+
!.env.example
|
|
21
|
+
scripts/sniff_log.json
|
|
22
|
+
scripts/*_requests*.json
|
|
23
|
+
# Frozen pre-refactor cookie loader, used as the oracle in
|
|
24
|
+
# tests/test_cookie_loading.py. Not part of the package.
|
|
25
|
+
.old_auth_for_tests.py
|
|
26
|
+
# Project memory / agent instructions (local, not shipped)
|
|
27
|
+
docs/
|
|
28
|
+
AGENTS.md
|
|
29
|
+
llms.txt
|
|
30
|
+
llms-full.txt
|
|
31
|
+
review.md
|
|
32
|
+
|
|
33
|
+
# OS
|
|
34
|
+
.DS_Store
|
|
35
|
+
Thumbs.db
|
|
36
|
+
# IDE
|
|
37
|
+
.vscode/
|
|
38
|
+
.idea/
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
ARG PYTHON_VERSION=3.12
|
|
3
|
+
|
|
4
|
+
# ---- base ----
|
|
5
|
+
FROM python:${PYTHON_VERSION}-slim AS base
|
|
6
|
+
ENV PYTHONDONTWRITEBYTECODE=1 \
|
|
7
|
+
PYTHONUNBUFFERED=1 \
|
|
8
|
+
UV_LINK_MODE=copy
|
|
9
|
+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
|
|
10
|
+
WORKDIR /app
|
|
11
|
+
|
|
12
|
+
# ---- slim (core only) ----
|
|
13
|
+
FROM base AS slim
|
|
14
|
+
COPY pyproject.toml uv.lock README.md ./
|
|
15
|
+
COPY threadstractormf ./threadstractormf
|
|
16
|
+
RUN uv sync --frozen --no-dev
|
|
17
|
+
ENV PATH="/app/.venv/bin:$PATH"
|
|
18
|
+
USER 1000
|
|
19
|
+
ENTRYPOINT ["threadstractormf"]
|
|
20
|
+
|
|
21
|
+
# ---- full (slim + antibot + browser) ----
|
|
22
|
+
FROM slim AS full
|
|
23
|
+
USER root
|
|
24
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
25
|
+
libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 \
|
|
26
|
+
libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 \
|
|
27
|
+
libgbm1 libasound2 libpango-1.0-0 libcairo2 \
|
|
28
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
29
|
+
RUN uv sync --frozen --no-dev --extra browser --extra antibot
|
|
30
|
+
ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright
|
|
31
|
+
RUN uv run playwright install chromium && chmod -R 755 /ms-playwright
|
|
32
|
+
ENV THREADS_USE_SANDBOX=1
|
|
33
|
+
ENV PATH="/app/.venv/bin:$PATH"
|
|
34
|
+
USER 1000
|
|
35
|
+
ENTRYPOINT ["threadstractormf"]
|