├── .dockerignore ├── .env.example ├── .envrc ├── .github ├── .wordlist.txt ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── 1.bug_report.yml │ └── 2.feature_request.yml ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── copyright.yml │ ├── docker-develop.yml │ ├── docker-latest.yml │ ├── docker-version.yml │ ├── increment-build.yml │ ├── release-master.yml │ ├── release-notification.yml │ ├── tag-cleanup.yml │ ├── tag-new-version.yml │ └── validate-pull.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .spellcheck.yml ├── Dockerfile ├── Dockerfile.arm7 ├── LICENSE ├── PART ├── README.md ├── VERSION ├── modules ├── database.py ├── helpers.py ├── hooks │ ├── develop.py │ ├── linux.py │ ├── macos.py │ ├── pull.py │ └── windows.py ├── iso.py ├── output.py ├── persistence.py └── validations.py ├── quickstart.py ├── quickstart.spec ├── requirements.txt ├── static ├── css │ └── styles.css ├── favicon.ico ├── favicon.png ├── fonts │ ├── 3d-ascii.flf │ ├── banner3-D.flf │ ├── big.flf │ ├── cyberlarge.flf │ ├── doom.flf │ ├── epic.flf │ ├── ghost.flf │ ├── slant.flf │ ├── small.flf │ └── standard.flf ├── images │ ├── default-episode_preview.png │ ├── default-episode_preview.webp │ ├── default-season_preview.png │ ├── default-season_preview.webp │ ├── default-sho_preview.png │ ├── default-sho_preview.webp │ ├── default.png │ ├── default_grey.png │ ├── header.png │ ├── logo.webp │ ├── overlays │ │ ├── epi-sho-episode-overlay_aspect.png │ │ ├── epi-sho-episode-overlay_audio_codec.png │ │ ├── epi-sho-episode-overlay_content_rating_au.png │ │ ├── epi-sho-episode-overlay_content_rating_commonsense.png │ │ ├── epi-sho-episode-overlay_content_rating_de.png │ │ ├── epi-sho-episode-overlay_content_rating_nz.png │ │ ├── epi-sho-episode-overlay_content_rating_uk.png │ │ ├── epi-sho-episode-overlay_content_rating_us_show.png │ │ ├── epi-sho-episode-overlay_direct_play.png │ │ ├── epi-sho-episode-overlay_episode_info.png │ │ ├── epi-sho-episode-overlay_language_count.png │ │ ├── epi-sho-episode-overlay_languages.png │ │ ├── epi-sho-episode-overlay_network.png │ │ ├── epi-sho-episode-overlay_resolution.png │ │ ├── epi-sho-episode-overlay_runtimes.png │ │ ├── epi-sho-episode-overlay_studio.png │ │ ├── epi-sho-episode-overlay_versions.png │ │ ├── epi-sho-episode-overlay_video_format.png │ │ ├── epi-sho-overlay_languages_use_subtitles.png │ │ ├── mov-movie-overlay_aspect.png │ │ ├── mov-movie-overlay_audio_codec.png │ │ ├── mov-movie-overlay_content_rating_au.png │ │ ├── mov-movie-overlay_content_rating_commonsense.png │ │ ├── mov-movie-overlay_content_rating_de.png │ │ ├── mov-movie-overlay_content_rating_nz.png │ │ ├── mov-movie-overlay_content_rating_uk.png │ │ ├── mov-movie-overlay_content_rating_us_movie.png │ │ ├── mov-movie-overlay_direct_play.png │ │ ├── mov-movie-overlay_language_count.png │ │ ├── mov-movie-overlay_languages.png │ │ ├── mov-movie-overlay_mediastinger.png │ │ ├── mov-movie-overlay_ratings.png │ │ ├── mov-movie-overlay_resolution.png │ │ ├── mov-movie-overlay_ribbon.png │ │ ├── mov-movie-overlay_runtimes.png │ │ ├── mov-movie-overlay_status.png │ │ ├── mov-movie-overlay_streaming.png │ │ ├── mov-movie-overlay_studio.png │ │ ├── mov-movie-overlay_versions.png │ │ ├── mov-movie-overlay_video_format.png │ │ ├── sho-season-season-overlay_aspect.png │ │ ├── sho-season-season-overlay_audio_codec.png │ │ ├── sho-season-season-overlay_content_rating_au.png │ │ ├── sho-season-season-overlay_content_rating_commonsense.png │ │ ├── sho-season-season-overlay_content_rating_de.png │ │ ├── sho-season-season-overlay_content_rating_nz.png │ │ ├── sho-season-season-overlay_content_rating_uk.png │ │ ├── sho-season-season-overlay_content_rating_us_show.png │ │ ├── sho-season-season-overlay_direct_play.png │ │ ├── sho-season-season-overlay_language_count.png │ │ ├── sho-season-season-overlay_languages.png │ │ ├── sho-season-season-overlay_mediastinger.png │ │ ├── sho-season-season-overlay_network.png │ │ ├── sho-season-season-overlay_ratings.png │ │ ├── sho-season-season-overlay_resolution.png │ │ ├── sho-season-season-overlay_ribbon.png │ │ ├── sho-season-season-overlay_runtimes.png │ │ ├── sho-season-season-overlay_status.png │ │ ├── sho-season-season-overlay_streaming.png │ │ ├── sho-season-season-overlay_studio.png │ │ ├── sho-season-season-overlay_versions.png │ │ ├── sho-season-season-overlay_video_format.png │ │ ├── sho-show-overlay_aspect.png │ │ ├── sho-show-overlay_audio_codec.png │ │ ├── sho-show-overlay_content_rating_au.png │ │ ├── sho-show-overlay_content_rating_commonsense.png │ │ ├── sho-show-overlay_content_rating_de.png │ │ ├── sho-show-overlay_content_rating_nz.png │ │ ├── sho-show-overlay_content_rating_uk.png │ │ ├── sho-show-overlay_content_rating_us_show.png │ │ ├── sho-show-overlay_direct_play.png │ │ ├── sho-show-overlay_language_count.png │ │ ├── sho-show-overlay_languages.png │ │ ├── sho-show-overlay_mediastinger.png │ │ ├── sho-show-overlay_network.png │ │ ├── sho-show-overlay_ratings.png │ │ ├── sho-show-overlay_resolution.png │ │ ├── sho-show-overlay_ribbon.png │ │ ├── sho-show-overlay_runtimes.png │ │ ├── sho-show-overlay_status.png │ │ ├── sho-show-overlay_streaming.png │ │ ├── sho-show-overlay_studio.png │ │ ├── sho-show-overlay_versions.png │ │ └── sho-show-overlay_video_format.png │ ├── qs_defaults_landscape.xcf │ ├── running-in-pwsh.png │ ├── system-tray-launcher-mac.png │ ├── system-tray-launcher-ubuntu.png │ ├── system-tray-launcher.png │ └── wizard.webp ├── json │ ├── quickstart_attributes.json │ ├── quickstart_collections.json │ └── quickstart_overlays.json └── local-js │ ├── 000-base.js │ ├── 001-start.js │ ├── 010-plex.js │ ├── 020-tmdb.js │ ├── 025-libraries.js │ ├── 027-playlist_files.js │ ├── 030-tautulli.js │ ├── 040-github.js │ ├── 050-omdb.js │ ├── 060-mdblist.js │ ├── 070-notifiarr.js │ ├── 080-gotify.js │ ├── 085-ntfy.js │ ├── 090-webhooks.js │ ├── 100-anidb.js │ ├── 110-radarr.js │ ├── 120-sonarr.js │ ├── 130-trakt.js │ ├── 140-mal.js │ ├── 150-settings.js │ ├── 900-final.js │ ├── eventHandler.js │ ├── imageHandler.js │ ├── overlayHandler.js │ └── validationHandler.js └── templates ├── 000-base.html ├── 001-navigation.html ├── 001-start.html ├── 010-plex.html ├── 020-tmdb.html ├── 025-libraries.html ├── 027-playlist_files.html ├── 030-tautulli.html ├── 040-github.html ├── 050-omdb.html ├── 060-mdblist.html ├── 070-notifiarr.html ├── 080-gotify.html ├── 085-ntfy.html ├── 090-webhooks.html ├── 100-anidb.html ├── 110-radarr.html ├── 120-sonarr.html ├── 130-trakt.html ├── 140-mal.html ├── 150-settings.html ├── 900-final.html ├── 910-sponsor.html ├── modals ├── 001-start.html ├── 010-plex.html ├── 020-tmdb.html ├── 025-libraries.html ├── 027-playlist_files.html ├── 030-tautulli.html ├── 040-github.html ├── 050-omdb.html ├── 060-mdblist.html ├── 070-notifiarr.html ├── 080-gotify.html ├── 085-ntfy.html ├── 090-webhooks.html ├── 100-anidb.html ├── 110-radarr.html ├── 120-sonarr.html ├── 130-trakt.html ├── 140-mal.html ├── 150-settings.html └── 900-final.html └── partials ├── _macros.html ├── _movie_attributes.html ├── _movie_collections.html ├── _movie_library_settings.html ├── _movie_overlays.html ├── _rename_modal.html ├── _show_attributes.html ├── _show_collections.html ├── _show_library_settings.html └── _show_overlays.html /.dockerignore: -------------------------------------------------------------------------------- 1 | **/dist 2 | **/build 3 | *.spec 4 | **/__pycache__ 5 | /.vscode 6 | **/log 7 | *.psd 8 | .git 9 | .github 10 | .idea 11 | config 12 | config/**/* 13 | docs 14 | json-schema 15 | venv 16 | .dockerignore 17 | .gitignore 18 | .readthedocs.yml 19 | .spellcheck.yml 20 | CHANGELOG 21 | Dockerfile 22 | Dockerfile.lxml 23 | LICENSE 24 | mkdocs.yml 25 | README.md 26 | test.py -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | QS_DEBUG=0 2 | QS_PORT=7171 3 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | layout pyenv 3.12.0 2 | python -m install --upgrade pip 3 | python -m pip install -r requirements.txt 4 | pre-commit install 5 | pre-commit autoupdate 6 | -------------------------------------------------------------------------------- /.github/.wordlist.txt: -------------------------------------------------------------------------------- 1 | bullmoose 2 | chazlarson 3 | config 4 | else's 5 | kometa 6 | quickstart 7 | repo 8 | ui 9 | walkthrough 10 | walkthroughs 11 | xxxx 12 | yozora 13 | yozoraxcii 14 | localhost 15 | https 16 | chmod 17 | MacOS 18 | exe -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: meisnate12 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/1.bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Please do not use bug reports for support issues or feature requests. 3 | title: '[Bug]: ' 4 | type: 'Bug' 5 | assignees: 'bullmoose20' 6 | 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: > 11 | **THIS IS NOT THE PLACE TO ASK FOR SUPPORT OR FEATURE REQUESTS!** 12 | Please use [Kometa Discord](https://kometa.wiki/en/latest/discord/) and post your question under the ` kometa-help` channel for support issues. 13 | - type: input 14 | id: version 15 | attributes: 16 | label: Version Number 17 | description: Can be found in the VERSION file 18 | placeholder: eg. 1.0.1 19 | validations: 20 | required: false 21 | - type: dropdown 22 | id: branch 23 | attributes: 24 | label: What branch are you on? 25 | options: 26 | - master 27 | - develop 28 | validations: 29 | required: true 30 | - type: textarea 31 | id: description 32 | attributes: 33 | label: Describe the Bug 34 | description: A clear and concise description of the bug. 35 | validations: 36 | required: true 37 | 38 | - type: input 39 | id: logs 40 | attributes: 41 | label: Logs 42 | description: > 43 | Please share the relevant log file with the error on [Gist](https://gist.github.com). 44 | placeholder: "https://gist.github.com" -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2.feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Post here if you have an idea about how to improve the script. 3 | title: '[Feature]: ' 4 | type: 'Feature' 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: > 10 | **THIS IS NOT THE PLACE TO ASK FOR SUPPORT!** 11 | Please use [Kometa Discord](https://kometa.wiki/en/latest/discord/) and post your question under the `kometa-help` channel for support issues. 12 | 13 | - type: textarea 14 | id: description 15 | attributes: 16 | label: Describe the Feature Request 17 | description: A clear and concise description of the feature request. 18 | validations: 19 | required: true -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "pip" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | target-branch: "develop" 13 | assignees: 14 | - "meisnate12" 15 | - package-ecosystem: github-actions 16 | directory: '/' 17 | schedule: 18 | interval: daily 19 | target-branch: "develop" 20 | assignees: 21 | - "meisnate12" 22 | ignore: 23 | - dependency-name: "salsify/action-detect-and-tag-new-version" 24 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## What type of PR is this? 2 | 3 | 7 | 8 | - [] Bug Fix (non-breaking change which fixes an issue) 9 | - [] Feature/Tweak (non-breaking change which adds new functionality or enhances existing functionality) 10 | - [] Breaking Change (fix or feature that would break any existing functionality for users) 11 | - [] Documentation Update 12 | - [] Other 13 | 14 | ## Description 15 | 16 | _Please replace this line with a meaningful description of your PR. What does it do? Why? Has it been tested? What were the results?_ 17 | 18 | ## Related Issues [optional] 19 | 20 | 25 | 26 | - Closes # 27 | 28 | ## Which Environment Did You Test On? 29 | 30 | 34 | 35 | - [] Local Install (Windows/Linux/Mac via `python quickstart.py`) 36 | - [] Windows Executable 37 | - [] Linux Executable 38 | - [] macOS Executable 39 | - [] Docker 40 | - [] Other 41 | -------------------------------------------------------------------------------- /.github/workflows/copyright.yml: -------------------------------------------------------------------------------- 1 | name: Update Copyright Year 2 | 3 | on: 4 | schedule: 5 | - cron: '0 3 1 1 *' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | update-copyright: 10 | runs-on: ubuntu-latest 11 | steps: 12 | 13 | - name: Get current year 14 | id: date 15 | run: echo "year=$(date +'%Y')" >> $GITHUB_OUTPUT 16 | 17 | - name: Create App Token 18 | uses: actions/create-github-app-token@v2 19 | id: app-token 20 | with: 21 | app-id: ${{ vars.APP_ID }} 22 | private-key: ${{ secrets.APP_TOKEN }} 23 | 24 | - name: Check Out Repo 25 | uses: actions/checkout@v4 26 | with: 27 | token: ${{ steps.app-token.outputs.token }} 28 | ref: develop 29 | 30 | - name: Update Copyright 31 | run: sed -i -E 's/(Copyright \(c\) ).+( meisnate12)/\1${{ steps.date.outputs.year }}\2/' LICENSE 32 | 33 | - name: Check Diff 34 | id: verify_diff 35 | run: | 36 | git diff --quiet . || echo "changed=true" >> $GITHUB_OUTPUT 37 | 38 | - name: Commit & Push Changes 39 | if: steps.verify_diff.outputs.changed == 'true' 40 | run: | 41 | git config --local user.email "action@github.com" 42 | git config --local user.name "GitHub Action" 43 | git add -A 44 | git commit -m "Copyright updated to ${{ steps.date.outputs.year }}" -a 45 | git push origin develop 46 | 47 | - name: Discord Failure Notification 48 | uses: Kometa-Team/discord-notifications@master 49 | if: failure() 50 | with: 51 | webhook_id_token: ${{ secrets.BUILD_WEBHOOK }} 52 | message: ${{ vars.BUILD_FAILURE_ROLE }} 53 | title: "${{ vars.REPO_NAME }}: ${{ vars.TEXT_COPYRIGHT }}" 54 | url: https://github.com/Kometa-Team/${{ vars.REPO_NAME }}/actions/runs/${{ github.run_id }} 55 | color: ${{ vars.COLOR_FAILURE }} 56 | username: ${{ vars.BOT_NAME }} 57 | avatar_url: ${{ vars.BOT_IMAGE }} 58 | author: ${{ vars.GIT_NAME }} 59 | author_icon_url: ${{ vars.GIT_IMAGE }} -------------------------------------------------------------------------------- /.github/workflows/docker-develop.yml: -------------------------------------------------------------------------------- 1 | name: Docker Develop Build 2 | 3 | on: 4 | workflow_run: 5 | workflows: ["Master Release"] 6 | types: 7 | - completed 8 | 9 | jobs: 10 | 11 | docker-build-develop: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | include: 17 | - platform: 'linux/amd64' 18 | dockerfile: 'Dockerfile' 19 | tag: 'amd64' 20 | - platform: 'linux/arm64' 21 | dockerfile: 'Dockerfile' 22 | tag: 'arm64' 23 | - platform: 'linux/arm/v7' 24 | dockerfile: 'Dockerfile.arm7' 25 | tag: 'armv7' 26 | steps: 27 | 28 | - name: Create App Token 29 | uses: actions/create-github-app-token@v2 30 | id: app-token 31 | with: 32 | app-id: ${{ vars.APP_ID }} 33 | private-key: ${{ secrets.APP_TOKEN }} 34 | 35 | - name: Check Out Repo 36 | uses: actions/checkout@v4 37 | with: 38 | token: ${{ steps.app-token.outputs.token }} 39 | ref: develop 40 | 41 | - name: Login to Docker Hub 42 | uses: docker/login-action@v3 43 | with: 44 | username: ${{ secrets.DOCKER_HUB_USERNAME }} 45 | password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} 46 | 47 | - name: Set up QEMU 48 | uses: docker/setup-qemu-action@master 49 | with: 50 | platforms: all 51 | 52 | - name: Set up Docker Buildx 53 | id: buildx 54 | uses: docker/setup-buildx-action@v3 55 | 56 | - name: Build and Push 57 | id: docker_build 58 | uses: docker/build-push-action@v6 59 | with: 60 | context: ./ 61 | file: ./${{ matrix.dockerfile }} 62 | build-args: | 63 | "BRANCH_NAME=develop" 64 | platforms: ${{ matrix.platform }} 65 | tags: ${{ vars.DOCKER_TEAM }}/${{ vars.DOCKER_REPO }}:develop-${{ matrix.tag }} 66 | push: true 67 | 68 | create-manifest: 69 | runs-on: ubuntu-latest 70 | needs: [ docker-build-develop ] 71 | 72 | steps: 73 | 74 | - name: Create App Token 75 | uses: actions/create-github-app-token@v2 76 | id: app-token 77 | with: 78 | app-id: ${{ vars.APP_ID }} 79 | private-key: ${{ secrets.APP_TOKEN }} 80 | 81 | - name: Check Out Repo 82 | uses: actions/checkout@v4 83 | with: 84 | token: ${{ steps.app-token.outputs.token }} 85 | ref: develop 86 | 87 | - name: Login to Docker Hub 88 | uses: docker/login-action@v3 89 | with: 90 | username: ${{ secrets.DOCKER_HUB_USERNAME }} 91 | password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} 92 | 93 | - name: Set up QEMU 94 | uses: docker/setup-qemu-action@master 95 | with: 96 | platforms: all 97 | 98 | - name: Create develop manifest and push 99 | run: | 100 | docker manifest create \ 101 | ${{ vars.DOCKER_TEAM }}/${{ vars.DOCKER_REPO }}:develop \ 102 | --amend ${{ vars.DOCKER_TEAM }}/${{ vars.DOCKER_REPO }}:develop-amd64 \ 103 | --amend ${{ vars.DOCKER_TEAM }}/${{ vars.DOCKER_REPO }}:develop-arm64 \ 104 | --amend ${{ vars.DOCKER_TEAM }}/${{ vars.DOCKER_REPO }}:develop-armv7 105 | docker manifest push ${{ vars.DOCKER_TEAM }}/${{ vars.DOCKER_REPO }}:develop 106 | 107 | 108 | build-notification: 109 | 110 | runs-on: ubuntu-latest 111 | needs: [ create-manifest ] 112 | steps: 113 | 114 | - name: Discord Success Notification 115 | uses: Kometa-Team/discord-notifications@master 116 | if: success() 117 | with: 118 | webhook_id_token: ${{ secrets.BUILD_WEBHOOK }} 119 | title: "${{ vars.NAME }} develop: ${{ vars.TEXT_SUCCESS }}" 120 | url: https://github.com/Kometa-Team/${{ vars.REPO_NAME }}/actions/runs/${{ github.run_id }} 121 | color: ${{ vars.COLOR_SUCCESS }} 122 | username: ${{ vars.BOT_NAME }} 123 | avatar_url: ${{ vars.BOT_IMAGE }} 124 | author: ${{ vars.DOCKER_NAME }} 125 | author_icon_url: ${{ vars.DOCKER_IMAGE }} 126 | 127 | - name: Discord Failure Notification 128 | uses: Kometa-Team/discord-notifications@master 129 | if: failure() 130 | with: 131 | webhook_id_token: ${{ secrets.BUILD_WEBHOOK }} 132 | message: ${{ vars.BUILD_FAILURE_ROLE }} 133 | title: "${{ vars.NAME }} develop: ${{ vars.TEXT_FAILURE }}" 134 | url: https://github.com/Kometa-Team/${{ vars.REPO_NAME }}/actions/runs/${{ github.run_id }} 135 | color: ${{ vars.COLOR_FAILURE }} 136 | username: ${{ vars.BOT_NAME }} 137 | avatar_url: ${{ vars.BOT_IMAGE }} 138 | author: ${{ vars.DOCKER_NAME }} 139 | author_icon_url: ${{ vars.DOCKER_IMAGE }} 140 | -------------------------------------------------------------------------------- /.github/workflows/docker-latest.yml: -------------------------------------------------------------------------------- 1 | name: Docker Latest Build 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | 7 | jobs: 8 | 9 | docker-build-latest: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | include: 15 | - platform: 'linux/amd64' 16 | dockerfile: 'Dockerfile' 17 | tag: 'amd64' 18 | - platform: 'linux/arm64' 19 | dockerfile: 'Dockerfile' 20 | tag: 'arm64' 21 | - platform: 'linux/arm/v7' 22 | dockerfile: 'Dockerfile.arm7' 23 | tag: 'armv7' 24 | steps: 25 | 26 | - name: Create App Token 27 | uses: actions/create-github-app-token@v2 28 | id: app-token 29 | with: 30 | app-id: ${{ vars.APP_ID }} 31 | private-key: ${{ secrets.APP_TOKEN }} 32 | 33 | - name: Check Out Repo 34 | uses: actions/checkout@v4 35 | with: 36 | token: ${{ steps.app-token.outputs.token }} 37 | 38 | - name: Login to Docker Hub 39 | uses: docker/login-action@v3 40 | with: 41 | username: ${{ secrets.DOCKER_HUB_USERNAME }} 42 | password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} 43 | 44 | - name: Set up QEMU 45 | uses: docker/setup-qemu-action@master 46 | with: 47 | platforms: all 48 | 49 | - name: Set up Docker Buildx 50 | id: buildx 51 | uses: docker/setup-buildx-action@v3 52 | 53 | - name: Build and Push 54 | id: docker_build 55 | uses: docker/build-push-action@v6 56 | with: 57 | context: ./ 58 | file: ./${{ matrix.dockerfile }} 59 | platforms: ${{ matrix.platform }} 60 | tags: ${{ vars.DOCKER_TEAM }}/${{ vars.DOCKER_REPO }}:-${{ matrix.tag }} 61 | 62 | create-manifest: 63 | runs-on: ubuntu-latest 64 | needs: [ docker-build-latest ] 65 | 66 | steps: 67 | 68 | - name: Create App Token 69 | uses: actions/create-github-app-token@v2 70 | id: app-token 71 | with: 72 | app-id: ${{ vars.APP_ID }} 73 | private-key: ${{ secrets.APP_TOKEN }} 74 | 75 | - name: Check Out Repo 76 | uses: actions/checkout@v4 77 | with: 78 | token: ${{ steps.app-token.outputs.token }} 79 | 80 | - name: Login to Docker Hub 81 | uses: docker/login-action@v3 82 | with: 83 | username: ${{ secrets.DOCKER_HUB_USERNAME }} 84 | password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} 85 | 86 | - name: Set up QEMU 87 | uses: docker/setup-qemu-action@master 88 | with: 89 | platforms: all 90 | 91 | - name: Create develop manifest and push 92 | run: | 93 | docker manifest create \ 94 | ${{ vars.DOCKER_TEAM }}/${{ vars.DOCKER_REPO }}:latest \ 95 | --amend ${{ vars.DOCKER_TEAM }}/${{ vars.DOCKER_REPO }}:latest-amd64 \ 96 | --amend ${{ vars.DOCKER_TEAM }}/${{ vars.DOCKER_REPO }}:latest-arm64 \ 97 | --amend ${{ vars.DOCKER_TEAM }}/${{ vars.DOCKER_REPO }}:latest-armv7 98 | docker manifest push ${{ vars.DOCKER_TEAM }}/${{ vars.DOCKER_REPO }}:latest 99 | 100 | 101 | build-notification: 102 | 103 | runs-on: ubuntu-latest 104 | needs: [ create-manifest ] 105 | steps: 106 | 107 | - name: Discord Success Notification 108 | uses: Kometa-Team/discord-notifications@master 109 | if: success() 110 | with: 111 | webhook_id_token: ${{ secrets.BUILD_WEBHOOK }} 112 | title: "${{ vars.NAME }} latest: ${{ vars.TEXT_SUCCESS }}" 113 | url: https://github.com/Kometa-Team/${{ vars.REPO_NAME }}/actions/runs/${{ github.run_id }} 114 | color: ${{ vars.COLOR_SUCCESS }} 115 | username: ${{ vars.BOT_NAME }} 116 | avatar_url: ${{ vars.BOT_IMAGE }} 117 | author: ${{ vars.DOCKER_NAME }} 118 | author_icon_url: ${{ vars.DOCKER_IMAGE }} 119 | 120 | - name: Discord Failure Notification 121 | uses: Kometa-Team/discord-notifications@master 122 | if: failure() 123 | with: 124 | webhook_id_token: ${{ secrets.BUILD_WEBHOOK }} 125 | message: ${{ vars.BUILD_FAILURE_ROLE }} 126 | title: "${{ vars.NAME }} latest: ${{ vars.TEXT_FAILURE }}" 127 | url: https://github.com/Kometa-Team/${{ vars.REPO_NAME }}/actions/runs/${{ github.run_id }} 128 | color: ${{ vars.COLOR_FAILURE }} 129 | username: ${{ vars.BOT_NAME }} 130 | avatar_url: ${{ vars.BOT_IMAGE }} 131 | author: ${{ vars.DOCKER_NAME }} 132 | author_icon_url: ${{ vars.DOCKER_IMAGE }} -------------------------------------------------------------------------------- /.github/workflows/docker-version.yml: -------------------------------------------------------------------------------- 1 | name: Docker Version Build 2 | 3 | on: 4 | create: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | 10 | docker-build-version: 11 | if: ${{ startsWith(github.ref, 'refs/tags/v0') || startsWith(github.ref, 'refs/tags/v1') || startsWith(github.ref, 'refs/tags/v2') }} 12 | runs-on: ubuntu-latest 13 | outputs: 14 | version: ${{ steps.get_version.outputs.VERSION }} 15 | steps: 16 | 17 | - name: Create App Token 18 | uses: actions/create-github-app-token@v2 19 | id: app-token 20 | with: 21 | app-id: ${{ vars.APP_ID }} 22 | private-key: ${{ secrets.APP_TOKEN }} 23 | 24 | - name: Check Out Repo 25 | uses: actions/checkout@v4 26 | with: 27 | token: ${{ steps.app-token.outputs.token }} 28 | fetch-depth: 0 29 | 30 | - name: Login to Docker Hub 31 | uses: docker/login-action@v3 32 | with: 33 | username: ${{ secrets.DOCKER_HUB_USERNAME }} 34 | password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} 35 | 36 | - name: Set up QEMU 37 | uses: docker/setup-qemu-action@master 38 | with: 39 | platforms: all 40 | 41 | - name: Set up Docker Buildx 42 | id: buildx 43 | uses: docker/setup-buildx-action@v3 44 | 45 | - name: Get the version 46 | id: get_version 47 | run: echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT 48 | 49 | - name: Build and Push 50 | id: docker_build 51 | uses: docker/build-push-action@v6 52 | with: 53 | context: ./ 54 | file: ./Dockerfile 55 | platforms: linux/amd64,linux/arm64,linux/arm/v7 56 | push: true 57 | tags: ${{ vars.DOCKER_TEAM }}/${{ vars.DOCKER_REPO }}:${{ steps.get_version.outputs.VERSION }} 58 | cache-from: type=gha 59 | cache-to: type=gha,mode=max 60 | 61 | - name: Discord Success Notification 62 | uses: Kometa-Team/discord-notifications@master 63 | if: success() 64 | with: 65 | webhook_id_token: ${{ secrets.BUILD_WEBHOOK }} 66 | title: "${{ vars.NAME }} ${{ steps.get_version.outputs.VERSION }}: ${{ vars.TEXT_SUCCESS }}" 67 | url: https://github.com/Kometa-Team/${{ vars.REPO_NAME }}/actions/runs/${{ github.run_id }} 68 | color: ${{ vars.COLOR_SUCCESS }} 69 | username: ${{ vars.BOT_NAME }} 70 | avatar_url: ${{ vars.BOT_IMAGE }} 71 | author: ${{ vars.DOCKER_NAME }} 72 | author_icon_url: ${{ vars.DOCKER_IMAGE }} 73 | 74 | - name: Discord Failure Notification 75 | uses: Kometa-Team/discord-notifications@master 76 | if: failure() 77 | with: 78 | webhook_id_token: ${{ secrets.BUILD_WEBHOOK }} 79 | message: ${{ vars.BUILD_FAILURE_ROLE }} 80 | title: "${{ vars.NAME }} ${{ steps.get_version.outputs.VERSION }}: ${{ vars.TEXT_FAILURE }}" 81 | url: https://github.com/Kometa-Team/${{ vars.REPO_NAME }}/actions/runs/${{ github.run_id }} 82 | color: ${{ vars.COLOR_FAILURE }} 83 | username: ${{ vars.BOT_NAME }} 84 | avatar_url: ${{ vars.BOT_IMAGE }} 85 | author: ${{ vars.DOCKER_NAME }} 86 | author_icon_url: ${{ vars.DOCKER_IMAGE }} 87 | 88 | make-releases: 89 | runs-on: ubuntu-latest 90 | needs: [ docker-build-version ] 91 | steps: 92 | 93 | - name: Check Out Repo 94 | uses: actions/checkout@v4 95 | with: 96 | fetch-depth: 0 97 | ref: develop 98 | 99 | - name: Build Changelog 100 | id: build-changelog 101 | run: | 102 | EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) 103 | echo "changelog<<$EOF" >> $GITHUB_OUTPUT 104 | echo "## Commits" >> $GITHUB_OUTPUT 105 | echo "" >> $GITHUB_OUTPUT 106 | echo "$(git log $(git describe --tags --abbrev=0 latest^)..${{ needs.docker-build-version.outputs.version }} --pretty='* [`%h`](https://github.com/Kometa-Team/Quickstart/commit/%H): %s (@%an)' --reverse)" >> $GITHUB_OUTPUT 107 | echo "" >> $GITHUB_OUTPUT 108 | echo "[**Full Code Changelog**](https://github.com/Kometa-Team/Quickstart/compare/$(git describe --tags --abbrev=0 latest^)...${{ needs.docker-build-version.outputs.version }})" >> $GITHUB_OUTPUT 109 | echo "$EOF" >> $GITHUB_OUTPUT 110 | 111 | - name: Delete Old Prelease and Tag 112 | run: gh release delete latest --cleanup-tag 113 | env: 114 | GITHUB_TOKEN: ${{ secrets.PAT }} 115 | 116 | - name: Create Prerelease 117 | uses: ncipollo/release-action@v1 118 | with: 119 | name: ${{ vars.NAME }} ${{ github.event.ref }} Build 0 120 | body: No Current Changes from the Master Branch. 121 | prerelease: true 122 | tag: latest 123 | token: ${{ secrets.PAT }} 124 | 125 | - name: Create Release 126 | uses: ncipollo/release-action@v1 127 | with: 128 | name: ${{ vars.NAME }} ${{ github.event.ref }} 129 | body: ${{ steps.build-changelog.outputs.changelog }} 130 | prerelease: false 131 | tag: ${{ github.event.ref }} 132 | token: ${{ secrets.PAT }} -------------------------------------------------------------------------------- /.github/workflows/release-master.yml: -------------------------------------------------------------------------------- 1 | name: Master Release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | release_type: 7 | type: choice 8 | description: Choose which type of release to perform. 9 | options: 10 | - major 11 | - minor 12 | - patch 13 | default: patch 14 | 15 | jobs: 16 | release-master: 17 | runs-on: ubuntu-latest 18 | steps: 19 | 20 | - name: Create App Token 21 | uses: actions/create-github-app-token@v2 22 | id: app-token 23 | with: 24 | app-id: ${{ vars.APP_ID }} 25 | private-key: ${{ secrets.APP_TOKEN }} 26 | 27 | - name: Check Out Repo 28 | uses: actions/checkout@v4 29 | with: 30 | token: ${{ steps.app-token.outputs.token }} 31 | ref: develop 32 | fetch-depth: 0 33 | 34 | - name: Create Release Commit and Synchronize Branches 35 | run: | 36 | value=$(cat VERSION) 37 | version="${value%-build*}" 38 | echo "CURRENT_VERSION: '${version}'" 39 | IFS='.' read -r MAJOR MINOR PATCH <<< "$version" 40 | 41 | if [[ "${{ github.event.inputs.release_type }}" == "major" ]]; then 42 | NEW_VERSION="$((MAJOR+1)).0.0" 43 | elif [[ "${{ github.event.inputs.release_type }}" == "minor" ]]; then 44 | NEW_VERSION="${MAJOR}.$((MINOR+1)).0" 45 | else 46 | NEW_VERSION="${MAJOR}.${MINOR}.$((PATCH+1))" 47 | fi 48 | 49 | echo "NEW_VERSION='${NEW_VERSION}'" 50 | echo "new_ver=$NEW_VERSION" >> $GITHUB_OUTPUT 51 | echo "$NEW_VERSION" > "VERSION" 52 | 53 | git config --local user.email "action@github.com" 54 | git config --local user.name "GitHub Action" 55 | git add VERSION 56 | git commit -m "${{ vars.NAME }} Release ${NEW_VERSION}" 57 | git push origin develop 58 | git push origin refs/heads/develop:refs/heads/master 59 | 60 | - name: Discord Success Notification 61 | uses: Kometa-Team/discord-notifications@master 62 | if: success() 63 | with: 64 | webhook_id_token: ${{ secrets.BUILD_WEBHOOK }} 65 | title: "${{ vars.NAME }} master release ${{ steps.release.outputs.new_ver }}: ${{ vars.TEXT_SUCCESS }}" 66 | url: https://github.com/Kometa-Team/${{ vars.REPO_NAME }}/actions/runs/${{ github.run_id }} 67 | color: ${{ vars.COLOR_SUCCESS }} 68 | username: ${{ vars.BOT_NAME }} 69 | avatar_url: ${{ vars.BOT_IMAGE }} 70 | author: ${{ vars.GIT_NAME }} 71 | author_icon_url: ${{ vars.GIT_IMAGE }} 72 | 73 | - name: Discord Failure Notification 74 | uses: Kometa-Team/discord-notifications@master 75 | if: failure() 76 | with: 77 | webhook_id_token: ${{ secrets.BUILD_WEBHOOK }} 78 | message: ${{ vars.BUILD_FAILURE_ROLE }} 79 | title: "${{ vars.NAME }} master release ${{ steps.release.outputs.new_ver }}: ${{ vars.TEXT_FAILURE }}" 80 | url: https://github.com/Kometa-Team/${{ vars.REPO_NAME }}/actions/runs/${{ github.run_id }} 81 | color: ${{ vars.COLOR_FAILURE }} 82 | username: ${{ vars.BOT_NAME }} 83 | avatar_url: ${{ vars.BOT_IMAGE }} 84 | author: ${{ vars.GIT_NAME }} 85 | author_icon_url: ${{ vars.GIT_IMAGE }} 86 | -------------------------------------------------------------------------------- /.github/workflows/release-notification.yml: -------------------------------------------------------------------------------- 1 | name: Release Notification 2 | 3 | on: 4 | release: 5 | types: [ published ] 6 | 7 | jobs: 8 | 9 | get-data: 10 | name: Get Data 11 | runs-on: ubuntu-latest 12 | outputs: 13 | version: ${{ steps.load-data.outputs.version }} 14 | commit-msg: ${{ steps.load-data.outputs.commit-msg }} 15 | steps: 16 | 17 | - name: Check Out Repo 18 | uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | 22 | - name: Get Data 23 | id: load-data 24 | run: | 25 | echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT 26 | echo "commit-msg=$(git log -1 HEAD --pretty=format:%s)" >> $GITHUB_OUTPUT 27 | 28 | build-releases: 29 | name: Build ${{ matrix.os_upper }} Installer 30 | runs-on: ${{ matrix.os }}-${{ matrix.os_version }} 31 | needs: [ get-data ] 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | include: 36 | - os: 'windows' 37 | os_upper: 'Windows' 38 | os_version: 'latest' 39 | ext: '.exe' 40 | - os: 'ubuntu' 41 | os_upper: 'Linux' 42 | os_version: 'latest' 43 | ext: '' 44 | - os: 'macos' 45 | os_upper: 'MacOS' 46 | os_version: '13' 47 | ext: '' 48 | 49 | steps: 50 | 51 | - name: Check Out Repo 52 | uses: actions/checkout@v4 53 | with: 54 | fetch-depth: 0 55 | 56 | - name: Setup Python 57 | uses: actions/setup-python@v5 58 | with: 59 | python-version: '3.13' 60 | 61 | - name: Install Requirements 62 | run: pip install -r requirements.txt pyinstaller 63 | 64 | - name: Build Executable 65 | shell: bash 66 | run: | 67 | tag_name=${{github.event.release.tag_name}} 68 | if [[ "${tag_name}" =~ ^(latest)$ ]]; then 69 | pyinstaller -y ./quickstart.spec -- --branch develop --build ${{ runner.os }} 70 | version_value=${{ needs.get-data.outputs.version }} 71 | else 72 | pyinstaller -y ./quickstart.spec -- --build ${{ runner.os }} 73 | version_value=${tag_name} 74 | fi 75 | mv dist/Quickstart${{ matrix.ext }} dist/Quickstart-${version_value}-${{ runner.os }}${{ matrix.ext }} 76 | gh release upload ${tag_name} dist/Quickstart-${version_value}-${{ runner.os }}${{ matrix.ext }} 77 | env: 78 | GITHUB_TOKEN: ${{ github.TOKEN }} 79 | 80 | release-notification: 81 | 82 | runs-on: ubuntu-latest 83 | needs: [ get-data, build-releases ] 84 | steps: 85 | 86 | - name: Send Discord Commit Notification 87 | if: github.event.release.prerelease 88 | uses: Kometa-Team/discord-notifications@master 89 | with: 90 | webhook_id_token: ${{ secrets.DEVELOP_WEBHOOK }} 91 | title: ${{ github.event.release.name }} 92 | url: https://github.com/Kometa-Team/Quickstart/releases/tag/latest 93 | description: ${{ needs.get-data.outputs.commit-msg }} 94 | message: ${{ vars.DEVELOP_ROLE }} - An update to ${{ vars.NAME }} has been published and is available to users of the **develop** branch. 95 | color: ${{ vars.COLOR_SUCCESS }} 96 | username: ${{ vars.BOT_NAME }} 97 | avatar_url: ${{ vars.BOT_IMAGE }} 98 | author: ${{ vars.NAME }} Develop Release 99 | author_icon_url: ${{ vars.RELEASE_IMAGE }} 100 | 101 | - name: Send Discord Release Notification 102 | if: "!github.event.release.prerelease" 103 | uses: Kometa-Team/discord-notifications@master 104 | with: 105 | webhook_id_token: ${{ secrets.RELEASE_WEBHOOK }} 106 | release: true 107 | title: Release VERSION 108 | message: ${{ vars.MASTER_ROLE }} - A new version of ${{ vars.NAME }} has been released and is available to all users 109 | color: ${{ vars.COLOR_SUCCESS }} 110 | username: ${{ vars.BOT_NAME }} 111 | avatar_url: ${{ vars.BOT_IMAGE }} 112 | author: ${{ vars.NAME }} Release 113 | author_icon_url: ${{ vars.RELEASE_IMAGE }} 114 | -------------------------------------------------------------------------------- /.github/workflows/tag-cleanup.yml: -------------------------------------------------------------------------------- 1 | name: Tag Cleanup 2 | 3 | on: 4 | pull_request_target: 5 | types: [closed, unlabeled] 6 | 7 | jobs: 8 | 9 | tag-cleanup: 10 | runs-on: ubuntu-latest 11 | if: github.event.action == 'closed' || contains(github.event.pull_request.labels.*.name, 'docker') == false 12 | steps: 13 | 14 | - name: Get Tag Name 15 | id: get-tag-name 16 | run: | 17 | branch_name=${{ github.event.pull_request.head.ref }} 18 | base_name="${repo_name%/*}" 19 | if [[ "${branch_name}" =~ ^(master|develop|nightly)$ ]]; then 20 | tag_name="${base_name}" 21 | else 22 | tag_name="${branch_name}" 23 | fi 24 | echo "tag-name=${tag_name}" >> $GITHUB_OUTPUT 25 | 26 | - name: remove tag 27 | run: | 28 | HUB_TOKEN=$(curl -s -H "Content-Type: application/json" -X POST -d "{\"username\": \"${{ secrets.DOCKER_HUB_USERNAME }}\", \"password\": \"${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}\"}" https://hub.docker.com/v2/users/login/ | jq -r .token) 29 | curl -i -X DELETE \ 30 | -H "Accept: application/json" \ 31 | -H "Authorization: JWT $HUB_TOKEN" \ 32 | https://hub.docker.com/v2/repositories/${{ vars.DOCKER_TEAM }}/${{ vars.DOCKER_REPO }}/tags/${{ steps.get-tag-name.outputs.tag_name }}/ 33 | -------------------------------------------------------------------------------- /.github/workflows/tag-new-version.yml: -------------------------------------------------------------------------------- 1 | name: Tag New Version 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | 7 | jobs: 8 | tag: 9 | runs-on: ubuntu-latest 10 | steps: 11 | 12 | - uses: actions/checkout@v4 13 | with: 14 | token: ${{ secrets.PAT }} 15 | fetch-depth: 2 16 | 17 | - uses: Kometa-Team/tag-new-version@master 18 | with: 19 | version-command: | 20 | cat VERSION -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore compiled Python files 2 | *.pyc 3 | 4 | # Ignore sqlite databases 5 | *.sqlite 6 | 7 | # Ignore quickstart database 8 | quickstart.db 9 | 10 | # Ignore Python bytecode cache 11 | __pycache__/ 12 | 13 | # Ignore Python virtual environments 14 | venv/ 15 | env/ 16 | 17 | # Ignore session files 18 | flask_session/ 19 | sessions/ 20 | 21 | # Ignore gotify file 22 | gotify.db 23 | 24 | # Ignore environment variables file 25 | .env 26 | 27 | # Ignore local configuration file 28 | config.yml 29 | 30 | # Ignore IDE files 31 | .idea/ 32 | 33 | # Ignore direnv venv files 34 | .direnv/ 35 | 36 | # Ignore logs 37 | logs/ 38 | 39 | # Ignore macOS system files 40 | .DS_Store 41 | 42 | # Ignore these direct 43 | uploads/ 44 | .vscode/ 45 | config/ 46 | json-schema/ 47 | build/ 48 | dist/ 49 | favicon.ico -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | exclude: '^.*bootstrap.bundle.js$' 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: v5.0.0 5 | hooks: 6 | - id: check-yaml 7 | - id: end-of-file-fixer 8 | - id: trailing-whitespace 9 | - id: check-case-conflict 10 | - id: check-json 11 | - id: check-merge-conflict 12 | - id: no-commit-to-branch 13 | - id: requirements-txt-fixer 14 | - repo: https://github.com/standard/standard 15 | rev: v17.1.2 16 | hooks: 17 | - id: standard 18 | -------------------------------------------------------------------------------- /.spellcheck.yml: -------------------------------------------------------------------------------- 1 | matrix: 2 | - name: Markdown 3 | sources: 4 | - '*.md' 5 | aspell: 6 | lang: en 7 | ignore-case: true 8 | dictionary: 9 | wordlists: 10 | - .github/.wordlist.txt 11 | encoding: utf-8 12 | pipeline: 13 | - pyspelling.filters.markdown: 14 | - pyspelling.filters.html: 15 | comments: false 16 | ignores: 17 | - code 18 | - pre 19 | default_encoding: utf-8 -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11-slim-buster 2 | ARG BRANCH_NAME=master 3 | ENV BRANCH_NAME=${BRANCH_NAME} 4 | ENV TINI_VERSION=v0.19.0 5 | ENV QUICKSTART_DOCKER=True 6 | COPY requirements.txt requirements.txt 7 | RUN echo "**** install system packages ****" \ 8 | && apt-get update \ 9 | && apt-get upgrade -y --no-install-recommends \ 10 | && apt-get install -y tzdata --no-install-recommends \ 11 | && apt-get install -y gcc g++ libxml2-dev libxslt-dev libz-dev libjpeg62-turbo-dev zlib1g-dev wget curl ffmpeg libsm6 libxext6 \ 12 | && wget -O /tini https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-"$(dpkg --print-architecture | awk -F- '{ print $NF }')" \ 13 | && chmod +x /tini \ 14 | && pip3 install --no-cache-dir --upgrade --requirement /requirements.txt \ 15 | && apt-get --purge autoremove gcc g++ libxml2-dev libxslt-dev libz-dev -y \ 16 | && apt-get clean \ 17 | && apt-get update \ 18 | && apt-get check \ 19 | && apt-get -f install \ 20 | && apt-get autoclean \ 21 | && rm -rf /requirements.txt /tmp/* /var/tmp/* /var/lib/apt/lists/* 22 | COPY . / 23 | VOLUME /config 24 | ENTRYPOINT ["/tini", "-s", "python3", "quickstart.py", "--"] -------------------------------------------------------------------------------- /Dockerfile.arm7: -------------------------------------------------------------------------------- 1 | FROM python:3.11-slim-buster 2 | ARG BRANCH_NAME=master 3 | ENV BRANCH_NAME=${BRANCH_NAME} 4 | ENV TINI_VERSION=v0.19.0 5 | ENV QUICKSTART_DOCKER=True 6 | COPY requirements.txt requirements.txt 7 | RUN echo "**** install system packages ****" \ 8 | && apt-get update \ 9 | && apt-get upgrade -y --no-install-recommends \ 10 | && apt-get install -y tzdata --no-install-recommends \ 11 | && apt-get install -y gcc g++ libxml2-dev libxslt-dev libz-dev libjpeg62-turbo-dev zlib1g-dev wget curl ffmpeg libsm6 libxext6 \ 12 | && wget -O /tini https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-"$(dpkg --print-architecture | awk -F- '{ print $NF }')" \ 13 | && chmod +x /tini \ 14 | && grep -v -i '^PyQt5' /requirements.txt > /tmp/filtered.txt \ 15 | && pip3 install --no-cache-dir --upgrade --requirement /tmp/filtered.txt \ 16 | && apt-get --purge autoremove gcc g++ libxml2-dev libxslt-dev libz-dev -y \ 17 | && apt-get clean \ 18 | && apt-get update \ 19 | && apt-get check \ 20 | && apt-get -f install \ 21 | && apt-get autoclean \ 22 | && rm -rf /requirements.txt /tmp/* /var/tmp/* /var/lib/apt/lists/* 23 | COPY . / 24 | VOLUME /config 25 | ENTRYPOINT ["/tini", "-s", "python3", "quickstart.py", "--"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 meisnate12 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. 22 | -------------------------------------------------------------------------------- /PART: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.8.6 2 | -------------------------------------------------------------------------------- /modules/database.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pickle 3 | import sqlite3 4 | from contextlib import closing 5 | 6 | from modules import helpers 7 | 8 | 9 | def get_database_path(): 10 | return os.path.join(helpers.CONFIG_DIR, "quickstart.sqlite") 11 | 12 | 13 | def persisted_section_table_create(): 14 | return """CREATE TABLE IF NOT EXISTS section_data ( 15 | name TEXT NOT NULL, 16 | section TEXT NOT NULL, 17 | validated BOOLEAN NOT NULL, 18 | user_entered BOOLEAN NOT NULL, 19 | data TEXT, 20 | PRIMARY KEY (name, section) 21 | )""" 22 | 23 | 24 | def save_section_data(section, validated, user_entered, data, name="default"): 25 | with sqlite3.connect(get_database_path(), detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES) as connection: 26 | connection.row_factory = sqlite3.Row 27 | with closing(connection.cursor()) as cursor: 28 | cursor.execute(persisted_section_table_create()) 29 | pickled_data = pickle.dumps(data) 30 | 31 | cursor.execute( 32 | """INSERT OR IGNORE INTO 33 | section_data(name, section, validated, user_entered, data) 34 | VALUES (?, ?, ?, ?, ?)""", 35 | (name, section, validated, user_entered, pickled_data), 36 | ) 37 | 38 | cursor.execute( 39 | """UPDATE section_data 40 | SET validated = ?, user_entered = ?, data = ? 41 | WHERE name == ? AND section == ?""", 42 | (validated, user_entered, pickled_data, name, section), 43 | ) 44 | 45 | 46 | def retrieve_section_data(name, section): 47 | with sqlite3.connect(get_database_path(), detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES) as connection: 48 | connection.row_factory = sqlite3.Row 49 | with closing(connection.cursor()) as cursor: 50 | cursor.execute(persisted_section_table_create()) 51 | cursor.execute( 52 | """SELECT validated, user_entered, data from section_data where name == ? AND section == ?""", 53 | (name, section), 54 | ) 55 | row = cursor.fetchone() 56 | if row: 57 | return ( 58 | helpers.booler(row["validated"]), 59 | helpers.booler(row["user_entered"]), 60 | pickle.loads(row["data"]), 61 | ) 62 | return False, False, None 63 | 64 | 65 | def reset_data(name, section=None): 66 | with sqlite3.connect(get_database_path(), detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES) as connection: 67 | connection.row_factory = sqlite3.Row 68 | with closing(connection.cursor()) as cursor: 69 | sql = "DELETE from section_data where name == ?" 70 | if section: 71 | cursor.execute(f"{sql} AND section == ?", (name, section)) 72 | else: 73 | cursor.execute(sql, (name,)) 74 | 75 | 76 | def get_unique_config_names(): 77 | with sqlite3.connect(get_database_path(), detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES) as connection: 78 | connection.row_factory = sqlite3.Row 79 | with closing(connection.cursor()) as cursor: 80 | cursor.execute("SELECT DISTINCT name FROM section_data ORDER BY name ASC") 81 | return [row["name"] for row in cursor.fetchall()] 82 | -------------------------------------------------------------------------------- /modules/hooks/develop.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | os.environ["BRANCH_NAME"] = "develop" 4 | -------------------------------------------------------------------------------- /modules/hooks/linux.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | os.environ["BUILD_OS"] = "linux" 4 | -------------------------------------------------------------------------------- /modules/hooks/macos.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | os.environ["BUILD_OS"] = "macos" 4 | -------------------------------------------------------------------------------- /modules/hooks/pull.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | os.environ["BRANCH_NAME"] = "pull" 4 | -------------------------------------------------------------------------------- /modules/hooks/windows.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | os.environ["BUILD_OS"] = "windows" 4 | -------------------------------------------------------------------------------- /modules/iso.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import io 3 | 4 | import requests 5 | 6 | _country_url = "https://raw.githubusercontent.com/datasets/country-codes/refs/heads/main/data/country-codes.csv" 7 | _language_url = "https://raw.githubusercontent.com/datasets/language-codes/refs/heads/main/data/language-codes-full.csv" 8 | _tag_url = "https://raw.githubusercontent.com/datasets/language-codes/refs/heads/main/data/ietf-language-tags.csv" 9 | 10 | 11 | def _read_csv(url): 12 | response = requests.get(url) 13 | csv_file = io.StringIO(response.text) 14 | return list(csv.reader(csv_file)) 15 | 16 | 17 | _tag_dict = {} 18 | for c in _read_csv(_tag_url): 19 | if c[1] not in _tag_dict: 20 | _tag_dict[c[1]] = [] 21 | if c[0] not in _tag_dict[c[1]]: 22 | _tag_dict[c[1]].append(c[0]) 23 | 24 | 25 | class Country: 26 | 27 | def __init__(self, data): 28 | self.alpha3 = data[2] 29 | self.alpha2 = data[9] 30 | self.formal = data[38] 31 | self.name = data[40] 32 | self.region = data[43] 33 | self.subregion = data[45] 34 | self.continent = data[49] 35 | self.languages = data[51].split(",") 36 | 37 | def __str__(self): 38 | return self.name 39 | 40 | def __repr__(self): 41 | return self.__str__() 42 | 43 | def __eq__(self, other): 44 | if isinstance(other, Country): 45 | return self.name == other.name 46 | else: 47 | return str(other) in [ 48 | self.name, 49 | self.alpha2, 50 | self.alpha3, 51 | self.alpha2.lower(), 52 | self.alpha3.lower(), 53 | ] 54 | 55 | 56 | class Languages: 57 | 58 | def __init__(self, data): 59 | self.alpha3 = data[0] 60 | self.alpha2 = data[2] if data[2] else None 61 | self.names = data[3].split("; ") 62 | self.name = self.names[0] 63 | if self.alpha2 in _tag_dict: 64 | self.tags = _tag_dict[self.alpha2] 65 | elif self.alpha3 in _tag_dict: 66 | self.tags = _tag_dict[self.alpha3] 67 | else: 68 | self.tags = [] 69 | 70 | def __str__(self): 71 | return self.name 72 | 73 | def __repr__(self): 74 | return self.__str__() 75 | 76 | def __eq__(self, other): 77 | if isinstance(other, Country): 78 | return self.name == other.name 79 | else: 80 | return str(other) in self.names + [ 81 | self.alpha2, 82 | self.alpha3, 83 | self.alpha2.lower(), 84 | self.alpha3.lower(), 85 | ] 86 | 87 | 88 | countries = [Country(c) for c in _read_csv(_country_url)] 89 | languages = [Languages(c) for c in _read_csv(_language_url)] 90 | 91 | 92 | def get_country(name=None, alpha2=None, alpha3=None): 93 | if all(x is None for x in [name, alpha2, alpha3]): 94 | raise ValueError("Either name, alpha2, or alpha3 is required") 95 | for country in countries: 96 | if name == country.name or str(alpha2).upper() == country.alpha2 or str(alpha3).upper() == country.alpha3: 97 | return country 98 | raise NameError("No Country found") 99 | 100 | 101 | def get_language(name=None, alpha2=None, alpha3=None): 102 | if all(x is None for x in [name, alpha2, alpha3]): 103 | raise ValueError("Either name, alpha2, or alpha3 is required") 104 | for language in languages: 105 | if name in language.names or str(alpha2).lower() == language.alpha2 or str(alpha3).lower() == language.alpha3: 106 | return language 107 | raise NameError("No Language found") 108 | -------------------------------------------------------------------------------- /quickstart.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | import argparse 3 | from PyInstaller.utils.hooks import collect_submodules 4 | 5 | parser = argparse.ArgumentParser() 6 | parser.add_argument("--installer", type=str, default="Quickstart") 7 | parser.add_argument("--branch", type=str, default="master") 8 | parser.add_argument("--build", type=str, default="windows") 9 | options = parser.parse_args() 10 | 11 | runtime_hooks = [] 12 | if options.branch == "develop": 13 | runtime_hooks.append('./modules/hooks/develop.py') 14 | elif options.branch == "pull": 15 | runtime_hooks.append('./modules/hooks/pull.py') 16 | 17 | if options.build == "ubuntu": 18 | runtime_hooks.append('./modules/hooks/linux.py') 19 | elif options.build == "macos": 20 | runtime_hooks.append('./modules/hooks/macos.py') 21 | else: 22 | runtime_hooks.append('./modules/hooks/windows.py') 23 | 24 | hiddenimports = ['flask', 'flask.cli', 'werkzeug', 'pyfiglet', 'pyfiglet.fonts'] 25 | hiddenimports += collect_submodules('flask') 26 | hiddenimports += collect_submodules('werkzeug') 27 | 28 | 29 | a = Analysis( 30 | ['quickstart.py'], 31 | pathex=[], 32 | binaries=[], 33 | datas=[ 34 | ('VERSION', '.'), 35 | ('static/fonts', 'pyfiglet/fonts'), 36 | ('static/json', 'static/json'), 37 | ('static', 'static'), 38 | ('templates', 'templates'), 39 | ('modules', 'modules'), 40 | ('.env.example', '.') 41 | ], 42 | hiddenimports=hiddenimports, 43 | hookspath=[], 44 | hooksconfig={}, 45 | runtime_hooks=runtime_hooks, 46 | excludes=['.env'], 47 | noarchive=False, 48 | optimize=0, 49 | ) 50 | pyz = PYZ(a.pure) 51 | 52 | exe = EXE( 53 | pyz, 54 | a.scripts, 55 | a.binaries, 56 | a.datas, 57 | [], 58 | name=options.installer, 59 | debug=False, 60 | bootloader_ignore_signals=False, 61 | strip=False, 62 | upx=True, 63 | upx_exclude=[], 64 | runtime_tmpdir=None, 65 | console=False, 66 | disable_windowed_traceback=False, 67 | argv_emulation=False, 68 | target_arch=None, 69 | codesign_identity=None, 70 | entitlements_file=None, 71 | icon=['static\\favicon.ico'], 72 | ) 73 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | cachelib==0.13.0 2 | Flask==3.1.0 3 | Flask-Session==0.8.0 4 | GitPython==3.1.44 5 | gunicorn==23.0.0 6 | jsonschema==4.23.0 7 | namesgenerator==0.3 8 | pillow==11.2.1 9 | PlexAPI==4.17.0 10 | pre-commit==4.1.0 11 | pyfiglet==1.0.2 12 | PyQt5==5.15.11; platform_system != "Linux" or platform_machine != "aarch64" 13 | python-dotenv==1.1.0 14 | requests==2.32.3 15 | ruamel.yaml==0.18.10 16 | waitress==3.0.2 17 | Werkzeug==3.1.3 18 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/favicon.ico -------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/favicon.png -------------------------------------------------------------------------------- /static/fonts/ghost.flf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/fonts/ghost.flf -------------------------------------------------------------------------------- /static/images/default-episode_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/default-episode_preview.png -------------------------------------------------------------------------------- /static/images/default-episode_preview.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/default-episode_preview.webp -------------------------------------------------------------------------------- /static/images/default-season_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/default-season_preview.png -------------------------------------------------------------------------------- /static/images/default-season_preview.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/default-season_preview.webp -------------------------------------------------------------------------------- /static/images/default-sho_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/default-sho_preview.png -------------------------------------------------------------------------------- /static/images/default-sho_preview.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/default-sho_preview.webp -------------------------------------------------------------------------------- /static/images/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/default.png -------------------------------------------------------------------------------- /static/images/default_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/default_grey.png -------------------------------------------------------------------------------- /static/images/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/header.png -------------------------------------------------------------------------------- /static/images/logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/logo.webp -------------------------------------------------------------------------------- /static/images/overlays/epi-sho-episode-overlay_aspect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/epi-sho-episode-overlay_aspect.png -------------------------------------------------------------------------------- /static/images/overlays/epi-sho-episode-overlay_audio_codec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/epi-sho-episode-overlay_audio_codec.png -------------------------------------------------------------------------------- /static/images/overlays/epi-sho-episode-overlay_content_rating_au.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/epi-sho-episode-overlay_content_rating_au.png -------------------------------------------------------------------------------- /static/images/overlays/epi-sho-episode-overlay_content_rating_commonsense.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/epi-sho-episode-overlay_content_rating_commonsense.png -------------------------------------------------------------------------------- /static/images/overlays/epi-sho-episode-overlay_content_rating_de.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/epi-sho-episode-overlay_content_rating_de.png -------------------------------------------------------------------------------- /static/images/overlays/epi-sho-episode-overlay_content_rating_nz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/epi-sho-episode-overlay_content_rating_nz.png -------------------------------------------------------------------------------- /static/images/overlays/epi-sho-episode-overlay_content_rating_uk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/epi-sho-episode-overlay_content_rating_uk.png -------------------------------------------------------------------------------- /static/images/overlays/epi-sho-episode-overlay_content_rating_us_show.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/epi-sho-episode-overlay_content_rating_us_show.png -------------------------------------------------------------------------------- /static/images/overlays/epi-sho-episode-overlay_direct_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/epi-sho-episode-overlay_direct_play.png -------------------------------------------------------------------------------- /static/images/overlays/epi-sho-episode-overlay_episode_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/epi-sho-episode-overlay_episode_info.png -------------------------------------------------------------------------------- /static/images/overlays/epi-sho-episode-overlay_language_count.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/epi-sho-episode-overlay_language_count.png -------------------------------------------------------------------------------- /static/images/overlays/epi-sho-episode-overlay_languages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/epi-sho-episode-overlay_languages.png -------------------------------------------------------------------------------- /static/images/overlays/epi-sho-episode-overlay_network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/epi-sho-episode-overlay_network.png -------------------------------------------------------------------------------- /static/images/overlays/epi-sho-episode-overlay_resolution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/epi-sho-episode-overlay_resolution.png -------------------------------------------------------------------------------- /static/images/overlays/epi-sho-episode-overlay_runtimes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/epi-sho-episode-overlay_runtimes.png -------------------------------------------------------------------------------- /static/images/overlays/epi-sho-episode-overlay_studio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/epi-sho-episode-overlay_studio.png -------------------------------------------------------------------------------- /static/images/overlays/epi-sho-episode-overlay_versions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/epi-sho-episode-overlay_versions.png -------------------------------------------------------------------------------- /static/images/overlays/epi-sho-episode-overlay_video_format.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/epi-sho-episode-overlay_video_format.png -------------------------------------------------------------------------------- /static/images/overlays/epi-sho-overlay_languages_use_subtitles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/epi-sho-overlay_languages_use_subtitles.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_aspect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_aspect.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_audio_codec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_audio_codec.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_content_rating_au.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_content_rating_au.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_content_rating_commonsense.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_content_rating_commonsense.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_content_rating_de.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_content_rating_de.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_content_rating_nz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_content_rating_nz.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_content_rating_uk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_content_rating_uk.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_content_rating_us_movie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_content_rating_us_movie.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_direct_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_direct_play.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_language_count.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_language_count.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_languages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_languages.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_mediastinger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_mediastinger.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_ratings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_ratings.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_resolution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_resolution.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_ribbon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_ribbon.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_runtimes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_runtimes.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_status.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_status.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_streaming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_streaming.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_studio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_studio.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_versions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_versions.png -------------------------------------------------------------------------------- /static/images/overlays/mov-movie-overlay_video_format.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/mov-movie-overlay_video_format.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_aspect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_aspect.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_audio_codec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_audio_codec.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_content_rating_au.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_content_rating_au.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_content_rating_commonsense.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_content_rating_commonsense.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_content_rating_de.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_content_rating_de.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_content_rating_nz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_content_rating_nz.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_content_rating_uk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_content_rating_uk.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_content_rating_us_show.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_content_rating_us_show.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_direct_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_direct_play.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_language_count.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_language_count.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_languages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_languages.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_mediastinger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_mediastinger.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_network.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_ratings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_ratings.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_resolution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_resolution.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_ribbon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_ribbon.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_runtimes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_runtimes.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_status.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_status.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_streaming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_streaming.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_studio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_studio.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_versions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_versions.png -------------------------------------------------------------------------------- /static/images/overlays/sho-season-season-overlay_video_format.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-season-season-overlay_video_format.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_aspect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_aspect.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_audio_codec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_audio_codec.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_content_rating_au.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_content_rating_au.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_content_rating_commonsense.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_content_rating_commonsense.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_content_rating_de.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_content_rating_de.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_content_rating_nz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_content_rating_nz.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_content_rating_uk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_content_rating_uk.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_content_rating_us_show.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_content_rating_us_show.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_direct_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_direct_play.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_language_count.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_language_count.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_languages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_languages.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_mediastinger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_mediastinger.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_network.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_ratings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_ratings.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_resolution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_resolution.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_ribbon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_ribbon.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_runtimes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_runtimes.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_status.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_status.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_streaming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_streaming.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_studio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_studio.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_versions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_versions.png -------------------------------------------------------------------------------- /static/images/overlays/sho-show-overlay_video_format.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/overlays/sho-show-overlay_video_format.png -------------------------------------------------------------------------------- /static/images/qs_defaults_landscape.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/qs_defaults_landscape.xcf -------------------------------------------------------------------------------- /static/images/running-in-pwsh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/running-in-pwsh.png -------------------------------------------------------------------------------- /static/images/system-tray-launcher-mac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/system-tray-launcher-mac.png -------------------------------------------------------------------------------- /static/images/system-tray-launcher-ubuntu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/system-tray-launcher-ubuntu.png -------------------------------------------------------------------------------- /static/images/system-tray-launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/system-tray-launcher.png -------------------------------------------------------------------------------- /static/images/wizard.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kometa-Team/Quickstart/16dd8e9fc9c0907fc95b22b29db9ce07df06d28e/static/images/wizard.webp -------------------------------------------------------------------------------- /static/local-js/027-playlist_files.js: -------------------------------------------------------------------------------- 1 | /* global $ */ 2 | 3 | $(document).ready(function () { 4 | const plexValid = $('#plex_valid').length > 0 && $('#plex_valid').data('plex-valid') === 'True' 5 | console.log('Plex Valid:', plexValid) 6 | 7 | if (!plexValid) { 8 | $('#libraries-container').hide() 9 | $('#validation-messages').html( 10 | 'Plex settings have not been validated successfully. Please return to the Plex page and hit the validate button and ensure success before returning here.
' 11 | ).show() 12 | } else { 13 | $('#libraries-container').show() 14 | $('#validation-messages').hide() 15 | } 16 | 17 | // Initialize checkboxes based on preselected libraries 18 | const selectedLibraries = $('#libraries').val().split(',').map(item => item.trim()) 19 | console.log('Preselected Libraries:', selectedLibraries) 20 | $('.library-checkbox').each(function () { 21 | if (selectedLibraries.includes($(this).val())) { 22 | $(this).prop('checked', true) 23 | } 24 | }) 25 | 26 | // Update hidden input field when checkboxes are changed 27 | $('.library-checkbox').change(function () { 28 | const updatedLibraries = [] 29 | $('.library-checkbox:checked').each(function () { 30 | updatedLibraries.push($(this).val()) 31 | }) 32 | $('#libraries').val(updatedLibraries.join(', ')) 33 | updateValidationState(updatedLibraries.length > 0) 34 | console.log('Updated Libraries:', updatedLibraries) 35 | }) 36 | 37 | // Update validation state 38 | function updateValidationState (isValid) { 39 | $('#playlist_files_validated').val(isValid ? 'true' : 'false') 40 | console.log('Validation State Updated:', isValid) 41 | } 42 | 43 | // Preserve data on form submission for backend processing 44 | $('#configForm').on('submit', function () { 45 | // Log the data being submitted for debugging 46 | console.log('Form Submitted:') 47 | console.log('Default:', $('#default').val()) 48 | console.log('Libraries:', $('#libraries').val()) 49 | console.log('Validated:', $('#playlist_files_validated').val()) 50 | console.log('Template Variables:', $('#template_variables').val()) 51 | }) 52 | }) 53 | -------------------------------------------------------------------------------- /static/local-js/030-tautulli.js: -------------------------------------------------------------------------------- 1 | /* global $, showSpinner, hideSpinner */ 2 | 3 | $(document).ready(function () { 4 | const apiKeyInput = document.getElementById('tautulli_apikey') 5 | const urlInput = document.getElementById('tautulli_url') 6 | const validateButton = document.getElementById('validateButton') 7 | const toggleButton = document.getElementById('toggleApikeyVisibility') 8 | const isValidated = document.getElementById('tautulli_validated').value.toLowerCase() 9 | 10 | console.log('Validated: ' + isValidated) 11 | 12 | // Set initial visibility based on API key value 13 | if (apiKeyInput.value.trim() === '') { 14 | apiKeyInput.setAttribute('type', 'text') // Show placeholder text 15 | toggleButton.innerHTML = '' // Set eye icon 16 | } else { 17 | apiKeyInput.setAttribute('type', 'password') // Hide actual key 18 | toggleButton.innerHTML = '' // Set eye-slash icon 19 | } 20 | 21 | // Disable validate button if already validated 22 | validateButton.disabled = isValidated === 'true' 23 | 24 | // Reset validation status when user types 25 | apiKeyInput.addEventListener('input', function () { 26 | document.getElementById('tautulli_validated').value = 'false' 27 | validateButton.disabled = false 28 | }) 29 | 30 | urlInput.addEventListener('input', function () { 31 | document.getElementById('tautulli_validated').value = 'false' 32 | validateButton.disabled = false 33 | }) 34 | }) 35 | 36 | document.getElementById('toggleApikeyVisibility').addEventListener('click', function () { 37 | const apikeyInput = document.getElementById('tautulli_apikey') 38 | const currentType = apikeyInput.getAttribute('type') 39 | apikeyInput.setAttribute('type', currentType === 'password' ? 'text' : 'password') 40 | this.innerHTML = currentType === 'password' ? '' : '' 41 | }) 42 | 43 | // Tautulli validation script 44 | document.getElementById('validateButton').addEventListener('click', function () { 45 | const tautulliUrl = document.getElementById('tautulli_url').value 46 | const tautulliApikey = document.getElementById('tautulli_apikey').value 47 | const statusMessage = document.getElementById('statusMessage') 48 | 49 | if (!tautulliUrl || !tautulliApikey) { 50 | statusMessage.textContent = 'Please enter both Tautulli URL and API Key.' 51 | statusMessage.style.display = 'block' 52 | return 53 | } 54 | showSpinner('validate') 55 | fetch('/validate_tautulli', { 56 | method: 'POST', 57 | headers: { 58 | 'Content-Type': 'application/json' 59 | }, 60 | body: JSON.stringify({ 61 | tautulli_url: tautulliUrl, 62 | tautulli_apikey: tautulliApikey 63 | }) 64 | }) 65 | .then((response) => response.json()) 66 | .then((data) => { 67 | if (data.valid) { 68 | hideSpinner('validate') 69 | document.getElementById('tautulli_validated').value = 'true' 70 | statusMessage.textContent = 'Tautulli server validated successfully!' 71 | statusMessage.style.color = '#75b798' 72 | document.getElementById('validateButton').disabled = true 73 | } else { 74 | document.getElementById('tautulli_validated').value = 'false' 75 | statusMessage.textContent = 'Failed to validate Tautulli server. Please check your URL and API Key.' 76 | statusMessage.style.color = '#ea868f' 77 | } 78 | statusMessage.style.display = 'block' 79 | }) 80 | .catch((error) => { 81 | console.error('Error validating Tautulli server:', error) 82 | statusMessage.textContent = 'An error occurred while validating Tautulli server.' 83 | statusMessage.style.color = '#ea868f' 84 | statusMessage.style.display = 'block' 85 | }) 86 | .finally(() => { 87 | hideSpinner('validate') 88 | }) 89 | }) 90 | 91 | document.getElementById('configForm').addEventListener('submit', function () { 92 | const apiKeyInput = document.getElementById('tautulli_apikey') 93 | const urlInput = document.getElementById('tautulli_url') 94 | if (!apiKeyInput.value) { 95 | apiKeyInput.value = '' 96 | } 97 | if (!urlInput.value) { 98 | urlInput.value = 'http://' 99 | } 100 | }) 101 | -------------------------------------------------------------------------------- /static/local-js/040-github.js: -------------------------------------------------------------------------------- 1 | /* global $, showSpinner, hideSpinner */ 2 | 3 | $(document).ready(function () { 4 | const apiKeyInput = document.getElementById('github_token') 5 | const validateButton = document.getElementById('validateButton') 6 | const toggleButton = document.getElementById('toggleApikeyVisibility') 7 | const isValidated = document.getElementById('github_validated').value.toLowerCase() === 'true' 8 | 9 | console.log('Validated: ' + isValidated) 10 | 11 | // Set initial visibility based on API key value 12 | if (apiKeyInput.value.trim() === '') { 13 | apiKeyInput.setAttribute('type', 'text') // Show placeholder text 14 | toggleButton.innerHTML = '' // Set eye-slash icon 15 | } else { 16 | apiKeyInput.setAttribute('type', 'password') // Hide actual key 17 | toggleButton.innerHTML = '' // Set eye icon 18 | } 19 | 20 | // Disable validate button if already validated 21 | validateButton.disabled = isValidated 22 | 23 | // Reset validation status when user types 24 | apiKeyInput.addEventListener('input', function () { 25 | document.getElementById('github_validated').value = 'false' 26 | validateButton.disabled = false 27 | }) 28 | }) 29 | 30 | document.getElementById('toggleApikeyVisibility').addEventListener('click', function () { 31 | const apikeyInput = document.getElementById('github_token') 32 | const currentType = apikeyInput.getAttribute('type') 33 | apikeyInput.setAttribute('type', currentType === 'password' ? 'text' : 'password') 34 | this.innerHTML = currentType === 'password' ? '' : '' 35 | }) 36 | 37 | document.getElementById('validateButton').addEventListener('click', function () { 38 | const apiKey = document.getElementById('github_token').value 39 | const statusMessage = document.getElementById('statusMessage') 40 | 41 | if (!apiKey) { 42 | statusMessage.textContent = 'Please enter a GitHub Personal Access Token.' 43 | statusMessage.style.color = '#ea868f' 44 | statusMessage.style.display = 'block' 45 | return 46 | } 47 | 48 | showSpinner('validate') 49 | 50 | fetch('/validate_github', { 51 | method: 'POST', 52 | headers: { 53 | 'Content-Type': 'application/json' 54 | }, 55 | body: JSON.stringify({ github_token: apiKey }) 56 | }) 57 | .then(response => response.json()) 58 | .then(data => { 59 | if (data.valid) { 60 | hideSpinner('validate') 61 | statusMessage.textContent = data.message 62 | statusMessage.style.color = '#75b798' 63 | document.getElementById('validateButton').disabled = true 64 | document.getElementById('github_validated').value = 'true' 65 | } else { 66 | statusMessage.textContent = data.message 67 | statusMessage.style.color = '#ea868f' 68 | } 69 | statusMessage.style.display = 'block' 70 | }) 71 | .catch(error => { 72 | hideSpinner('validate') 73 | console.log(error) 74 | statusMessage.textContent = 'Error validating GitHub token.' 75 | statusMessage.style.color = '#ea868f' 76 | statusMessage.style.display = 'block' 77 | document.getElementById('github_validated').value = 'false' 78 | }) 79 | }) 80 | 81 | document.getElementById('configForm').addEventListener('submit', function () { 82 | const apiKeyInput = document.getElementById('github_token') 83 | if (!apiKeyInput.value) { 84 | apiKeyInput.value = '' 85 | } 86 | }) 87 | -------------------------------------------------------------------------------- /static/local-js/050-omdb.js: -------------------------------------------------------------------------------- 1 | /* global $, showSpinner, hideSpinner */ 2 | 3 | $(document).ready(function () { 4 | const apiKeyInput = document.getElementById('omdb_apikey') 5 | const validateButton = document.getElementById('validateButton') 6 | const toggleButton = document.getElementById('toggleApikeyVisibility') 7 | const isValidated = document.getElementById('omdb_validated').value.toLowerCase() 8 | 9 | console.log('Validated: ' + isValidated) 10 | 11 | // Set initial visibility based on API key value 12 | if (apiKeyInput.value.trim() === '') { 13 | apiKeyInput.setAttribute('type', 'text') // Show placeholder text 14 | toggleButton.innerHTML = '' // Set eye-slash icon 15 | } else { 16 | apiKeyInput.setAttribute('type', 'password') // Hide actual key 17 | toggleButton.innerHTML = '' // Set eye icon 18 | } 19 | 20 | // Disable validate button if already validated 21 | validateButton.disabled = isValidated === 'true' 22 | 23 | // Reset validation status when user types 24 | apiKeyInput.addEventListener('input', function () { 25 | document.getElementById('omdb_validated').value = 'false' 26 | validateButton.disabled = false 27 | }) 28 | }) 29 | 30 | document.getElementById('toggleApikeyVisibility').addEventListener('click', function () { 31 | const apikeyInput = document.getElementById('omdb_apikey') 32 | const currentType = apikeyInput.getAttribute('type') 33 | apikeyInput.setAttribute('type', currentType === 'password' ? 'text' : 'password') 34 | this.innerHTML = currentType === 'password' ? '' : '' 35 | }) 36 | 37 | document.getElementById('validateButton').addEventListener('click', function () { 38 | const apiKey = document.getElementById('omdb_apikey').value 39 | const statusMessage = document.getElementById('statusMessage') 40 | 41 | if (!apiKey) { 42 | statusMessage.textContent = 'Please enter an OMDb API key.' 43 | statusMessage.style.color = '#ea868f' 44 | statusMessage.style.display = 'block' 45 | return 46 | } 47 | 48 | showSpinner('validate') 49 | 50 | fetch('/validate_omdb', { 51 | method: 'POST', 52 | headers: { 53 | 'Content-Type': 'application/json' 54 | }, 55 | body: JSON.stringify({ omdb_apikey: apiKey }) 56 | }) 57 | .then((response) => response.json()) 58 | .then((data) => { 59 | if (data.valid) { 60 | hideSpinner('validate') 61 | document.getElementById('omdb_validated').value = 'true' 62 | statusMessage.textContent = 'OMDb API key is valid.' 63 | statusMessage.style.color = '#75b798' 64 | document.getElementById('validateButton').disabled = true 65 | } else { 66 | hideSpinner('validate') 67 | statusMessage.textContent = 'OMDb API key is invalid.' 68 | statusMessage.style.color = '#ea868f' 69 | } 70 | statusMessage.style.display = 'block' 71 | }) 72 | .catch(error => { 73 | hideSpinner('validate') 74 | console.error('Error validating OMDb server:', error) 75 | statusMessage.textContent = 'Error validating OMDb API key.' 76 | statusMessage.style.color = '#ea868f' 77 | statusMessage.style.display = 'block' 78 | document.getElementById('omdb_validated').value = 'false' 79 | }) 80 | }) 81 | 82 | document.getElementById('configForm').addEventListener('submit', function (event) { 83 | const apiKeyInput = document.getElementById('omdb_apikey') 84 | const cacheExpiration = document.getElementById('omdb_cache_expiration') 85 | if (!apiKeyInput.value) { 86 | apiKeyInput.value = '' 87 | cacheExpiration.value = '1' 88 | } 89 | }) 90 | -------------------------------------------------------------------------------- /static/local-js/060-mdblist.js: -------------------------------------------------------------------------------- 1 | /* global $, showSpinner, hideSpinner */ 2 | 3 | $(document).ready(function () { 4 | const apiKeyInput = document.getElementById('mdblist_apikey') 5 | const validateButton = document.getElementById('validateButton') 6 | const toggleButton = document.getElementById('toggleApikeyVisibility') 7 | const isValidated = document.getElementById('mdblist_validated').value.toLowerCase() 8 | 9 | console.log('Validated: ' + isValidated) 10 | 11 | // Set initial visibility based on API key value 12 | if (apiKeyInput.value.trim() === '') { 13 | apiKeyInput.setAttribute('type', 'text') // Show placeholder text 14 | toggleButton.innerHTML = '' // Ensure eye-slash icon 15 | } else { 16 | apiKeyInput.setAttribute('type', 'password') // Hide actual key 17 | toggleButton.innerHTML = '' // Ensure eye icon 18 | } 19 | 20 | // Disable validate button if already validated 21 | validateButton.disabled = isValidated === 'true' 22 | 23 | // Reset validation status when user types 24 | apiKeyInput.addEventListener('input', function () { 25 | document.getElementById('mdblist_validated').value = 'false' 26 | validateButton.disabled = false 27 | }) 28 | 29 | document.getElementById('validateButton').addEventListener('click', function () { 30 | const apiKey = apiKeyInput.value 31 | const statusMessage = document.getElementById('statusMessage') 32 | 33 | if (!apiKey) { 34 | statusMessage.textContent = 'Please enter an API key.' 35 | statusMessage.style.color = '#ea868f' 36 | statusMessage.style.display = 'block' 37 | return 38 | } 39 | 40 | showSpinner('validate') 41 | 42 | fetch('/validate_mdblist', { 43 | method: 'POST', 44 | headers: { 45 | 'Content-Type': 'application/json' 46 | }, 47 | body: JSON.stringify({ mdblist_apikey: apiKey }) 48 | }) 49 | .then(response => response.json()) 50 | .then(data => { 51 | if (data.valid) { 52 | console.log('valid') 53 | hideSpinner('validate') 54 | document.getElementById('mdblist_validated').value = 'true' 55 | statusMessage.textContent = 'API key is valid!' 56 | statusMessage.style.color = '#75b798' 57 | validateButton.disabled = true 58 | } else { 59 | console.log('NOT valid') 60 | document.getElementById('mdblist_validated').value = 'false' 61 | statusMessage.textContent = 'Failed to validate MDBList server. Please check your API Key.' 62 | statusMessage.style.color = '#ea868f' 63 | } 64 | statusMessage.style.display = 'block' 65 | }) 66 | .catch(error => { 67 | console.error('Error validating MDBList server:', error) 68 | statusMessage.textContent = 'An error occurred. Please try again.' 69 | statusMessage.style.color = '#ea868f' 70 | statusMessage.style.display = 'block' 71 | }) 72 | .finally(() => { 73 | hideSpinner('validate') 74 | statusMessage.style.display = 'block' 75 | }) 76 | }) 77 | 78 | document.getElementById('toggleApikeyVisibility').addEventListener('click', function () { 79 | const currentType = apiKeyInput.getAttribute('type') 80 | apiKeyInput.setAttribute('type', currentType === 'password' ? 'text' : 'password') 81 | this.innerHTML = currentType === 'password' ? '' : '' 82 | }) 83 | }) 84 | 85 | document.getElementById('configForm').addEventListener('submit', function () { 86 | const apiKeyInput = document.getElementById('mdblist_apikey') 87 | const cacheExpiration = document.getElementById('mdblist_cache_expiration') 88 | if (!apiKeyInput.value) { 89 | apiKeyInput.value = '' 90 | cacheExpiration.value = '1' 91 | } 92 | }) 93 | -------------------------------------------------------------------------------- /static/local-js/070-notifiarr.js: -------------------------------------------------------------------------------- 1 | /* global $, validateButton, showSpinner, hideSpinner */ 2 | 3 | $(document).ready(function () { 4 | const apiKeyInput = document.getElementById('notifiarr_apikey') 5 | const validateButton = document.getElementById('validateButton') 6 | const toggleButton = document.getElementById('toggleApikeyVisibility') 7 | const isValidated = document.getElementById('notifiarr_validated').value.toLowerCase() 8 | 9 | console.log('Validated: ' + isValidated) 10 | 11 | // Set initial visibility based on API key value 12 | if (apiKeyInput.value.trim() === '') { 13 | apiKeyInput.setAttribute('type', 'text') // Show placeholder text 14 | toggleButton.innerHTML = '' // Set eye icon 15 | } else { 16 | apiKeyInput.setAttribute('type', 'password') // Hide actual key 17 | toggleButton.innerHTML = '' // Set eye-slash icon 18 | } 19 | 20 | // Disable validate button if already validated 21 | validateButton.disabled = isValidated === 'true' 22 | 23 | // Reset validation status when user types 24 | apiKeyInput.addEventListener('input', function () { 25 | document.getElementById('notifiarr_validated').value = 'false' 26 | validateButton.disabled = false 27 | }) 28 | }) 29 | 30 | async function validateNotifiarrApikey (apikey) { 31 | showSpinner('validate') 32 | const apiUrl = '/validate_notifiarr' 33 | const response = await fetch(apiUrl, { 34 | method: 'POST', 35 | headers: { 36 | 'Content-Type': 'application/json' 37 | }, 38 | body: JSON.stringify({ notifiarr_apikey: apikey }) 39 | }) 40 | 41 | if (response.ok) { 42 | hideSpinner('validate') 43 | const data = await response.json() 44 | return data.valid 45 | } else { 46 | hideSpinner('validate') 47 | const errorData = await response.json() 48 | console.error('Error validating Notifiarr apikey:', errorData.message) 49 | return false 50 | } 51 | } 52 | 53 | document.getElementById('validateButton').addEventListener('click', function () { 54 | const apiKey = document.getElementById('notifiarr_apikey').value 55 | const statusMessage = document.getElementById('statusMessage') 56 | 57 | if (!apiKey) { 58 | statusMessage.textContent = 'Please enter a Notifiarr API key.' 59 | statusMessage.style.color = '#ea868f' 60 | statusMessage.style.display = 'block' 61 | return 62 | } 63 | 64 | validateButton.disabled = true 65 | 66 | validateNotifiarrApikey(apiKey).then(isValid => { 67 | if (isValid) { 68 | document.getElementById('notifiarr_validated').value = 'true' 69 | statusMessage.textContent = 'Notifiarr API key is valid.' 70 | statusMessage.style.color = '#75b798' 71 | validateButton.disabled = true 72 | } else { 73 | document.getElementById('notifiarr_validated').value = 'false' 74 | statusMessage.textContent = 'Notifiarr API key is invalid.' 75 | statusMessage.style.color = '#ea868f' 76 | validateButton.disabled = false 77 | } 78 | statusMessage.style.display = 'block' 79 | }) 80 | }) 81 | 82 | document.getElementById('toggleApikeyVisibility').addEventListener('click', function () { 83 | const apikeyInput = document.getElementById('notifiarr_apikey') 84 | const currentType = apikeyInput.getAttribute('type') 85 | apikeyInput.setAttribute('type', currentType === 'password' ? 'text' : 'password') 86 | this.innerHTML = currentType === 'password' ? '' : '' 87 | }) 88 | 89 | document.getElementById('configForm').addEventListener('submit', function (event) { 90 | const apiKeyInput = document.getElementById('notifiarr_apikey') 91 | if (!apiKeyInput.value) { 92 | apiKeyInput.value = '' 93 | } 94 | }) 95 | -------------------------------------------------------------------------------- /static/local-js/080-gotify.js: -------------------------------------------------------------------------------- 1 | /* global $, showSpinner, hideSpinner */ 2 | 3 | $(document).ready(function () { 4 | const gotifyTokenInput = document.getElementById('gotify_token') 5 | const validateButton = document.getElementById('validateButton') 6 | const toggleButton = document.getElementById('toggleTokenVisibility') 7 | const isValidated = document.getElementById('gotify_validated').value.toLowerCase() 8 | 9 | console.log('Validated: ' + isValidated) 10 | 11 | // Set initial visibility based on API key value 12 | if (gotifyTokenInput.value.trim() === '') { 13 | gotifyTokenInput.setAttribute('type', 'text') // Show placeholder text 14 | toggleButton.innerHTML = '' // Set eye icon 15 | } else { 16 | gotifyTokenInput.setAttribute('type', 'password') // Hide actual key 17 | toggleButton.innerHTML = '' // Set eye-slash icon 18 | } 19 | 20 | // Disable validate button if already validated 21 | validateButton.disabled = isValidated === 'true' 22 | 23 | // Reset validation status when user types 24 | gotifyTokenInput.addEventListener('input', function () { 25 | document.getElementById('gotify_validated').value = 'false' 26 | validateButton.disabled = false 27 | }) 28 | 29 | document.getElementById('gotify_url').addEventListener('input', function () { 30 | document.getElementById('gotify_validated').value = 'false' 31 | validateButton.disabled = false 32 | }) 33 | }) 34 | 35 | // Function to toggle API key visibility 36 | document.getElementById('toggleTokenVisibility').addEventListener('click', function () { 37 | const tokenInput = document.getElementById('gotify_token') 38 | const currentType = tokenInput.getAttribute('type') 39 | tokenInput.setAttribute('type', currentType === 'password' ? 'text' : 'password') 40 | this.innerHTML = currentType === 'password' ? '' : '' 41 | }) 42 | 43 | /* eslint-disable camelcase */ 44 | // Event listener for the validate button 45 | document.getElementById('validateButton').addEventListener('click', function () { 46 | const gotify_url = document.getElementById('gotify_url').value 47 | const gotify_token = document.getElementById('gotify_token').value 48 | const statusMessage = document.getElementById('statusMessage') 49 | 50 | if (!gotify_url || !gotify_token) { 51 | statusMessage.textContent = 'Please enter both Gotify URL and Token.' 52 | statusMessage.style.color = '#ea868f' 53 | statusMessage.style.display = 'block' 54 | return 55 | } 56 | 57 | showSpinner('validate') 58 | 59 | fetch('/validate_gotify', { 60 | method: 'POST', 61 | headers: { 62 | 'Content-Type': 'application/json' 63 | }, 64 | body: JSON.stringify({ 65 | gotify_url, 66 | gotify_token 67 | }) 68 | }) 69 | .then((response) => response.json()) 70 | .then((data) => { 71 | if (data.valid) { 72 | hideSpinner('validate') 73 | document.getElementById('gotify_validated').value = 'true' 74 | statusMessage.textContent = 'Gotify credentials validated successfully!' 75 | statusMessage.style.color = '#75b798' 76 | } else { 77 | hideSpinner('validate') 78 | document.getElementById('gotify_validated').value = 'false' 79 | statusMessage.textContent = data.error 80 | statusMessage.style.color = '#ea868f' 81 | } 82 | document.getElementById('validateButton').disabled = data.valid 83 | statusMessage.style.display = 'block' 84 | }) 85 | .catch((error) => { 86 | hideSpinner('validate') 87 | console.error('Error validating Gotify credentials:', error) 88 | statusMessage.textContent = 'An error occurred while validating Gotify credentials.' 89 | statusMessage.style.color = '#ea868f' 90 | statusMessage.style.display = 'block' 91 | }) 92 | }) 93 | /* eslint-enable camelcase */ 94 | -------------------------------------------------------------------------------- /static/local-js/085-ntfy.js: -------------------------------------------------------------------------------- 1 | /* global $, showSpinner, hideSpinner */ 2 | 3 | $(document).ready(function () { 4 | const tokenInput = document.getElementById('ntfy_token') 5 | const toggleButton = document.getElementById('toggleTokenVisibility') 6 | const isValidated = document.getElementById('ntfy_validated').value.toLowerCase() 7 | const validateButton = document.getElementById('validateButton') 8 | 9 | console.log('Validated: ' + isValidated) 10 | 11 | // Set initial visibility based on API key value 12 | if (tokenInput.value.trim() === '') { 13 | tokenInput.setAttribute('type', 'text') // Show placeholder text 14 | toggleButton.innerHTML = '' // Set eye-slash icon 15 | } else { 16 | tokenInput.setAttribute('type', 'password') // Hide actual key 17 | toggleButton.innerHTML = '' // Set eye icon 18 | } 19 | 20 | if (isValidated === 'true') { 21 | validateButton.disabled = true 22 | } else { 23 | validateButton.disabled = false 24 | } 25 | 26 | tokenInput.addEventListener('input', function () { 27 | document.getElementById('ntfy_validated').value = 'false' 28 | validateButton.disabled = false 29 | }) 30 | 31 | document.getElementById('ntfy_url').addEventListener('input', function () { 32 | document.getElementById('ntfy_validated').value = 'false' 33 | validateButton.disabled = false 34 | }) 35 | 36 | document.getElementById('ntfy_topic').addEventListener('input', function () { 37 | document.getElementById('ntfy_validated').value = 'false' 38 | validateButton.disabled = false 39 | }) 40 | }) 41 | 42 | // Function to toggle API key visibility 43 | document.getElementById('toggleTokenVisibility').addEventListener('click', function () { 44 | const tokenInput = document.getElementById('ntfy_token') 45 | const currentType = tokenInput.getAttribute('type') 46 | tokenInput.setAttribute('type', currentType === 'password' ? 'text' : 'password') 47 | this.innerHTML = currentType === 'password' ? '' : '' 48 | }) 49 | 50 | /* eslint-disable camelcase */ 51 | // Event listener for the validate button 52 | document.getElementById('validateButton').addEventListener('click', function () { 53 | const ntfy_url = document.getElementById('ntfy_url').value 54 | const ntfy_token = document.getElementById('ntfy_token').value 55 | const ntfy_topic = document.getElementById('ntfy_topic').value 56 | const statusMessage = document.getElementById('statusMessage') 57 | 58 | if (!ntfy_url || !ntfy_token || !ntfy_topic) { 59 | statusMessage.textContent = 'Please enter ntfy URL, Token and Topic.' 60 | statusMessage.style.color = '#ea868f' 61 | statusMessage.style.display = 'block' 62 | return 63 | } 64 | 65 | showSpinner('validate') 66 | 67 | fetch('/validate_ntfy', { 68 | method: 'POST', 69 | headers: { 70 | 'Content-Type': 'application/json' 71 | }, 72 | body: JSON.stringify({ 73 | ntfy_url, 74 | ntfy_token, 75 | ntfy_topic 76 | }) 77 | }) 78 | .then((response) => response.json()) 79 | .then((data) => { 80 | if (data.valid) { 81 | hideSpinner('validate') 82 | document.getElementById('ntfy_validated').value = 'true' 83 | statusMessage.textContent = 'ntfy credentials validated successfully! Ensure you are subscribed to topic to see test message.' 84 | statusMessage.style.color = '#75b798' 85 | } else { 86 | hideSpinner('validate') 87 | document.getElementById('ntfy_validated').value = 'false' 88 | statusMessage.textContent = data.error 89 | statusMessage.style.color = '#ea868f' 90 | } 91 | document.getElementById('validateButton').disabled = data.valid 92 | statusMessage.style.display = 'block' 93 | }) 94 | .catch((error) => { 95 | hideSpinner('validate') 96 | console.error('Error validating ntfy credentials:', error) 97 | statusMessage.textContent = 'An error occurred while validating ntfy credentials.' 98 | statusMessage.style.color = '#ea868f' 99 | statusMessage.style.display = 'block' 100 | }) 101 | }) 102 | /* eslint-enable camelcase */ 103 | -------------------------------------------------------------------------------- /static/local-js/090-webhooks.js: -------------------------------------------------------------------------------- 1 | /* global $ */ 2 | 3 | const validatedWebhooks = {} 4 | 5 | function setWebhookValidated (state, webhookType = null) { 6 | document.getElementById('webhooks_validated').value = state ? 'true' : 'false' 7 | 8 | if (webhookType) { 9 | // Enable the validate button if the URL changes and validation is false 10 | const validateButton = document.querySelector(`#webhooks_${webhookType}_custom .validate-button`) 11 | if (validateButton) { 12 | validateButton.disabled = state 13 | } 14 | } 15 | } 16 | 17 | function showCustomInput (selectElement, isValidated) { 18 | const customInputId = selectElement.id + '_custom' 19 | console.log(`showCustomInput called for: ${selectElement.id}, isValidated: ${isValidated}`) 20 | if (selectElement.value === 'custom') { 21 | document.getElementById(customInputId).style.display = 'block' 22 | if (isValidated === true) { 23 | setWebhookValidated(true, selectElement.id) 24 | } else { 25 | setWebhookValidated(false, selectElement.id) 26 | } 27 | } else { 28 | document.getElementById(customInputId).style.display = 'none' 29 | validatedWebhooks[selectElement.id] = true 30 | updateValidationState() 31 | } 32 | } 33 | 34 | function updateValidationState () { 35 | const allValid = Object.values(validatedWebhooks).every(state => state === true) 36 | console.log('Validation State Updated:', validatedWebhooks, `All Valid: ${allValid}`) 37 | setWebhookValidated(allValid) 38 | } 39 | 40 | $(document).ready(function () { 41 | const isValidated = document.getElementById('webhooks_validated').value.toLowerCase() === 'true' 42 | console.log('Page Load - Is Validated:', isValidated) 43 | 44 | $('select.form-select').each(function () { 45 | const selectElement = this 46 | const customInputId = selectElement.id + '_custom' 47 | const customUrl = $('#' + customInputId).find('input.custom-webhook-url').val() 48 | 49 | showCustomInput(selectElement, isValidated) 50 | 51 | if (selectElement.value === 'custom' && customUrl) { 52 | validatedWebhooks[selectElement.id] = isValidated 53 | console.log(`Custom webhook found: ${selectElement.id}, URL: ${customUrl}`) 54 | } else { 55 | validatedWebhooks[selectElement.id] = true 56 | } 57 | }) 58 | 59 | if (isValidated === true) { 60 | $('.validate-button').prop('disabled', true) 61 | } else { 62 | $('.validate-button').prop('disabled', false) 63 | } 64 | 65 | // Debugging for navigation actions 66 | document.getElementById('configForm').addEventListener('submit', function (event) { 67 | const actionType = event.submitter?.getAttribute('onclick')?.includes('loading') ? event.submitter.innerText.trim() : 'unknown' 68 | console.log(`Form Submitted - Action: ${actionType}`) 69 | 70 | $('select.form-select').each(function () { 71 | if ($(this).val() === 'custom') { 72 | const customInputId = $(this).attr('id') + '_custom' 73 | const customUrl = $('#' + customInputId).find('input.custom-webhook-url').val() 74 | if (customUrl) { 75 | console.log(`Serializing custom webhook for dropdown: ${$(this).attr('id')}, URL: ${customUrl}`) 76 | $(this).append('') 77 | $(this).val(customUrl) 78 | } else { 79 | console.log(`Custom webhook dropdown ${$(this).attr('id')} has no URL to serialize.`) 80 | } 81 | } 82 | }) 83 | }) 84 | }) 85 | 86 | /* eslint-disable no-unused-vars, no-undef */ 87 | function validateWebhook (webhookType) { 88 | const inputGroup = $('#webhooks_' + webhookType + '_custom').find('.input-group') 89 | const webhookUrl = inputGroup.find('input.custom-webhook-url').val() 90 | const validationMessage = inputGroup.siblings('.validation-message') 91 | const validateButton = inputGroup.find('.validate-button') 92 | const webhookTypeFormatted = webhookType.replace(/_/g, ' ').replace(/\b\w/g, function (l) { return l.toUpperCase() }) 93 | 94 | console.log(`Validating webhook: ${webhookType}, URL: ${webhookUrl}`) 95 | 96 | showSpinner(webhookType) 97 | validationMessage.html('') 98 | validationMessage.show() 99 | 100 | fetch('/validate_webhook', { 101 | method: 'POST', 102 | headers: { 103 | 'Content-Type': 'application/json' 104 | }, 105 | body: JSON.stringify({ 106 | webhook_url: webhookUrl, 107 | message: 'Kometa Quickstart Test message for ' + webhookTypeFormatted + ' webhook' 108 | }) 109 | }) 110 | .then(response => response.json()) 111 | .then(data => { 112 | if (data.success) { 113 | console.log(`Webhook validation successful for: ${webhookType}`) 114 | hideSpinner(webhookType) 115 | validationMessage.html('') 116 | validateButton.prop('disabled', true) 117 | validatedWebhooks['webhooks_' + webhookType] = true 118 | } else { 119 | console.error(`Webhook validation failed for: ${webhookType}, Error: ${data.error}`) 120 | hideSpinner(webhookType) 121 | validationMessage.html('') 122 | validatedWebhooks['webhooks_' + webhookType] = false 123 | } 124 | updateValidationState() 125 | }) 126 | .catch((error) => { 127 | console.error(`Error during webhook validation for: ${webhookType}`, error) 128 | hideSpinner(webhookType) 129 | validationMessage.html('') 130 | validatedWebhooks['webhooks_' + webhookType] = false 131 | updateValidationState() 132 | }) 133 | } 134 | /* eslint-enable no-unused-vars, no-undef */ 135 | -------------------------------------------------------------------------------- /static/local-js/100-anidb.js: -------------------------------------------------------------------------------- 1 | /* global $, showSpinner, hideSpinner */ 2 | 3 | $(document).ready(function () { 4 | const passwordInput = document.getElementById('anidb_password') 5 | const toggleButton = document.getElementById('togglePasswordVisibility') 6 | const validateButton = document.getElementById('validateButton') 7 | const isValidatedElement = document.getElementById('anidb_validated') 8 | const isValidated = isValidatedElement.value.toLowerCase() 9 | 10 | console.log('Validated:', isValidated) 11 | 12 | // Set initial visibility based on password value 13 | if (passwordInput.value.trim() === '') { 14 | passwordInput.setAttribute('type', 'text') // Show placeholder text 15 | toggleButton.innerHTML = '' // Show eye-slash 16 | } else { 17 | passwordInput.setAttribute('type', 'password') // Hide actual password 18 | toggleButton.innerHTML = '' // Show eye 19 | } 20 | 21 | // Disable validate button if already validated 22 | validateButton.disabled = isValidated === 'true' 23 | 24 | // Reset validation status when user types 25 | const inputFields = ['anidb_client', 'anidb_version', 'anidb_username', 'anidb_password'] 26 | inputFields.forEach(field => { 27 | const inputElement = document.getElementById(field) 28 | if (inputElement) { 29 | inputElement.addEventListener('input', function () { 30 | isValidatedElement.value = 'false' 31 | validateButton.disabled = false 32 | }) 33 | } else { 34 | console.warn(`Warning: Element with ID '${field}' not found.`) 35 | } 36 | }) 37 | }) 38 | 39 | document.getElementById('togglePasswordVisibility').addEventListener('click', function () { 40 | const passwordInput = document.getElementById('anidb_password') 41 | const currentType = passwordInput.getAttribute('type') 42 | passwordInput.setAttribute('type', currentType === 'password' ? 'text' : 'password') 43 | this.innerHTML = currentType === 'password' ? '' : '' 44 | }) 45 | 46 | document.getElementById('validateButton').addEventListener('click', function () { 47 | const username = document.getElementById('anidb_username').value 48 | const password = document.getElementById('anidb_password').value 49 | const client = document.getElementById('anidb_client').value 50 | const clientver = document.getElementById('anidb_version').value 51 | const statusMessage = document.getElementById('statusMessage') 52 | const validateButton = document.getElementById('validateButton') 53 | 54 | if (!username || !password || !client || !clientver) { 55 | statusMessage.textContent = 'Please enter all required fields.' 56 | statusMessage.style.color = '#ea868f' 57 | statusMessage.style.display = 'block' 58 | return 59 | } 60 | 61 | showSpinner('validate') 62 | fetch('/validate_anidb', { 63 | method: 'POST', 64 | headers: { 65 | 'Content-Type': 'application/json' 66 | }, 67 | body: JSON.stringify({ username, password, client, clientver }) 68 | }) 69 | .then((response) => response.json()) 70 | .then((data) => { 71 | if (data.valid) { 72 | hideSpinner('validate') 73 | document.getElementById('anidb_validated').value = 'true' 74 | statusMessage.textContent = 'AniDB credentials are valid.' 75 | statusMessage.style.color = '#75b798' 76 | validateButton.disabled = true 77 | } else { 78 | document.getElementById('anidb_validated').value = 'false' 79 | statusMessage.textContent = `Error: ${data.error}` 80 | statusMessage.style.color = '#ea868f' 81 | validateButton.disabled = false 82 | } 83 | statusMessage.style.display = 'block' 84 | }) 85 | .catch((error) => { 86 | hideSpinner('validate') 87 | console.error('Error:', error) 88 | statusMessage.textContent = 'Error validating AniDB credentials.' 89 | statusMessage.style.color = '#ea868f' 90 | statusMessage.style.display = 'block' 91 | validateButton.disabled = false 92 | }) 93 | }) 94 | -------------------------------------------------------------------------------- /static/local-js/900-final.js: -------------------------------------------------------------------------------- 1 | /* global $ */ 2 | 3 | $(document).ready(function () { 4 | // Debugging: Confirm jQuery is loaded 5 | // console.log("jQuery loaded."); 6 | 7 | // Fetch the validation status 8 | const plexValid = $('#plex_valid').data('plex-valid') === 'True' 9 | const tmdbValid = $('#tmdb_valid').data('tmdb-valid') === 'True' 10 | const libsValid = $('#libs_valid').data('libs-valid') === 'True' 11 | const settValid = $('#sett_valid').data('sett-valid') === 'True' 12 | const yamlValid = $('#yaml_valid').data('yaml-valid') === 'True' 13 | // const validationError = $('#validation-error').val().trim() 14 | // Debugging: Check the values of the meta tags 15 | 16 | const showYAML = plexValid && tmdbValid && libsValid && settValid && yamlValid 17 | 18 | console.log('Plex Valid:', plexValid) 19 | console.log('TMDb Valid:', tmdbValid) 20 | console.log('LIBS Valid:', libsValid) 21 | console.log('Settings Valid:', settValid) 22 | console.log('YAML Valid:', yamlValid) 23 | console.log('Show YAML:', showYAML) 24 | 25 | // Initialize validation messages array 26 | const validationMessages = [] 27 | 28 | // Add messages based on validation status 29 | if (!plexValid) { 30 | validationMessages.push( 31 | 'Plex settings have not been validated successfully. Please return to the Plex page and hit the validate button and ensure success before returning here.
' 32 | ) 33 | } 34 | if (!tmdbValid) { 35 | validationMessages.push( 36 | 'TMDb settings have not been validated successfully. Please return to the TMDb page and hit the validate button and ensure success before returning here.
' 37 | ) 38 | } 39 | if (!libsValid) { 40 | validationMessages.push( 41 | 'Libraries page settings have not been validated successfully. Please return to the Libraries page and ensure you make appropriate selections before returning here.
' 42 | ) 43 | } 44 | 45 | if (!settValid) { 46 | validationMessages.push( 47 | 'Settings page values have likely been skipped. Please return to the Settings page and ensure you make appropriate selections before returning here.
' 48 | ) 49 | } 50 | 51 | // If there are validation messages, display them 52 | if (!showYAML) { 53 | if (validationMessages.length > 0) { 54 | $('#validation-messages').html(validationMessages.join('
')).show() 55 | } else { 56 | $('#validation-messages').html('').hide() 57 | } 58 | 59 | $('#no-validation-warning, #yaml-warnings, #yaml-warning-msg, #validation-error').removeClass('d-none') 60 | // Hide the download button 61 | $('#download-btn').addClass('d-none') 62 | $('#download-redacted-btn').addClass('d-none') 63 | } else { 64 | $('#no-validation-warning, #yaml-warnings, #yaml-warning-msg, #validation-error').addClass('d-none') 65 | $('#yaml-content, #final-yaml, #download-btn, #download-redacted-btn').removeClass('d-none') 66 | } 67 | 68 | // Debugging: Confirm if validation messages div is updated 69 | // console.log("Validation Messages:", $('#validation-messages').html()); 70 | }) 71 | 72 | document.getElementById('header-style').addEventListener('change', function () { 73 | document.getElementById('configForm').submit() 74 | }) 75 | -------------------------------------------------------------------------------- /templates/001-navigation.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 |
6 |

{{ page_info['title'] }}

7 |
8 | 9 | 10 | 77 | 78 |
79 | -------------------------------------------------------------------------------- /templates/001-start.html: -------------------------------------------------------------------------------- 1 | {% extends "000-base.html" %} {% block content %} 2 | {% include "001-navigation.html" %} 3 | {% include "modals/" + page_info['template_name'] + ".html" ignore missing %} 4 |
5 |

Read this warning (hover over the icon): 6 | 9 | 10 |

11 |
12 |
13 |

Quickstart is a Web UI that will guide you through creating a Configuration File for Kometa.

14 |

Click here for a more in-depth 15 | explanation and example of what Quickstart does

16 |

To use Quickstart, you will need at least a Plex URL & Token, and a TMDb API Key.

17 |

We strongly suggest you set up test libraries if this is your first time running 18 | Quickstart/Kometa, click here to learn more

19 |

Review the Config Data section below and press the arrow button above when you are ready to begin.

20 |

21 | 24 |
25 |

The config name below is used for saving your config and progress within the Quickstart database. You 26 | can change it if you wish before getting started, or choose one of your previously-created configs from the 27 | drop-down.

28 |
29 | 30 |
31 | 41 | 42 |
43 | 44 |
46 | 48 | 49 |
50 |
51 | 52 |
53 | 54 | 58 | 59 | 60 | 64 |
65 |
66 | 67 | 68 |
69 | 70 | 71 | 90 | {% endblock %} 91 | -------------------------------------------------------------------------------- /templates/020-tmdb.html: -------------------------------------------------------------------------------- 1 | {% extends "000-base.html" %} {% block content %} 2 | {% include "001-navigation.html" %} 3 | {% include "modals/" + page_info['template_name'] + ".html" ignore missing %} 4 |
5 | 6 | TMDb API Key 7 | 9 | 10 | 11 | 12 | 15 | 18 | 21 |
22 | 23 | 24 | 25 | 30 |
31 |
32 |
33 |
34 | 36 | Language 37 | 39 | 40 | 41 | 42 | 49 |
50 | 51 |
52 |
53 |
54 | 55 | Region 56 | 58 | 59 | 60 | 61 | 71 |
72 | 73 |
74 |
75 | 76 |
77 | 79 | Cache Expiration 80 | 82 | 83 | 84 | 85 | 87 |
88 | 89 | 90 | 91 | {% endblock %} 92 | -------------------------------------------------------------------------------- /templates/025-libraries.html: -------------------------------------------------------------------------------- 1 | {% extends "000-base.html" %} 2 | {% block content %} 3 | {% include "001-navigation.html" %} 4 | {% include "modals/" + page_info['template_name'] + ".html" ignore missing %} 5 | 6 | 7 | 8 | 9 | 42 | 43 | 44 | {% for library in movie_libraries %} 45 |
46 |
47 |

{{ library.name }} (Movie)

48 |
49 |
50 | {% include "partials/_movie_library_settings.html" %} 51 |
52 |
53 | {% endfor %} 54 | 55 | 56 | {% for library in show_libraries %} 57 |
58 |
59 |

{{ library.name }} (TV Show)

60 |
61 |
62 | {% include "partials/_show_library_settings.html" %} 63 |
64 |
65 | {% endfor %} 66 | 67 | 68 | 73 | 74 | 79 | 80 |

81 | 82 | 83 | {% include "partials/_rename_modal.html" %} 84 | 85 | 86 |
87 | 88 | 89 | 103 | 104 | {% endblock %} 105 | -------------------------------------------------------------------------------- /templates/027-playlist_files.html: -------------------------------------------------------------------------------- 1 | {% extends "000-base.html" %} {% block content %} 2 | {% include "001-navigation.html" %} 3 | {% include "modals/" + page_info['template_name'] + ".html" ignore missing %} 4 | 6 | 33 |
34 | 39 |
40 | 41 | 42 | {% endblock %} 43 | -------------------------------------------------------------------------------- /templates/030-tautulli.html: -------------------------------------------------------------------------------- 1 | {% extends "000-base.html" %} {% block content %} 2 | {% include "001-navigation.html" %} 3 | {% include "modals/" + page_info['template_name'] + ".html" ignore missing %} 4 |
5 | 6 | Tautulli URL 7 | 9 | 10 | 11 | 12 | 15 |
16 | 17 |
18 | 19 | API Key 20 | 22 | 23 | 24 | 25 | 28 | 31 | 34 |
35 | 36 | 37 | 38 | 43 |
44 | 45 | 46 | {% endblock %} 47 | -------------------------------------------------------------------------------- /templates/040-github.html: -------------------------------------------------------------------------------- 1 | {% extends "000-base.html" %} {% block content %} 2 | {% include "001-navigation.html" %} 3 | {% include "modals/" + page_info['template_name'] + ".html" ignore missing %} 4 |
5 | 6 | GitHub Personal Access Token 7 | 10 | 11 | 12 | 13 | 16 | 19 | 22 |
23 | 24 | 25 | 26 | 31 |
32 | 33 | 34 | {% endblock %} 35 | -------------------------------------------------------------------------------- /templates/050-omdb.html: -------------------------------------------------------------------------------- 1 | {% extends "000-base.html" %} {% block content %} 2 | {% include "001-navigation.html" %} 3 | {% include "modals/" + page_info['template_name'] + ".html" ignore missing %} 4 |
5 | 6 | OMDb API Key 7 | 9 | 10 | 11 | 12 | 15 | 18 | 21 |
22 | 23 | 24 | 29 |
30 | 31 |
32 | 34 | Cache Expiration (in days) 35 | 37 | 38 | 39 | 40 | 43 |
44 | 45 | 46 | 47 | {% endblock %} 48 | -------------------------------------------------------------------------------- /templates/060-mdblist.html: -------------------------------------------------------------------------------- 1 | {% extends "000-base.html" %} {% block content %} 2 | {% include "001-navigation.html" %} 3 | {% include "modals/" + page_info['template_name'] + ".html" ignore missing %} 4 |
5 | 6 | MDBList API Key 7 | 9 | 10 | 11 | 12 | 15 | 18 | 21 |
22 | 23 | 28 |
29 |
30 | 32 | Cache Expiration (in days) 33 | 35 | 36 | 37 | 38 | 40 |
41 | 42 | 43 | {% endblock %} 44 | -------------------------------------------------------------------------------- /templates/070-notifiarr.html: -------------------------------------------------------------------------------- 1 | {% extends "000-base.html" %} {% block content %} 2 | {% include "001-navigation.html" %} 3 | {% include "modals/" + page_info['template_name'] + ".html" ignore missing %} 4 |
5 | 6 | Notifiarr API Key 7 | 9 | 10 | 11 | 12 | 15 | 18 | 21 |
22 | 23 | 24 | 25 | 30 |
31 | 32 | 33 | {% endblock %} 34 | -------------------------------------------------------------------------------- /templates/080-gotify.html: -------------------------------------------------------------------------------- 1 | {% extends "000-base.html" %} {% block content %} 2 | {% include "001-navigation.html" %} 3 | {% include "modals/" + page_info['template_name'] + ".html" ignore missing %} 4 |
5 | 6 | Gotify URL 7 | 9 | 10 | 11 | 12 | 15 |
16 | 17 |
18 | 19 | Gotify API Key 20 | 22 | 23 | 24 | 25 | 28 | 31 | 34 |
35 | 36 | 37 | 38 | 43 |
44 | 45 | 46 | {% endblock %} 47 | -------------------------------------------------------------------------------- /templates/085-ntfy.html: -------------------------------------------------------------------------------- 1 | {% extends "000-base.html" %} {% block content %} 2 | {% include "001-navigation.html" %} 3 | {% include "modals/" + page_info['template_name'] + ".html" ignore missing %} 4 |
5 | 6 | ntfy URL 7 | 9 | 10 | 11 | 12 | 15 |
16 | 17 |
18 | 19 | ntfy Access Token 20 | 22 | 23 | 24 | 25 | 28 | 31 | 34 |
35 | 36 | 37 |
38 | 39 | ntfy Topic 40 | 42 | 43 | 44 | 45 | 48 |
49 | 50 | 51 | 56 |
57 | 58 | 59 | {% endblock %} 60 | -------------------------------------------------------------------------------- /templates/090-webhooks.html: -------------------------------------------------------------------------------- 1 | {% extends "000-base.html" %} {% block content %} 2 | {% include "001-navigation.html" %} 3 | {% include "modals/" + page_info['template_name'] + ".html" ignore missing %} 4 | {% for webhook in ["error", "run_start", "run_end", "changes", "version", "delete"] %} 5 |
6 |
7 | 9 | {{ webhook|replace('_', ' ')|title }} Webhook 10 | 12 | 13 | 14 | 15 | 39 |
40 | 41 | 42 | 43 | 67 | 68 | 69 |
70 | {% endfor %} 71 | 76 | 77 | 78 | {% endblock %} 79 | -------------------------------------------------------------------------------- /templates/100-anidb.html: -------------------------------------------------------------------------------- 1 | {% extends "000-base.html" %} {% block content %} 2 | {% include "001-navigation.html" %} 3 | {% include "modals/" + page_info['template_name'] + ".html" ignore missing %} 4 |
5 | 6 | AniDB Client 7 | 9 | 10 | 11 | 12 | 15 |
16 | 17 |
18 | 19 | Client Version 20 | 22 | 23 | 24 | 25 | 27 |
28 | 29 |
30 | 31 | Username 32 | 34 | 35 | 36 | 37 | 40 |
41 | 42 |
43 | 44 | AniDB Password 45 | 47 | 48 | 49 | 50 | 53 | 56 | 59 |
60 | 61 | 62 | 73 | 74 |
75 | 76 |
77 | 78 | Language 79 | 81 | 82 | 83 | 84 | 90 |
91 | 92 |
93 | 95 | Cache Expiration (in days) 96 | 98 | 99 | 100 | 101 | 103 |
104 | 105 | 106 | 107 | {% endblock %} 108 | -------------------------------------------------------------------------------- /templates/130-trakt.html: -------------------------------------------------------------------------------- 1 | {% extends "000-base.html" %} {% block content %} 2 | {% include "001-navigation.html" %} 3 | {% include "modals/" + page_info['template_name'] + ".html" ignore missing %} 4 |
5 | 6 | Client ID 7 | 9 | 10 | 11 | 12 | 15 |
16 |
17 | 18 | Client Secret 19 | 21 | 22 | 23 | 24 | 28 | 31 | 34 |
35 | 36 | 37 | 38 | 39 |
40 | When you click "Retrieve PIN" above, you will be taken to a Trakt web page. Log in and allow your application access 41 | to your Trakt account. Trakt will display a PIN. Copy that PIN and paste it into the "Trakt PIN" field below.
42 |
43 |
44 | If you have a PIN you generated in the last few minutes, enter it below.
45 |
46 |
47 | 48 | Trakt PIN 49 | 51 | 52 | 53 | 54 | 56 | 59 |
60 | 61 | 63 | 65 | 67 | 69 | 71 | 73 | 78 |
79 | 80 | 81 | {% endblock %} 82 | -------------------------------------------------------------------------------- /templates/140-mal.html: -------------------------------------------------------------------------------- 1 | {% extends "000-base.html" %} {% block content %} 2 | {% include "001-navigation.html" %} 3 | {% include "modals/" + page_info['template_name'] + ".html" ignore missing %} 4 |
5 | 6 | Client ID 7 | 9 | 10 | 11 | 12 | 15 |
16 |
17 | 19 |
20 |
21 | 22 | Client Secret 23 | 25 | 26 | 27 | 28 | 32 | 35 | 38 |
39 | 40 |
41 | 42 | Cache Expiration 43 | 45 | 46 | 47 | 48 | 50 |
51 | 52 | 53 | 54 |
55 | 56 | Localhost URL 57 | 59 | 60 | 61 | 62 | 65 | 68 |
69 | 74 |
75 |
76 | 78 | 80 | 82 | 84 |
85 | 86 | 87 | 88 | {% endblock %} 89 | -------------------------------------------------------------------------------- /templates/900-final.html: -------------------------------------------------------------------------------- 1 | {% extends "000-base.html" %} {% block content %} 2 | {% include "001-navigation.html" %} 3 | {% include "modals/" + page_info['template_name'] + ".html" ignore missing %} 4 |
5 | 12 | 13 |
14 | 15 | 17 | 21 |
22 |
23 |
These error(s) occurred while validating your config:

24 |
25 | 27 |
28 | 29 |
30 |
31 |
32 |
Here is the YAML that was generated as a result of your inputs.

33 | 34 | 37 |
38 | 39 |
40 | {% if yaml_content %} 41 |
42 | Download Config 43 | Download Redacted 44 | Config 45 | {% endif %} 46 | 47 | {% endblock %} 48 | -------------------------------------------------------------------------------- /templates/910-sponsor.html: -------------------------------------------------------------------------------- 1 | {% extends "000-base.html" %} {% block content %} 2 | {% include "001-navigation.html" %} 3 | {% include "modals/" + page_info['template_name'] + ".html" ignore missing %} 4 |
5 |

Support Quickstart

6 |

7 | Thank you for using Quickstart and Kometa! Your support helps us keep the project running and growing. 8 | If you would like to consider donating towards the project, it is greatly appreciated. Donations are completely optional, and feel free to contribute as little or as much as you are able to. 9 |

10 |

Select the amount you wish to contribute:

11 | 12 | 13 |
14 | 15 | 16 |
17 | 18 | 19 |
20 | 21 |
22 | 23 | 24 |
25 |
26 | 27 | 28 |
29 |
30 | 31 | 32 | Sponsor Now 33 | 34 | 35 |

36 | Note: You will need a GitHub account to make the payment. Once you have sponsored the project, you can ask for the Sponsor role in the 37 | Kometa Discord Server. 38 |

39 |
40 | 41 | 70 | {% endblock %} 71 | -------------------------------------------------------------------------------- /templates/modals/001-start.html: -------------------------------------------------------------------------------- 1 | 41 | 74 | -------------------------------------------------------------------------------- /templates/modals/010-plex.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /templates/modals/020-tmdb.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /templates/modals/025-libraries.html: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /templates/modals/027-playlist_files.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /templates/modals/030-tautulli.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /templates/modals/040-github.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /templates/modals/050-omdb.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /templates/modals/060-mdblist.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /templates/modals/070-notifiarr.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /templates/modals/080-gotify.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /templates/modals/085-ntfy.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /templates/modals/090-webhooks.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /templates/modals/100-anidb.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /templates/modals/110-radarr.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /templates/modals/120-sonarr.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /templates/modals/130-trakt.html: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /templates/modals/140-mal.html: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /templates/modals/150-settings.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /templates/modals/900-final.html: -------------------------------------------------------------------------------- 1 |
2 |

Thank you for using Quickstart

3 |
4 |

Click here for some guidance on 5 | next steps. 6 |

7 | 8 | 38 | -------------------------------------------------------------------------------- /templates/partials/_movie_collections.html: -------------------------------------------------------------------------------- 1 |

Select which Collection Files you would like to run against your libraries. You can hover over the magnifying 2 | glass to see some of the collections that the file will create, or click on the name of the file to be taken 3 | to it's wiki page.

4 | {% import 'partials/_macros.html' as macros %} 5 | 6 | {% for group in collection_config %} 7 | {{ macros.collection_group_section(library, data, version_info, group) }} 8 | {% endfor %} 9 | -------------------------------------------------------------------------------- /templates/partials/_movie_library_settings.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | 6 |
7 |

8 | 12 |

13 |
14 |
15 | {% include "partials/_movie_collections.html" %} 16 |
17 |
18 |
19 | 20 | 21 |
22 |

23 | 27 |

28 |
29 |
30 | {% include "partials/_movie_overlays.html" %} 31 |
32 |
33 |
34 | 35 | 36 |
37 |

38 | 42 |

43 |
44 |
45 | {% include "partials/_movie_attributes.html" %} 46 |
47 |
48 |
49 |
50 |
51 | -------------------------------------------------------------------------------- /templates/partials/_movie_overlays.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |

5 | 10 |

11 |
13 |
14 |

15 | Selecting any overlays will automatically update the preview image. You can upload a custom base image 16 | from a file or a URL to preview overlays on your Movie poster. 17 |

18 | 19 |
20 |
21 |
22 | 23 |
24 |
25 | 34 | 35 | 37 | 38 |
39 | 40 | 42 | 43 |
44 | 46 | 48 |
49 | 50 |
51 | 54 | 57 |
58 |
59 | 60 | 61 |
62 |
Movie Image
63 | Movie Preview 67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | 76 | 77 | {% import 'partials/_macros.html' as macros %} 78 | {% for group in overlay_config %} 79 | {{ macros.overlay_group_section(library, data, version_info, group) }} 80 | {% endfor %} 81 | -------------------------------------------------------------------------------- /templates/partials/_rename_modal.html: -------------------------------------------------------------------------------- 1 | 2 | 40 | -------------------------------------------------------------------------------- /templates/partials/_show_collections.html: -------------------------------------------------------------------------------- 1 |

Select which Collection Files you would like to run against your libraries. You can hover over the magnifying 2 | glass to see some of the collections that the file will create, or click on the name of the file to be taken 3 | to it's wiki page.

4 | {% import 'partials/_macros.html' as macros %} 5 | 6 | {% for group in collection_config %} 7 | {{ macros.collection_group_section(library, data, version_info, group) }} 8 | {% endfor %} 9 | -------------------------------------------------------------------------------- /templates/partials/_show_library_settings.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | 6 |
7 |

8 | 12 |

13 |
14 |
15 | {% include "partials/_show_collections.html" %} 16 |
17 |
18 |
19 | 20 | 21 |
22 |

23 | 27 |

28 |
29 |
30 | {% include "partials/_show_overlays.html" %} 31 |
32 |
33 |
34 | 35 | 36 |
37 |

38 | 42 |

43 |
44 |
45 | {% include "partials/_show_attributes.html" %} 46 |
47 |
48 |
49 |
50 |
51 | -------------------------------------------------------------------------------- /templates/partials/_show_overlays.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |

5 | 10 |

11 |
13 |
14 |

15 | Selecting any overlays will automatically update the preview. You can upload different base images for Show, 16 | Season, and Episode posters. 17 |

18 | 19 | {% for level in ['show', 'season', 'episode'] %} 20 |
21 |
22 |
23 | 24 |
25 |
26 | 35 | 36 | 39 | 40 |
41 | 42 | 44 | 45 |
46 | 48 | 50 |
51 | 52 |
53 | 57 | 61 |
62 |
63 | 64 | 65 |
66 |
{{ level }} Image
67 | {{ level.title() }} Preview 72 |
73 |
74 |
75 |
76 | {% endfor %} 77 |
78 |
79 |
80 |
81 | 82 | {% import 'partials/_macros.html' as macros %} 83 | {% for group in overlay_config %} 84 | {{ macros.overlay_group_section(library, data, version_info, group) }} 85 | {% endfor %} 86 | --------------------------------------------------------------------------------