ownfoil 2.4.0.dev1__tar.gz → 2.4.2__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 (197) hide show
  1. ownfoil-2.4.2/.github/workflows/build_check.yml +20 -0
  2. ownfoil-2.4.2/.github/workflows/fixed_comment.yml +19 -0
  3. ownfoil-2.4.2/.github/workflows/region_titles.yml +136 -0
  4. ownfoil-2.4.2/.github/workflows/release_close_fixed.yml +24 -0
  5. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/.github/workflows/stale.yml +1 -1
  6. ownfoil-2.4.2/.github/workflows/test.yml +37 -0
  7. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/.gitignore +3 -0
  8. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/Dockerfile +1 -1
  9. ownfoil-2.4.2/Install.md +208 -0
  10. ownfoil-2.4.2/PKG-INFO +79 -0
  11. ownfoil-2.4.2/README.md +45 -0
  12. ownfoil-2.4.2/Usage.md +311 -0
  13. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/app.py +97 -189
  14. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/auth.py +2 -2
  15. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/clients/__init__.py +2 -2
  16. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/clients/sphaira.py +20 -63
  17. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/constants.py +17 -7
  18. ownfoil-2.4.2/app/containers/__init__.py +8 -0
  19. ownfoil-2.4.2/app/containers/cnmt.py +67 -0
  20. ownfoil-2.4.0.dev1/app/file_compression.py → ownfoil-2.4.2/app/containers/compression.py +8 -17
  21. ownfoil-2.4.2/app/containers/container.py +28 -0
  22. ownfoil-2.4.2/app/containers/verification.py +161 -0
  23. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/db.py +129 -21
  24. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/file_watcher.py +22 -8
  25. ownfoil-2.4.2/app/gql/README.md +550 -0
  26. ownfoil-2.4.2/app/gql/__init__.py +5 -0
  27. ownfoil-2.4.2/app/gql/cache.py +77 -0
  28. ownfoil-2.4.2/app/gql/context.py +40 -0
  29. ownfoil-2.4.2/app/gql/docs.py +44 -0
  30. ownfoil-2.4.2/app/gql/filters.py +665 -0
  31. ownfoil-2.4.2/app/gql/mutations.py +238 -0
  32. ownfoil-2.4.2/app/gql/resolvers.py +1174 -0
  33. ownfoil-2.4.2/app/gql/scalars.py +15 -0
  34. ownfoil-2.4.2/app/gql/schema.py +227 -0
  35. ownfoil-2.4.2/app/gql/selection.py +43 -0
  36. ownfoil-2.4.2/app/gql/types.py +557 -0
  37. ownfoil-2.4.2/app/gql/view.py +111 -0
  38. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/library.py +34 -208
  39. ownfoil-2.4.2/app/local.py +76 -0
  40. ownfoil-2.4.2/app/migrations/versions/a7b8c9d0e1f2_add_title_overrides.py +61 -0
  41. ownfoil-2.4.2/app/migrations/versions/b8c9d0e1f2a3_add_added_at_to_files.py +29 -0
  42. ownfoil-2.4.2/app/migrations/versions/c9d0e1f2a3b4_add_verification_to_files.py +37 -0
  43. ownfoil-2.4.2/app/migrations/versions/e5f6a7b8c9d0_add_release_date_to_apps.py +25 -0
  44. ownfoil-2.4.2/app/migrations/versions/f6a7b8c9d0e1_add_app_files_file_id_index.py +25 -0
  45. ownfoil-2.4.2/app/realtime.py +233 -0
  46. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/run.py +5 -1
  47. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/settings.py +7 -0
  48. ownfoil-2.4.2/app/static/js/format.js +9 -0
  49. ownfoil-2.4.2/app/static/js/realtime.js +87 -0
  50. ownfoil-2.4.2/app/static/style.css +411 -0
  51. ownfoil-2.4.2/app/task_events.py +150 -0
  52. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/tasks.py +304 -182
  53. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/templates/index.html +316 -265
  54. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/templates/nav.html +1 -1
  55. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/templates/settings.html +151 -93
  56. ownfoil-2.4.2/app/templates/settings_layout.html +33 -0
  57. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/templates/setup.html +2 -6
  58. ownfoil-2.4.2/app/templates/stats.html +169 -0
  59. ownfoil-2.4.2/app/templates/tasks.html +467 -0
  60. ownfoil-2.4.2/app/titledb/__init__.py +9 -0
  61. ownfoil-2.4.2/app/titledb/schema.py +142 -0
  62. ownfoil-2.4.2/app/titledb/store.py +616 -0
  63. ownfoil-2.4.2/app/titledb/update.py +106 -0
  64. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/titles.py +22 -84
  65. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/utils.py +83 -27
  66. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/worker.py +5 -1
  67. ownfoil-2.4.2/ownfoil/__init__.py +0 -0
  68. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/ownfoil/cli.py +14 -1
  69. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/pyproject.toml +7 -9
  70. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/requirements.txt +5 -3
  71. ownfoil-2.4.2/tests/capture/README.md +47 -0
  72. ownfoil-2.4.2/tests/capture/fixture.py +105 -0
  73. ownfoil-2.4.2/tests/capture/run_capture.py +128 -0
  74. ownfoil-2.4.2/tests/capture/sanitize.py +137 -0
  75. ownfoil-2.4.2/tests/capture/scenarios.py +150 -0
  76. ownfoil-2.4.2/tests/capture/server.py +217 -0
  77. ownfoil-2.4.2/tests/captures/cyberfoil/admin-browse.json +537 -0
  78. ownfoil-2.4.2/tests/captures/cyberfoil/authenticated-browse.json +537 -0
  79. ownfoil-2.4.2/tests/captures/cyberfoil/client-disabled.json +275 -0
  80. ownfoil-2.4.2/tests/captures/cyberfoil/content-filter.json +375 -0
  81. ownfoil-2.4.2/tests/captures/cyberfoil/download-repeat.json +243 -0
  82. ownfoil-2.4.2/tests/captures/cyberfoil/download.json +833 -0
  83. ownfoil-2.4.2/tests/captures/cyberfoil/free-browsing.json +46 -0
  84. ownfoil-2.4.2/tests/captures/cyberfoil/no-shop-access.json +275 -0
  85. ownfoil-2.4.2/tests/captures/cyberfoil/private-no-credentials.json +263 -0
  86. ownfoil-2.4.2/tests/captures/cyberfoil/public-browse.json +517 -0
  87. ownfoil-2.4.2/tests/captures/cyberfoil/unknown-user.json +275 -0
  88. ownfoil-2.4.2/tests/captures/cyberfoil/wrong-password.json +275 -0
  89. ownfoil-2.4.2/tests/captures/sphaira/admin-browse.json +97 -0
  90. ownfoil-2.4.2/tests/captures/sphaira/authenticated-browse.json +97 -0
  91. ownfoil-2.4.2/tests/captures/sphaira/client-disabled.json +97 -0
  92. ownfoil-2.4.2/tests/captures/sphaira/content-filter.json +97 -0
  93. ownfoil-2.4.2/tests/captures/sphaira/download-repeat.json +195 -0
  94. ownfoil-2.4.2/tests/captures/sphaira/download.json +297 -0
  95. ownfoil-2.4.2/tests/captures/sphaira/free-browsing.json +673 -0
  96. ownfoil-2.4.2/tests/captures/sphaira/no-shop-access.json +97 -0
  97. ownfoil-2.4.2/tests/captures/sphaira/private-no-credentials.json +93 -0
  98. ownfoil-2.4.2/tests/captures/sphaira/public-browse.json +93 -0
  99. ownfoil-2.4.2/tests/captures/sphaira/unknown-user.json +97 -0
  100. ownfoil-2.4.2/tests/captures/sphaira/wrong-password.json +97 -0
  101. ownfoil-2.4.2/tests/captures/tinfoil/admin-browse.json +127 -0
  102. ownfoil-2.4.2/tests/captures/tinfoil/authenticated-browse.json +127 -0
  103. ownfoil-2.4.2/tests/captures/tinfoil/client-disabled.json +127 -0
  104. ownfoil-2.4.2/tests/captures/tinfoil/content-filter.json +127 -0
  105. ownfoil-2.4.2/tests/captures/tinfoil/download-repeat.json +125 -0
  106. ownfoil-2.4.2/tests/captures/tinfoil/download.json +127 -0
  107. ownfoil-2.4.2/tests/captures/tinfoil/free-browsing.json +437 -0
  108. ownfoil-2.4.2/tests/captures/tinfoil/no-shop-access.json +127 -0
  109. ownfoil-2.4.2/tests/captures/tinfoil/private-no-credentials.json +123 -0
  110. ownfoil-2.4.2/tests/captures/tinfoil/public-browse.json +123 -0
  111. ownfoil-2.4.2/tests/captures/tinfoil/tinfoil-plaintext.json +133 -0
  112. ownfoil-2.4.2/tests/captures/tinfoil/unknown-user.json +127 -0
  113. ownfoil-2.4.2/tests/captures/tinfoil/wrong-password.json +127 -0
  114. ownfoil-2.4.2/tests/conftest.py +69 -0
  115. ownfoil-2.4.2/tests/test_clients.py +592 -0
  116. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/tests/test_compression.py +221 -100
  117. ownfoil-2.4.2/tests/test_containers.py +66 -0
  118. ownfoil-2.4.2/tests/test_db_paths.py +31 -0
  119. ownfoil-2.4.2/tests/test_download_count.py +109 -0
  120. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/tests/test_file_watcher.py +122 -3
  121. ownfoil-2.4.2/tests/test_first_run.py +134 -0
  122. ownfoil-2.4.2/tests/test_gql_app_cards.py +212 -0
  123. ownfoil-2.4.2/tests/test_gql_catalogue.py +160 -0
  124. ownfoil-2.4.2/tests/test_gql_etag.py +178 -0
  125. ownfoil-2.4.2/tests/test_gql_graph.py +755 -0
  126. ownfoil-2.4.2/tests/test_gql_mutations.py +257 -0
  127. ownfoil-2.4.2/tests/test_gql_schema_docs.py +58 -0
  128. ownfoil-2.4.2/tests/test_gql_verification_status.py +174 -0
  129. ownfoil-2.4.2/tests/test_lan_ip.py +99 -0
  130. ownfoil-2.4.2/tests/test_library_files.py +37 -0
  131. ownfoil-2.4.2/tests/test_network_path.py +67 -0
  132. ownfoil-2.4.2/tests/test_open_browser.py +77 -0
  133. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/tests/test_organizer_paths.py +13 -1
  134. ownfoil-2.4.2/tests/test_realtime.py +230 -0
  135. ownfoil-2.4.2/tests/test_secret_key.py +38 -0
  136. ownfoil-2.4.2/tests/test_task_display_names.py +60 -0
  137. ownfoil-2.4.2/tests/test_task_events.py +321 -0
  138. ownfoil-2.4.2/tests/test_titledb_lifecycle.py +402 -0
  139. ownfoil-2.4.2/tests/test_titledb_schedule.py +75 -0
  140. ownfoil-2.4.2/tests/test_titledb_update.py +188 -0
  141. ownfoil-2.4.2/tests/test_verification.py +447 -0
  142. ownfoil-2.4.0.dev1/.github/workflows/region_titles.yml +0 -73
  143. ownfoil-2.4.0.dev1/PKG-INFO +0 -221
  144. ownfoil-2.4.0.dev1/README.md +0 -189
  145. ownfoil-2.4.0.dev1/app/local.py +0 -42
  146. ownfoil-2.4.0.dev1/app/shop.py +0 -52
  147. ownfoil-2.4.0.dev1/app/static/style.css +0 -125
  148. ownfoil-2.4.0.dev1/app/titledb.py +0 -100
  149. ownfoil-2.4.0.dev1/app/titledb_store.py +0 -533
  150. ownfoil-2.4.0.dev1/ownfoil/__init__.py +0 -1
  151. ownfoil-2.4.0.dev1/tests/conftest.py +0 -11
  152. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/.devcontainer/devcontainer.json +0 -0
  153. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/.devcontainer/post-create.sh +0 -0
  154. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/.github/dependabot.yml +0 -0
  155. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/.github/release.yml +0 -0
  156. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/.github/workflows/docker.yml +0 -0
  157. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/.github/workflows/docker_pr.yml +0 -0
  158. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/.github/workflows/pypi.yml +0 -0
  159. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/LICENSE.md +0 -0
  160. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/clients/client.py +0 -0
  161. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/clients/cyberfoil.py +0 -0
  162. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/clients/tinfoil.py +0 -0
  163. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/migrations/README +0 -0
  164. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/migrations/alembic.ini +0 -0
  165. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/migrations/env.py +0 -0
  166. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/migrations/script.py.mako +0 -0
  167. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/migrations/versions/78c33e9bffce_drop_old_files_table_and_create_new_.py +0 -0
  168. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/migrations/versions/a1b2c3d4e5f6_add_tasks_and_ignored_events.py +0 -0
  169. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/migrations/versions/b2c3d4e5f6a7_add_organized_column_to_files.py +0 -0
  170. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/migrations/versions/c3d4e5f6a7b8_add_worker_id_to_tasks.py +0 -0
  171. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/migrations/versions/d4e5f6a7b8c9_add_temp_files.py +0 -0
  172. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/static/favicon/apple-touch-icon.png +0 -0
  173. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/static/favicon/favicon-96x96.png +0 -0
  174. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/static/favicon/favicon.ico +0 -0
  175. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/static/favicon/favicon.svg +0 -0
  176. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/static/favicon/site.webmanifest +0 -0
  177. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/static/favicon/web-app-manifest-192x192.png +0 -0
  178. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/static/favicon/web-app-manifest-512x512.png +0 -0
  179. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/static/logo/logo-1024.png +0 -0
  180. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/static/logo/logo-128.png +0 -0
  181. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/static/logo/logo-256.png +0 -0
  182. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/static/logo/logo-512.png +0 -0
  183. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/static/logo/logo-64.png +0 -0
  184. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/static/logo/logo.svg +0 -0
  185. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/templates/base.html +0 -0
  186. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/templates/login.html +0 -0
  187. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/templates/profile.html +0 -0
  188. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/app/worker_pool.py +0 -0
  189. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/chart/Chart.yaml +0 -0
  190. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/chart/readme.md +0 -0
  191. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/chart/templates/deploy.yaml +0 -0
  192. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/chart/templates/ingress.yaml +0 -0
  193. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/chart/templates/pvc.yaml +0 -0
  194. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/chart/templates/services.yaml +0 -0
  195. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/chart/values.yaml +0 -0
  196. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/docker/run.sh +0 -0
  197. {ownfoil-2.4.0.dev1 → ownfoil-2.4.2}/docker-compose.yml +0 -0
