├── .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('
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 |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 |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 |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 |