collapsarr 0.1.3__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.
- collapsarr-0.1.3/.dockerignore +56 -0
- collapsarr-0.1.3/.env.example +16 -0
- collapsarr-0.1.3/.gitattributes +2 -0
- collapsarr-0.1.3/.github/brand/icon-512.png +0 -0
- collapsarr-0.1.3/.github/brand/social-preview.png +0 -0
- collapsarr-0.1.3/.github/brand/social-preview.svg +21 -0
- collapsarr-0.1.3/.github/workflows/release.yml +219 -0
- collapsarr-0.1.3/.gitignore +61 -0
- collapsarr-0.1.3/Dockerfile +85 -0
- collapsarr-0.1.3/LICENSE +674 -0
- collapsarr-0.1.3/PKG-INFO +218 -0
- collapsarr-0.1.3/README.md +192 -0
- collapsarr-0.1.3/collapsarr/__init__.py +15 -0
- collapsarr-0.1.3/collapsarr/__main__.py +25 -0
- collapsarr-0.1.3/collapsarr/arr/__init__.py +76 -0
- collapsarr-0.1.3/collapsarr/arr/client.py +71 -0
- collapsarr-0.1.3/collapsarr/arr/files.py +211 -0
- collapsarr-0.1.3/collapsarr/arr/models.py +147 -0
- collapsarr-0.1.3/collapsarr/arr/routes.py +290 -0
- collapsarr-0.1.3/collapsarr/arr/service.py +222 -0
- collapsarr-0.1.3/collapsarr/arr/webhooks.py +200 -0
- collapsarr-0.1.3/collapsarr/auth.py +83 -0
- collapsarr-0.1.3/collapsarr/config.py +95 -0
- collapsarr-0.1.3/collapsarr/database.py +82 -0
- collapsarr-0.1.3/collapsarr/downmix/__init__.py +54 -0
- collapsarr-0.1.3/collapsarr/downmix/apply.py +241 -0
- collapsarr-0.1.3/collapsarr/downmix/pipeline.py +235 -0
- collapsarr-0.1.3/collapsarr/downmix/probe.py +310 -0
- collapsarr-0.1.3/collapsarr/downmix/remux.py +284 -0
- collapsarr-0.1.3/collapsarr/downmix/targets.py +145 -0
- collapsarr-0.1.3/collapsarr/frontend.py +61 -0
- collapsarr-0.1.3/collapsarr/health.py +111 -0
- collapsarr-0.1.3/collapsarr/jobs/__init__.py +49 -0
- collapsarr-0.1.3/collapsarr/jobs/failure_notify.py +145 -0
- collapsarr-0.1.3/collapsarr/jobs/history.py +153 -0
- collapsarr-0.1.3/collapsarr/jobs/models.py +90 -0
- collapsarr-0.1.3/collapsarr/jobs/queue.py +382 -0
- collapsarr-0.1.3/collapsarr/jobs/routes.py +193 -0
- collapsarr-0.1.3/collapsarr/jobs/scheduler.py +402 -0
- collapsarr-0.1.3/collapsarr/main.py +212 -0
- collapsarr-0.1.3/collapsarr/media/__init__.py +38 -0
- collapsarr-0.1.3/collapsarr/media/models.py +132 -0
- collapsarr-0.1.3/collapsarr/media/routes.py +88 -0
- collapsarr-0.1.3/collapsarr/media/service.py +241 -0
- collapsarr-0.1.3/collapsarr/notify/__init__.py +37 -0
- collapsarr-0.1.3/collapsarr/notify/dispatch.py +162 -0
- collapsarr-0.1.3/collapsarr/notify/models.py +63 -0
- collapsarr-0.1.3/collapsarr/notify/routes.py +105 -0
- collapsarr-0.1.3/collapsarr/notify/service.py +80 -0
- collapsarr-0.1.3/collapsarr/settings/__init__.py +29 -0
- collapsarr-0.1.3/collapsarr/settings/models.py +107 -0
- collapsarr-0.1.3/collapsarr/settings/routes.py +157 -0
- collapsarr-0.1.3/collapsarr/settings/service.py +158 -0
- collapsarr-0.1.3/docker-entrypoint.sh +24 -0
- collapsarr-0.1.3/frontend/README.md +48 -0
- collapsarr-0.1.3/frontend/dist/apple-touch-icon.png +0 -0
- collapsarr-0.1.3/frontend/dist/assets/index-Dfozi1oM.js +68 -0
- collapsarr-0.1.3/frontend/dist/assets/index-J2qCuzXj.css +1 -0
- collapsarr-0.1.3/frontend/dist/favicon-32.png +0 -0
- collapsarr-0.1.3/frontend/dist/favicon.svg +10 -0
- collapsarr-0.1.3/frontend/dist/index.html +18 -0
- collapsarr-0.1.3/frontend/eslint.config.js +28 -0
- collapsarr-0.1.3/frontend/index.html +17 -0
- collapsarr-0.1.3/frontend/package-lock.json +4700 -0
- collapsarr-0.1.3/frontend/package.json +37 -0
- collapsarr-0.1.3/frontend/public/apple-touch-icon.png +0 -0
- collapsarr-0.1.3/frontend/public/favicon-32.png +0 -0
- collapsarr-0.1.3/frontend/public/favicon.svg +10 -0
- collapsarr-0.1.3/frontend/src/api/activity.ts +52 -0
- collapsarr-0.1.3/frontend/src/api/client.ts +91 -0
- collapsarr-0.1.3/frontend/src/api/health.ts +16 -0
- collapsarr-0.1.3/frontend/src/api/instances.ts +106 -0
- collapsarr-0.1.3/frontend/src/api/notifiers.ts +30 -0
- collapsarr-0.1.3/frontend/src/api/settings.ts +28 -0
- collapsarr-0.1.3/frontend/src/api/wanted.ts +19 -0
- collapsarr-0.1.3/frontend/src/components/AppShell.tsx +24 -0
- collapsarr-0.1.3/frontend/src/components/HealthBanner.tsx +49 -0
- collapsarr-0.1.3/frontend/src/components/PlaceholderView.tsx +42 -0
- collapsarr-0.1.3/frontend/src/components/Sidebar.tsx +37 -0
- collapsarr-0.1.3/frontend/src/components/icons.tsx +80 -0
- collapsarr-0.1.3/frontend/src/components/settings/ConnectSection.tsx +162 -0
- collapsarr-0.1.3/frontend/src/components/settings/GeneralSection.tsx +275 -0
- collapsarr-0.1.3/frontend/src/components/settings/InstancesSection.tsx +369 -0
- collapsarr-0.1.3/frontend/src/components/settings/PathMappingsPanel.tsx +294 -0
- collapsarr-0.1.3/frontend/src/components/settings/TargetsSection.tsx +148 -0
- collapsarr-0.1.3/frontend/src/main.tsx +18 -0
- collapsarr-0.1.3/frontend/src/pages/ActivityPage.tsx +195 -0
- collapsarr-0.1.3/frontend/src/pages/FileDetailPage.tsx +370 -0
- collapsarr-0.1.3/frontend/src/pages/SettingsPage.tsx +30 -0
- collapsarr-0.1.3/frontend/src/pages/WantedPage.tsx +127 -0
- collapsarr-0.1.3/frontend/src/routes/nav.tsx +25 -0
- collapsarr-0.1.3/frontend/src/routes/router.tsx +21 -0
- collapsarr-0.1.3/frontend/src/styles/global.css +915 -0
- collapsarr-0.1.3/frontend/src/styles/theme.css +63 -0
- collapsarr-0.1.3/frontend/src/test/activityPage.test.tsx +147 -0
- collapsarr-0.1.3/frontend/src/test/apiClient.test.ts +78 -0
- collapsarr-0.1.3/frontend/src/test/appShell.test.tsx +89 -0
- collapsarr-0.1.3/frontend/src/test/fileDetailPage.test.tsx +238 -0
- collapsarr-0.1.3/frontend/src/test/healthBanner.test.tsx +49 -0
- collapsarr-0.1.3/frontend/src/test/settingsConnect.test.tsx +109 -0
- collapsarr-0.1.3/frontend/src/test/settingsGeneral.test.tsx +145 -0
- collapsarr-0.1.3/frontend/src/test/settingsInstances.test.tsx +223 -0
- collapsarr-0.1.3/frontend/src/test/settingsPage.test.tsx +67 -0
- collapsarr-0.1.3/frontend/src/test/settingsTargets.test.tsx +124 -0
- collapsarr-0.1.3/frontend/src/test/setup.ts +49 -0
- collapsarr-0.1.3/frontend/src/test/wantedPage.test.tsx +97 -0
- collapsarr-0.1.3/frontend/src/types/activity.ts +58 -0
- collapsarr-0.1.3/frontend/src/types/health.ts +17 -0
- collapsarr-0.1.3/frontend/src/types/instances.ts +71 -0
- collapsarr-0.1.3/frontend/src/types/notifiers.ts +29 -0
- collapsarr-0.1.3/frontend/src/types/settings.ts +41 -0
- collapsarr-0.1.3/frontend/src/types/wanted.ts +25 -0
- collapsarr-0.1.3/frontend/src/vite-env.d.ts +1 -0
- collapsarr-0.1.3/frontend/tsconfig.json +25 -0
- collapsarr-0.1.3/frontend/vite.config.ts +24 -0
- collapsarr-0.1.3/hatch_build.py +33 -0
- collapsarr-0.1.3/pyproject.toml +93 -0
- collapsarr-0.1.3/tests/__init__.py +0 -0
- collapsarr-0.1.3/tests/conftest.py +48 -0
- collapsarr-0.1.3/tests/fixtures/arr/radarr_movie_list.json +76 -0
- collapsarr-0.1.3/tests/fixtures/arr/radarr_status_ok.json +31 -0
- collapsarr-0.1.3/tests/fixtures/arr/radarr_webhook_on_import.json +39 -0
- collapsarr-0.1.3/tests/fixtures/arr/radarr_webhook_on_upgrade.json +39 -0
- collapsarr-0.1.3/tests/fixtures/arr/sonarr_episodefiles_series1.json +42 -0
- collapsarr-0.1.3/tests/fixtures/arr/sonarr_series_list.json +22 -0
- collapsarr-0.1.3/tests/fixtures/arr/sonarr_status_ok.json +31 -0
- collapsarr-0.1.3/tests/fixtures/arr/sonarr_webhook_on_import.json +43 -0
- collapsarr-0.1.3/tests/fixtures/arr/sonarr_webhook_on_upgrade.json +43 -0
- collapsarr-0.1.3/tests/fixtures/arr/sonarr_webhook_test.json +13 -0
- collapsarr-0.1.3/tests/fixtures/arr/unauthorized.json +3 -0
- collapsarr-0.1.3/tests/fixtures/downmix/multi_lang.mkv +0 -0
- collapsarr-0.1.3/tests/fixtures/downmix/stereo_eng.mkv +0 -0
- collapsarr-0.1.3/tests/fixtures/downmix/surround_51.mkv +0 -0
- collapsarr-0.1.3/tests/fixtures/downmix/surround_71.mkv +0 -0
- collapsarr-0.1.3/tests/fixtures/downmix/untagged_language.mkv +0 -0
- collapsarr-0.1.3/tests/fixtures/downmix/video_audio_subs.mkv +0 -0
- collapsarr-0.1.3/tests/test_arr_client.py +135 -0
- collapsarr-0.1.3/tests/test_arr_files.py +200 -0
- collapsarr-0.1.3/tests/test_arr_models.py +248 -0
- collapsarr-0.1.3/tests/test_arr_routes.py +298 -0
- collapsarr-0.1.3/tests/test_arr_service.py +302 -0
- collapsarr-0.1.3/tests/test_arr_webhooks.py +433 -0
- collapsarr-0.1.3/tests/test_auth.py +53 -0
- collapsarr-0.1.3/tests/test_config.py +51 -0
- collapsarr-0.1.3/tests/test_downmix_apply.py +324 -0
- collapsarr-0.1.3/tests/test_downmix_pipeline.py +388 -0
- collapsarr-0.1.3/tests/test_downmix_probe.py +364 -0
- collapsarr-0.1.3/tests/test_downmix_remux.py +439 -0
- collapsarr-0.1.3/tests/test_downmix_targets.py +176 -0
- collapsarr-0.1.3/tests/test_frontend.py +74 -0
- collapsarr-0.1.3/tests/test_health.py +216 -0
- collapsarr-0.1.3/tests/test_health_check.py +138 -0
- collapsarr-0.1.3/tests/test_jobs_failure_notify.py +251 -0
- collapsarr-0.1.3/tests/test_jobs_history.py +413 -0
- collapsarr-0.1.3/tests/test_jobs_queue.py +328 -0
- collapsarr-0.1.3/tests/test_jobs_routes.py +302 -0
- collapsarr-0.1.3/tests/test_jobs_scheduler.py +608 -0
- collapsarr-0.1.3/tests/test_main.py +45 -0
- collapsarr-0.1.3/tests/test_media_service.py +411 -0
- collapsarr-0.1.3/tests/test_notify_dispatch.py +266 -0
- collapsarr-0.1.3/tests/test_notify_routes.py +126 -0
- collapsarr-0.1.3/tests/test_notify_service.py +148 -0
- collapsarr-0.1.3/tests/test_settings_routes.py +128 -0
- collapsarr-0.1.3/tests/test_settings_service.py +236 -0
- collapsarr-0.1.3/tests/test_wanted_routes.py +126 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Keep the build context small and reproducible. The frontend bundle and Python
|
|
2
|
+
# wheel are rebuilt inside the image, so host-built artifacts are excluded.
|
|
3
|
+
|
|
4
|
+
# VCS / tooling
|
|
5
|
+
.git
|
|
6
|
+
.gitignore
|
|
7
|
+
.gitattributes
|
|
8
|
+
.github
|
|
9
|
+
.claude
|
|
10
|
+
.agents
|
|
11
|
+
|
|
12
|
+
# Python caches & local envs
|
|
13
|
+
**/__pycache__
|
|
14
|
+
*.py[cod]
|
|
15
|
+
*.egg-info
|
|
16
|
+
.venv
|
|
17
|
+
venv
|
|
18
|
+
env
|
|
19
|
+
build
|
|
20
|
+
dist
|
|
21
|
+
.pytest_cache
|
|
22
|
+
.mypy_cache
|
|
23
|
+
.ruff_cache
|
|
24
|
+
.coverage
|
|
25
|
+
htmlcov
|
|
26
|
+
|
|
27
|
+
# Node / frontend (deps + host build output rebuilt in-image)
|
|
28
|
+
node_modules
|
|
29
|
+
frontend/node_modules
|
|
30
|
+
frontend/dist
|
|
31
|
+
|
|
32
|
+
# Runtime data & secrets
|
|
33
|
+
.env
|
|
34
|
+
.env.*
|
|
35
|
+
!.env.example
|
|
36
|
+
/config
|
|
37
|
+
*.db
|
|
38
|
+
*.sqlite
|
|
39
|
+
*.sqlite3
|
|
40
|
+
*.sqlite3-journal
|
|
41
|
+
logs
|
|
42
|
+
*.log
|
|
43
|
+
|
|
44
|
+
# Test media / scratch
|
|
45
|
+
/samples
|
|
46
|
+
/media
|
|
47
|
+
|
|
48
|
+
# Docs & editor/OS junk
|
|
49
|
+
docs
|
|
50
|
+
*.md
|
|
51
|
+
!README.md
|
|
52
|
+
.idea
|
|
53
|
+
.vscode
|
|
54
|
+
*.swp
|
|
55
|
+
.DS_Store
|
|
56
|
+
Thumbs.db
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Collapsarr configuration.
|
|
2
|
+
# Copy to `.env` and adjust as needed. All variables are optional — the values
|
|
3
|
+
# below are the built-in defaults. Environment variables take precedence.
|
|
4
|
+
|
|
5
|
+
# Filesystem path to the SQLite database file.
|
|
6
|
+
COLLAPSARR_DATABASE_PATH=/config/collapsarr.db
|
|
7
|
+
|
|
8
|
+
# Full SQLAlchemy database URL. When set, overrides COLLAPSARR_DATABASE_PATH.
|
|
9
|
+
# COLLAPSARR_DATABASE_URL=sqlite:////config/collapsarr.db
|
|
10
|
+
|
|
11
|
+
# API server bind address and port.
|
|
12
|
+
COLLAPSARR_HOST=0.0.0.0
|
|
13
|
+
COLLAPSARR_PORT=8282
|
|
14
|
+
|
|
15
|
+
# Log level (DEBUG, INFO, WARNING, ERROR).
|
|
16
|
+
COLLAPSARR_LOG_LEVEL=INFO
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="640" viewBox="0 0 1280 640">
|
|
2
|
+
<rect x="0" y="0" width="1280" height="640" fill="#18151f"/>
|
|
3
|
+
|
|
4
|
+
<!-- glyph: Outer Survivors, scaled 1.3x -->
|
|
5
|
+
<g fill="#8B5CF6">
|
|
6
|
+
<rect x="176.0" y="203" width="44.2" height="130" rx="7.8"/>
|
|
7
|
+
<rect x="257.9" y="138" width="44.2" height="195" rx="7.8"/>
|
|
8
|
+
<rect x="339.8" y="203" width="44.2" height="130" rx="7.8"/>
|
|
9
|
+
<rect x="85.0" y="203" width="62.4" height="299" rx="13"/>
|
|
10
|
+
<rect x="412.6" y="203" width="62.4" height="299" rx="13"/>
|
|
11
|
+
</g>
|
|
12
|
+
|
|
13
|
+
<text x="560" y="240" font-family="Menlo, ui-monospace, monospace" font-size="20"
|
|
14
|
+
letter-spacing="3" fill="#8B5CF6">AUDIO DOWNMIX FOR SONARR & RADARR</text>
|
|
15
|
+
|
|
16
|
+
<text x="557" y="345" font-family="Futura, Avenir Next, sans-serif" font-weight="bold"
|
|
17
|
+
font-size="104" fill="#f3f1fa">Collapsarr</text>
|
|
18
|
+
|
|
19
|
+
<text x="560" y="392" font-family="Helvetica Neue, Helvetica, sans-serif" font-size="30"
|
|
20
|
+
fill="#9791ae">Enjoy your media on any speaker setup.</text>
|
|
21
|
+
</svg>
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Tag-triggered release pipeline (COL-41).
|
|
2
|
+
#
|
|
3
|
+
# Fires on a semver tag (v*.*.*) that lives on `main`, then:
|
|
4
|
+
# 1. guard — refuse to release a tag that is not contained in main.
|
|
5
|
+
# 2. test-* — backend (pytest/ruff/mypy) + frontend (vitest/tsc/eslint);
|
|
6
|
+
# any failure fails the pipeline before anything is published.
|
|
7
|
+
# 3. build-wheel — build the frontend FIRST, then `python -m build` so the
|
|
8
|
+
# wheel bundles frontend/dist -> collapsarr/static (COL-40).
|
|
9
|
+
# 4. docker — multi-arch (amd64+arm64) image build & push to Docker Hub.
|
|
10
|
+
# 5. pypi-publish — publish the wheel/sdist to PyPI via OIDC Trusted Publishing.
|
|
11
|
+
# 6. github-release — cut a GitHub Release with the built artifacts attached.
|
|
12
|
+
#
|
|
13
|
+
# Credentials come from GitHub Actions secrets / OIDC that the HITL epic (COL-9)
|
|
14
|
+
# populates; this workflow only wires them up. It is intentionally reviewable and
|
|
15
|
+
# dry-runnable before those secrets exist — nothing here is evaluated until a real
|
|
16
|
+
# tag is pushed.
|
|
17
|
+
#
|
|
18
|
+
# Named secrets / tokens this workflow reads:
|
|
19
|
+
# - Docker Hub push : secrets.DOCKERHUB_USERNAME + secrets.DOCKERHUB_TOKEN
|
|
20
|
+
# - GitHub Release : secrets.GITHUB_TOKEN (auto-provided by Actions)
|
|
21
|
+
# - PyPI push : OIDC Trusted Publishing (no secret; requires a PyPI
|
|
22
|
+
# "pending publisher" configured for this repo/workflow).
|
|
23
|
+
|
|
24
|
+
name: release
|
|
25
|
+
|
|
26
|
+
on:
|
|
27
|
+
push:
|
|
28
|
+
tags:
|
|
29
|
+
- 'v*.*.*'
|
|
30
|
+
|
|
31
|
+
permissions:
|
|
32
|
+
contents: read
|
|
33
|
+
|
|
34
|
+
env:
|
|
35
|
+
IMAGE_NAME: odxnsson/collapsarr
|
|
36
|
+
PYTHON_VERSION: '3.12'
|
|
37
|
+
NODE_VERSION: '20'
|
|
38
|
+
|
|
39
|
+
jobs:
|
|
40
|
+
# ---- Guard: enforce the "tag pushed to main" acceptance criterion -----------
|
|
41
|
+
guard:
|
|
42
|
+
name: Verify tag is on main
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
steps:
|
|
45
|
+
- uses: actions/checkout@v4
|
|
46
|
+
with:
|
|
47
|
+
fetch-depth: 0
|
|
48
|
+
- name: Ensure the tagged commit is contained in main
|
|
49
|
+
run: |
|
|
50
|
+
git fetch origin main
|
|
51
|
+
if git merge-base --is-ancestor "${GITHUB_SHA}" FETCH_HEAD; then
|
|
52
|
+
echo "Tag ${GITHUB_REF_NAME} (${GITHUB_SHA}) is on main — proceeding."
|
|
53
|
+
else
|
|
54
|
+
echo "::error::Tag ${GITHUB_REF_NAME} is not on main; refusing to release."
|
|
55
|
+
exit 1
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# ---- Test suite (must pass before any build/publish) ------------------------
|
|
59
|
+
test-backend:
|
|
60
|
+
name: Backend tests (pytest / ruff / mypy)
|
|
61
|
+
needs: guard
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
steps:
|
|
64
|
+
- uses: actions/checkout@v4
|
|
65
|
+
- name: Install FFmpeg
|
|
66
|
+
run: sudo apt-get update && sudo apt-get install -y ffmpeg
|
|
67
|
+
- uses: actions/setup-python@v5
|
|
68
|
+
with:
|
|
69
|
+
python-version: ${{ env.PYTHON_VERSION }}
|
|
70
|
+
- name: Install backend with dev extras
|
|
71
|
+
run: pip install -e ".[dev]"
|
|
72
|
+
- name: Ruff
|
|
73
|
+
run: ruff check collapsarr tests
|
|
74
|
+
- name: Mypy
|
|
75
|
+
run: mypy
|
|
76
|
+
- name: Pytest
|
|
77
|
+
run: pytest
|
|
78
|
+
|
|
79
|
+
test-frontend:
|
|
80
|
+
name: Frontend tests (vitest / tsc / eslint)
|
|
81
|
+
needs: guard
|
|
82
|
+
runs-on: ubuntu-latest
|
|
83
|
+
defaults:
|
|
84
|
+
run:
|
|
85
|
+
working-directory: frontend
|
|
86
|
+
steps:
|
|
87
|
+
- uses: actions/checkout@v4
|
|
88
|
+
- uses: actions/setup-node@v4
|
|
89
|
+
with:
|
|
90
|
+
node-version: ${{ env.NODE_VERSION }}
|
|
91
|
+
cache: npm
|
|
92
|
+
cache-dependency-path: frontend/package-lock.json
|
|
93
|
+
- name: Install dependencies
|
|
94
|
+
run: npm ci
|
|
95
|
+
- name: Lint
|
|
96
|
+
run: npm run lint
|
|
97
|
+
- name: Typecheck
|
|
98
|
+
run: npm run typecheck
|
|
99
|
+
- name: Test
|
|
100
|
+
run: npm run test
|
|
101
|
+
|
|
102
|
+
# ---- Build the wheel + sdist ------------------------------------------------
|
|
103
|
+
# Order matters: the frontend is built FIRST so frontend/dist exists when
|
|
104
|
+
# `python -m build` runs, otherwise the wheel ships without the UI (COL-40).
|
|
105
|
+
build-wheel:
|
|
106
|
+
name: Build wheel + sdist
|
|
107
|
+
needs: [test-backend, test-frontend]
|
|
108
|
+
runs-on: ubuntu-latest
|
|
109
|
+
steps:
|
|
110
|
+
- uses: actions/checkout@v4
|
|
111
|
+
- uses: actions/setup-node@v4
|
|
112
|
+
with:
|
|
113
|
+
node-version: ${{ env.NODE_VERSION }}
|
|
114
|
+
cache: npm
|
|
115
|
+
cache-dependency-path: frontend/package-lock.json
|
|
116
|
+
- name: Build frontend (produces frontend/dist)
|
|
117
|
+
working-directory: frontend
|
|
118
|
+
run: |
|
|
119
|
+
npm ci
|
|
120
|
+
npm run build
|
|
121
|
+
- uses: actions/setup-python@v5
|
|
122
|
+
with:
|
|
123
|
+
python-version: ${{ env.PYTHON_VERSION }}
|
|
124
|
+
- name: Stamp package version from the git tag
|
|
125
|
+
run: |
|
|
126
|
+
VERSION="${GITHUB_REF_NAME#v}"
|
|
127
|
+
echo "Releasing version ${VERSION}"
|
|
128
|
+
sed -i "s/^version = \".*\"/version = \"${VERSION}\"/" pyproject.toml
|
|
129
|
+
- name: Build distributions
|
|
130
|
+
run: |
|
|
131
|
+
python -m pip install --upgrade build
|
|
132
|
+
python -m build
|
|
133
|
+
- name: Verify the frontend was bundled into the wheel
|
|
134
|
+
run: |
|
|
135
|
+
python - <<'PY'
|
|
136
|
+
import glob, zipfile, sys
|
|
137
|
+
wheels = glob.glob("dist/*.whl")
|
|
138
|
+
if not wheels:
|
|
139
|
+
sys.exit("no wheel produced")
|
|
140
|
+
names = zipfile.ZipFile(wheels[0]).namelist()
|
|
141
|
+
if not any(n.startswith("collapsarr/static/") for n in names):
|
|
142
|
+
sys.exit("frontend assets missing from wheel (collapsarr/static/ not found)")
|
|
143
|
+
print("OK: frontend bundled into", wheels[0])
|
|
144
|
+
PY
|
|
145
|
+
- name: Upload distributions
|
|
146
|
+
uses: actions/upload-artifact@v4
|
|
147
|
+
with:
|
|
148
|
+
name: dist
|
|
149
|
+
path: dist/
|
|
150
|
+
if-no-files-found: error
|
|
151
|
+
|
|
152
|
+
# ---- Multi-arch Docker image build & push -----------------------------------
|
|
153
|
+
docker:
|
|
154
|
+
name: Build & push multi-arch image
|
|
155
|
+
needs: [test-backend, test-frontend]
|
|
156
|
+
runs-on: ubuntu-latest
|
|
157
|
+
steps:
|
|
158
|
+
- uses: actions/checkout@v4
|
|
159
|
+
- name: Derive image version from tag
|
|
160
|
+
id: meta
|
|
161
|
+
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
|
|
162
|
+
- name: Set up QEMU
|
|
163
|
+
uses: docker/setup-qemu-action@v3
|
|
164
|
+
- name: Set up Buildx
|
|
165
|
+
uses: docker/setup-buildx-action@v3
|
|
166
|
+
- name: Log in to Docker Hub
|
|
167
|
+
uses: docker/login-action@v3
|
|
168
|
+
with:
|
|
169
|
+
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
170
|
+
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
171
|
+
- name: Build and push
|
|
172
|
+
uses: docker/build-push-action@v6
|
|
173
|
+
with:
|
|
174
|
+
context: .
|
|
175
|
+
platforms: linux/amd64,linux/arm64
|
|
176
|
+
push: true
|
|
177
|
+
tags: |
|
|
178
|
+
${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
|
|
179
|
+
${{ env.IMAGE_NAME }}:latest
|
|
180
|
+
provenance: false
|
|
181
|
+
|
|
182
|
+
# ---- Publish to PyPI (OIDC Trusted Publishing — no API token needed) --------
|
|
183
|
+
pypi-publish:
|
|
184
|
+
name: Publish to PyPI
|
|
185
|
+
needs: build-wheel
|
|
186
|
+
runs-on: ubuntu-latest
|
|
187
|
+
permissions:
|
|
188
|
+
id-token: write
|
|
189
|
+
steps:
|
|
190
|
+
- name: Download distributions
|
|
191
|
+
uses: actions/download-artifact@v4
|
|
192
|
+
with:
|
|
193
|
+
name: dist
|
|
194
|
+
path: dist/
|
|
195
|
+
- name: Publish to PyPI
|
|
196
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
197
|
+
with:
|
|
198
|
+
packages-dir: dist/
|
|
199
|
+
|
|
200
|
+
# ---- Cut the GitHub Release -------------------------------------------------
|
|
201
|
+
github-release:
|
|
202
|
+
name: Create GitHub Release
|
|
203
|
+
needs: [build-wheel, docker, pypi-publish]
|
|
204
|
+
runs-on: ubuntu-latest
|
|
205
|
+
permissions:
|
|
206
|
+
contents: write
|
|
207
|
+
steps:
|
|
208
|
+
- name: Download distributions
|
|
209
|
+
uses: actions/download-artifact@v4
|
|
210
|
+
with:
|
|
211
|
+
name: dist
|
|
212
|
+
path: dist/
|
|
213
|
+
- name: Create GitHub Release
|
|
214
|
+
uses: softprops/action-gh-release@v2
|
|
215
|
+
with:
|
|
216
|
+
generate_release_notes: true
|
|
217
|
+
files: dist/*
|
|
218
|
+
env:
|
|
219
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# ---- Secrets & local config (NEVER commit these) ----
|
|
2
|
+
.env
|
|
3
|
+
.env.*
|
|
4
|
+
!.env.example
|
|
5
|
+
*.local
|
|
6
|
+
docker-compose.override.yml
|
|
7
|
+
|
|
8
|
+
# ---- App runtime data (arr apps store a SQLite DB + config) ----
|
|
9
|
+
/config/
|
|
10
|
+
*.db
|
|
11
|
+
*.sqlite
|
|
12
|
+
*.sqlite3
|
|
13
|
+
*.sqlite3-journal
|
|
14
|
+
logs/
|
|
15
|
+
*.log
|
|
16
|
+
|
|
17
|
+
# ---- Test media / FFmpeg scratch output ----
|
|
18
|
+
# keep small committed fixtures under tests/fixtures/, ignore the rest
|
|
19
|
+
/samples/
|
|
20
|
+
/media/
|
|
21
|
+
*.mkv
|
|
22
|
+
*.mp4
|
|
23
|
+
*.srt
|
|
24
|
+
!tests/fixtures/**
|
|
25
|
+
|
|
26
|
+
# ---- Python ----
|
|
27
|
+
__pycache__/
|
|
28
|
+
*.py[cod]
|
|
29
|
+
*.egg-info/
|
|
30
|
+
.eggs/
|
|
31
|
+
.venv/
|
|
32
|
+
venv/
|
|
33
|
+
env/
|
|
34
|
+
build/
|
|
35
|
+
dist/
|
|
36
|
+
.pytest_cache/
|
|
37
|
+
.mypy_cache/
|
|
38
|
+
.ruff_cache/
|
|
39
|
+
.coverage
|
|
40
|
+
htmlcov/
|
|
41
|
+
|
|
42
|
+
# ---- Node / frontend ----
|
|
43
|
+
node_modules/
|
|
44
|
+
.npm
|
|
45
|
+
npm-debug.log*
|
|
46
|
+
yarn-error.log*
|
|
47
|
+
.pnp.*
|
|
48
|
+
.vite/
|
|
49
|
+
coverage/
|
|
50
|
+
|
|
51
|
+
# ---- Editors / OS junk ----
|
|
52
|
+
.DS_Store
|
|
53
|
+
Thumbs.db
|
|
54
|
+
.idea/
|
|
55
|
+
.vscode/
|
|
56
|
+
*.swp
|
|
57
|
+
|
|
58
|
+
# ---- Agents ----
|
|
59
|
+
.agents/
|
|
60
|
+
.claude/
|
|
61
|
+
docs/
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Collapsarr container image (COL-39).
|
|
2
|
+
#
|
|
3
|
+
# Multi-stage, multi-arch (linux/amd64 + linux/arm64) build:
|
|
4
|
+
# 1. `frontend` — Node stage that builds the Vite/React UI into /frontend/dist.
|
|
5
|
+
# 2. `builder` — Python stage that builds the collapsarr wheel via hatchling,
|
|
6
|
+
# bundling the frontend stage's output into it (COL-40).
|
|
7
|
+
# 3. final — python:3.12-slim + FFmpeg that installs the wheel (UI
|
|
8
|
+
# included) and runs as a PUID/PGID-adjustable user
|
|
9
|
+
# (linuxserver.io convention).
|
|
10
|
+
#
|
|
11
|
+
# Build for one arch locally:
|
|
12
|
+
# docker build -t collapsarr:dev .
|
|
13
|
+
# Build/push multi-arch via buildx:
|
|
14
|
+
# docker buildx build --platform linux/amd64,linux/arm64 -t <repo>/collapsarr:tag --push .
|
|
15
|
+
|
|
16
|
+
# ---- Stage 1: build the frontend ------------------------------------------------
|
|
17
|
+
FROM node:20-slim AS frontend
|
|
18
|
+
WORKDIR /frontend
|
|
19
|
+
|
|
20
|
+
# Install deps against the lockfile first so this layer caches across source edits.
|
|
21
|
+
COPY frontend/package.json frontend/package-lock.json ./
|
|
22
|
+
RUN npm ci
|
|
23
|
+
|
|
24
|
+
COPY frontend/ ./
|
|
25
|
+
RUN npm run build
|
|
26
|
+
|
|
27
|
+
# ---- Stage 2: build the Python wheel -------------------------------------------
|
|
28
|
+
FROM python:3.12-slim AS builder
|
|
29
|
+
WORKDIR /build
|
|
30
|
+
|
|
31
|
+
RUN pip install --no-cache-dir build
|
|
32
|
+
|
|
33
|
+
# pyproject reads README.md (readme) and LICENSE (license-files) at build time.
|
|
34
|
+
# hatch_build.py is the custom build hook that bundles frontend/dist into the
|
|
35
|
+
# wheel as collapsarr/static (COL-40) — it requires frontend/dist to exist,
|
|
36
|
+
# hence copying the frontend stage's output in before building.
|
|
37
|
+
COPY pyproject.toml README.md LICENSE hatch_build.py ./
|
|
38
|
+
COPY collapsarr/ ./collapsarr/
|
|
39
|
+
COPY --from=frontend /frontend/dist ./frontend/dist
|
|
40
|
+
|
|
41
|
+
RUN python -m build --wheel --outdir /dist
|
|
42
|
+
|
|
43
|
+
# ---- Stage 3: runtime image ----------------------------------------------------
|
|
44
|
+
FROM python:3.12-slim
|
|
45
|
+
|
|
46
|
+
# FFmpeg is the core dependency (downmix pipeline); gosu drops privileges to the
|
|
47
|
+
# PUID/PGID user at start-up. --no-install-recommends + apt-list cleanup keep the
|
|
48
|
+
# layer small.
|
|
49
|
+
RUN apt-get update \
|
|
50
|
+
&& apt-get install -y --no-install-recommends ffmpeg gosu \
|
|
51
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
52
|
+
|
|
53
|
+
# Non-root run user (linuxserver.io "abc"); the entrypoint remaps it to the
|
|
54
|
+
# requested PUID/PGID at container start.
|
|
55
|
+
RUN groupadd -g 1000 abc \
|
|
56
|
+
&& useradd -o -m -u 1000 -g abc -d /config -s /usr/sbin/nologin abc
|
|
57
|
+
|
|
58
|
+
# Install the app from the wheel built in the previous stage. The wheel already
|
|
59
|
+
# bundles the built UI as collapsarr/static (COL-40) and FastAPI serves it
|
|
60
|
+
# directly from there — no separate copy of the frontend build needed here.
|
|
61
|
+
COPY --from=builder /dist/*.whl /tmp/
|
|
62
|
+
RUN pip install --no-cache-dir /tmp/*.whl && rm -rf /tmp/*.whl
|
|
63
|
+
|
|
64
|
+
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
|
65
|
+
RUN chmod +x /docker-entrypoint.sh
|
|
66
|
+
|
|
67
|
+
# Runtime configuration. COLLAPSARR_* mirror the app defaults (collapsarr/config.py);
|
|
68
|
+
# PUID/PGID are consumed by the entrypoint.
|
|
69
|
+
ENV COLLAPSARR_HOST=0.0.0.0 \
|
|
70
|
+
COLLAPSARR_PORT=8282 \
|
|
71
|
+
COLLAPSARR_DATABASE_PATH=/config/collapsarr.db \
|
|
72
|
+
PUID=1000 \
|
|
73
|
+
PGID=1000
|
|
74
|
+
|
|
75
|
+
# Persistent data (SQLite DB + config) lives here.
|
|
76
|
+
VOLUME /config
|
|
77
|
+
|
|
78
|
+
EXPOSE 8282
|
|
79
|
+
|
|
80
|
+
# Liveness probe using the stdlib so no extra packages are needed.
|
|
81
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
82
|
+
CMD python -c "import sys,urllib.request; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8282/health').status==200 else 1)"
|
|
83
|
+
|
|
84
|
+
ENTRYPOINT ["/docker-entrypoint.sh"]
|
|
85
|
+
CMD ["collapsarr"]
|