magazarr 0.1.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.
Files changed (42) hide show
  1. magazarr-0.1.0/.github/workflows/PullRequests.yml +70 -0
  2. magazarr-0.1.0/.github/workflows/Release.yml +252 -0
  3. magazarr-0.1.0/.gitignore +18 -0
  4. magazarr-0.1.0/.vscode/launch.json +19 -0
  5. magazarr-0.1.0/.vscode/tasks.json +52 -0
  6. magazarr-0.1.0/LICENSE +21 -0
  7. magazarr-0.1.0/Magazarr.py +9 -0
  8. magazarr-0.1.0/PKG-INFO +127 -0
  9. magazarr-0.1.0/README.md +92 -0
  10. magazarr-0.1.0/assets/magazarr-icon.png +0 -0
  11. magazarr-0.1.0/assets/magazarr-logo.png +0 -0
  12. magazarr-0.1.0/docker/.dockerignore +2 -0
  13. magazarr-0.1.0/docker/Dockerfile +26 -0
  14. magazarr-0.1.0/docker/docker-compose.yml +15 -0
  15. magazarr-0.1.0/magazarr/__init__.py +43 -0
  16. magazarr-0.1.0/magazarr/automation.py +299 -0
  17. magazarr-0.1.0/magazarr/cover.py +45 -0
  18. magazarr-0.1.0/magazarr/db.py +742 -0
  19. magazarr-0.1.0/magazarr/downloads.py +83 -0
  20. magazarr-0.1.0/magazarr/importer.py +179 -0
  21. magazarr-0.1.0/magazarr/notifications.py +112 -0
  22. magazarr-0.1.0/magazarr/opds.py +246 -0
  23. magazarr-0.1.0/magazarr/quasarr_client.py +156 -0
  24. magazarr-0.1.0/magazarr/search.py +232 -0
  25. magazarr-0.1.0/magazarr/server.py +43 -0
  26. magazarr-0.1.0/magazarr/settings.py +90 -0
  27. magazarr-0.1.0/magazarr/utils.py +314 -0
  28. magazarr-0.1.0/magazarr/version.py +9 -0
  29. magazarr-0.1.0/magazarr/web.py +1490 -0
  30. magazarr-0.1.0/pre-commit.py +26 -0
  31. magazarr-0.1.0/pyproject.toml +50 -0
  32. magazarr-0.1.0/tests/test_automation.py +36 -0
  33. magazarr-0.1.0/tests/test_cover.py +33 -0
  34. magazarr-0.1.0/tests/test_importer.py +99 -0
  35. magazarr-0.1.0/tests/test_notifications.py +79 -0
  36. magazarr-0.1.0/tests/test_opds.py +22 -0
  37. magazarr-0.1.0/tests/test_quasarr_client.py +75 -0
  38. magazarr-0.1.0/tests/test_search.py +206 -0
  39. magazarr-0.1.0/tests/test_settings.py +36 -0
  40. magazarr-0.1.0/tests/test_utils.py +88 -0
  41. magazarr-0.1.0/tests/test_web.py +39 -0
  42. magazarr-0.1.0/uv.lock +385 -0