@@ -0,0 +1,20 @@
1
+ name: Validate Python package build
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - '**'
7
+
8
+ jobs:
9
+ build:
10
+ name: uv build
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Checkout
14
+ uses: actions/checkout@v7
15
+
16
+ - name: Install uv
17
+ uses: astral-sh/setup-uv@v7
18
+
19
+ - name: Build package
20
+ run: uv build
@@ -0,0 +1,19 @@
1
+ name: 'Comment on fixed issues'
2
+ on:
3
+ issues:
4
+ types: [labeled]
5
+
6
+ jobs:
7
+ comment:
8
+ if: github.event.label.name == 'fixed'
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ issues: write
12
+ steps:
13
+ - name: Comment on issue
14
+ env:
15
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
16
+ GH_REPO: ${{ github.repository }}
17
+ ISSUE: ${{ github.event.issue.number }}
18
+ run: |
19
+ gh issue comment "$ISSUE" --body 'This issue is fixed in the `develop` branch/image, you can try it following the [installation instructions](https://github.com/a1ex4/ownfoil/blob/develop/Install.md#available-versions). The issue will be closed when the next release is published.'
@@ -0,0 +1,136 @@
1
+ # This is a basic workflow to help you get started with Actions
2
+
3
+ name: Build titledb
4
+
5
+ # Controls when the workflow will run
6
+ on:
7
+ schedule:
8
+ - cron: '0 */4 * * *'
9
+ # Allows you to run this workflow manually from the Actions tab
10
+ workflow_dispatch:
11
+ inputs:
12
+ force:
13
+ description: 'Rebuild even if blawar/titledb has not moved'
14
+ type: boolean
15
+ default: false
16
+
17
+ # A workflow run is made up of one or more jobs that can run sequentially or in parallel
18
+ jobs:
19
+ # This workflow contains a single job called "build"
20
+ build:
21
+ # The type of runner that the job will run on
22
+ runs-on: ubuntu-latest
23
+
24
+ permissions:
25
+ contents: write
26
+ actions: write
27
+
28
+ # Steps represent a sequence of tasks that will be executed as part of the job
29
+ steps:
30
+ # A no-op run must not conclude as a success: nightly.link serves the latest *successful*
31
+ # run's artifact to pre-release Ownfoil versions, so it has to keep pointing at a run that
32
+ # actually uploaded one. Cancelling leaves that resolution untouched.
33
+ - name: Skip when blawar/titledb has not moved
34
+ env:
35
+ GH_TOKEN: ${{ github.token }}
36
+ # This step runs before the checkout, so gh has no local repo to infer from
37
+ GH_REPO: ${{ github.repository }}
38
+ run: |
39
+ set -euo pipefail
40
+ remote=$(git ls-remote https://github.com/blawar/titledb.git HEAD | cut -f1)
41
+ published=$(curl -sfL "https://github.com/${{ github.repository }}/releases/download/titledb/latest" || true)
42
+ echo "upstream=${remote} published=${published:-<none>}"
43
+ echo "titledb_sha=${remote}" >> "$GITHUB_ENV"
44
+ if [ "$remote" = "$published" ] && [ "${{ inputs.force }}" != "true" ]; then
45
+ echo "titledb unchanged, cancelling this run"
46
+ gh run cancel "${{ github.run_id }}"
47
+ sleep 60
48
+ fi
49
+
50
+ # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
51
+ - uses: actions/checkout@v7
52
+ - uses: actions/setup-python@v7
53
+ with:
54
+ python-version: '3.10'
55
+
56
+ # Runs a set of commands using the runners shell
57
+ - name: Setup folders
58
+ run: |
59
+ set -euo pipefail
60
+ sudo apt update && sudo apt install -y libcurl4-openssl-dev libssl-dev
61
+ git clone https://github.com/blawar/nut
62
+ cd nut
63
+ pip install -r requirements.txt
64
+ mkdir build_artefacts titledb
65
+ curl -fsSL --retry 3 "https://github.com/blawar/titledb/archive/${titledb_sha}.tar.gz" \
66
+ | tar -xz --strip-components=1 -C titledb \
67
+ --exclude=ncas.json --exclude=cheats.json
68
+ echo "formatted_date=$(date +"%Y-%m-%d_%H%M%S")" >> $GITHUB_ENV
69
+ # Short hash inside the artefact: consumed by Ownfoil < 2.4 through nightly.link.
70
+ # Explicitly 7 chars, matching what --short gave on the old shallow clone.
71
+ touch "build_artefacts/latest_${titledb_sha:0:7}"
72
+ # Full hash outside the artefact: the release marker, and what the skip check compares.
73
+ # Both are now the sha the tarball was pinned to, so they agree by construction.
74
+ echo "$titledb_sha" > latest
75
+
76
+ - uses: jannekem/run-python-script-action@v1
77
+ name: Generate region titles
78
+ with:
79
+ script: |
80
+ import os, sys
81
+ import json
82
+
83
+ os.chdir('nut')
84
+ sys.path.append(os.getcwd())
85
+ import nut
86
+
87
+ with open('titledb/languages.json') as f:
88
+ languages = json.load(f)
89
+ languages = dict(sorted(languages.items()))
90
+ for region in languages.keys():
91
+ for language in languages[region]:
92
+ print(region, language)
93
+ nut.importRegion(region, language)
94
+ os.rename('titledb/titles.json', f'build_artefacts/titles.{region}.{language}.json')
95
+
96
+ for f in [
97
+ 'cnmts.json',
98
+ 'languages.json',
99
+ 'versions.json',
100
+ 'versions.txt',
101
+ ]:
102
+ os.rename(f'titledb/{f}', f'build_artefacts/{f}')
103
+
104
+
105
+ # Must stay ahead of the compression step: pre-release versions read the uncompressed
106
+ # JSON straight out of this zip.
107
+ - uses: actions/upload-artifact@v7
108
+ with:
109
+ name: titledb
110
+ path: nut/build_artefacts/*
111
+ compression-level: 9
112
+
113
+ - name: Compress artefacts
114
+ run: zstd -12 -T0 --rm -q nut/build_artefacts/*.json nut/build_artefacts/versions.txt
115
+
116
+ - name: Publish to the titledb release
117
+ env:
118
+ GH_TOKEN: ${{ github.token }}
119
+ run: |
120
+ set -euo pipefail
121
+ gh release view titledb >/dev/null 2>&1 || \
122
+ gh release create titledb --prerelease --latest=false --title 'titledb data' \
123
+ --notes 'Rolling titledb dataset consumed by Ownfoil. Assets are replaced in place; `latest` holds the blawar/titledb commit they were built from and is always written last.'
124
+ uploaded=false
125
+ for attempt in 1 2 3; do
126
+ if gh release upload titledb nut/build_artefacts/*.zst --clobber; then
127
+ uploaded=true
128
+ break
129
+ fi
130
+ echo "upload attempt ${attempt} failed, retrying"
131
+ sleep 30
132
+ done
133
+ # Guards the invariant below: never publish the marker over a half-uploaded dataset
134
+ [ "$uploaded" = true ]
135
+ # Written last, alone: a client that sees a new hash knows every asset is already in place
136
+ gh release upload titledb nut/latest --clobber
@@ -0,0 +1,24 @@
1
+ name: 'Close fixed issues on release'
2
+ on:
3
+ release:
4
+ types: [released]
5
+
6
+ jobs:
7
+ close:
8
+ if: '!github.event.release.prerelease'
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ issues: write
12
+ steps:
13
+ - name: Close issues labeled fixed
14
+ env:
15
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
16
+ GH_REPO: ${{ github.repository }}
17
+ TAG: ${{ github.event.release.tag_name }}
18
+ URL: ${{ github.event.release.html_url }}
19
+ run: |
20
+ body="This issue has been fixed and released in [Ownfoil ${TAG}](${URL}), closing as completed. Feel free to re-open if needed."
21
+ for issue in $(gh issue list --label fixed --state open --limit 200 --json number --jq '.[].number'); do
22
+ gh issue edit "$issue" --remove-label fixed
23
+ gh issue close "$issue" --reason completed --comment "$body"
24
+ done
@@ -9,7 +9,7 @@ jobs:
9
9
  runs-on: ubuntu-latest
10
10
  steps:
11
11
  - name: Close stale issues
12
- uses: actions/stale@v10
12
+ uses: actions/stale@v11
13
13
  with:
14
14
  days-before-stale: 30
15
15
  days-before-close: 7
@@ -0,0 +1,37 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ test:
10
+ name: Tests on ${{ matrix.os }}
11
+ if: ${{ !startsWith(github.event.head_commit.message, 'ci') && !startsWith(github.event.head_commit.message, 'doc') }}
12
+ runs-on: ${{ matrix.os }}
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ os: [ubuntu-latest, windows-latest]
17
+
18
+ steps:
19
+ - name: Checkout
20
+ uses: actions/checkout@v7
21
+
22
+ - name: Set up uv
23
+ uses: astral-sh/setup-uv@v7
24
+ with:
25
+ python-version: '3.14'
26
+ enable-cache: true
27
+ cache-dependency-glob: requirements.txt
28
+
29
+ - name: Create virtualenv
30
+ run: uv venv
31
+
32
+ - name: Install dependencies
33
+ run: uv pip install --requirement requirements.txt pytest
34
+
35
+ - name: Run tests
36
+ run: uv run pytest tests -v
37
+ timeout-minutes: 20
@@ -2,6 +2,9 @@ __pycache__
2
2
  config
3
3
  data
4
4
 
5
+ # capture harness workdir: fixture db, settings and the pseudonym map
6
+ tests/capture/.workdir
7
+
5
8
  # venv
6
9
  /bin
7
10
  /venv
@@ -2,7 +2,7 @@ FROM python:3.14-alpine
2
2
 
3
3
  # Install platform-specific build dependencies
4
4
  ARG TARGETPLATFORM
5
- RUN apk update && apk add --no-cache bash sudo git \
5
+ RUN apk update && apk add --no-cache bash sudo \
6
6
  && if [ "$TARGETPLATFORM" = "linux/arm/v6" ] || [ "$TARGETPLATFORM" = "linux/arm/v7" ]; then \
7
7
  apk add --no-cache build-base gcc musl-dev jpeg-dev zlib-dev libffi-dev cairo-dev pango-dev gdk-pixbuf-dev; \
8
8
  fi
@@ -0,0 +1,208 @@
1
+ # Installing Ownfoil
2
+
3
+ Ownfoil is a web server listening on port `8465`, so anything that can run a Docker container or a Python environment will do - a NAS, a Raspberry Pi, an old laptop or your own desktop.
4
+
5
+ - [Using Docker](#using-docker)
6
+ - [Using uv (Windows users do this)](#using-uv)
7
+ - [Using Unraid](#using-unraid)
8
+ - [Using Proxmox LXC](#using-proxmox-lxc)
9
+ - [Using the Helm chart](#using-the-helm-chart)
10
+ - [Available versions](#available-versions)
11
+
12
+ > [!CAUTION]
13
+ > There is __no website associated with this project__, only this GitHub repo.
14
+ > Ownfoil is __not released as an application or an executable file__ - DO NOT download or execute anything related to Ownfoil outside of this repository and its instructions.
15
+
16
+ # Using Docker
17
+ Ownfoil is shipped as a docker container for easy deployment, data persistency and updates. If you are unfamiliar with Docker, check [the installation documentation here](https://docs.docker.com/engine/install/).
18
+ ## Docker run
19
+
20
+ <details>
21
+
22
+ Running this command will start the shop on local port `8465` with the library in `/your/game/directory`, and persist the `data` and `config` directories:
23
+ ```
24
+ docker run -d -p 8465:8465 \
25
+ -v /your/game/directory:/games \
26
+ -v ./config:/app/config \
27
+ -v ./data:/app/data \
28
+ --name ownfoil \
29
+ a1ex4/ownfoil
30
+ ```
31
+ To see the logs of the container:
32
+
33
+ docker logs -f ownfoil
34
+
35
+ </details>
36
+
37
+ ## Docker compose
38
+ <details>
39
+
40
+ Create a file named `docker-compose.yml` with the following content:
41
+ ```
42
+ ---
43
+ services:
44
+ ownfoil:
45
+ container_name: ownfoil
46
+ image: a1ex4/ownfoil
47
+ # environment:
48
+ # # For write permission in config directory
49
+ # - PUID=1000
50
+ # - PGID=1000
51
+ # # to create/update an admin user at startup
52
+ # - USER_ADMIN_NAME=admin
53
+ # - USER_ADMIN_PASSWORD=asdvnf!546
54
+ # # to create/update a regular user at startup
55
+ # - USER_GUEST_NAME=guest
56
+ # - USER_GUEST_PASSWORD=oerze!@8981
57
+ volumes:
58
+ - /your/game/directory:/games
59
+ - ./data:/app/data
60
+ - ./config:/app/config
61
+ ports:
62
+ - "8465:8465"
63
+ ```
64
+
65
+ You can then create and start the container with the command (executed in the same directory as the docker-compose file):
66
+
67
+ docker-compose up -d
68
+
69
+ This is usefull if you don't want to remember the `docker run` command and have a persistent and reproductible container configuration.
70
+ </details>
71
+
72
+ ## Volumes
73
+
74
+ | Container path | What to mount there |
75
+ | --- | --- |
76
+ | `/games` | Your library. You can mount several of them, and add each one in the `Settings`. |
77
+ | `/app/config` | The config directory. Back this one up. |
78
+ | `/app/data` | The titledb cache. |
79
+
80
+ ## Environment variables
81
+
82
+ | Variable | Description |
83
+ | --- | --- |
84
+ | `PUID` / `PGID` | UID and GID of the user running the app in the container, `1000:1000` by default. |
85
+ | `USER_ADMIN_NAME` / `USER_ADMIN_PASSWORD` | Creates (or updates) an admin user at every startup. |
86
+ | `USER_GUEST_NAME` / `USER_GUEST_PASSWORD` | Creates (or updates) a regular user with shop access at every startup. |
87
+
88
+ > [!TIP]
89
+ > You can control the `UID` and `GID` of the user running the app in the container with the `PUID` and `PGID` environment variables. By default the user is created with `1000:1000`. If you want to have the same ownership for mounted directories, you need to set those variables with the UID and GID returned by the `id` command.
90
+
91
+ # Using uv
92
+ Ownfoil can be run with [uv](https://docs.astral.sh/uv/getting-started/installation/) without cloning this repository or managing a Python environment yourself. This is the easiest way to run it on Windows.
93
+ <details>
94
+
95
+ After installing `uv`, open a terminal (Press `Win + R`, type `wt`, and press `Enter`), then either:
96
+ * Run it once, without installing anything permanently:
97
+ ```
98
+ uvx ownfoil
99
+ ```
100
+ * Install it as a persistent uv tool, so the `ownfoil` command stays available:
101
+ ```
102
+ uv tool install ownfoil
103
+ ownfoil
104
+ ```
105
+ By default, `config/` and `data/` are created in the current directory. Pass a directory to use instead (works the same way with `uvx`, just append it after `ownfoil`):
106
+ ```
107
+ ownfoil /path/to/persist
108
+ uvx ownfoil /path/to/persist
109
+ ```
110
+
111
+ On Windows, the first run also creates an `ownfoil.bat` next to `config/` and `data/`, so you can start Ownfoil again by double-clicking it instead of reopening a terminal.
112
+ The Web UI opens in your default browser once the server is ready. Pass `--no-browser` (or set `OWNFOIL_NO_BROWSER=1`) to disable it:
113
+
114
+ ```
115
+ ownfoil --no-browser
116
+ ```
117
+
118
+ On Windows, Gunicorn cannot run, so it's best suited for local/personal use there rather than a production deployment.
119
+ </details>
120
+
121
+ # Using Unraid
122
+
123
+ Ownfoil is available on the [Community Apps store](https://ca.unraid.net/apps/ownfoil-19wo90o0t5ul8s), install it from the template.
124
+
125
+ # Using Proxmox LXC
126
+
127
+ If you run Proxmox, the [community scripts](https://community-scripts.org/scripts/ownfoil) project maintains a script that builds an LXC container with Ownfoil already installed and running as a service - follow the instructions on that page.
128
+
129
+ > [!TIP]
130
+ > That script is maintained by the community scripts project, not by this repository. If something goes wrong with the container itself, report it to them rather than here.
131
+
132
+ # Using the Helm chart
133
+
134
+ The chart lives in [`chart/`](./chart) of this repository - there is no chart repository to add, so clone this repo first, then from the `chart` directory:
135
+
136
+ ```
137
+ helm upgrade --install ownfoil ./ -n namespace -f values.yaml
138
+ ```
139
+
140
+ # Remote access and HTTPS
141
+
142
+ Exposing your shop to the internet is done with a reverse proxy in front of Ownfoil, and there are a few things it must get right.
143
+
144
+ * Send the `X-Forwarded-Proto` header, and make sure it does __not__ serve the shop over plain `http` (or redirect it to `https`). Ownfoil only enforces host verification on secure requests, so a proxy that leaves `http` open means anyone who learns your URL can use your shop.
145
+ * Don't set a small request body limit, clients download multi-GB files through it.
146
+
147
+ Once that is done, set the `Shop URL` in the `Settings` to the same hostname - see [Shop](./Usage.md#shop) for what it does and how to configure it.
148
+
149
+ # First run
150
+
151
+ On the first start Ownfoil creates the `config` and `data` directories, downloads titledb, and starts serving the Web UI on port `8465`. Open it with your computer/server IP and port, i.e. `http://localhost:8465` from the same computer or `http://192.168.1.100:8465` from a device in your network.
152
+
153
+ > [!CAUTION]
154
+ > Until an admin account is created, __authentication is disabled and anyone who can reach the Web UI can change the configuration of your shop__. Creating that account is the first thing to do.
155
+
156
+ Head over to [First steps](./Usage.md#using-ownfoil) to configure your shop.
157
+
158
+ # Updating
159
+
160
+ Updating is just replacing the version you run.
161
+
162
+ * Docker: `docker pull a1ex4/ownfoil` then recreate the container
163
+ * Docker compose: `docker compose pull && docker compose up -d`
164
+ * uv tool: `uv tool upgrade ownfoil`
165
+ * uvx: nothing to do, it fetches the latest version every time
166
+ * Helm: bump `image.tag` and run the `helm upgrade` command again
167
+
168
+ # Available versions
169
+
170
+ Whichever way you install it, you run one of these:
171
+
172
+ | Version | What you get |
173
+ | --- | --- |
174
+ | `latest` | The most recent release, default when you don't select a version in particular. |
175
+ | `develop` | The development branch - the next release as it is being written, with new features first and you contribute to testing the next version. |
176
+ | A version number | That exact release, and the way to stay on a known version. |
177
+
178
+ Versions are `major.minor.patch`, and each part can be used on its own to pin a specific "release channel" when upgrading:
179
+
180
+ * [![major version](https://img.shields.io/badge/dynamic/regex?url=https%3A%2F%2Fapi.github.com%2Frepos%2Fa1ex4%2Fownfoil%2Freleases%2Flatest&search=%22tag_name%22%3A%5Cs%2A%22%28%5B0-9%5D%2B%29&replace=%241&label=)](https://github.com/a1ex4/ownfoil/releases/latest) is the `major` version, unlikely to change. Using it means the latest release of that major version.
181
+ * [![minor version](https://img.shields.io/badge/dynamic/regex?url=https%3A%2F%2Fapi.github.com%2Frepos%2Fa1ex4%2Fownfoil%2Freleases%2Flatest&search=%22tag_name%22%3A%5Cs%2A%22%28%5B0-9%5D%2B%5C.%5B0-9%5D%2B%29&replace=%241&label=)](https://github.com/a1ex4/ownfoil/releases/latest) is the `minor` version, bumped when new features are introduced. Using it means the latest patch of that minor version, so including bug fixes but no new features.
182
+ * [![patch version](https://img.shields.io/badge/dynamic/regex?url=https%3A%2F%2Fapi.github.com%2Frepos%2Fa1ex4%2Fownfoil%2Freleases%2Flatest&search=%22tag_name%22%3A%5Cs%2A%22%28%5B0-9%5D%2B%5C.%5B0-9%5D%2B%5C.%5B0-9%5D%2B%29&replace=%241&label=)](https://github.com/a1ex4/ownfoil/releases/latest) is the `patch` version, increased for a release that only fixes bugs in the latest minor version.
183
+
184
+ Releases and what changed in them are on the [releases page](https://github.com/a1ex4/ownfoil/releases).
185
+
186
+ ## Docker
187
+
188
+ The version is the image tag, `latest` when you don't set one:
189
+
190
+ docker run ... a1ex4/ownfoil:2.3
191
+
192
+ In the compose file it is the `image` line:
193
+
194
+ image: a1ex4/ownfoil:develop
195
+
196
+ Every branch of this repository is published under its own name too, i.e. `a1ex4/ownfoil:fix-something`, to test a fix before it is released.
197
+
198
+ ## uv
199
+
200
+ Ownfoil is published on PyPI from `2.4.0` onwards, so a version is pinned like for any Python package:
201
+
202
+ uvx ownfoil@2.4.0
203
+ uv tool install ownfoil==2.4.0
204
+
205
+ `develop` is not published there, install it from git instead:
206
+
207
+ uvx --from git+https://github.com/a1ex4/ownfoil@develop ownfoil
208
+ uv tool install git+https://github.com/a1ex4/ownfoil@develop
ownfoil-2.4.2/PKG-INFO ADDED
@@ -0,0 +1,79 @@
1
+ Metadata-Version: 2.5
2
+ Name: ownfoil
3
+ Version: 2.4.2
4
+ Summary: Self-hosted Nintendo Switch library manager, with automated management tasks, multi user authentication, serving your library to multiple supported clients on your Switch.
5
+ Project-URL: Homepage, https://github.com/a1ex4/ownfoil
6
+ Project-URL: Repository, https://github.com/a1ex4/ownfoil
7
+ Project-URL: Issues, https://github.com/a1ex4/ownfoil/issues
8
+ Author-email: a1ex4 <12754559+a1ex4@users.noreply.github.com>
9
+ License-Expression: AGPL-3.0-or-later
10
+ License-File: LICENSE.md
11
+ Keywords: atmosphere,homebrew,nintendo-switch,self-hosted,switch,tinfoil
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Topic :: Games/Entertainment
17
+ Requires-Python: >=3.11
18
+ Requires-Dist: flask-login==0.6.3
19
+ Requires-Dist: flask-migrate==4.1.0
20
+ Requires-Dist: flask-sock==0.7.0
21
+ Requires-Dist: flask-sqlalchemy==3.1.1
22
+ Requires-Dist: flask==3.1.3
23
+ Requires-Dist: gunicorn==26.2.0; sys_platform != 'win32'
24
+ Requires-Dist: nstools==2.0.0b7
25
+ Requires-Dist: nsz==5.0.0
26
+ Requires-Dist: pyyaml==6.0.3
27
+ Requires-Dist: requests==2.34.2
28
+ Requires-Dist: setproctitle==1.3.7
29
+ Requires-Dist: strawberry-graphql[flask]==0.324.4
30
+ Requires-Dist: watchdog==6.0.0
31
+ Requires-Dist: werkzeug==3.1.8
32
+ Requires-Dist: zstandard==0.25.0
33
+ Description-Content-Type: text/markdown
34
+
35
+ # <img src="https://github.com/user-attachments/assets/3cfdf010-50c3-41ae-aa86-e31b22466686" height="28"> Ownfoil
36
+ [![Static Badge](https://img.shields.io/badge/github-repo-blue?logo=github)](https://github.com/a1ex4/ownfoil)
37
+ [![Latest Release](https://img.shields.io/docker/v/a1ex4/ownfoil?sort=semver)](https://github.com/a1ex4/ownfoil/releases/latest)
38
+ [![Docker Image Size (latest semver)](https://img.shields.io/docker/image-size/a1ex4/ownfoil?sort=date&arch=amd64)](https://hub.docker.com/r/a1ex4/ownfoil/tags)
39
+ [![Docker Pulls](https://img.shields.io/docker/pulls/a1ex4/ownfoil?)](https://hub.docker.com/r/a1ex4/ownfoil)
40
+ [![Unraid downloads](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fca.unraid.net%2Fapi%2Fsearch%3Fquery%3Downfoil%26type%3Ddocker&query=%24.hits%5B0%5D.chartData.totalDownloadsChart.data%5B6%5D&label=unraid%20downloads&color=F15A2C)](https://ca.unraid.net/apps/ownfoil-19wo90o0t5ul8s)
41
+ ![Image archs](https://img.shields.io/badge/platforms-amd64%20%7C%20%20arm64%2Fv8%20%7C%20arm%2Fv7%20%7C%20arm%2Fv6-8A2BE2?style=flat-square)[![Nokia support](https://img.shields.io/badge/%7C%20Nokia%203310-8A2BE2?style=flat-square)](https://www.reddit.com/r/SwitchPirates/s/zy5ELiUW9y)
42
+ [![Tinfoil Version](https://img.shields.io/badge/Tinfoil-v20.0-da1c5c)](https://tinfoil.io/Download)
43
+ [![Sphaira Version](https://img.shields.io/badge/Sphaira-v1.0.6-%233cd57a)](https://github.com/NaGaa95/sphaira)
44
+ [![CyberFoil Version](https://img.shields.io/badge/CyberFoil-v1.4.5-firebrick)](https://github.com/luketanti/CyberFoil)
45
+
46
+
47
+ Ownfoil is a Nintendo Switch library manager, that will also turn your library into a fully customizable and self-hosted Shop, supporting multiple clients. The goal of this project is to manage your library, identify any missing content (DLCs or updates) and provide a user friendly way to browse and install your content. Some of the features include:
48
+ - multi user authentication
49
+ - web interface for configuration and browsing the library
50
+ - content identification using content decryption or filename
51
+ - automatic library organization, verification and compression
52
+ - console keys management
53
+ - multiple clients support
54
+ - shop customization
55
+
56
+ # Installation
57
+
58
+ Head over to [Install.md](./Install.md) for the full instructions:
59
+
60
+ - [Using Docker](./Install.md#using-docker)
61
+ - [Using uv (Windows users do this)](./Install.md#using-uv)
62
+ - [Using Unraid](./Install.md#using-unraid)
63
+ - [Using Proxmox LXC](./Install.md#using-proxmox-lxc)
64
+ - [Using the Helm chart](./Install.md#using-the-helm-chart)
65
+
66
+ > [!CAUTION]
67
+ > There is __no website associated with this project__, only this GitHub repo.
68
+ > Ownfoil is __not released as an application or an executable file__ - DO NOT download or execute anything related to Ownfoil outside of this repository and its instructions.
69
+
70
+ # Usage
71
+
72
+ Configuring your shop, your clients and every setting available is documented in [Usage.md](./Usage.md). Start with [First steps](./Usage.md#first-steps), or jump straight to the [settings reference](./Usage.md#settings-reference).
73
+
74
+ # Credits
75
+
76
+ Thanks to the following projects and their maintainers for making Ownfoil possible:
77
+ - [@blawar](https://github.com/blawar) for Tinfoil, Fs, the nsz format, TitleDB
78
+ - [@nicoboss](https://github.com/nicoboss) for [nsz](https://github.com/nicoboss/nsz)
79
+ - [@seiya-dev](https://github.com/seiya-dev) for [NSTools](https://github.com/seiya-dev/NSTools)
@@ -0,0 +1,45 @@
1
+ # <img src="https://github.com/user-attachments/assets/3cfdf010-50c3-41ae-aa86-e31b22466686" height="28"> Ownfoil
2
+ [![Static Badge](https://img.shields.io/badge/github-repo-blue?logo=github)](https://github.com/a1ex4/ownfoil)
3
+ [![Latest Release](https://img.shields.io/docker/v/a1ex4/ownfoil?sort=semver)](https://github.com/a1ex4/ownfoil/releases/latest)
4
+ [![Docker Image Size (latest semver)](https://img.shields.io/docker/image-size/a1ex4/ownfoil?sort=date&arch=amd64)](https://hub.docker.com/r/a1ex4/ownfoil/tags)
5
+ [![Docker Pulls](https://img.shields.io/docker/pulls/a1ex4/ownfoil?)](https://hub.docker.com/r/a1ex4/ownfoil)
6
+ [![Unraid downloads](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fca.unraid.net%2Fapi%2Fsearch%3Fquery%3Downfoil%26type%3Ddocker&query=%24.hits%5B0%5D.chartData.totalDownloadsChart.data%5B6%5D&label=unraid%20downloads&color=F15A2C)](https://ca.unraid.net/apps/ownfoil-19wo90o0t5ul8s)
7
+ ![Image archs](https://img.shields.io/badge/platforms-amd64%20%7C%20%20arm64%2Fv8%20%7C%20arm%2Fv7%20%7C%20arm%2Fv6-8A2BE2?style=flat-square)[![Nokia support](https://img.shields.io/badge/%7C%20Nokia%203310-8A2BE2?style=flat-square)](https://www.reddit.com/r/SwitchPirates/s/zy5ELiUW9y)
8
+ [![Tinfoil Version](https://img.shields.io/badge/Tinfoil-v20.0-da1c5c)](https://tinfoil.io/Download)
9
+ [![Sphaira Version](https://img.shields.io/badge/Sphaira-v1.0.6-%233cd57a)](https://github.com/NaGaa95/sphaira)
10
+ [![CyberFoil Version](https://img.shields.io/badge/CyberFoil-v1.4.5-firebrick)](https://github.com/luketanti/CyberFoil)
11
+
12
+
13
+ Ownfoil is a Nintendo Switch library manager, that will also turn your library into a fully customizable and self-hosted Shop, supporting multiple clients. The goal of this project is to manage your library, identify any missing content (DLCs or updates) and provide a user friendly way to browse and install your content. Some of the features include:
14
+ - multi user authentication
15
+ - web interface for configuration and browsing the library
16
+ - content identification using content decryption or filename
17
+ - automatic library organization, verification and compression
18
+ - console keys management
19
+ - multiple clients support
20
+ - shop customization
21
+
22
+ # Installation
23
+
24
+ Head over to [Install.md](./Install.md) for the full instructions:
25
+
26
+ - [Using Docker](./Install.md#using-docker)
27
+ - [Using uv (Windows users do this)](./Install.md#using-uv)
28
+ - [Using Unraid](./Install.md#using-unraid)
29
+ - [Using Proxmox LXC](./Install.md#using-proxmox-lxc)
30
+ - [Using the Helm chart](./Install.md#using-the-helm-chart)
31
+
32
+ > [!CAUTION]
33
+ > There is __no website associated with this project__, only this GitHub repo.
34
+ > Ownfoil is __not released as an application or an executable file__ - DO NOT download or execute anything related to Ownfoil outside of this repository and its instructions.
35
+
36
+ # Usage
37
+
38
+ Configuring your shop, your clients and every setting available is documented in [Usage.md](./Usage.md). Start with [First steps](./Usage.md#first-steps), or jump straight to the [settings reference](./Usage.md#settings-reference).
39
+
40
+ # Credits
41
+
42
+ Thanks to the following projects and their maintainers for making Ownfoil possible:
43
+ - [@blawar](https://github.com/blawar) for Tinfoil, Fs, the nsz format, TitleDB
44
+ - [@nicoboss](https://github.com/nicoboss) for [nsz](https://github.com/nicoboss/nsz)
45
+ - [@seiya-dev](https://github.com/seiya-dev) for [NSTools](https://github.com/seiya-dev/NSTools)