@@ -0,0 +1,70 @@
1
+ name: Pull Requests
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ pull_request:
6
+
7
+ concurrency:
8
+ group: ${{ github.workflow }}-${{ github.ref }}
9
+ cancel-in-progress: true
10
+
11
+ jobs:
12
+ quality-check:
13
+ name: Check
14
+ runs-on: ubuntu-latest
15
+ timeout-minutes: 3
16
+ steps:
17
+ - uses: actions/checkout@v6
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.12"
21
+ - name: Install uv
22
+ uses: astral-sh/setup-uv@v5
23
+ with:
24
+ enable-cache: true
25
+ - name: Run checks
26
+ run: |
27
+ uv sync --group dev
28
+ uv run pre-commit.py --ci
29
+
30
+ build-wheel:
31
+ name: Build Wheel
32
+ runs-on: ubuntu-latest
33
+ timeout-minutes: 3
34
+ needs: quality-check
35
+ steps:
36
+ - uses: actions/checkout@v6
37
+ - uses: actions/setup-python@v5
38
+ with:
39
+ python-version: "3.12"
40
+ - name: Install uv
41
+ uses: astral-sh/setup-uv@v5
42
+ with:
43
+ enable-cache: true
44
+ - name: Build wheel
45
+ run: uv build
46
+ - uses: actions/upload-artifact@v4
47
+ with:
48
+ name: wheel
49
+ path: ./dist/*
50
+
51
+ docker-smoke:
52
+ name: Docker Build Smoke
53
+ runs-on: ubuntu-latest
54
+ timeout-minutes: 5
55
+ needs: build-wheel
56
+ steps:
57
+ - uses: actions/checkout@v6
58
+ - uses: actions/download-artifact@v4
59
+ with:
60
+ name: wheel
61
+ path: ./docker/dist
62
+ - uses: docker/setup-buildx-action@v3
63
+ - name: Build image
64
+ uses: docker/build-push-action@v6
65
+ with:
66
+ context: ./docker
67
+ platforms: linux/amd64
68
+ push: false
69
+ load: true
70
+ tags: magazarr:ci
@@ -0,0 +1,252 @@
1
+ name: Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ skip_pypi:
7
+ default: false
8
+ description: "Skip PyPI upload"
9
+ type: boolean
10
+ push:
11
+ paths-ignore:
12
+ - ".github/**"
13
+ - "docker/**"
14
+ branches:
15
+ - main
16
+
17
+ env:
18
+ PACKAGE_NAME: magazarr
19
+ GHCR_ENDPOINT: ghcr.io/${{ github.repository_owner }}/magazarr
20
+ DOCKERHUB_ENDPOINT: ${{ github.repository_owner }}/docker-magazarr
21
+ DESCRIPTION: "Magazarr manages magazine downloads through Quasarr and exposes OPDS."
22
+
23
+ jobs:
24
+ version:
25
+ name: Get Version
26
+ runs-on: ubuntu-latest
27
+ timeout-minutes: 1
28
+ outputs:
29
+ version: ${{ steps.version.outputs.version }}
30
+ steps:
31
+ - uses: actions/checkout@v6
32
+ - uses: actions/setup-python@v5
33
+ with:
34
+ python-version: "3.12"
35
+ - name: Install uv
36
+ uses: astral-sh/setup-uv@v5
37
+ with:
38
+ enable-cache: true
39
+ - name: Get Version
40
+ id: version
41
+ run: |
42
+ VERSION=$(uv run python magazarr/version.py)
43
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
44
+ echo "Version: $VERSION"
45
+
46
+ build-wheel:
47
+ name: Build Wheel
48
+ runs-on: ubuntu-latest
49
+ timeout-minutes: 3
50
+ outputs:
51
+ attestation-id: ${{ steps.attest.outputs.attestation-id }}
52
+ permissions:
53
+ contents: read
54
+ id-token: write
55
+ attestations: write
56
+ steps:
57
+ - uses: actions/checkout@v6
58
+ - uses: actions/setup-python@v5
59
+ with:
60
+ python-version: "3.12"
61
+ - name: Install uv
62
+ uses: astral-sh/setup-uv@v5
63
+ with:
64
+ enable-cache: true
65
+ - name: Run checks
66
+ run: |
67
+ uv sync --group dev
68
+ uv run pre-commit.py --ci
69
+ - name: Build wheel
70
+ run: uv build
71
+ - name: Generate artifact attestation
72
+ id: attest
73
+ uses: actions/attest-build-provenance@v2
74
+ with:
75
+ subject-path: "dist/*.whl"
76
+ - uses: actions/upload-artifact@v4
77
+ with:
78
+ name: wheel
79
+ path: ./dist/*
80
+
81
+ build-docker-amd64:
82
+ name: Build Docker (AMD64)
83
+ runs-on: ubuntu-latest
84
+ timeout-minutes: 5
85
+ needs: [version, build-wheel]
86
+ permissions:
87
+ contents: read
88
+ packages: write
89
+ steps:
90
+ - uses: actions/checkout@v6
91
+ - uses: actions/download-artifact@v4
92
+ with:
93
+ name: wheel
94
+ path: ./docker/dist
95
+ - uses: docker/setup-buildx-action@v3
96
+ - uses: docker/login-action@v3
97
+ with:
98
+ registry: ghcr.io
99
+ username: ${{ github.actor }}
100
+ password: ${{ secrets.GITHUB_TOKEN }}
101
+ - uses: docker/login-action@v3
102
+ with:
103
+ username: ${{ secrets.DOCKERUSER }}
104
+ password: ${{ secrets.DOCKERPASS }}
105
+ - name: Build and Push
106
+ uses: docker/build-push-action@v6
107
+ with:
108
+ context: ./docker
109
+ platforms: linux/amd64
110
+ push: true
111
+ provenance: false
112
+ sbom: false
113
+ annotations: |
114
+ org.opencontainers.image.description=${{ env.DESCRIPTION }}
115
+ tags: |
116
+ ${{ env.GHCR_ENDPOINT }}:latest-amd64
117
+ ${{ env.GHCR_ENDPOINT }}:${{ needs.version.outputs.version }}-amd64
118
+ ${{ env.DOCKERHUB_ENDPOINT }}:latest-amd64
119
+ ${{ env.DOCKERHUB_ENDPOINT }}:${{ needs.version.outputs.version }}-amd64
120
+ cache-from: type=gha,scope=amd64
121
+ cache-to: type=gha,mode=max,scope=amd64
122
+
123
+ build-docker-arm64:
124
+ name: Build Docker (ARM64)
125
+ runs-on: ubuntu-24.04-arm
126
+ timeout-minutes: 5
127
+ needs: [version, build-wheel]
128
+ permissions:
129
+ contents: read
130
+ packages: write
131
+ steps:
132
+ - uses: actions/checkout@v6
133
+ - uses: actions/download-artifact@v4
134
+ with:
135
+ name: wheel
136
+ path: ./docker/dist
137
+ - uses: docker/setup-buildx-action@v3
138
+ - uses: docker/login-action@v3
139
+ with:
140
+ registry: ghcr.io
141
+ username: ${{ github.actor }}
142
+ password: ${{ secrets.GITHUB_TOKEN }}
143
+ - uses: docker/login-action@v3
144
+ with:
145
+ username: ${{ secrets.DOCKERUSER }}
146
+ password: ${{ secrets.DOCKERPASS }}
147
+ - name: Build and Push
148
+ uses: docker/build-push-action@v6
149
+ with:
150
+ context: ./docker
151
+ platforms: linux/arm64
152
+ push: true
153
+ provenance: false
154
+ sbom: false
155
+ annotations: |
156
+ org.opencontainers.image.description=${{ env.DESCRIPTION }}
157
+ tags: |
158
+ ${{ env.GHCR_ENDPOINT }}:latest-arm64
159
+ ${{ env.GHCR_ENDPOINT }}:${{ needs.version.outputs.version }}-arm64
160
+ ${{ env.DOCKERHUB_ENDPOINT }}:latest-arm64
161
+ ${{ env.DOCKERHUB_ENDPOINT }}:${{ needs.version.outputs.version }}-arm64
162
+ cache-from: type=gha,scope=arm64
163
+ cache-to: type=gha,mode=max,scope=arm64
164
+
165
+ merge-docker-manifest:
166
+ name: Merge Docker Manifests
167
+ runs-on: ubuntu-latest
168
+ timeout-minutes: 2
169
+ needs: [version, build-docker-amd64, build-docker-arm64]
170
+ permissions:
171
+ packages: write
172
+ steps:
173
+ - uses: docker/setup-buildx-action@v3
174
+ - uses: docker/login-action@v3
175
+ with:
176
+ registry: ghcr.io
177
+ username: ${{ github.actor }}
178
+ password: ${{ secrets.GITHUB_TOKEN }}
179
+ - uses: docker/login-action@v3
180
+ with:
181
+ username: ${{ secrets.DOCKERUSER }}
182
+ password: ${{ secrets.DOCKERPASS }}
183
+ - name: Create and Push Manifests
184
+ run: |
185
+ TAG_AMD64="${{ needs.version.outputs.version }}-amd64"
186
+ TAG_ARM64="${{ needs.version.outputs.version }}-arm64"
187
+ ANNOTATION="index:org.opencontainers.image.description=$DESCRIPTION"
188
+ docker buildx imagetools create -t ${{ env.GHCR_ENDPOINT }}:latest --annotation "$ANNOTATION" ${{ env.GHCR_ENDPOINT }}:latest-amd64 ${{ env.GHCR_ENDPOINT }}:latest-arm64
189
+ docker buildx imagetools create -t ${{ env.GHCR_ENDPOINT }}:${{ needs.version.outputs.version }} --annotation "$ANNOTATION" ${{ env.GHCR_ENDPOINT }}:${TAG_AMD64} ${{ env.GHCR_ENDPOINT }}:${TAG_ARM64}
190
+ docker buildx imagetools create -t ${{ env.DOCKERHUB_ENDPOINT }}:latest ${{ env.DOCKERHUB_ENDPOINT }}:latest-amd64 ${{ env.DOCKERHUB_ENDPOINT }}:latest-arm64
191
+ docker buildx imagetools create -t ${{ env.DOCKERHUB_ENDPOINT }}:${{ needs.version.outputs.version }} ${{ env.DOCKERHUB_ENDPOINT }}:${TAG_AMD64} ${{ env.DOCKERHUB_ENDPOINT }}:${TAG_ARM64}
192
+
193
+ release:
194
+ name: Create Release
195
+ runs-on: ubuntu-latest
196
+ timeout-minutes: 2
197
+ needs: [version, build-wheel, merge-docker-manifest]
198
+ permissions:
199
+ contents: write
200
+ steps:
201
+ - uses: actions/checkout@v6
202
+ with:
203
+ fetch-depth: 0
204
+ - uses: actions/download-artifact@v4
205
+ with:
206
+ name: wheel
207
+ path: ./wheel
208
+ - name: Install uv
209
+ uses: astral-sh/setup-uv@v5
210
+ with:
211
+ enable-cache: true
212
+ - name: Publish to PyPI
213
+ if: ${{ !inputs.skip_pypi }}
214
+ run: uv publish ./wheel/* --token ${{ secrets.PYPI_TOKEN }}
215
+ - name: Generate changelog
216
+ id: changelog
217
+ uses: metcalfc/changelog-generator@v4.6.2
218
+ with:
219
+ myToken: ${{ secrets.GITHUB_TOKEN }}
220
+ - name: Create Release Body
221
+ run: |
222
+ echo "### Docker:" > release_body.md
223
+ echo "\`docker pull ${{ env.GHCR_ENDPOINT }}:latest\`" >> release_body.md
224
+ echo "### Python:" >> release_body.md
225
+ echo "\`uv tool upgrade magazarr\`" >> release_body.md
226
+ echo "### Changelog:" >> release_body.md
227
+ echo "${{ steps.changelog.outputs.changelog }}" >> release_body.md
228
+ echo "[Attestation](https://github.com/${{ github.repository }}/attestations/${{ needs.build-wheel.outputs.attestation-id }})" >> release_body.md
229
+ - name: Create Release
230
+ uses: ncipollo/release-action@v1
231
+ with:
232
+ artifacts: "./wheel/*.whl"
233
+ artifactErrorsFailBuild: true
234
+ bodyFile: "release_body.md"
235
+ tag: v.${{ needs.version.outputs.version }}
236
+
237
+ job-summary:
238
+ name: Build Report
239
+ runs-on: ubuntu-latest
240
+ timeout-minutes: 1
241
+ needs: [version, build-wheel, build-docker-amd64, build-docker-arm64, release]
242
+ if: always()
243
+ steps:
244
+ - name: Generate Job Summary
245
+ run: |
246
+ echo "## Release Production Report" >> $GITHUB_STEP_SUMMARY
247
+ echo "| Artifact | Status |" >> $GITHUB_STEP_SUMMARY
248
+ echo "| --- | --- |" >> $GITHUB_STEP_SUMMARY
249
+ echo "| Python Wheel | ${{ needs.build-wheel.result == 'success' && 'OK' || 'FAILED' }} |" >> $GITHUB_STEP_SUMMARY
250
+ echo "| Docker AMD64 | ${{ needs.build-docker-amd64.result == 'success' && 'OK' || 'FAILED' }} |" >> $GITHUB_STEP_SUMMARY
251
+ echo "| Docker ARM64 | ${{ needs.build-docker-arm64.result == 'success' && 'OK' || 'FAILED' }} |" >> $GITHUB_STEP_SUMMARY
252
+ echo "| GitHub Release | ${{ needs.release.result == 'success' && 'Published' || 'FAILED' }} |" >> $GITHUB_STEP_SUMMARY
@@ -0,0 +1,18 @@
1
+ .venv/
2
+ .pytest_cache/
3
+ .ruff_cache/
4
+ __pycache__/
5
+ *.py[cod]
6
+ dist/
7
+ docker/dist/
8
+
9
+ # Local runtime and imported media. Never commit database or library content.
10
+ /data/
11
+ /library/
12
+ /downloads/
13
+ /imports/
14
+ *.db
15
+ *.sqlite
16
+ *.sqlite3
17
+
18
+ .DS_Store
@@ -0,0 +1,19 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "name": "Magazarr",
6
+ "type": "debugpy",
7
+ "request": "launch",
8
+ "program": "${workspaceFolder}/Magazarr.py",
9
+ "console": "integratedTerminal",
10
+ "consoleTitle": "Magazarr",
11
+ "env": {
12
+ "PORT": "8090",
13
+ "LISTEN": "127.0.0.1",
14
+ "MAGAZARR_CONFIG_DIR": "${workspaceFolder}/data"
15
+ },
16
+ "justMyCode": true
17
+ }
18
+ ]
19
+ }
@@ -0,0 +1,52 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "tasks": [
4
+ {
5
+ "label": "Setup",
6
+ "type": "shell",
7
+ "command": "uv sync --group dev --group build",
8
+ "problemMatcher": []
9
+ },
10
+ {
11
+ "label": "Run Magazarr",
12
+ "type": "shell",
13
+ "command": "uv run magazarr",
14
+ "options": {
15
+ "env": {
16
+ "PORT": "8090",
17
+ "LISTEN": "127.0.0.1",
18
+ "MAGAZARR_CONFIG_DIR": "${workspaceFolder}/data"
19
+ }
20
+ },
21
+ "isBackground": true,
22
+ "problemMatcher": []
23
+ },
24
+ {
25
+ "label": "Checks",
26
+ "type": "shell",
27
+ "command": "uv run pre-commit.py --ci",
28
+ "group": "test",
29
+ "problemMatcher": []
30
+ },
31
+ {
32
+ "label": "Build Wheel",
33
+ "type": "shell",
34
+ "command": "uv build",
35
+ "group": "build",
36
+ "problemMatcher": []
37
+ },
38
+ {
39
+ "label": "Build Docker Local",
40
+ "type": "shell",
41
+ "command": "uv build && mkdir -p docker/dist && cp dist/*.whl docker/dist/ && docker build -t magazarr:local docker",
42
+ "group": "build",
43
+ "problemMatcher": []
44
+ },
45
+ {
46
+ "label": "Clean Generated",
47
+ "type": "shell",
48
+ "command": "rm -rf .venv .pytest_cache .ruff_cache dist docker/dist magazarr/__pycache__ tests/__pycache__",
49
+ "problemMatcher": []
50
+ }
51
+ ]
52
+ }
magazarr-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 RiX
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,9 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import multiprocessing
4
+
5
+ import magazarr
6
+
7
+ if __name__ == "__main__":
8
+ multiprocessing.freeze_support()
9
+ magazarr.run()
@@ -0,0 +1,127 @@
1
+ Metadata-Version: 2.4
2
+ Name: magazarr
3
+ Version: 0.1.0
4
+ Summary: Tiny magazine manager for Quasarr with OPDS output.
5
+ Author: RiX
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 RiX
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ License-File: LICENSE
28
+ Requires-Python: >=3.12
29
+ Requires-Dist: bottle>=0.13.4
30
+ Requires-Dist: loguru>=0.7.3
31
+ Requires-Dist: pymupdf>=1.24.0
32
+ Requires-Dist: python-dotenv>=1.2.1
33
+ Requires-Dist: requests>=2.32.5
34
+ Description-Content-Type: text/markdown
35
+
36
+ <p align="center">
37
+ <img src="assets/magazarr-logo.png" alt="Magazarr" width="520">
38
+ </p>
39
+
40
+ Magazarr is a small magazine-only companion for Quasarr.
41
+
42
+ It keeps a free-text magazine list, searches Quasarr for recent issues, sends chosen releases back to Quasarr for download, imports the largest PDF from completed JDownloader folders, and exposes the library through OPDS.
43
+
44
+ Magazarr automatically searches active titles every 60 minutes by default and checks completed downloads for import every 5 minutes by default. Set either interval to `0` to disable that background task.
45
+
46
+ ## Run
47
+
48
+ ```bash
49
+ uv run magazarr
50
+ ```
51
+
52
+ Open `http://127.0.0.1:8090`.
53
+
54
+ ## Docker
55
+
56
+ Local build:
57
+
58
+ ```bash
59
+ uv build
60
+ mkdir -p docker/dist
61
+ cp dist/*.whl docker/dist/
62
+ docker build -t magazarr:local docker
63
+ ```
64
+
65
+ Local run:
66
+
67
+ ```bash
68
+ docker run --rm \
69
+ -p 8090:8090 \
70
+ -v "$PWD/config:/config" \
71
+ -v "$PWD/library:/library" \
72
+ -v "$PWD/output:/output" \
73
+ magazarr:local
74
+ ```
75
+
76
+ Compose template lives at `docker/docker-compose.yml`. Replace `ghcr.io/your-github-user/magazarr:latest` with your published image.
77
+
78
+ ## Required Settings
79
+
80
+ - Quasarr URL and API key.
81
+ - JDownloader import root. If JDownloader runs in a separate Docker container, its default completed-download path is usually `/output`. Mount the same host download volume into Magazarr at the same path and set the import root to `/output`.
82
+ - Completed package folders reported by Quasarr must be inside the import root before Magazarr imports the PDF and deletes the completed package subfolder. If a completed PDF is directly in the import root, Magazarr deletes only that file after import and keeps the root folder.
83
+ - Library directory for imported PDFs.
84
+
85
+ ## Run Quasarr Locally For Testing
86
+
87
+ In a second terminal:
88
+
89
+ ```bash
90
+ cd ~/PythonProjects/Quasarr
91
+ INTERNAL_ADDRESS=http://127.0.0.1:8080 uv run quasarr
92
+ ```
93
+
94
+ Use the API key printed by Quasarr in Magazarr settings:
95
+
96
+ - Quasarr URL: `http://127.0.0.1:8080`
97
+ - Search category: `7000`
98
+ - Download category: `docs`
99
+
100
+ Quasarr still needs working JDownloader credentials and at least one configured magazine-capable hostname before a real download can complete.
101
+
102
+ ## Quasarr Integration
103
+
104
+ Magazarr uses the Quasarr Newznab/SABnzbd shim:
105
+
106
+ - Search: `GET /api?t=search&q=<title>&cat=7000&apikey=<key>`
107
+ - Download: `GET /api?mode=addurl&name=<quasarr download link>&cat=docs&apikey=<key>`
108
+ - Import status: `GET /api?mode=history&apikey=<key>`
109
+
110
+ The user agent is intentionally `lazylibrarian` because current Quasarr accepts generic document searches from that client string.
111
+
112
+ ## OPDS
113
+
114
+ OPDS root is:
115
+
116
+ ```text
117
+ http://127.0.0.1:8090/opds
118
+ ```
119
+
120
+ Optional basic auth applies only to OPDS routes.
121
+
122
+ Imported PDF issue entries expose a cover image link. Magazarr renders page 1 of the
123
+ PDF as a cached PNG on first cover request.
124
+
125
+ ## License
126
+
127
+ MIT License. Copyright (c) 2026 RiX.
@@ -0,0 +1,92 @@
1
+ <p align="center">
2
+ <img src="assets/magazarr-logo.png" alt="Magazarr" width="520">
3
+ </p>
4
+
5
+ Magazarr is a small magazine-only companion for Quasarr.
6
+
7
+ It keeps a free-text magazine list, searches Quasarr for recent issues, sends chosen releases back to Quasarr for download, imports the largest PDF from completed JDownloader folders, and exposes the library through OPDS.
8
+
9
+ Magazarr automatically searches active titles every 60 minutes by default and checks completed downloads for import every 5 minutes by default. Set either interval to `0` to disable that background task.
10
+
11
+ ## Run
12
+
13
+ ```bash
14
+ uv run magazarr
15
+ ```
16
+
17
+ Open `http://127.0.0.1:8090`.
18
+
19
+ ## Docker
20
+
21
+ Local build:
22
+
23
+ ```bash
24
+ uv build
25
+ mkdir -p docker/dist
26
+ cp dist/*.whl docker/dist/
27
+ docker build -t magazarr:local docker
28
+ ```
29
+
30
+ Local run:
31
+
32
+ ```bash
33
+ docker run --rm \
34
+ -p 8090:8090 \
35
+ -v "$PWD/config:/config" \
36
+ -v "$PWD/library:/library" \
37
+ -v "$PWD/output:/output" \
38
+ magazarr:local
39
+ ```
40
+
41
+ Compose template lives at `docker/docker-compose.yml`. Replace `ghcr.io/your-github-user/magazarr:latest` with your published image.
42
+
43
+ ## Required Settings
44
+
45
+ - Quasarr URL and API key.
46
+ - JDownloader import root. If JDownloader runs in a separate Docker container, its default completed-download path is usually `/output`. Mount the same host download volume into Magazarr at the same path and set the import root to `/output`.
47
+ - Completed package folders reported by Quasarr must be inside the import root before Magazarr imports the PDF and deletes the completed package subfolder. If a completed PDF is directly in the import root, Magazarr deletes only that file after import and keeps the root folder.
48
+ - Library directory for imported PDFs.
49
+
50
+ ## Run Quasarr Locally For Testing
51
+
52
+ In a second terminal:
53
+
54
+ ```bash
55
+ cd ~/PythonProjects/Quasarr
56
+ INTERNAL_ADDRESS=http://127.0.0.1:8080 uv run quasarr
57
+ ```
58
+
59
+ Use the API key printed by Quasarr in Magazarr settings:
60
+
61
+ - Quasarr URL: `http://127.0.0.1:8080`
62
+ - Search category: `7000`
63
+ - Download category: `docs`
64
+
65
+ Quasarr still needs working JDownloader credentials and at least one configured magazine-capable hostname before a real download can complete.
66
+
67
+ ## Quasarr Integration
68
+
69
+ Magazarr uses the Quasarr Newznab/SABnzbd shim:
70
+
71
+ - Search: `GET /api?t=search&q=<title>&cat=7000&apikey=<key>`
72
+ - Download: `GET /api?mode=addurl&name=<quasarr download link>&cat=docs&apikey=<key>`
73
+ - Import status: `GET /api?mode=history&apikey=<key>`
74
+
75
+ The user agent is intentionally `lazylibrarian` because current Quasarr accepts generic document searches from that client string.
76
+
77
+ ## OPDS
78
+
79
+ OPDS root is:
80
+
81
+ ```text
82
+ http://127.0.0.1:8090/opds
83
+ ```
84
+
85
+ Optional basic auth applies only to OPDS routes.
86
+
87
+ Imported PDF issue entries expose a cover image link. Magazarr renders page 1 of the
88
+ PDF as a cached PNG on first cover request.
89
+
90
+ ## License
91
+
92
+ MIT License. Copyright (c) 2026 RiX.
Binary file
Binary file
@@ -0,0 +1,2 @@
1
+ __pycache__/
2
+ *.pyc