├── .dockerignore ├── .flake8 ├── .github ├── dependabot.yml ├── label-actions.yml ├── semantic.yml └── workflows │ ├── CI.yml │ ├── ci-docker.yml │ ├── codeql.yml │ ├── issues.yml │ ├── localize.yml │ ├── python-flake8.yml │ ├── release-notifier.yml │ ├── update-changelog.yml │ ├── update-docs.yml │ └── yaml-lint.yml ├── .gitignore ├── .gitmodules ├── .readthedocs.yaml ├── .rstcheck.cfg ├── Contents ├── Code │ ├── __init__.py │ ├── constants.py │ ├── default_prefs.py │ ├── general_helper.py │ ├── lizardbyte_db_helper.py │ ├── migration_helper.py │ ├── plex_api_helper.py │ ├── scheduled_tasks.py │ ├── themerr_db_helper.py │ ├── tmdb_helper.py │ ├── webapp.py │ └── youtube_dl_helper.py ├── DefaultPrefs.json ├── Resources │ ├── attribution.png │ ├── icon-default.png │ └── web │ │ ├── css │ │ └── custom.css │ │ ├── images │ │ ├── favicon.ico │ │ └── icon.png │ │ ├── js │ │ └── translations.js │ │ └── templates │ │ ├── base.html │ │ ├── home.html │ │ ├── home_db_not_cached.html │ │ ├── navbar.html │ │ └── translations.html └── Strings │ ├── aa │ └── LC_MESSAGES │ │ └── themerr-plex.po │ ├── de.json │ ├── de │ └── LC_MESSAGES │ │ └── themerr-plex.po │ ├── en-gb.json │ ├── en-us.json │ ├── en.json │ ├── en │ └── LC_MESSAGES │ │ └── themerr-plex.po │ ├── en_GB │ └── LC_MESSAGES │ │ └── themerr-plex.po │ ├── en_US │ └── LC_MESSAGES │ │ └── themerr-plex.po │ ├── es.json │ ├── es │ └── LC_MESSAGES │ │ └── themerr-plex.po │ ├── fr.json │ ├── fr │ └── LC_MESSAGES │ │ └── themerr-plex.po │ ├── it.json │ ├── it │ └── LC_MESSAGES │ │ └── themerr-plex.po │ ├── ja.json │ ├── ja │ └── LC_MESSAGES │ │ └── themerr-plex.po │ ├── pt.json │ ├── pt │ └── LC_MESSAGES │ │ └── themerr-plex.po │ ├── ru.json │ ├── ru │ └── LC_MESSAGES │ │ └── themerr-plex.po │ ├── sv.json │ ├── sv │ └── LC_MESSAGES │ │ └── themerr-plex.po │ ├── themerr-plex.po │ ├── tr.json │ ├── tr │ └── LC_MESSAGES │ │ └── themerr-plex.po │ ├── zh.json │ └── zh │ └── LC_MESSAGES │ └── themerr-plex.po ├── DOCKER_README.md ├── Dockerfile ├── LICENSE ├── README.rst ├── codecov.yml ├── crowdin.yml ├── docs ├── Makefile ├── make.bat └── source │ ├── about │ ├── changelog.rst │ ├── docker.rst │ ├── installation.rst │ ├── overview.rst │ ├── troubleshooting.rst │ └── usage.rst │ ├── code_docs │ ├── general_helper.rst │ ├── lizardbyte_db_helper.rst │ ├── main.rst │ ├── migration_helper.rst │ ├── plex_api_helper.rst │ ├── scheduled_tasks.rst │ ├── themerr_db_helper.rst │ ├── tmdb_helper.rst │ ├── webapp.rst │ └── youtube_dl_helper.rst │ ├── conf.py │ ├── contributing │ ├── build.rst │ ├── contributing.rst │ ├── database.rst │ └── testing.rst │ ├── global.rst │ ├── index.rst │ └── toc.rst ├── package.json ├── requirements-build.txt ├── requirements-dev.txt ├── requirements.txt ├── scripts ├── _locale.py ├── babel.cfg └── build_plist.py └── tests ├── __init__.py ├── ci ├── __init__.py └── test_docs.py ├── conftest.py ├── data └── video_stub.mp4 ├── functional ├── __init__.py ├── test_plex_plugin.py └── test_webapp.py └── unit ├── __init__.py ├── test_code.py ├── test_general_helper.py ├── test_lizardbyte_db_helper.py ├── test_migration_helper.py ├── test_plex_api_helper.py ├── test_scheduled_tasks.py ├── test_themerr_db_helper.py ├── test_tmdb_helper.py ├── test_webapp.py └── test_youtube_dl_helper.py /.dockerignore: -------------------------------------------------------------------------------- 1 | # ignore git files 2 | .git* 3 | 4 | # ignore hidden files 5 | .* 6 | 7 | # ignore directories 8 | docs/ 9 | tests/ 10 | 11 | # ignore venv when building locally 12 | venv/ 13 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | filename = 3 | *.py, 4 | *.pys 5 | max-line-length = 120 6 | extend-exclude = 7 | venv/ 8 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This file is centrally managed in https://github.com//.github/ 3 | # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in 4 | # the above-mentioned repo. 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "docker" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | time: "08:00" 13 | open-pull-requests-limit: 10 14 | 15 | - package-ecosystem: "github-actions" 16 | directory: "/" 17 | schedule: 18 | interval: "daily" 19 | time: "08:30" 20 | open-pull-requests-limit: 10 21 | 22 | - package-ecosystem: "npm" 23 | directory: "/" 24 | schedule: 25 | interval: "daily" 26 | time: "09:00" 27 | open-pull-requests-limit: 10 28 | 29 | - package-ecosystem: "nuget" 30 | directory: "/" 31 | schedule: 32 | interval: "daily" 33 | time: "09:30" 34 | open-pull-requests-limit: 10 35 | 36 | - package-ecosystem: "pip" 37 | directory: "/" 38 | schedule: 39 | interval: "daily" 40 | time: "10:00" 41 | open-pull-requests-limit: 10 42 | 43 | - package-ecosystem: "gitsubmodule" 44 | directory: "/" 45 | schedule: 46 | interval: "daily" 47 | time: "10:30" 48 | open-pull-requests-limit: 10 49 | -------------------------------------------------------------------------------- /.github/label-actions.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This file is centrally managed in https://github.com//.github/ 3 | # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in 4 | # the above-mentioned repo. 5 | 6 | # Configuration for Label Actions - https://github.com/dessant/label-actions 7 | 8 | added: 9 | comment: > 10 | This feature has been added and will be available in the next release. 11 | fixed: 12 | comment: > 13 | This issue has been fixed and will be available in the next release. 14 | invalid:duplicate: 15 | comment: > 16 | :wave: @{issue-author}, this appears to be a duplicate of a pre-existing issue. 17 | close: true 18 | lock: true 19 | unlabel: 'status:awaiting-triage' 20 | 21 | -invalid:duplicate: 22 | reopen: true 23 | unlock: true 24 | 25 | invalid:support: 26 | comment: > 27 | :wave: @{issue-author}, we use the issue tracker exclusively for bug reports. 28 | However, this issue appears to be a support request. Please use our 29 | [Support Center](https://app.lizardbyte.dev/support) for support issues. Thanks. 30 | close: true 31 | lock: true 32 | lock-reason: 'off-topic' 33 | unlabel: 'status:awaiting-triage' 34 | 35 | -invalid:support: 36 | reopen: true 37 | unlock: true 38 | 39 | invalid:template-incomplete: 40 | issues: 41 | comment: > 42 | :wave: @{issue-author}, please edit your issue to complete the template with 43 | all the required info. Your issue will be automatically closed in 5 days if 44 | the template is not completed. Thanks. 45 | prs: 46 | comment: > 47 | :wave: @{issue-author}, please edit your PR to complete the template with 48 | all the required info. Your PR will be automatically closed in 5 days if 49 | the template is not completed. Thanks. 50 | -------------------------------------------------------------------------------- /.github/semantic.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This file is centrally managed in https://github.com//.github/ 3 | # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in 4 | # the above-mentioned repo. 5 | 6 | # This is the configuration file for https://github.com/Ezard/semantic-prs 7 | 8 | enabled: true 9 | titleOnly: true # We only use the PR title as we squash and merge 10 | commitsOnly: false 11 | titleAndCommits: false 12 | anyCommit: false 13 | allowMergeCommits: false 14 | allowRevertCommits: false 15 | -------------------------------------------------------------------------------- /.github/workflows/issues.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This action is centrally managed in https://github.com//.github/ 3 | # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in 4 | # the above-mentioned repo. 5 | 6 | # Label and un-label actions using `../label-actions.yml`. 7 | 8 | name: Issues 9 | 10 | on: 11 | issues: 12 | types: [labeled, unlabeled] 13 | discussion: 14 | types: [labeled, unlabeled] 15 | 16 | jobs: 17 | label: 18 | name: Label Actions 19 | if: startsWith(github.repository, 'LizardByte/') 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Label Actions 23 | uses: dessant/label-actions@v4 24 | with: 25 | github-token: ${{ secrets.GH_BOT_TOKEN }} 26 | -------------------------------------------------------------------------------- /.github/workflows/localize.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: localize 3 | 4 | on: 5 | push: 6 | branches: [master] 7 | paths: # prevents workflow from running unless these files change 8 | - '.github/workflows/localize.yml' 9 | - 'Contents/Strings/Themerr-plex.po' 10 | - 'Contents/Code/**.py' 11 | - 'Contents/Resources/web/templates/**' 12 | workflow_dispatch: 13 | 14 | jobs: 15 | localize: 16 | name: Update Localization 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | with: 23 | submodules: recursive 24 | 25 | - name: Set up Python 26 | uses: LizardByte/setup-python-action@v2024.609.5111 27 | with: 28 | python-version: '2.7' 29 | 30 | - name: Set up Python Dependencies 31 | run: | 32 | python -m pip install --upgrade pip setuptools requests 33 | python -m pip install -r requirements.txt # requests is required to install python-plexapi 34 | 35 | - name: Update Strings 36 | run: | 37 | python ./scripts/_locale.py --extract 38 | 39 | - name: git diff 40 | run: | 41 | # disable the pager 42 | git config --global pager.diff false 43 | 44 | # print the git diff 45 | git diff Contents/Strings/themerr-plex.po 46 | 47 | # set the variable with minimal output, replacing `\t` with ` ` 48 | OUTPUT=$(git diff --numstat Contents/Strings/themerr-plex.po | sed -e "s#\t# #g") 49 | echo "git_diff=${OUTPUT}" >> $GITHUB_ENV 50 | 51 | - name: git reset 52 | if: ${{ env.git_diff == '1 1 Contents/Strings/themerr-plex.po' }} # only run if more than 1 line changed 53 | run: | 54 | git reset --hard 55 | 56 | - name: Get current date 57 | id: date 58 | run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT 59 | 60 | - name: Create/Update Pull Request 61 | uses: peter-evans/create-pull-request@v7 62 | with: 63 | add-paths: | 64 | Contents/Strings/*.po 65 | token: ${{ secrets.GH_BOT_TOKEN }} # must trigger PR tests 66 | commit-message: "chore(l10n): new babel updates" 67 | branch: localize/update 68 | delete-branch: true 69 | base: master 70 | title: "chore(l10n): new babel updates" 71 | body: | 72 | Update report 73 | - Updated ${{ steps.date.outputs.date }} 74 | - Auto-generated by [create-pull-request][1] 75 | 76 | [1]: https://github.com/peter-evans/create-pull-request 77 | labels: | 78 | babel 79 | l10n 80 | -------------------------------------------------------------------------------- /.github/workflows/python-flake8.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This action is centrally managed in https://github.com//.github/ 3 | # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in 4 | # the above-mentioned repo. 5 | 6 | # Lint python files with flake8. 7 | 8 | name: flake8 9 | 10 | on: 11 | pull_request: 12 | branches: [master] 13 | types: [opened, synchronize, reopened] 14 | 15 | concurrency: 16 | group: "${{ github.workflow }}-${{ github.ref }}" 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | flake8: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v4 25 | 26 | - name: Set up Python 27 | uses: actions/setup-python@v5 # https://github.com/actions/setup-python 28 | with: 29 | python-version: '3.10' 30 | 31 | - name: Install dependencies 32 | run: | 33 | # pin flake8 before v6.0.0 due to removal of support for type comments (required for Python 2.7 type hints) 34 | python -m pip install --upgrade pip setuptools "flake8<6" 35 | 36 | - name: Test with flake8 37 | run: | 38 | python -m flake8 --verbose 39 | -------------------------------------------------------------------------------- /.github/workflows/release-notifier.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This action is centrally managed in https://github.com//.github/ 3 | # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in 4 | # the above-mentioned repo. 5 | 6 | # Send release notification to various platforms. 7 | 8 | name: Release Notifications 9 | 10 | on: 11 | release: 12 | types: 13 | - released # this triggers when a release is published, but does not include pre-releases or drafts 14 | 15 | jobs: 16 | simplified_changelog: 17 | if: >- 18 | startsWith(github.repository, 'LizardByte/') && 19 | !github.event.release.prerelease && 20 | !github.event.release.draft 21 | outputs: 22 | SIMPLIFIED_BODY: ${{ steps.output.outputs.SIMPLIFIED_BODY }} 23 | runs-on: ubuntu-latest 24 | steps: 25 | - name: remove contributors section 26 | env: 27 | RELEASE_BODY: ${{ github.event.release.body }} 28 | id: output 29 | run: | 30 | echo "${RELEASE_BODY}" > ./release_body.md 31 | modified_body=$(sed '/^---$/d; /^## Contributors$/,/<\/a>/d' ./release_body.md) 32 | echo "modified_body: ${modified_body}" 33 | 34 | # use a heredoc to ensure the output is multiline 35 | echo "SIMPLIFIED_BODY<> $GITHUB_OUTPUT 36 | echo "${modified_body}" >> $GITHUB_OUTPUT 37 | echo "EOF" >> $GITHUB_OUTPUT 38 | 39 | discord: 40 | if: >- 41 | startsWith(github.repository, 'LizardByte/') && 42 | !github.event.release.prerelease && 43 | !github.event.release.draft 44 | needs: simplified_changelog 45 | runs-on: ubuntu-latest 46 | steps: 47 | - name: discord 48 | uses: sarisia/actions-status-discord@v1 49 | with: 50 | avatar_url: ${{ secrets.ORG_LOGO_URL }} 51 | color: 0x00ff00 52 | description: ${{ needs.simplified_changelog.outputs.SIMPLIFIED_BODY }} 53 | nodetail: true 54 | nofail: false 55 | title: ${{ github.event.repository.name }} ${{ github.ref_name }} Released 56 | url: ${{ github.event.release.html_url }} 57 | username: ${{ secrets.DISCORD_USERNAME }} 58 | webhook: ${{ secrets.DISCORD_RELEASE_WEBHOOK }} 59 | 60 | facebook_group: 61 | if: >- 62 | startsWith(github.repository, 'LizardByte/') && 63 | !github.event.release.prerelease && 64 | !github.event.release.draft 65 | runs-on: ubuntu-latest 66 | steps: 67 | - name: facebook-post-action 68 | uses: ReenigneArcher/facebook-post-action@v1 69 | with: 70 | page_id: ${{ secrets.FACEBOOK_GROUP_ID }} 71 | access_token: ${{ secrets.FACEBOOK_ACCESS_TOKEN }} 72 | message: | 73 | ${{ github.event.repository.name }} ${{ github.ref_name }} Released 74 | url: ${{ github.event.release.html_url }} 75 | 76 | facebook_page: 77 | if: >- 78 | startsWith(github.repository, 'LizardByte/') && 79 | !github.event.release.prerelease && 80 | !github.event.release.draft 81 | runs-on: ubuntu-latest 82 | steps: 83 | - name: facebook-post-action 84 | uses: ReenigneArcher/facebook-post-action@v1 85 | with: 86 | page_id: ${{ secrets.FACEBOOK_PAGE_ID }} 87 | access_token: ${{ secrets.FACEBOOK_ACCESS_TOKEN }} 88 | message: | 89 | ${{ github.event.repository.name }} ${{ github.ref_name }} Released 90 | url: ${{ github.event.release.html_url }} 91 | 92 | reddit: 93 | if: >- 94 | startsWith(github.repository, 'LizardByte/') && 95 | !github.event.release.prerelease && 96 | !github.event.release.draft 97 | needs: simplified_changelog 98 | runs-on: ubuntu-latest 99 | steps: 100 | - name: reddit 101 | uses: bluwy/release-for-reddit-action@v2 102 | with: 103 | username: ${{ secrets.REDDIT_USERNAME }} 104 | password: ${{ secrets.REDDIT_PASSWORD }} 105 | app-id: ${{ secrets.REDDIT_CLIENT_ID }} 106 | app-secret: ${{ secrets.REDDIT_CLIENT_SECRET }} 107 | subreddit: ${{ secrets.REDDIT_SUBREDDIT }} 108 | title: ${{ github.event.repository.name }} ${{ github.ref_name }} Released 109 | url: ${{ github.event.release.html_url }} 110 | flair-id: ${{ secrets.REDDIT_FLAIR_ID }} # https://www.reddit.com/r/>/api/link_flair.json 111 | comment: ${{ needs.simplified_changelog.outputs.SIMPLIFIED_BODY }} 112 | 113 | x: 114 | if: >- 115 | startsWith(github.repository, 'LizardByte/') && 116 | !github.event.release.prerelease && 117 | !github.event.release.draft 118 | runs-on: ubuntu-latest 119 | steps: 120 | - name: x 121 | uses: nearform-actions/github-action-notify-twitter@v1 122 | with: 123 | message: ${{ github.event.release.html_url }} 124 | twitter-app-key: ${{ secrets.X_APP_KEY }} 125 | twitter-app-secret: ${{ secrets.X_APP_SECRET }} 126 | twitter-access-token: ${{ secrets.X_ACCESS_TOKEN }} 127 | twitter-access-token-secret: ${{ secrets.X_ACCESS_TOKEN_SECRET }} 128 | -------------------------------------------------------------------------------- /.github/workflows/update-changelog.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This action is centrally managed in https://github.com//.github/ 3 | # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in 4 | # the above-mentioned repo. 5 | 6 | # Update changelog on release events. 7 | 8 | name: Update changelog 9 | 10 | on: 11 | release: 12 | types: [created, edited, deleted] 13 | workflow_dispatch: 14 | 15 | concurrency: 16 | group: "${{ github.workflow }}" 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | update-changelog: 21 | if: >- 22 | github.event_name == 'workflow_dispatch' || 23 | (!github.event.release.prerelease && !github.event.release.draft) 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: Update Changelog 27 | uses: LizardByte/update-changelog-action@v2024.609.4705 28 | with: 29 | changelogBranch: changelog 30 | changelogFile: CHANGELOG.md 31 | token: ${{ secrets.GH_BOT_TOKEN }} 32 | -------------------------------------------------------------------------------- /.github/workflows/update-docs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This action is centrally managed in https://github.com//.github/ 3 | # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in 4 | # the above-mentioned repo. 5 | 6 | # Use the `rtd` repository label to identify repositories that should trigger have this workflow. 7 | # If the project slug is not the repository name, add a repository variable named `READTHEDOCS_SLUG` with the value of 8 | # the ReadTheDocs project slug. 9 | 10 | # Update readthedocs on release events. 11 | 12 | name: Update docs 13 | 14 | on: 15 | release: 16 | types: [created, edited, deleted] 17 | 18 | concurrency: 19 | group: "${{ github.workflow }}-${{ github.event.release.tag_name }}" 20 | cancel-in-progress: true 21 | 22 | jobs: 23 | update-docs: 24 | env: 25 | RTD_SLUG: ${{ vars.READTHEDOCS_SLUG }} 26 | RTD_TOKEN: ${{ secrets.READTHEDOCS_TOKEN }} 27 | TAG: ${{ github.event.release.tag_name }} 28 | if: >- 29 | !github.event.release.draft 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Get RTD_SLUG 33 | run: | 34 | # if the RTD_SLUG is not set, use the repository name in lowercase 35 | if [ -z "${RTD_SLUG}" ]; then 36 | RTD_SLUG=$(echo "${{ github.event.repository.name }}" | tr '[:upper:]' '[:lower:]') 37 | fi 38 | echo "RTD_SLUG=${RTD_SLUG}" >> $GITHUB_ENV 39 | 40 | - name: Deactivate deleted release 41 | if: >- 42 | github.event_name == 'release' && 43 | github.event.action == 'deleted' 44 | run: | 45 | json_body=$(jq -n \ 46 | --arg active "false" \ 47 | --arg hidden "false" \ 48 | --arg privacy_level "public" \ 49 | '{active: $active, hidden: $hidden, privacy_level: $privacy_level}') 50 | 51 | curl \ 52 | -X PATCH \ 53 | -H "Authorization: Token ${RTD_TOKEN}" \ 54 | https://readthedocs.org/api/v3/projects/${RTD_SLUG}/versions/${TAG}/ \ 55 | -H "Content-Type: application/json" \ 56 | -d "$json_body" 57 | 58 | - name: Check if edited release is latest GitHub release 59 | id: check 60 | if: >- 61 | github.event_name == 'release' && 62 | github.event.action == 'edited' 63 | uses: actions/github-script@v7 64 | with: 65 | script: | 66 | const latestRelease = await github.rest.repos.getLatestRelease({ 67 | owner: context.repo.owner, 68 | repo: context.repo.repo 69 | }); 70 | 71 | core.setOutput('isLatestRelease', latestRelease.data.tag_name === context.payload.release.tag_name); 72 | 73 | - name: Update RTD project 74 | # changing the default branch in readthedocs makes "latest" point to that branch/tag 75 | # we can also update other properties like description, etc. 76 | if: >- 77 | steps.check.outputs.isLatestRelease == 'true' 78 | run: | 79 | json_body=$(jq -n \ 80 | --arg default_branch "${TAG}" \ 81 | --arg description "${{ github.event.repository.description }}" \ 82 | '{default_branch: $default_branch}') 83 | 84 | curl \ 85 | -X PATCH \ 86 | -H "Authorization: Token ${RTD_TOKEN}" \ 87 | https://readthedocs.org/api/v3/projects/${RTD_SLUG}/ \ 88 | -H "Content-Type: application/json" \ 89 | -d "$json_body" 90 | -------------------------------------------------------------------------------- /.github/workflows/yaml-lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This action is centrally managed in https://github.com//.github/ 3 | # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in 4 | # the above-mentioned repo. 5 | 6 | # Lint yaml files. 7 | 8 | name: yaml lint 9 | 10 | on: 11 | pull_request: 12 | branches: [master] 13 | types: [opened, synchronize, reopened] 14 | 15 | concurrency: 16 | group: "${{ github.workflow }}-${{ github.ref }}" 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | yaml-lint: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v4 25 | 26 | - name: Find additional files 27 | id: find-files 28 | run: | 29 | # space separated list of files 30 | FILES=.clang-format 31 | 32 | # empty placeholder 33 | FOUND="" 34 | 35 | for FILE in ${FILES}; do 36 | if [ -f "$FILE" ] 37 | then 38 | FOUND="$FOUND $FILE" 39 | fi 40 | done 41 | 42 | echo "found=${FOUND}" >> $GITHUB_OUTPUT 43 | 44 | - name: yaml lint 45 | id: yaml-lint 46 | uses: ibiqlik/action-yamllint@v3 47 | with: 48 | # https://yamllint.readthedocs.io/en/stable/configuration.html#default-configuration 49 | config_data: | 50 | extends: default 51 | rules: 52 | comments: 53 | level: error 54 | line-length: 55 | max: 120 56 | truthy: 57 | # GitHub uses "on" for workflow event triggers 58 | # .clang-format file has options of "Yes" "No" that will be caught by this, so changed to "warning" 59 | allowed-values: ['true', 'false', 'on'] 60 | check-keys: true 61 | level: warning 62 | file_or_dir: . ${{ steps.find-files.outputs.found }} 63 | 64 | - name: Log 65 | run: | 66 | cat "${{ steps.yaml-lint.outputs.logfile }}" >> $GITHUB_STEP_SUMMARY 67 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | .idea/ 161 | 162 | # Remove the agent Info.plist since we are building it 163 | Contents/Info.plist 164 | 165 | # Remove plexhints files 166 | plexhints-temp 167 | *cache.sqlite 168 | 169 | # Remove python modules 170 | Contents/Libraries/Shared/ 171 | 172 | # npm 173 | node_modules/ 174 | package-lock.json 175 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third-party/youtube-dl"] 2 | path = third-party/youtube-dl 3 | url = https://github.com/ytdl-org/youtube-dl.git 4 | branch = master 5 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # .readthedocs.yaml 3 | # Read the Docs configuration file 4 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 5 | 6 | # Required 7 | version: 2 8 | 9 | # Set the version of Python 10 | build: 11 | os: ubuntu-20.04 12 | tools: 13 | python: "2.7" 14 | jobs: 15 | pre_build: 16 | - python ./scripts/build_plist.py 17 | post_build: 18 | - rstcheck -r . # lint rst files 19 | # - rstfmt --check --diff -w 120 . # check rst formatting 20 | 21 | # submodules required to include youtube-dl 22 | submodules: 23 | include: all 24 | recursive: true 25 | 26 | # Build documentation in the docs/ directory with Sphinx 27 | sphinx: 28 | builder: html 29 | configuration: docs/source/conf.py 30 | fail_on_warning: true 31 | 32 | # Using Sphinx, build docs in additional formats 33 | formats: all 34 | 35 | python: 36 | install: 37 | - requirements: requirements.txt # plugin requirements 38 | - requirements: requirements-dev.txt # docs requirements 39 | -------------------------------------------------------------------------------- /.rstcheck.cfg: -------------------------------------------------------------------------------- 1 | # configuration file for rstcheck, an rst linting tool 2 | # https://rstcheck.readthedocs.io/en/latest/usage/config 3 | 4 | [rstcheck] 5 | ignore_directives = 6 | automodule, 7 | include, 8 | mdinclude, 9 | todo, 10 | ignore_roles = 11 | modname, 12 | -------------------------------------------------------------------------------- /Contents/Code/constants.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # standard imports 4 | import plistlib 5 | import os 6 | 7 | # plex debugging 8 | try: 9 | import plexhints # noqa: F401 10 | except ImportError: 11 | pass 12 | else: # the code is running outside of Plex 13 | from plexhints.core_kit import Core # core kit 14 | 15 | # get plugin directory from core kit 16 | plugin_directory = Core.bundle_path 17 | if plugin_directory.endswith('test.bundle'): 18 | # use current directory instead, to allow for testing outside of Plex 19 | if os.path.basename(os.getcwd()) == 'docs': 20 | # use parent directory if current directory is 'docs' 21 | plugin_directory = os.path.dirname(os.getcwd()) 22 | else: 23 | plugin_directory = os.getcwd() 24 | 25 | # get identifier and version from Info.plist file 26 | info_file_path = os.path.join(plugin_directory, 'Contents', 'Info.plist') 27 | try: 28 | info_plist = plistlib.readPlist(pathOrFile=info_file_path) 29 | except IOError: 30 | info_plist = dict( 31 | CFBundleIdentifier='dev.lizardbyte.themerr-plex', 32 | PlexBundleVersion='0.0.0' 33 | ) 34 | plugin_identifier = info_plist['CFBundleIdentifier'] 35 | version = info_plist['PlexBundleVersion'] 36 | 37 | app_support_directory = Core.app_support_path 38 | metadata_base_directory = os.path.join(app_support_directory, 'Metadata') 39 | plugin_support_directory = os.path.join(app_support_directory, 'Plug-in Support') 40 | plugin_support_data_directory = os.path.join(plugin_support_directory, 'Data') 41 | themerr_data_directory = os.path.join(plugin_support_data_directory, plugin_identifier, 'DataItems') 42 | 43 | contributes_to = [ 44 | 'tv.plex.agents.movie', # new movie agent 45 | 'tv.plex.agents.series', # new tv show agent 46 | 'com.plexapp.agents.imdb', # legacy movie agent 47 | 'com.plexapp.agents.themoviedb', # legacy movie and tv show agent 48 | 'com.plexapp.agents.thetvdb', # legacy tv show agent 49 | 'dev.lizardbyte.retroarcher-plex' # retroarcher plugin 50 | ] 51 | 52 | guid_map = dict( 53 | imdb='imdb', 54 | tmdb='themoviedb', 55 | tvdb='thetvdb' 56 | ) 57 | 58 | metadata_type_map = dict( 59 | album='Albums', 60 | artist='Artists', 61 | collection='Collections', 62 | movie='Movies', 63 | show='TV Shows' 64 | ) 65 | 66 | # the explicit IPv4 address is used because `localhost` can resolve to ::1, which `websocket` rejects 67 | plex_url = 'http://127.0.0.1:32400' 68 | plex_token = os.environ.get('PLEXTOKEN') 69 | 70 | plex_section_type_settings_map = dict( 71 | album=9, 72 | artist=8, 73 | movie=1, 74 | photo=13, 75 | show=2, 76 | ) 77 | 78 | # issue url constants 79 | base_url = 'https://github.com/LizardByte/ThemerrDB/issues/new?assignees=' 80 | issue_label = 'request-theme' 81 | issue_template = 'theme.yml' 82 | url_name = 'database_url' 83 | title_prefix = dict( 84 | games='[GAME]: ', 85 | game_collections='[GAME COLLECTION]: ', 86 | game_franchises='[GAME FRANCHISE]: ', 87 | movies='[MOVIE]: ', 88 | movie_collections='[MOVIE COLLECTION]: ', 89 | tv_shows='[TV SHOW]: ', 90 | ) 91 | url_prefix = dict( 92 | games='https://www.igdb.com/games/', 93 | game_collections='https://www.igdb.com/collections/', 94 | game_franchises='https://www.igdb.com/franchises/', 95 | movies='https://www.themoviedb.org/movie/', 96 | movie_collections='https://www.themoviedb.org/collection/', 97 | tv_shows='https://www.themoviedb.org/tv/', 98 | ) 99 | 100 | # two additional strings to fill in later, item title and item url 101 | issue_urls = dict( 102 | games='{}&labels={}&template={}&title={}{}&{}={}{}'.format( 103 | base_url, issue_label, issue_template, title_prefix['games'], '{}', url_name, url_prefix['games'], '{}'), 104 | game_collections='{}&labels={}&template={}&title={}{}&{}={}{}'.format( 105 | base_url, issue_label, issue_template, title_prefix['game_collections'], '{}', url_name, 106 | url_prefix['game_collections'], '{}'), 107 | game_franchises='{}&labels={}&template={}&title={}{}&{}={}{}'.format( 108 | base_url, issue_label, issue_template, title_prefix['game_franchises'], '{}', url_name, 109 | url_prefix['game_franchises'], '{}'), 110 | movies='{}&labels={}&template={}&title={}{}&{}={}{}'.format( 111 | base_url, issue_label, issue_template, title_prefix['movies'], '{}', url_name, url_prefix['movies'], '{}'), 112 | movie_collections='{}&labels={}&template={}&title={}{}&{}={}{}'.format( 113 | base_url, issue_label, issue_template, title_prefix['movie_collections'], '{}', url_name, 114 | url_prefix['movie_collections'], '{}'), 115 | tv_shows='{}&labels={}&template={}&title={}{}&{}={}{}'.format( 116 | base_url, issue_label, issue_template, title_prefix['tv_shows'], '{}', url_name, url_prefix['tv_shows'], '{}'), 117 | ) 118 | 119 | media_type_dict = dict( 120 | art=dict( 121 | method=lambda item: item.uploadArt, 122 | type='art', 123 | name='art', 124 | themerr_data_key='art_url', 125 | remove_pref='bool_remove_unused_art', 126 | plex_field='art', 127 | ), 128 | posters=dict( 129 | method=lambda item: item.uploadPoster, 130 | type='posters', 131 | name='poster', 132 | themerr_data_key='poster_url', 133 | remove_pref='bool_remove_unused_posters', 134 | plex_field='thumb', 135 | ), 136 | themes=dict( 137 | method=lambda item: item.uploadTheme, 138 | type='themes', 139 | name='theme', 140 | themerr_data_key='youtube_theme_url', 141 | remove_pref='bool_remove_unused_theme_songs', 142 | plex_field='theme', 143 | ), 144 | ) 145 | -------------------------------------------------------------------------------- /Contents/Code/default_prefs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | default_prefs = dict( 4 | bool_plex_movie_support='True', 5 | bool_plex_series_support='True', 6 | bool_overwrite_plex_provided_themes='False', 7 | bool_prefer_mp4a_codec='True', 8 | bool_remove_unused_theme_songs='True', 9 | bool_remove_unused_art='False', 10 | bool_remove_unused_posters='False', 11 | bool_auto_update_items='True', 12 | bool_auto_update_movie_themes='True', 13 | bool_auto_update_tv_themes='True', 14 | bool_auto_update_collection_themes='True', 15 | bool_update_collection_metadata_plex_movie='False', 16 | bool_update_collection_metadata_legacy='True', 17 | int_update_themes_interval='60', 18 | int_update_database_cache_interval='60', 19 | int_plexapi_plexapi_timeout='180', 20 | int_plexapi_upload_retries_max='3', 21 | int_plexapi_upload_threads='3', 22 | str_youtube_cookies='', 23 | enum_webapp_locale='en', 24 | str_webapp_http_host='0.0.0.0', 25 | int_webapp_http_port='9494', 26 | bool_webapp_log_werkzeug_messages='False', 27 | bool_migrate_locked_themes='False', 28 | bool_migrate_locked_collection_fields='False', 29 | bool_ignore_locked_fields='False', 30 | ) 31 | -------------------------------------------------------------------------------- /Contents/Code/lizardbyte_db_helper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # plex debugging 4 | try: 5 | import plexhints # noqa: F401 6 | except ImportError: 7 | pass 8 | else: # the code is running outside of Plex 9 | from plexhints.log_kit import Log # log kit 10 | from plexhints.parse_kit import JSON # parse kit 11 | 12 | # imports from Libraries\Shared 13 | from typing import Optional, Tuple 14 | 15 | collection_types = dict( 16 | game_collections=dict( 17 | db_base_url='https://db.lizardbyte.dev/collections' 18 | ), 19 | game_franchises=dict( 20 | db_base_url='https://db.lizardbyte.dev/franchises' 21 | ), 22 | ) 23 | 24 | 25 | def get_igdb_id_from_collection(search_query, collection_type=None): 26 | # type: (str, Optional[str]) -> Optional[Tuple[int, str]] 27 | """ 28 | Search for a collection by name. 29 | 30 | Match a collection by name against the LizardByte db (clone of IGDB), to get the collection ID. 31 | 32 | Parameters 33 | ---------- 34 | search_query : str 35 | Collection name to search for. 36 | collection_type : Optional[str] 37 | Collection type to search for. Valid values are 'game_collections' and 'game_franchises'. If not provided, will 38 | first search for 'game_collections', then 'game_franchises', returning the first match. 39 | 40 | Returns 41 | ------- 42 | Optional[Tuple[int, str]] 43 | Tuple of ``id`` and ``collection_type`` if found, otherwise None. 44 | 45 | Examples 46 | -------- 47 | >>> get_igdb_id_from_collection(search_query='James Bond', collection_type='game_collections') 48 | 326 49 | >>> get_igdb_id_from_collection(search_query='James Bond', collection_type='game_franchises') 50 | 37 51 | """ 52 | Log.Debug('Searching LizardByte db for collection: {}'.format(search_query)) 53 | 54 | if collection_type is None: 55 | collection_types_list = ['game_collections', 'game_franchises'] 56 | else: 57 | collection_types_list = [collection_type] 58 | 59 | for collection_type in collection_types_list: 60 | try: 61 | db_base_url = collection_types[collection_type]['db_base_url'] 62 | except KeyError: 63 | Log.Error('Invalid collection type: {}'.format(collection_type)) 64 | else: 65 | url = '{}/all.json'.format(db_base_url) 66 | try: 67 | collection_data = JSON.ObjectFromURL(url=url, headers=dict(Accept='application/json'), 68 | cacheTime=0, errors='strict') 69 | except ValueError as e: 70 | Log.Error('Error getting collection data: {}'.format(e)) 71 | else: 72 | for _ in collection_data: 73 | if search_query.lower() == collection_data[_]['name'].lower(): 74 | return collection_data[_]['id'], collection_type 75 | -------------------------------------------------------------------------------- /Contents/Code/scheduled_tasks.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # standard imports 4 | import logging 5 | import threading 6 | import time 7 | 8 | # plex debugging 9 | try: 10 | import plexhints # noqa: F401 11 | except ImportError: 12 | pass 13 | else: # the code is running outside of Plex 14 | from plexhints.log_kit import Log # log kit 15 | from plexhints.prefs_kit import Prefs # prefs kit 16 | 17 | # imports from Libraries\Shared 18 | import schedule 19 | from typing import Any, Callable, Iterable, Mapping, Optional 20 | 21 | # local imports 22 | from constants import plugin_identifier 23 | from plex_api_helper import scheduled_update 24 | from webapp import cache_data 25 | 26 | # setup logging for schedule 27 | Log.Info('Adding schedule log handlers to plex plugin logger') 28 | 29 | # get the plugin logger 30 | plugin_logger = logging.getLogger(plugin_identifier) 31 | 32 | schedule.logger.handlers = plugin_logger.handlers 33 | schedule.logger.setLevel(plugin_logger.level) 34 | 35 | # test message 36 | schedule.logger.info('schedule logger test message') 37 | 38 | 39 | def run_threaded(target, daemon=None, args=(), **kwargs): 40 | # type: (Callable, Optional[bool], Iterable, Mapping[str, Any]) -> threading.Thread 41 | """ 42 | Run a function in a thread. 43 | 44 | Allows to run a function in a thread, which is useful for long-running tasks, and it 45 | allows the main thread to continue. 46 | 47 | Parameters 48 | ---------- 49 | target : Callable 50 | The function to run in a thread. 51 | daemon : Optional[py:class:`bool`] 52 | Whether the thread should be a daemon thread. 53 | args : Iterable 54 | The positional arguments to pass to the function. 55 | kwargs : Mapping[str, Any] 56 | The keyword arguments to pass to the function. 57 | 58 | Returns 59 | ------- 60 | threading.Thread 61 | The thread that the function is running in. 62 | 63 | Examples 64 | -------- 65 | >>> run_threaded(target=Log.Info, daemon=True, args=['Hello, world!']) 66 | "Hello, world!" 67 | """ 68 | job_thread = threading.Thread(target=target, args=args, kwargs=kwargs) 69 | if daemon: 70 | job_thread.daemon = True 71 | job_thread.start() 72 | return job_thread 73 | 74 | 75 | def schedule_loop(): 76 | # type: () -> None 77 | """ 78 | Start the schedule loop. 79 | 80 | Before the schedule loop is started, all jobs are run once. 81 | 82 | Examples 83 | -------- 84 | >>> schedule_loop() 85 | ... 86 | """ 87 | time.sleep(60) # give a little time for the server to start 88 | schedule.run_all() # run all jobs once 89 | 90 | while True: 91 | schedule.run_pending() 92 | time.sleep(1) 93 | 94 | 95 | def setup_scheduling(): 96 | # type: () -> None 97 | """ 98 | Sets up the scheduled tasks. 99 | 100 | The Tasks setup depend on the preferences set by the user. 101 | 102 | Examples 103 | -------- 104 | >>> setup_scheduling() 105 | ... 106 | 107 | See Also 108 | -------- 109 | plex_api_helper.scheduled_update : Scheduled function to update the themes. 110 | """ 111 | if Prefs['bool_auto_update_items']: 112 | schedule.every(max(15, int(Prefs['int_update_themes_interval']))).minutes.do( 113 | job_func=run_threaded, 114 | target=scheduled_update 115 | ) 116 | 117 | schedule.every(max(15, int(Prefs['int_update_database_cache_interval']))).minutes.do( 118 | job_func=run_threaded, 119 | target=cache_data 120 | ) 121 | 122 | run_threaded(target=schedule_loop, daemon=True) # start the schedule loop in a thread 123 | -------------------------------------------------------------------------------- /Contents/Code/themerr_db_helper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # standard imports 4 | from threading import Lock 5 | import time 6 | 7 | # plex debugging 8 | try: 9 | import plexhints # noqa: F401 10 | except ImportError: 11 | pass 12 | else: # the code is running outside of Plex 13 | from plexhints.log_kit import Log # log kit 14 | from plexhints.parse_kit import JSON # parse kit 15 | 16 | # imports from Libraries\Shared 17 | from typing import Union 18 | 19 | database_cache = {} 20 | last_cache_update = 0 21 | 22 | db_field_name = dict( 23 | games={'igdb': 'id'}, 24 | game_collections={'igdb': 'id'}, 25 | game_franchises={'igdb': 'id'}, 26 | movies={'themoviedb': 'id', 'imdb': 'imdb_id'}, 27 | movie_collections={'themoviedb': 'id'}, 28 | tv_shows={'themoviedb': 'id'}, 29 | ) 30 | 31 | lock = Lock() 32 | 33 | 34 | def update_cache(): 35 | # type: () -> None 36 | """ 37 | Update the ThemerrDB cache. 38 | 39 | The pages.json file is fetched for all database types, then each all_page_N.json file is fetched to form the 40 | complete set of available IDs. 41 | 42 | Attempting to update the cache while an update is already in progress will wait until the current update is 43 | complete. 44 | 45 | Updating the cache less than an hour after the last update is a no-op. 46 | """ 47 | Log.Info('Updating ThemerrDB cache') 48 | 49 | global last_cache_update 50 | 51 | if time.time() - last_cache_update < 3600: 52 | Log.Info('Cache updated less than an hour ago, skipping') 53 | return 54 | 55 | with lock: 56 | for database_type, databases in db_field_name.items(): 57 | try: 58 | pages = JSON.ObjectFromURL( 59 | cacheTime=3600, 60 | url='https://app.lizardbyte.dev/ThemerrDB/{}/pages.json'.format(database_type), 61 | errors='ignore' # don't crash the plugin 62 | ) 63 | page_count = pages['pages'] 64 | 65 | id_index = {db: set() for db in databases} 66 | 67 | for page in range(page_count): 68 | page_data = JSON.ObjectFromURL( 69 | cacheTime=3600, 70 | url='https://app.lizardbyte.dev/ThemerrDB/{}/all_page_{}.json'.format(database_type, page + 1), 71 | errors='ignore' # don't crash the plugin 72 | ) 73 | 74 | for db in databases: 75 | id_index[db].update(str(item[db_field_name[database_type][db]]) for item in page_data) 76 | 77 | database_cache[database_type] = id_index 78 | 79 | Log.Info('{}: database updated'.format(database_type)) 80 | except Exception as e: 81 | Log.Error('{}: Error retrieving page index from ThemerrDB: {}'.format(database_type, e)) 82 | 83 | database_cache[database_type] = {} 84 | 85 | last_cache_update = time.time() 86 | 87 | 88 | def item_exists(database_type, database, id): 89 | # type: (str, str, Union[int, str]) -> bool 90 | """ 91 | Check if an item exists in the ThemerrDB. 92 | 93 | Parameters 94 | ---------- 95 | database_type : str 96 | The type of database to check for the item. 97 | 98 | database : str 99 | The database to check for the item. 100 | 101 | id : Union[int, str] 102 | The ID of the item to check for. 103 | 104 | Returns 105 | ------- 106 | py:class:`bool` 107 | True if the item exists in the ThemerrDB, otherwise False. 108 | 109 | Examples 110 | -------- 111 | >>> item_exists(database_type='games', database='igdb', id=1234) 112 | True 113 | 114 | >>> item_exists(database_type='movies', database='themoviedb', id=1234) 115 | False 116 | """ 117 | if database_type not in db_field_name: 118 | Log.Critical('"{}" is not a valid database_type. Allowed values are: {}' 119 | .format(database_type, db_field_name.keys())) 120 | return False 121 | 122 | if database_type not in database_cache: 123 | update_cache() 124 | 125 | type_cache = database_cache[database_type] 126 | return database in type_cache and str(id) in type_cache[database] 127 | -------------------------------------------------------------------------------- /Contents/Code/tmdb_helper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # plex debugging 4 | try: 5 | import plexhints # noqa: F401 6 | except ImportError: 7 | pass 8 | else: # the code is running outside of Plex 9 | from plexhints.constant_kit import CACHE_1DAY # constant kit 10 | from plexhints.log_kit import Log # log kit 11 | from plexhints.parse_kit import JSON # parse kit 12 | from plexhints.util_kit import String # util kit 13 | 14 | # imports from Libraries\Shared 15 | from typing import Optional, Union 16 | 17 | # url borrowed from TheMovieDB.bundle 18 | tmdb_base_url = 'http://127.0.0.1:32400/services/tmdb?uri=' 19 | 20 | 21 | def get_tmdb_id_from_external_id(external_id, database, item_type): 22 | # type: (Union[int, str], str, str) -> Optional[int] 23 | """ 24 | Convert IMDB ID to TMDB ID. 25 | 26 | Use the builtin Plex tmdb api service to search for a movie by IMDB ID. 27 | 28 | Parameters 29 | ---------- 30 | external_id : Union[int, str] 31 | External ID to convert. 32 | database : str 33 | Database to search. Must be one of 'imdb' or 'tvdb'. 34 | item_type : str 35 | Item type to search. Must be one of 'movie' or 'tv'. 36 | 37 | Returns 38 | ------- 39 | Optional[int] 40 | Return TMDB ID if found, otherwise None. 41 | 42 | Examples 43 | -------- 44 | >>> get_tmdb_id_from_external_id(imdb_id='tt1254207', database='imdb', item_type='movie') 45 | 10378 46 | >>> get_tmdb_id_from_external_id(imdb_id='268592', database='tvdb', item_type='tv') 47 | 48866 48 | """ 49 | if database.lower() not in ['imdb', 'tvdb']: 50 | Log.Exception('Invalid database: {}'.format(database)) 51 | return 52 | if item_type.lower() not in ['movie', 'tv']: 53 | Log.Exception('Invalid item type: {}'.format(item_type)) 54 | return 55 | 56 | # according to https://www.themoviedb.org/talk/5f6a0500688cd000351c1712 we can search by external id 57 | # https://api.themoviedb.org/3/find/tt0458290?api_key=###&external_source=imdb_id 58 | find_url_suffix = 'find/{}?external_source={}_id' 59 | 60 | url = '{}/{}'.format( 61 | tmdb_base_url, 62 | find_url_suffix.format(String.Quote(s=str(external_id), usePlus=True), database.lower()) 63 | ) 64 | try: 65 | tmdb_data = JSON.ObjectFromURL( 66 | url=url, sleep=2.0, headers=dict(Accept='application/json'), cacheTime=CACHE_1DAY, errors='strict') 67 | except Exception as e: 68 | Log.Debug('Error converting external ID to TMDB ID: {}'.format(e)) 69 | else: 70 | Log.Debug('TMDB data: {}'.format(tmdb_data)) 71 | try: 72 | # this is already an integer, but let's force it 73 | tmdb_id = int(tmdb_data['{}_results'.format(item_type.lower())][0]['id']) 74 | except (IndexError, KeyError, ValueError): 75 | Log.Debug('Error converting external ID to TMDB ID: {}'.format(tmdb_data)) 76 | else: 77 | return tmdb_id 78 | 79 | 80 | def get_tmdb_id_from_collection(search_query): 81 | # type: (str) -> Optional[int] 82 | """ 83 | Search for a collection by name. 84 | 85 | Use the builtin Plex tmdb api service to search for a tmdb collection by name. 86 | 87 | Parameters 88 | ---------- 89 | search_query : str 90 | Name of collection to search for. 91 | 92 | Returns 93 | ------- 94 | Optional[int] 95 | Return collection ID if found, otherwise None. 96 | 97 | Examples 98 | -------- 99 | >>> get_tmdb_id_from_collection(search_query='James Bond Collection') 100 | 645 101 | >>> get_tmdb_id_from_collection(search_query='James Bond') 102 | 645 103 | """ 104 | # /search/collection?query=James%20Bond%20Collection&include_adult=false&language=en-US&page=1" 105 | query_url = 'search/collection?query={}' 106 | query_item = search_query.split('&', 1)[0] 107 | 108 | # Plex returns 500 error if spaces are in collection query, same with `_`, `+`, and `%20`... so use `-` 109 | url = '{}/{}'.format(tmdb_base_url, query_url.format(String.Quote( 110 | s=search_query.replace(' ', '-'), usePlus=False))) 111 | try: 112 | tmdb_data = JSON.ObjectFromURL( 113 | url=url, sleep=2.0, headers=dict(Accept='application/json'), cacheTime=CACHE_1DAY, errors='strict') 114 | except Exception as e: 115 | Log.Debug('Error searching for collection {}: {}'.format(search_query, e)) 116 | else: 117 | collection_id = None 118 | Log.Debug('TMDB data: {}'.format(tmdb_data)) 119 | 120 | end_string = 'Collection' # collection names on themoviedb end with 'Collection' 121 | try: 122 | for result in tmdb_data['results']: 123 | if result['name'].lower() == query_item.lower() or \ 124 | '{} {}'.format(query_item.lower(), end_string).lower() == result['name'].lower(): 125 | collection_id = int(result['id']) 126 | except (IndexError, KeyError, ValueError): 127 | Log.Debug('Error searching for collection {}: {}'.format(search_query, tmdb_data)) 128 | else: 129 | return collection_id 130 | -------------------------------------------------------------------------------- /Contents/DefaultPrefs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "bool_plex_movie_support", 4 | "type": "bool", 5 | "label": "bool_plex_movie_support", 6 | "default": "True", 7 | "secure": "false" 8 | }, 9 | { 10 | "id": "bool_plex_series_support", 11 | "type": "bool", 12 | "label": "bool_plex_series_support", 13 | "default": "True", 14 | "secure": "false" 15 | }, 16 | { 17 | "id": "bool_overwrite_plex_provided_themes", 18 | "type": "bool", 19 | "label": "bool_overwrite_plex_provided_themes", 20 | "default": "False", 21 | "secure": "false" 22 | }, 23 | { 24 | "id": "bool_prefer_mp4a_codec", 25 | "type": "bool", 26 | "label": "bool_prefer_mp4a_codec", 27 | "default": "True", 28 | "secure": "false" 29 | }, 30 | { 31 | "id": "bool_remove_unused_theme_songs", 32 | "type": "bool", 33 | "label": "bool_remove_unused_theme_songs", 34 | "default": "True", 35 | "secure": "false" 36 | }, 37 | { 38 | "id": "bool_remove_unused_art", 39 | "type": "bool", 40 | "label": "bool_remove_unused_art", 41 | "default": "False", 42 | "secure": "false" 43 | }, 44 | { 45 | "id": "bool_remove_unused_posters", 46 | "type": "bool", 47 | "label": "bool_remove_unused_posters", 48 | "default": "False", 49 | "secure": "false" 50 | }, 51 | { 52 | "id": "bool_auto_update_items", 53 | "type": "bool", 54 | "label": "bool_auto_update_items", 55 | "default": "True", 56 | "secure": "false" 57 | }, 58 | { 59 | "id": "bool_auto_update_movie_themes", 60 | "type": "bool", 61 | "label": "bool_auto_update_movie_themes", 62 | "default": "True", 63 | "secure": "false" 64 | }, 65 | { 66 | "id": "bool_auto_update_tv_themes", 67 | "type": "bool", 68 | "label": "bool_auto_update_tv_themes", 69 | "default": "True", 70 | "secure": "false" 71 | }, 72 | { 73 | "id": "bool_auto_update_collection_themes", 74 | "type": "bool", 75 | "label": "bool_auto_update_collection_themes", 76 | "default": "True", 77 | "secure": "false" 78 | }, 79 | { 80 | "id": "bool_update_collection_metadata_plex_movie", 81 | "type": "bool", 82 | "label": "bool_update_collection_metadata_plex_movie", 83 | "default": "False", 84 | "secure": "false" 85 | }, 86 | { 87 | "id": "bool_update_collection_metadata_legacy", 88 | "type": "bool", 89 | "label": "bool_update_collection_metadata_legacy", 90 | "default": "True", 91 | "secure": "false" 92 | }, 93 | { 94 | "id": "int_update_themes_interval", 95 | "type": "text", 96 | "label": "int_update_themes_interval", 97 | "default": "60", 98 | "secure": "false" 99 | }, 100 | { 101 | "id": "int_update_database_cache_interval", 102 | "type": "text", 103 | "label": "int_update_database_cache_interval", 104 | "default": "60", 105 | "secure": "false" 106 | }, 107 | { 108 | "id": "int_plexapi_plexapi_timeout", 109 | "type": "text", 110 | "label": "int_plexapi_plexapi_timeout", 111 | "default": "180", 112 | "secure": "false" 113 | }, 114 | { 115 | "id": "int_plexapi_upload_retries_max", 116 | "type": "text", 117 | "label": "int_plexapi_upload_retries_max", 118 | "default": "6", 119 | "secure": "false" 120 | }, 121 | { 122 | "id": "int_plexapi_upload_threads", 123 | "type": "text", 124 | "label": "int_plexapi_upload_threads", 125 | "default": "3", 126 | "secure": "false" 127 | }, 128 | { 129 | "id": "str_youtube_cookies", 130 | "type": "text", 131 | "label": "str_youtube_cookies", 132 | "default": "", 133 | "secure": "true" 134 | }, 135 | { 136 | "id": "enum_webapp_locale", 137 | "type": "enum", 138 | "label": "enum_webapp_locale", 139 | "default": "en", 140 | "values": [ 141 | "de", 142 | "en", 143 | "en_GB", 144 | "en_US", 145 | "es", 146 | "fr", 147 | "it", 148 | "ja", 149 | "pt", 150 | "ru", 151 | "sv", 152 | "tr", 153 | "zh" 154 | ] 155 | }, 156 | { 157 | "id": "str_webapp_http_host", 158 | "type": "text", 159 | "label": "str_webapp_http_host", 160 | "default": "0.0.0.0", 161 | "secure": "false" 162 | }, 163 | { 164 | "id": "int_webapp_http_port", 165 | "type": "text", 166 | "label": "int_webapp_http_port", 167 | "default": "9494", 168 | "secure": "false" 169 | }, 170 | { 171 | "id": "bool_webapp_log_werkzeug_messages", 172 | "type": "bool", 173 | "label": "bool_webapp_log_werkzeug_messages", 174 | "default": "False", 175 | "secure": "false" 176 | }, 177 | { 178 | "id": "bool_migrate_locked_themes", 179 | "type": "bool", 180 | "label": "bool_migrate_locked_themes", 181 | "default": "False", 182 | "secure": "false" 183 | }, 184 | { 185 | "id": "bool_migrate_locked_collection_fields", 186 | "type": "bool", 187 | "label": "bool_migrate_locked_collection_fields", 188 | "default": "False", 189 | "secure": "false" 190 | }, 191 | { 192 | "id": "bool_ignore_locked_fields", 193 | "type": "bool", 194 | "label": "bool_ignore_locked_fields", 195 | "default": "False", 196 | "secure": "false" 197 | } 198 | ] 199 | -------------------------------------------------------------------------------- /Contents/Resources/attribution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Themerr-plex/6580224a7f39c2c943993585853e2f0547dd30a8/Contents/Resources/attribution.png -------------------------------------------------------------------------------- /Contents/Resources/icon-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Themerr-plex/6580224a7f39c2c943993585853e2f0547dd30a8/Contents/Resources/icon-default.png -------------------------------------------------------------------------------- /Contents/Resources/web/css/custom.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 80px; 3 | } 4 | 5 | /*Apply to elements that serve as anchors*/ 6 | .offset-anchor { 7 | border-top: 80px solid transparent; 8 | margin: -80px 0 0; 9 | -webkit-background-clip: padding-box; 10 | -moz-background-clip: padding; 11 | background-clip: padding-box; 12 | } 13 | 14 | /*Because offset-anchor causes sections to overlap the bottom of previous ones,*/ 15 | /*we need to put content higher so links aren't blocked by the transparent border.*/ 16 | .container { 17 | position: relative; 18 | /*z-index: 1;*/ 19 | } 20 | 21 | .navbar { 22 | min-height: 80px; 23 | border-bottom: 1px solid white; 24 | } 25 | 26 | .navbar-brand { 27 | font-size: 1.75rem; 28 | } 29 | 30 | .nav-link { 31 | font-size: 1.25rem; 32 | } 33 | 34 | .nav-link-sm { 35 | font-size: 1rem; 36 | } 37 | 38 | .bg-dark { 39 | --bs-bg-opacity: 1; 40 | background-color: #151515 !important; 41 | } 42 | 43 | .bg-dark-gray { 44 | --bs-bg-opacity: 1; 45 | background-color: #303436 !important; 46 | } 47 | 48 | .carousel-overlay-title { 49 | position: absolute; 50 | top: 20vh; 51 | right: 10vw; 52 | padding: 1rem; 53 | text-shadow: 2px 2px 5px black; 54 | user-select: none; 55 | z-index: 1; 56 | } 57 | 58 | .carousel-overlay-subtitle { 59 | position: absolute; 60 | top: 30vh; 61 | right: 10vw; 62 | padding: 1rem; 63 | text-shadow: 2px 2px 5px black; 64 | user-select: none; 65 | z-index: 1; 66 | } 67 | 68 | .carousel-item.active, .carousel-item .view { 69 | height: 50vh !important; 70 | } 71 | 72 | .carousel-item img { 73 | object-fit: cover; 74 | width:100% !important; 75 | height: 100% !important; 76 | } 77 | 78 | .content h1 { 79 | font-size: 55px; 80 | } 81 | 82 | @media screen and (max-width: 320px) { 83 | .content h1 { 84 | font-size: 30px; 85 | } 86 | } 87 | 88 | @media screen and (min-width: 321px) and (max-width: 768px) { 89 | .content h1 { 90 | font-size: 30px; 91 | } 92 | } 93 | 94 | .content h5 { 95 | font-size: 23px; 96 | margin-left: 80px; 97 | margin-right: 80px; 98 | } 99 | 100 | @media screen and (max-width: 320px) { 101 | .content h5 { 102 | font-size: 18px; 103 | margin-left: 20px; 104 | margin-right: 20px 105 | } 106 | } 107 | 108 | @media screen and (min-width: 321px) and (max-width: 768px) { 109 | .content h5 { 110 | font-size: 18px; 111 | margin-left: 20px; 112 | margin-right: 20px; 113 | } 114 | } 115 | 116 | .feature { 117 | display: inline-flex; 118 | align-items: center; 119 | justify-content: center; 120 | height: 3rem; 121 | width: 3rem; 122 | font-size: 1.5rem; 123 | } 124 | 125 | .widgetbot { 126 | display: flex; 127 | align-items: center; 128 | justify-content: space-around; 129 | } 130 | 131 | .feedback { 132 | display: flex; 133 | align-items: center; 134 | justify-content: space-around; 135 | } 136 | 137 | iframe.feedback { 138 | width: 100vw; 139 | min-height: calc(100vh - 80px - 72px); /* subtract navbar and footer height */ 140 | } 141 | 142 | @media screen and (min-width: 321px) and (max-width: 768px) { 143 | iframe.feedback { 144 | min-height: calc(100vh - 80px - 93px); /* subtract navbar and footer height */ 145 | } 146 | } 147 | 148 | @media screen and (max-width: 320px) { 149 | iframe.feedback { 150 | min-height: calc(100vh - 80px - 114px); /* subtract navbar and footer height */ 151 | } 152 | } 153 | 154 | /*card image hover*/ 155 | .hover-zoom { 156 | overflow: hidden; 157 | } 158 | 159 | .hover-zoom img { 160 | transition: all 1.5s ease; 161 | } 162 | 163 | .hover-zoom:hover img { 164 | transform: scale(1.1); 165 | } 166 | 167 | .progress-bar:first-child { 168 | overflow: visible; 169 | z-index: 999; 170 | } 171 | -------------------------------------------------------------------------------- /Contents/Resources/web/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Themerr-plex/6580224a7f39c2c943993585853e2f0547dd30a8/Contents/Resources/web/images/favicon.ico -------------------------------------------------------------------------------- /Contents/Resources/web/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Themerr-plex/6580224a7f39c2c943993585853e2f0547dd30a8/Contents/Resources/web/images/icon.png -------------------------------------------------------------------------------- /Contents/Resources/web/js/translations.js: -------------------------------------------------------------------------------- 1 | let translations = null 2 | 3 | let getTranslation = function(string) { 4 | // download translations 5 | if (translations === null) { 6 | $.ajax({ 7 | async: false, 8 | url: "/translations/", 9 | type: "GET", 10 | dataType: "json", 11 | success: function (result) { 12 | translations = result 13 | } 14 | }) 15 | } 16 | 17 | if (translations) { 18 | try { 19 | if (translations[string]) { 20 | return translations[string] 21 | } else { 22 | return string 23 | } 24 | } catch (err) { 25 | return string 26 | } 27 | } 28 | else { 29 | // could not download translations 30 | return string 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Contents/Resources/web/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% if title %} 5 | Themerr-plex - {{ title }} 6 | {% else %} 7 | Themerr-plex 8 | {% endif %} 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | {% block modals %}{% endblock %} 37 |
38 | 39 | {% include 'navbar.html' %} 40 | 41 | 42 | {% block content %}{% endblock %} 43 |
44 | {% block scripts %}{% endblock %} 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Contents/Resources/web/templates/home_db_not_cached.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block modals %} 3 | {% endblock modals %} 4 | 5 | {% block content %} 6 |
7 |
8 | 9 |

{{ _('Database is being cached, please try again soon.') }}

10 | 11 |
12 |
13 | {% endblock content %} 14 | 15 | {% block scripts %} 16 | {% endblock scripts %} 17 | -------------------------------------------------------------------------------- /Contents/Resources/web/templates/navbar.html: -------------------------------------------------------------------------------- 1 | 65 | -------------------------------------------------------------------------------- /Contents/Resources/web/templates/translations.html: -------------------------------------------------------------------------------- 1 | {# This template is used to extract translations used in javascript files #} 2 | -------------------------------------------------------------------------------- /Contents/Strings/aa/LC_MESSAGES/themerr-plex.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: lizardbyte\n" 4 | "Report-Msgid-Bugs-To: github.com/themerr-plex\n" 5 | "POT-Creation-Date: 2023-08-05 13:12-0400\n" 6 | "PO-Revision-Date: 2023-08-06 14:47\n" 7 | "Last-Translator: \n" 8 | "Language-Team: Afar\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Generated-By: Babel 2.9.1\n" 13 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 14 | "X-Crowdin-Project: lizardbyte\n" 15 | "X-Crowdin-Project-ID: 606145\n" 16 | "X-Crowdin-Language: aa\n" 17 | "X-Crowdin-File: /[LizardByte.Themerr-plex] nightly/themerr-plex.po\n" 18 | "X-Crowdin-File-ID: 25\n" 19 | "Language: aa_ER\n" 20 | 21 | #: Contents/Resources/web/templates/home.html:65 22 | msgid "Title" 23 | msgstr "crwdns5109:0crwdne5109:0" 24 | 25 | #: Contents/Resources/web/templates/home.html:66 26 | msgid "Year" 27 | msgstr "crwdns5111:0crwdne5111:0" 28 | 29 | #: Contents/Resources/web/templates/home.html:67 30 | msgid "Contribute" 31 | msgstr "crwdns5113:0crwdne5113:0" 32 | 33 | #: Contents/Resources/web/templates/home.html:81 34 | msgid "Add" 35 | msgstr "crwdns5115:0crwdne5115:0" 36 | 37 | #: Contents/Resources/web/templates/home.html:84 38 | msgid "Edit" 39 | msgstr "crwdns5117:0crwdne5117:0" 40 | 41 | #: Contents/Resources/web/templates/navbar.html:23 42 | msgid "Donate" 43 | msgstr "crwdns5119:0crwdne5119:0" 44 | 45 | #: Contents/Resources/web/templates/navbar.html:28 46 | msgid "GitHub Sponsors" 47 | msgstr "crwdns5121:0crwdne5121:0" 48 | 49 | #: Contents/Resources/web/templates/navbar.html:44 50 | msgid "Support" 51 | msgstr "crwdns5123:0crwdne5123:0" 52 | 53 | #: Contents/Resources/web/templates/navbar.html:49 54 | msgid "Docs" 55 | msgstr "crwdns5125:0crwdne5125:0" 56 | 57 | #: Contents/Resources/web/templates/navbar.html:53 58 | msgid "Support Center" 59 | msgstr "crwdns5127:0crwdne5127:0" 60 | 61 | #: Contents/Resources/web/templates/navbar.html:59 62 | msgid "Home" 63 | msgstr "crwdns5129:0crwdne5129:0" 64 | 65 | -------------------------------------------------------------------------------- /Contents/Strings/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "bool_plex_movie_support": "Plex Movie Agent Unterstützung (Themes zum aktualisierten Plex Movie Agent hinzufügen)", 3 | "bool_plex_series_support": "Plex Series Agent-Support (Themes dem aktualisierten Plex Series-Agent hinzufügen)", 4 | "bool_overwrite_plex_provided_themes": "Plex angegebene Themes überschreiben", 5 | "bool_prefer_mp4a_codec": "MP4A AAC Codec bevorzugen (Erhöht die Kompatibilität mit Apple-Geräten)", 6 | "bool_remove_unused_theme_songs": "Ungenutzte Theme-Lieder entfernen (Freischalten des Plex-Metadaten-Verzeichnisses)", 7 | "bool_remove_unused_art": "Unbenutzte Kunst entfernen (gilt für Sammlungen, freigegeben Platz in Ihrem Plex Metadaten-Verzeichnis)", 8 | "bool_remove_unused_posters": "Unbenutzte Poster entfernen (gilt für Sammlungen, freigegeben Platz im Plex Metadaten-Verzeichnis)", 9 | "bool_auto_update_items": "Elemente automatisch aktualisieren (nur Elemente wurden geändert oder in ThemerrDB)", 10 | "bool_auto_update_movie_themes": "Film-Themes beim automatischen Update aktualisieren", 11 | "bool_auto_update_tv_themes": "TV-Show-Themes beim automatischen Update aktualisieren", 12 | "bool_auto_update_collection_themes": "Sammlungsthemen beim automatischen Update aktualisieren", 13 | "bool_update_collection_metadata_plex_movie": "Metadaten der Sammlung für Plex Movie Agent aktualisieren (Aktualisiere Poster, Kunst und Zusammenfassung)", 14 | "bool_update_collection_metadata_legacy": "Metadaten der Sammlung für ältere Agenten aktualisieren (Updates, Poster, Kunst und Zusammenfassung)", 15 | "int_update_themes_interval": "Intervall für automatische Update-Aufgabe in Minuten (min: 15)", 16 | "int_update_database_cache_interval": "Intervall für Datenbank-Cache-Aktualisierungsaufgabe in Minuten (min: 15)", 17 | "int_plexapi_plexapi_timeout": "PlexAPI Timeout in Sekunden (min: 1)", 18 | "int_plexapi_upload_retries_max": "Max. Retries, ganze Zahl (min: 0)", 19 | "int_plexapi_upload_threads": "Multiprozess-Threads, ganze Zahl (min: 1)", 20 | "str_youtube_cookies": "YouTube Cookies (JSON-Format)", 21 | "enum_webapp_locale": "Web UI Locale", 22 | "str_webapp_http_host": "Web-UI-Host-Adresse (erfordert Plex Media Server Neustart)", 23 | "int_webapp_http_port": "Web-UI-Port (erfordert Plex Media Server Neustart)", 24 | "bool_webapp_log_werkzeug_messages": "Alle Webserver-Nachrichten protokollieren (Neustart des Plex Media Server erforderlich)", 25 | "bool_migrate_locked_themes": "Motive von < v0.3.0 migrieren (Wenn Sie Themerr vor v0.3.0 verwendet haben, setzen Sie dies auf True)", 26 | "bool_migrate_locked_collection_fields": "Metadaten der Sammlung von < v0.3.0 migrieren (Wenn Sie Themerr vor v0.3.0 verwendet haben, setzen Sie dies auf True)", 27 | "bool_ignore_locked_fields": "Gesperrte Felder ignorieren (Medien immer hochladen, auch wenn Felder gesperrt sind)" 28 | } 29 | -------------------------------------------------------------------------------- /Contents/Strings/de/LC_MESSAGES/themerr-plex.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: lizardbyte\n" 4 | "Report-Msgid-Bugs-To: github.com/themerr-plex\n" 5 | "POT-Creation-Date: 2024-03-15 14:05+0000\n" 6 | "PO-Revision-Date: 2024-03-15 21:02\n" 7 | "Last-Translator: \n" 8 | "Language-Team: German\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Generated-By: Babel 2.9.1\n" 13 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 14 | "X-Crowdin-Project: lizardbyte\n" 15 | "X-Crowdin-Project-ID: 606145\n" 16 | "X-Crowdin-Language: de\n" 17 | "X-Crowdin-File: /[LizardByte.Themerr-plex] master/themerr-plex.po\n" 18 | "X-Crowdin-File-ID: 5784\n" 19 | "Language: de_DE\n" 20 | 21 | #: Contents/Resources/web/templates/home.html:35 22 | msgid "Games" 23 | msgstr "Spiele" 24 | 25 | #: Contents/Resources/web/templates/home.html:37 26 | msgid "Movies" 27 | msgstr "Filme" 28 | 29 | #: Contents/Resources/web/templates/home.html:39 30 | msgid "Shows" 31 | msgstr "Serien" 32 | 33 | #: Contents/Resources/web/templates/home.html:41 34 | msgid "Artists" 35 | msgstr "Künstler" 36 | 37 | #: Contents/Resources/web/templates/home.html:43 38 | msgid "Photos" 39 | msgstr "Fotos" 40 | 41 | #: Contents/Resources/web/templates/home.html:45 42 | msgid "Items" 43 | msgstr "Artikel" 44 | 45 | #: Contents/Resources/web/templates/home.html:88 46 | msgid "Collections" 47 | msgstr "Sammlungen" 48 | 49 | #: Contents/Resources/web/templates/home.html:108 50 | msgid "Title" 51 | msgstr "Titel" 52 | 53 | #: Contents/Resources/web/templates/home.html:109 54 | msgid "Type" 55 | msgstr "Tippe" 56 | 57 | #: Contents/Resources/web/templates/home.html:110 58 | msgid "Year" 59 | msgstr "Jahr" 60 | 61 | #: Contents/Resources/web/templates/home.html:111 62 | msgid "Contribute" 63 | msgstr "Beitragen" 64 | 65 | #: Contents/Resources/web/templates/home.html:112 66 | msgid "Status" 67 | msgstr "Status" 68 | 69 | #: Contents/Resources/web/templates/home.html:128 70 | msgid "Add" 71 | msgstr "hinzufügen" 72 | 73 | #: Contents/Resources/web/templates/home.html:131 74 | msgid "Edit" 75 | msgstr "bearbeiten" 76 | 77 | #: Contents/Resources/web/templates/home.html:147 78 | msgid "No known ID" 79 | msgstr "Keine ID bekannt" 80 | 81 | #: Contents/Resources/web/templates/home.html:152 82 | msgid "Plex provided" 83 | msgstr "Plex bereitgestellt" 84 | 85 | #: Contents/Resources/web/templates/home.html:154 86 | msgid "User provided" 87 | msgstr "Benutzer bereitgestellt" 88 | 89 | #: Contents/Resources/web/templates/home.html:156 90 | msgid "Themerr provided" 91 | msgstr "Themerr bereitgestellt" 92 | 93 | #: Contents/Resources/web/templates/home.html:158 94 | msgid "provided" 95 | msgstr "bereitgestellt" 96 | 97 | #: Contents/Resources/web/templates/home.html:160 98 | msgid "Unknown provider" 99 | msgstr "Unbekannter Anbieter" 100 | 101 | #: Contents/Resources/web/templates/home.html:162 102 | msgid "Missing from ThemerrDB" 103 | msgstr "Fehlt in ThemerrDB" 104 | 105 | #: Contents/Resources/web/templates/home.html:164 106 | msgid "Failed to download" 107 | msgstr "Download fehlgeschlagen" 108 | 109 | #: Contents/Resources/web/templates/home.html:166 110 | msgid "Unknown status" 111 | msgstr "Unbekannter Status" 112 | 113 | #: Contents/Resources/web/templates/home_db_not_cached.html:9 114 | msgid "Database is being cached, please try again soon." 115 | msgstr "Datenbank wird zwischengespeichert, bitte versuchen Sie es bald erneut." 116 | 117 | #: Contents/Resources/web/templates/navbar.html:23 118 | msgid "Donate" 119 | msgstr "Spenden" 120 | 121 | #: Contents/Resources/web/templates/navbar.html:28 122 | msgid "GitHub Sponsors" 123 | msgstr "GitHub Sponsoren" 124 | 125 | #: Contents/Resources/web/templates/navbar.html:44 126 | msgid "Support" 127 | msgstr "Hilfe" 128 | 129 | #: Contents/Resources/web/templates/navbar.html:49 130 | msgid "Docs" 131 | msgstr "Dokumente" 132 | 133 | #: Contents/Resources/web/templates/navbar.html:53 134 | msgid "Support Center" 135 | msgstr "Hilfecenter" 136 | 137 | #: Contents/Resources/web/templates/navbar.html:59 138 | msgid "Home" 139 | msgstr "Startseite" 140 | 141 | -------------------------------------------------------------------------------- /Contents/Strings/en-gb.json: -------------------------------------------------------------------------------- 1 | { 2 | "bool_plex_movie_support": "Plex Movie agent support (Add themes to the updated Plex Movie agent)", 3 | "bool_plex_series_support": "Plex Series agent support (Add themes to the updated Plex Series agent)", 4 | "bool_overwrite_plex_provided_themes": "Overwrite Plex provided themes", 5 | "bool_prefer_mp4a_codec": "Prefer MP4A AAC Codec (Improves compatibility with Apple devices)", 6 | "bool_remove_unused_theme_songs": "Remove unused theme songs (frees up space in your Plex metadata directory)", 7 | "bool_remove_unused_art": "Remove unused art (applies to collections, frees up space in your Plex metadata directory)", 8 | "bool_remove_unused_posters": "Remove unused posters (applies to collections, frees up space in your Plex metadata directory)", 9 | "bool_auto_update_items": "Automatically update items (only items changed or previously missing in ThemerrDB)", 10 | "bool_auto_update_movie_themes": "Update movie themes during automatic update", 11 | "bool_auto_update_tv_themes": "Update tv show themes during automatic update", 12 | "bool_auto_update_collection_themes": "Update collection themes during automatic update", 13 | "bool_update_collection_metadata_plex_movie": "Update collection metadata for Plex Movie agent (Updates poster, art, and summary)", 14 | "bool_update_collection_metadata_legacy": "Update collection metadata for legacy agents (Updates poster, art, and summary)", 15 | "int_update_themes_interval": "Interval for automatic update task, in minutes (min: 15)", 16 | "int_update_database_cache_interval": "Interval for database cache update task, in minutes (min: 15)", 17 | "int_plexapi_plexapi_timeout": "PlexAPI Timeout, in seconds (min: 1)", 18 | "int_plexapi_upload_retries_max": "Max Retries, integer (min: 0)", 19 | "int_plexapi_upload_threads": "Multiprocessing Threads, integer (min: 1)", 20 | "str_youtube_cookies": "YouTube Cookies (JSON format)", 21 | "enum_webapp_locale": "Web UI Locale", 22 | "str_webapp_http_host": "Web UI Host Address (requires Plex Media Server restart)", 23 | "int_webapp_http_port": "Web UI Port (requires Plex Media Server restart)", 24 | "bool_webapp_log_werkzeug_messages": "Log all web server messages (requires Plex Media Server restart)", 25 | "bool_migrate_locked_themes": "Migrate themes from < v0.3.0 (If you used Themerr before v0.3.0, set this to True)", 26 | "bool_migrate_locked_collection_fields": "Migrate collection metadata from < v0.3.0 (If you used Themerr before v0.3.0, set this to True)", 27 | "bool_ignore_locked_fields": "Ignore locked fields (Always upload media, even if fields are locked)" 28 | } 29 | -------------------------------------------------------------------------------- /Contents/Strings/en-us.json: -------------------------------------------------------------------------------- 1 | { 2 | "bool_plex_movie_support": "Plex Movie agent support (Add themes to the updated Plex Movie agent)", 3 | "bool_plex_series_support": "Plex Series agent support (Add themes to the updated Plex Series agent)", 4 | "bool_overwrite_plex_provided_themes": "Overwrite Plex provided themes", 5 | "bool_prefer_mp4a_codec": "Prefer MP4A AAC Codec (Improves compatibility with Apple devices)", 6 | "bool_remove_unused_theme_songs": "Remove unused theme songs (frees up space in your Plex metadata directory)", 7 | "bool_remove_unused_art": "Remove unused art (applies to collections, frees up space in your Plex metadata directory)", 8 | "bool_remove_unused_posters": "Remove unused posters (applies to collections, frees up space in your Plex metadata directory)", 9 | "bool_auto_update_items": "Automatically update items (only items changed or previously missing in ThemerrDB)", 10 | "bool_auto_update_movie_themes": "Update movie themes during automatic update", 11 | "bool_auto_update_tv_themes": "Update tv show themes during automatic update", 12 | "bool_auto_update_collection_themes": "Update collection themes during automatic update", 13 | "bool_update_collection_metadata_plex_movie": "Update collection metadata for Plex Movie agent (Updates poster, art, and summary)", 14 | "bool_update_collection_metadata_legacy": "Update collection metadata for legacy agents (Updates poster, art, and summary)", 15 | "int_update_themes_interval": "Interval for automatic update task, in minutes (min: 15)", 16 | "int_update_database_cache_interval": "Interval for database cache update task, in minutes (min: 15)", 17 | "int_plexapi_plexapi_timeout": "PlexAPI Timeout, in seconds (min: 1)", 18 | "int_plexapi_upload_retries_max": "Max Retries, integer (min: 0)", 19 | "int_plexapi_upload_threads": "Multiprocessing Threads, integer (min: 1)", 20 | "str_youtube_cookies": "YouTube Cookies (JSON format)", 21 | "enum_webapp_locale": "Web UI Locale", 22 | "str_webapp_http_host": "Web UI Host Address (requires Plex Media Server restart)", 23 | "int_webapp_http_port": "Web UI Port (requires Plex Media Server restart)", 24 | "bool_webapp_log_werkzeug_messages": "Log all web server messages (requires Plex Media Server restart)", 25 | "bool_migrate_locked_themes": "Migrate themes from < v0.3.0 (If you used Themerr before v0.3.0, set this to True)", 26 | "bool_migrate_locked_collection_fields": "Migrate collection metadata from < v0.3.0 (If you used Themerr before v0.3.0, set this to True)", 27 | "bool_ignore_locked_fields": "Ignore locked fields (Always upload media, even if fields are locked)" 28 | } 29 | -------------------------------------------------------------------------------- /Contents/Strings/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "bool_plex_movie_support": "Plex Movie agent support (Add themes to the updated Plex Movie agent)", 3 | "bool_plex_series_support": "Plex Series agent support (Add themes to the updated Plex Series agent)", 4 | "bool_overwrite_plex_provided_themes": "Overwrite Plex provided themes", 5 | "bool_prefer_mp4a_codec": "Prefer MP4A AAC Codec (Improves compatibility with Apple devices)", 6 | "bool_remove_unused_theme_songs": "Remove unused theme songs (frees up space in your Plex metadata directory)", 7 | "bool_remove_unused_art": "Remove unused art (applies to collections, frees up space in your Plex metadata directory)", 8 | "bool_remove_unused_posters": "Remove unused posters (applies to collections, frees up space in your Plex metadata directory)", 9 | "bool_auto_update_items": "Automatically update items (only items changed or previously missing in ThemerrDB)", 10 | "bool_auto_update_movie_themes": "Update movie themes during automatic update", 11 | "bool_auto_update_tv_themes": "Update tv show themes during automatic update", 12 | "bool_auto_update_collection_themes": "Update collection themes during automatic update", 13 | "bool_update_collection_metadata_plex_movie": "Update collection metadata for Plex Movie agent (Updates poster, art, and summary)", 14 | "bool_update_collection_metadata_legacy": "Update collection metadata for legacy agents (Updates poster, art, and summary)", 15 | "int_update_themes_interval": "Interval for automatic update task, in minutes (min: 15)", 16 | "int_update_database_cache_interval": "Interval for database cache update task, in minutes (min: 15)", 17 | "int_plexapi_plexapi_timeout": "PlexAPI Timeout, in seconds (min: 1)", 18 | "int_plexapi_upload_retries_max": "Max Retries, integer (min: 0)", 19 | "int_plexapi_upload_threads": "Multiprocessing Threads, integer (min: 1)", 20 | "str_youtube_cookies": "YouTube Cookies (JSON format)", 21 | "enum_webapp_locale": "Web UI Locale", 22 | "str_webapp_http_host": "Web UI Host Address (requires Plex Media Server restart)", 23 | "int_webapp_http_port": "Web UI Port (requires Plex Media Server restart)", 24 | "bool_webapp_log_werkzeug_messages": "Log all web server messages (requires Plex Media Server restart)", 25 | "bool_migrate_locked_themes": "Migrate themes from < v0.3.0 (If you used Themerr before v0.3.0, set this to True)", 26 | "bool_migrate_locked_collection_fields": "Migrate collection metadata from < v0.3.0 (If you used Themerr before v0.3.0, set this to True)", 27 | "bool_ignore_locked_fields": "Ignore locked fields (Always upload media, even if fields are locked)" 28 | } 29 | -------------------------------------------------------------------------------- /Contents/Strings/en/LC_MESSAGES/themerr-plex.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: lizardbyte\n" 4 | "Report-Msgid-Bugs-To: github.com/themerr-plex\n" 5 | "POT-Creation-Date: 2024-03-15 14:05+0000\n" 6 | "PO-Revision-Date: 2024-03-15 21:02\n" 7 | "Last-Translator: \n" 8 | "Language-Team: English\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Generated-By: Babel 2.9.1\n" 13 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 14 | "X-Crowdin-Project: lizardbyte\n" 15 | "X-Crowdin-Project-ID: 606145\n" 16 | "X-Crowdin-Language: en\n" 17 | "X-Crowdin-File: /[LizardByte.Themerr-plex] master/themerr-plex.po\n" 18 | "X-Crowdin-File-ID: 5784\n" 19 | "Language: en_US\n" 20 | 21 | #: Contents/Resources/web/templates/home.html:35 22 | msgid "Games" 23 | msgstr "Games" 24 | 25 | #: Contents/Resources/web/templates/home.html:37 26 | msgid "Movies" 27 | msgstr "Movies" 28 | 29 | #: Contents/Resources/web/templates/home.html:39 30 | msgid "Shows" 31 | msgstr "Shows" 32 | 33 | #: Contents/Resources/web/templates/home.html:41 34 | msgid "Artists" 35 | msgstr "Artists" 36 | 37 | #: Contents/Resources/web/templates/home.html:43 38 | msgid "Photos" 39 | msgstr "Photos" 40 | 41 | #: Contents/Resources/web/templates/home.html:45 42 | msgid "Items" 43 | msgstr "Items" 44 | 45 | #: Contents/Resources/web/templates/home.html:88 46 | msgid "Collections" 47 | msgstr "Collections" 48 | 49 | #: Contents/Resources/web/templates/home.html:108 50 | msgid "Title" 51 | msgstr "Title" 52 | 53 | #: Contents/Resources/web/templates/home.html:109 54 | msgid "Type" 55 | msgstr "Type" 56 | 57 | #: Contents/Resources/web/templates/home.html:110 58 | msgid "Year" 59 | msgstr "Year" 60 | 61 | #: Contents/Resources/web/templates/home.html:111 62 | msgid "Contribute" 63 | msgstr "Contribute" 64 | 65 | #: Contents/Resources/web/templates/home.html:112 66 | msgid "Status" 67 | msgstr "Status" 68 | 69 | #: Contents/Resources/web/templates/home.html:128 70 | msgid "Add" 71 | msgstr "Add" 72 | 73 | #: Contents/Resources/web/templates/home.html:131 74 | msgid "Edit" 75 | msgstr "Edit" 76 | 77 | #: Contents/Resources/web/templates/home.html:147 78 | msgid "No known ID" 79 | msgstr "No known ID" 80 | 81 | #: Contents/Resources/web/templates/home.html:152 82 | msgid "Plex provided" 83 | msgstr "Plex provided" 84 | 85 | #: Contents/Resources/web/templates/home.html:154 86 | msgid "User provided" 87 | msgstr "User provided" 88 | 89 | #: Contents/Resources/web/templates/home.html:156 90 | msgid "Themerr provided" 91 | msgstr "Themerr provided" 92 | 93 | #: Contents/Resources/web/templates/home.html:158 94 | msgid "provided" 95 | msgstr "provided" 96 | 97 | #: Contents/Resources/web/templates/home.html:160 98 | msgid "Unknown provider" 99 | msgstr "Unknown provider" 100 | 101 | #: Contents/Resources/web/templates/home.html:162 102 | msgid "Missing from ThemerrDB" 103 | msgstr "Missing from ThemerrDB" 104 | 105 | #: Contents/Resources/web/templates/home.html:164 106 | msgid "Failed to download" 107 | msgstr "Failed to download" 108 | 109 | #: Contents/Resources/web/templates/home.html:166 110 | msgid "Unknown status" 111 | msgstr "Unknown status" 112 | 113 | #: Contents/Resources/web/templates/home_db_not_cached.html:9 114 | msgid "Database is being cached, please try again soon." 115 | msgstr "Database is being cached, please try again soon." 116 | 117 | #: Contents/Resources/web/templates/navbar.html:23 118 | msgid "Donate" 119 | msgstr "Donate" 120 | 121 | #: Contents/Resources/web/templates/navbar.html:28 122 | msgid "GitHub Sponsors" 123 | msgstr "GitHub Sponsors" 124 | 125 | #: Contents/Resources/web/templates/navbar.html:44 126 | msgid "Support" 127 | msgstr "Support" 128 | 129 | #: Contents/Resources/web/templates/navbar.html:49 130 | msgid "Docs" 131 | msgstr "Docs" 132 | 133 | #: Contents/Resources/web/templates/navbar.html:53 134 | msgid "Support Center" 135 | msgstr "Support Center" 136 | 137 | #: Contents/Resources/web/templates/navbar.html:59 138 | msgid "Home" 139 | msgstr "Home" 140 | 141 | -------------------------------------------------------------------------------- /Contents/Strings/en_GB/LC_MESSAGES/themerr-plex.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: lizardbyte\n" 4 | "Report-Msgid-Bugs-To: github.com/themerr-plex\n" 5 | "POT-Creation-Date: 2024-03-15 14:05+0000\n" 6 | "PO-Revision-Date: 2024-03-15 21:02\n" 7 | "Last-Translator: \n" 8 | "Language-Team: English, United Kingdom\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Generated-By: Babel 2.9.1\n" 13 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 14 | "X-Crowdin-Project: lizardbyte\n" 15 | "X-Crowdin-Project-ID: 606145\n" 16 | "X-Crowdin-Language: en-GB\n" 17 | "X-Crowdin-File: /[LizardByte.Themerr-plex] master/themerr-plex.po\n" 18 | "X-Crowdin-File-ID: 5784\n" 19 | "Language: en_GB\n" 20 | 21 | #: Contents/Resources/web/templates/home.html:35 22 | msgid "Games" 23 | msgstr "Games" 24 | 25 | #: Contents/Resources/web/templates/home.html:37 26 | msgid "Movies" 27 | msgstr "Movies" 28 | 29 | #: Contents/Resources/web/templates/home.html:39 30 | msgid "Shows" 31 | msgstr "Shows" 32 | 33 | #: Contents/Resources/web/templates/home.html:41 34 | msgid "Artists" 35 | msgstr "Artists" 36 | 37 | #: Contents/Resources/web/templates/home.html:43 38 | msgid "Photos" 39 | msgstr "Photos" 40 | 41 | #: Contents/Resources/web/templates/home.html:45 42 | msgid "Items" 43 | msgstr "Items" 44 | 45 | #: Contents/Resources/web/templates/home.html:88 46 | msgid "Collections" 47 | msgstr "Collections" 48 | 49 | #: Contents/Resources/web/templates/home.html:108 50 | msgid "Title" 51 | msgstr "Title" 52 | 53 | #: Contents/Resources/web/templates/home.html:109 54 | msgid "Type" 55 | msgstr "Type" 56 | 57 | #: Contents/Resources/web/templates/home.html:110 58 | msgid "Year" 59 | msgstr "Year" 60 | 61 | #: Contents/Resources/web/templates/home.html:111 62 | msgid "Contribute" 63 | msgstr "Contribute" 64 | 65 | #: Contents/Resources/web/templates/home.html:112 66 | msgid "Status" 67 | msgstr "Status" 68 | 69 | #: Contents/Resources/web/templates/home.html:128 70 | msgid "Add" 71 | msgstr "Add" 72 | 73 | #: Contents/Resources/web/templates/home.html:131 74 | msgid "Edit" 75 | msgstr "Edit" 76 | 77 | #: Contents/Resources/web/templates/home.html:147 78 | msgid "No known ID" 79 | msgstr "No known ID" 80 | 81 | #: Contents/Resources/web/templates/home.html:152 82 | msgid "Plex provided" 83 | msgstr "Plex provided" 84 | 85 | #: Contents/Resources/web/templates/home.html:154 86 | msgid "User provided" 87 | msgstr "User provided" 88 | 89 | #: Contents/Resources/web/templates/home.html:156 90 | msgid "Themerr provided" 91 | msgstr "Themerr provided" 92 | 93 | #: Contents/Resources/web/templates/home.html:158 94 | msgid "provided" 95 | msgstr "provided" 96 | 97 | #: Contents/Resources/web/templates/home.html:160 98 | msgid "Unknown provider" 99 | msgstr "Unknown provider" 100 | 101 | #: Contents/Resources/web/templates/home.html:162 102 | msgid "Missing from ThemerrDB" 103 | msgstr "Missing from ThemerrDB" 104 | 105 | #: Contents/Resources/web/templates/home.html:164 106 | msgid "Failed to download" 107 | msgstr "Failed to download" 108 | 109 | #: Contents/Resources/web/templates/home.html:166 110 | msgid "Unknown status" 111 | msgstr "Unknown status" 112 | 113 | #: Contents/Resources/web/templates/home_db_not_cached.html:9 114 | msgid "Database is being cached, please try again soon." 115 | msgstr "Database is being cached, please try again soon." 116 | 117 | #: Contents/Resources/web/templates/navbar.html:23 118 | msgid "Donate" 119 | msgstr "Donate" 120 | 121 | #: Contents/Resources/web/templates/navbar.html:28 122 | msgid "GitHub Sponsors" 123 | msgstr "GitHub Sponsors" 124 | 125 | #: Contents/Resources/web/templates/navbar.html:44 126 | msgid "Support" 127 | msgstr "Support" 128 | 129 | #: Contents/Resources/web/templates/navbar.html:49 130 | msgid "Docs" 131 | msgstr "Docs" 132 | 133 | #: Contents/Resources/web/templates/navbar.html:53 134 | msgid "Support Center" 135 | msgstr "Support Centre" 136 | 137 | #: Contents/Resources/web/templates/navbar.html:59 138 | msgid "Home" 139 | msgstr "Home" 140 | 141 | -------------------------------------------------------------------------------- /Contents/Strings/en_US/LC_MESSAGES/themerr-plex.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: lizardbyte\n" 4 | "Report-Msgid-Bugs-To: github.com/themerr-plex\n" 5 | "POT-Creation-Date: 2024-03-15 14:05+0000\n" 6 | "PO-Revision-Date: 2024-03-15 21:02\n" 7 | "Last-Translator: \n" 8 | "Language-Team: English, United States\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Generated-By: Babel 2.9.1\n" 13 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 14 | "X-Crowdin-Project: lizardbyte\n" 15 | "X-Crowdin-Project-ID: 606145\n" 16 | "X-Crowdin-Language: en-US\n" 17 | "X-Crowdin-File: /[LizardByte.Themerr-plex] master/themerr-plex.po\n" 18 | "X-Crowdin-File-ID: 5784\n" 19 | "Language: en_US\n" 20 | 21 | #: Contents/Resources/web/templates/home.html:35 22 | msgid "Games" 23 | msgstr "Games" 24 | 25 | #: Contents/Resources/web/templates/home.html:37 26 | msgid "Movies" 27 | msgstr "Movies" 28 | 29 | #: Contents/Resources/web/templates/home.html:39 30 | msgid "Shows" 31 | msgstr "Shows" 32 | 33 | #: Contents/Resources/web/templates/home.html:41 34 | msgid "Artists" 35 | msgstr "Artists" 36 | 37 | #: Contents/Resources/web/templates/home.html:43 38 | msgid "Photos" 39 | msgstr "Photos" 40 | 41 | #: Contents/Resources/web/templates/home.html:45 42 | msgid "Items" 43 | msgstr "Items" 44 | 45 | #: Contents/Resources/web/templates/home.html:88 46 | msgid "Collections" 47 | msgstr "Collections" 48 | 49 | #: Contents/Resources/web/templates/home.html:108 50 | msgid "Title" 51 | msgstr "Title" 52 | 53 | #: Contents/Resources/web/templates/home.html:109 54 | msgid "Type" 55 | msgstr "Type" 56 | 57 | #: Contents/Resources/web/templates/home.html:110 58 | msgid "Year" 59 | msgstr "Year" 60 | 61 | #: Contents/Resources/web/templates/home.html:111 62 | msgid "Contribute" 63 | msgstr "Contribute" 64 | 65 | #: Contents/Resources/web/templates/home.html:112 66 | msgid "Status" 67 | msgstr "Status" 68 | 69 | #: Contents/Resources/web/templates/home.html:128 70 | msgid "Add" 71 | msgstr "Add" 72 | 73 | #: Contents/Resources/web/templates/home.html:131 74 | msgid "Edit" 75 | msgstr "Edit" 76 | 77 | #: Contents/Resources/web/templates/home.html:147 78 | msgid "No known ID" 79 | msgstr "No known ID" 80 | 81 | #: Contents/Resources/web/templates/home.html:152 82 | msgid "Plex provided" 83 | msgstr "Plex provided" 84 | 85 | #: Contents/Resources/web/templates/home.html:154 86 | msgid "User provided" 87 | msgstr "User provided" 88 | 89 | #: Contents/Resources/web/templates/home.html:156 90 | msgid "Themerr provided" 91 | msgstr "Themerr provided" 92 | 93 | #: Contents/Resources/web/templates/home.html:158 94 | msgid "provided" 95 | msgstr "provided" 96 | 97 | #: Contents/Resources/web/templates/home.html:160 98 | msgid "Unknown provider" 99 | msgstr "Unknown provider" 100 | 101 | #: Contents/Resources/web/templates/home.html:162 102 | msgid "Missing from ThemerrDB" 103 | msgstr "Missing from ThemerrDB" 104 | 105 | #: Contents/Resources/web/templates/home.html:164 106 | msgid "Failed to download" 107 | msgstr "Failed to download" 108 | 109 | #: Contents/Resources/web/templates/home.html:166 110 | msgid "Unknown status" 111 | msgstr "Unknown status" 112 | 113 | #: Contents/Resources/web/templates/home_db_not_cached.html:9 114 | msgid "Database is being cached, please try again soon." 115 | msgstr "Database is being cached, please try again soon." 116 | 117 | #: Contents/Resources/web/templates/navbar.html:23 118 | msgid "Donate" 119 | msgstr "Donate" 120 | 121 | #: Contents/Resources/web/templates/navbar.html:28 122 | msgid "GitHub Sponsors" 123 | msgstr "GitHub Sponsors" 124 | 125 | #: Contents/Resources/web/templates/navbar.html:44 126 | msgid "Support" 127 | msgstr "Support" 128 | 129 | #: Contents/Resources/web/templates/navbar.html:49 130 | msgid "Docs" 131 | msgstr "Docs" 132 | 133 | #: Contents/Resources/web/templates/navbar.html:53 134 | msgid "Support Center" 135 | msgstr "Support Center" 136 | 137 | #: Contents/Resources/web/templates/navbar.html:59 138 | msgid "Home" 139 | msgstr "Home" 140 | 141 | -------------------------------------------------------------------------------- /Contents/Strings/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "bool_plex_movie_support": "Soporte de agente de película de Plex (Añadir temas al agente de película de Plex actualizado)", 3 | "bool_plex_series_support": "Soporte de agente de series de Plex (Añadir temas al agente actualizado de series de Plex)", 4 | "bool_overwrite_plex_provided_themes": "Sobrescribir temas proporcionados por Plex", 5 | "bool_prefer_mp4a_codec": "Preferir MP4A AAC Codec (mejora la compatibilidad con dispositivos Apple)", 6 | "bool_remove_unused_theme_songs": "Eliminar canciones de tema no utilizadas (libera espacio en el directorio de metadatos de Plex)", 7 | "bool_remove_unused_art": "Eliminar el arte no utilizado (se aplica a las colecciones, libera espacio en el directorio de metadatos de Plex)", 8 | "bool_remove_unused_posters": "Eliminar pósters no utilizados (se aplica a las colecciones, libera espacio en el directorio de metadatos de Plex)", 9 | "bool_auto_update_items": "Actualizar automáticamente los elementos (sólo elementos cambiados o faltantes en ThemerrDB)", 10 | "bool_auto_update_movie_themes": "Actualizar temas de película durante la actualización automática", 11 | "bool_auto_update_tv_themes": "Actualizar temas de series de tv durante la actualización automática", 12 | "bool_auto_update_collection_themes": "Actualizar temas de la colección durante la actualización automática", 13 | "bool_update_collection_metadata_plex_movie": "Actualizar metadatos de colección para agente de película de Plex (Actualizaciones de póster, arte y resumen)", 14 | "bool_update_collection_metadata_legacy": "Actualizar metadatos de colección para agentes heredados (Actualizaciones de póster, arte y resumen)", 15 | "int_update_themes_interval": "Intervalo para la tarea de actualización automática, en minutos (min: 15)", 16 | "int_update_database_cache_interval": "Intervalo para la tarea de actualización de la caché de base de datos, en minutos (min: 15)", 17 | "int_plexapi_plexapi_timeout": "Tiempo de espera de PlexAPI, en segundos (min: 1)", 18 | "int_plexapi_upload_retries_max": "Máx. Reintentos, entero (min: 0)", 19 | "int_plexapi_upload_threads": "Multiprocesamiento de hilos, entero (min: 1)", 20 | "str_youtube_cookies": "Cookies de YouTube (formato JSON)", 21 | "enum_webapp_locale": "Web UI Locale", 22 | "str_webapp_http_host": "Dirección de host Web UI (requiere reiniciar Plex Media Server)", 23 | "int_webapp_http_port": "Puerto Web UI (requiere reiniciar Plex Media Server)", 24 | "bool_webapp_log_werkzeug_messages": "Registrar todos los mensajes del servidor web (requiere reiniciar Plex Media Server)", 25 | "bool_migrate_locked_themes": "Migrar temas desde < v0.3.0 (Si utilizaba Themerr antes de v0.3.0, establezca este valor en True)", 26 | "bool_migrate_locked_collection_fields": "Migrar temas desde < v0.3.0 (Si utilizaba Themerr antes de v0.3.0, establezca este valor en True)", 27 | "bool_ignore_locked_fields": "Ignorar campos bloqueados (Siempre subir medios, incluso si los campos están bloqueados)" 28 | } 29 | -------------------------------------------------------------------------------- /Contents/Strings/es/LC_MESSAGES/themerr-plex.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: lizardbyte\n" 4 | "Report-Msgid-Bugs-To: github.com/themerr-plex\n" 5 | "POT-Creation-Date: 2024-03-15 14:05+0000\n" 6 | "PO-Revision-Date: 2024-03-15 21:02\n" 7 | "Last-Translator: \n" 8 | "Language-Team: Spanish\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Generated-By: Babel 2.9.1\n" 13 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 14 | "X-Crowdin-Project: lizardbyte\n" 15 | "X-Crowdin-Project-ID: 606145\n" 16 | "X-Crowdin-Language: es-ES\n" 17 | "X-Crowdin-File: /[LizardByte.Themerr-plex] master/themerr-plex.po\n" 18 | "X-Crowdin-File-ID: 5784\n" 19 | "Language: es_ES\n" 20 | 21 | #: Contents/Resources/web/templates/home.html:35 22 | msgid "Games" 23 | msgstr "Juegos" 24 | 25 | #: Contents/Resources/web/templates/home.html:37 26 | msgid "Movies" 27 | msgstr "Películas" 28 | 29 | #: Contents/Resources/web/templates/home.html:39 30 | msgid "Shows" 31 | msgstr "Series" 32 | 33 | #: Contents/Resources/web/templates/home.html:41 34 | msgid "Artists" 35 | msgstr "Artistas" 36 | 37 | #: Contents/Resources/web/templates/home.html:43 38 | msgid "Photos" 39 | msgstr "Fotos" 40 | 41 | #: Contents/Resources/web/templates/home.html:45 42 | msgid "Items" 43 | msgstr "Elementos" 44 | 45 | #: Contents/Resources/web/templates/home.html:88 46 | msgid "Collections" 47 | msgstr "Colecciones" 48 | 49 | #: Contents/Resources/web/templates/home.html:108 50 | msgid "Title" 51 | msgstr "Título" 52 | 53 | #: Contents/Resources/web/templates/home.html:109 54 | msgid "Type" 55 | msgstr "Tipo" 56 | 57 | #: Contents/Resources/web/templates/home.html:110 58 | msgid "Year" 59 | msgstr "Año" 60 | 61 | #: Contents/Resources/web/templates/home.html:111 62 | msgid "Contribute" 63 | msgstr "Contribuir" 64 | 65 | #: Contents/Resources/web/templates/home.html:112 66 | msgid "Status" 67 | msgstr "Estado" 68 | 69 | #: Contents/Resources/web/templates/home.html:128 70 | msgid "Add" 71 | msgstr "Añadir" 72 | 73 | #: Contents/Resources/web/templates/home.html:131 74 | msgid "Edit" 75 | msgstr "Editar" 76 | 77 | #: Contents/Resources/web/templates/home.html:147 78 | msgid "No known ID" 79 | msgstr "Sin identificación conocida" 80 | 81 | #: Contents/Resources/web/templates/home.html:152 82 | msgid "Plex provided" 83 | msgstr "Plex proporcionado" 84 | 85 | #: Contents/Resources/web/templates/home.html:154 86 | msgid "User provided" 87 | msgstr "Proporcionado por el usuario" 88 | 89 | #: Contents/Resources/web/templates/home.html:156 90 | msgid "Themerr provided" 91 | msgstr "Proporcionado por Themerr" 92 | 93 | #: Contents/Resources/web/templates/home.html:158 94 | msgid "provided" 95 | msgstr "proporcionado" 96 | 97 | #: Contents/Resources/web/templates/home.html:160 98 | msgid "Unknown provider" 99 | msgstr "Proveedor desconocido" 100 | 101 | #: Contents/Resources/web/templates/home.html:162 102 | msgid "Missing from ThemerrDB" 103 | msgstr "Falta en ThemerrDB" 104 | 105 | #: Contents/Resources/web/templates/home.html:164 106 | msgid "Failed to download" 107 | msgstr "Error de descarga" 108 | 109 | #: Contents/Resources/web/templates/home.html:166 110 | msgid "Unknown status" 111 | msgstr "Estado desconocido" 112 | 113 | #: Contents/Resources/web/templates/home_db_not_cached.html:9 114 | msgid "Database is being cached, please try again soon." 115 | msgstr "La base de datos se está almacenando en caché, inténtelo de nuevo en breve." 116 | 117 | #: Contents/Resources/web/templates/navbar.html:23 118 | msgid "Donate" 119 | msgstr "Donar" 120 | 121 | #: Contents/Resources/web/templates/navbar.html:28 122 | msgid "GitHub Sponsors" 123 | msgstr "Patrocinadores de GitHub" 124 | 125 | #: Contents/Resources/web/templates/navbar.html:44 126 | msgid "Support" 127 | msgstr "Soporte" 128 | 129 | #: Contents/Resources/web/templates/navbar.html:49 130 | msgid "Docs" 131 | msgstr "Docs" 132 | 133 | #: Contents/Resources/web/templates/navbar.html:53 134 | msgid "Support Center" 135 | msgstr "Centro de soporte" 136 | 137 | #: Contents/Resources/web/templates/navbar.html:59 138 | msgid "Home" 139 | msgstr "Inicio" 140 | 141 | -------------------------------------------------------------------------------- /Contents/Strings/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "bool_plex_movie_support": "Prise en charge de Plex Movie (Ajouter des thèmes à la mise à jour de Plex Movie agent)", 3 | "bool_plex_series_support": "Prise en charge des agents de la Série Plex (Ajouter des thèmes à l'agent de la Série Plex mis à jour)", 4 | "bool_overwrite_plex_provided_themes": "Écraser les thèmes fournis par Plex", 5 | "bool_prefer_mp4a_codec": "Préférez le Codec AAC MP4A (Améliore la compatibilité avec les appareils Apple)", 6 | "bool_remove_unused_theme_songs": "Supprimer les chansons de thème inutilisées (libère de l'espace dans le répertoire de métadonnées de Plex)", 7 | "bool_remove_unused_art": "Supprimer les œuvres inutilisées (s'applique aux collections, libère de l'espace dans votre répertoire de métadonnées de Plex)", 8 | "bool_remove_unused_posters": "Supprimer les affiches inutilisées (s'applique aux collections, libère de l'espace dans le répertoire de vos métadonnées de Plex)", 9 | "bool_auto_update_items": "Mettre à jour automatiquement les éléments (seuls les éléments modifiés ou précédemment manquants dans ThemerrDB)", 10 | "bool_auto_update_movie_themes": "Mettre à jour les thèmes de film lors de la mise à jour automatique", 11 | "bool_auto_update_tv_themes": "Mettre à jour les thèmes de la télévision lors de la mise à jour automatique", 12 | "bool_auto_update_collection_themes": "Mettre à jour les thèmes de collection lors de la mise à jour automatique", 13 | "bool_update_collection_metadata_plex_movie": "Mettre à jour les métadonnées de la collection pour Plex Movie agent (Met à jour l'affiche, l'art et le résumé)", 14 | "bool_update_collection_metadata_legacy": "Mettre à jour les métadonnées de la collection des anciens agents (mise à jour de l'affiche, de l'art et du résumé)", 15 | "int_update_themes_interval": "Intervalle pour la tâche de mise à jour automatique, en minutes (min. 15)", 16 | "int_update_database_cache_interval": "Intervalle pour la tâche de mise à jour du cache de la base de données, en minutes (min. 15)", 17 | "int_plexapi_plexapi_timeout": "Délai d'attente de PlexAPI, en secondes (min : 1)", 18 | "int_plexapi_upload_retries_max": "Nombre maximum de tentatives, nombre entier (min : 0)", 19 | "int_plexapi_upload_threads": "Fil multitraitement, entier (min: 1)", 20 | "str_youtube_cookies": "Cookies YouTube (format JSON)", 21 | "enum_webapp_locale": "Web UI Locale", 22 | "str_webapp_http_host": "Adresse de l'hôte de l'interface utilisateur Web (nécessite le redémarrage de Plex Media Server)", 23 | "int_webapp_http_port": "Port de l'interface Web (nécessite le redémarrage de Plex Media Server)", 24 | "bool_webapp_log_werkzeug_messages": "Journaliser tous les messages du serveur web (nécessite le redémarrage de Plex Media Server)", 25 | "bool_migrate_locked_themes": "Migrer les thèmes de < v0.3.0 (Si vous avez utilisé Themerr avant la v0.3.0, définissez ceci à True)", 26 | "bool_migrate_locked_collection_fields": "Migrer les métadonnées de la collection de < v0.3.0 (Si vous avez utilisé Themerr avant la v0.3.0, définissez ceci à True)", 27 | "bool_ignore_locked_fields": "Ignorer les champs verrouillés (Toujours télécharger les médias, même si les champs sont verrouillés)" 28 | } 29 | -------------------------------------------------------------------------------- /Contents/Strings/fr/LC_MESSAGES/themerr-plex.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: lizardbyte\n" 4 | "Report-Msgid-Bugs-To: github.com/themerr-plex\n" 5 | "POT-Creation-Date: 2024-03-15 14:05+0000\n" 6 | "PO-Revision-Date: 2024-03-15 21:02\n" 7 | "Last-Translator: \n" 8 | "Language-Team: French\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Generated-By: Babel 2.9.1\n" 13 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 14 | "X-Crowdin-Project: lizardbyte\n" 15 | "X-Crowdin-Project-ID: 606145\n" 16 | "X-Crowdin-Language: fr\n" 17 | "X-Crowdin-File: /[LizardByte.Themerr-plex] master/themerr-plex.po\n" 18 | "X-Crowdin-File-ID: 5784\n" 19 | "Language: fr_FR\n" 20 | 21 | #: Contents/Resources/web/templates/home.html:35 22 | msgid "Games" 23 | msgstr "Jeux" 24 | 25 | #: Contents/Resources/web/templates/home.html:37 26 | msgid "Movies" 27 | msgstr "Films" 28 | 29 | #: Contents/Resources/web/templates/home.html:39 30 | msgid "Shows" 31 | msgstr "Séries" 32 | 33 | #: Contents/Resources/web/templates/home.html:41 34 | msgid "Artists" 35 | msgstr "Artistes" 36 | 37 | #: Contents/Resources/web/templates/home.html:43 38 | msgid "Photos" 39 | msgstr "Photos" 40 | 41 | #: Contents/Resources/web/templates/home.html:45 42 | msgid "Items" 43 | msgstr "Éléments" 44 | 45 | #: Contents/Resources/web/templates/home.html:88 46 | msgid "Collections" 47 | msgstr "Collections" 48 | 49 | #: Contents/Resources/web/templates/home.html:108 50 | msgid "Title" 51 | msgstr "Titre" 52 | 53 | #: Contents/Resources/web/templates/home.html:109 54 | msgid "Type" 55 | msgstr "Type" 56 | 57 | #: Contents/Resources/web/templates/home.html:110 58 | msgid "Year" 59 | msgstr "Année" 60 | 61 | #: Contents/Resources/web/templates/home.html:111 62 | msgid "Contribute" 63 | msgstr "Contribuer" 64 | 65 | #: Contents/Resources/web/templates/home.html:112 66 | msgid "Status" 67 | msgstr "Statut" 68 | 69 | #: Contents/Resources/web/templates/home.html:128 70 | msgid "Add" 71 | msgstr "Ajouter" 72 | 73 | #: Contents/Resources/web/templates/home.html:131 74 | msgid "Edit" 75 | msgstr "Modifier" 76 | 77 | #: Contents/Resources/web/templates/home.html:147 78 | msgid "No known ID" 79 | msgstr "Aucun ID connu" 80 | 81 | #: Contents/Resources/web/templates/home.html:152 82 | msgid "Plex provided" 83 | msgstr "Plex fourni" 84 | 85 | #: Contents/Resources/web/templates/home.html:154 86 | msgid "User provided" 87 | msgstr "Fourni par l'utilisateur" 88 | 89 | #: Contents/Resources/web/templates/home.html:156 90 | msgid "Themerr provided" 91 | msgstr "Fourni par Themerr" 92 | 93 | #: Contents/Resources/web/templates/home.html:158 94 | msgid "provided" 95 | msgstr "prévu" 96 | 97 | #: Contents/Resources/web/templates/home.html:160 98 | msgid "Unknown provider" 99 | msgstr "Fournisseur inconnu" 100 | 101 | #: Contents/Resources/web/templates/home.html:162 102 | msgid "Missing from ThemerrDB" 103 | msgstr "Absent de ThemerrDB" 104 | 105 | #: Contents/Resources/web/templates/home.html:164 106 | msgid "Failed to download" 107 | msgstr "Téléchargement échoué" 108 | 109 | #: Contents/Resources/web/templates/home.html:166 110 | msgid "Unknown status" 111 | msgstr "État inconnu" 112 | 113 | #: Contents/Resources/web/templates/home_db_not_cached.html:9 114 | msgid "Database is being cached, please try again soon." 115 | msgstr "La base de données est en cours de mise en cache, veuillez réessayer ultérieurement." 116 | 117 | #: Contents/Resources/web/templates/navbar.html:23 118 | msgid "Donate" 119 | msgstr "Faire un don" 120 | 121 | #: Contents/Resources/web/templates/navbar.html:28 122 | msgid "GitHub Sponsors" 123 | msgstr "GitHub Sponsors" 124 | 125 | #: Contents/Resources/web/templates/navbar.html:44 126 | msgid "Support" 127 | msgstr "Aide" 128 | 129 | #: Contents/Resources/web/templates/navbar.html:49 130 | msgid "Docs" 131 | msgstr "Documentation" 132 | 133 | #: Contents/Resources/web/templates/navbar.html:53 134 | msgid "Support Center" 135 | msgstr "Centre d'aide" 136 | 137 | #: Contents/Resources/web/templates/navbar.html:59 138 | msgid "Home" 139 | msgstr "Accueil" 140 | 141 | -------------------------------------------------------------------------------- /Contents/Strings/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "bool_plex_movie_support": "Supporto agente Plex Movie (Aggiungi temi all'agente Plex Movie aggiornato)", 3 | "bool_plex_series_support": "Supporto agente Plex Series (Aggiungi temi all'agente Plex Series aggiornato)", 4 | "bool_overwrite_plex_provided_themes": "Sovrascrivi i temi forniti da Plex", 5 | "bool_prefer_mp4a_codec": "Preferire MP4A AAC Codec (Migliora la compatibilità con i dispositivi Apple)", 6 | "bool_remove_unused_theme_songs": "Rimuove i brani del tema inutilizzati (libera spazio nella directory dei metadati di Plex)", 7 | "bool_remove_unused_art": "Rimuovi l'arte inutilizzata (si applica alle collezioni, libera spazio nella tua directory dei metadati di Plex)", 8 | "bool_remove_unused_posters": "Rimuovi i poster inutilizzati (si applica alle collezioni, libera spazio nella directory dei metadati di Plex)", 9 | "bool_auto_update_items": "Aggiorna automaticamente gli elementi (solo gli elementi cambiati o mancanti in ThemerrDB)", 10 | "bool_auto_update_movie_themes": "Aggiorna i temi del film durante l'aggiornamento automatico", 11 | "bool_auto_update_tv_themes": "Aggiorna temi TV durante l'aggiornamento automatico", 12 | "bool_auto_update_collection_themes": "Aggiorna i temi della collezione durante l'aggiornamento automatico", 13 | "bool_update_collection_metadata_plex_movie": "Aggiorna i metadati della collezione per Plex Movie agent (Aggiorna poster, arte e riepilogo)", 14 | "bool_update_collection_metadata_legacy": "Aggiorna i metadati della raccolta per gli agenti legacy (Aggiorna poster, arte e riepilogo)", 15 | "int_update_themes_interval": "Intervallo per attività di aggiornamento automatico, in minuti (min: 15)", 16 | "int_update_database_cache_interval": "Intervallo per il compito di aggiornamento della cache del database, in minuti (min: 15)", 17 | "int_plexapi_plexapi_timeout": "Timeout PlexAPI, in secondi (min: 1)", 18 | "int_plexapi_upload_retries_max": "Max Retries, intero (min: 0)", 19 | "int_plexapi_upload_threads": "Discussioni di elaborazione multiple, intere (min: 1)", 20 | "str_youtube_cookies": "Cookie su YouTube (formato JSON)", 21 | "enum_webapp_locale": "Web UI Locale", 22 | "str_webapp_http_host": "Indirizzo Host UI Web (richiede il riavvio di Plex Media Server)", 23 | "int_webapp_http_port": "Porta UI Web (richiede il riavvio di Plex Media Server)", 24 | "bool_webapp_log_werkzeug_messages": "Registra tutti i messaggi del server web (richiede il riavvio di Plex Media Server)", 25 | "bool_migrate_locked_themes": "Migra temi da < v0.3.0 (Se hai usato Themerr prima di v0.3.0, impostalo su True)", 26 | "bool_migrate_locked_collection_fields": "Migra i metadati della collezione da < v0.3.0 (Se hai usato Themerr prima di v0.3.0, impostalo su True)", 27 | "bool_ignore_locked_fields": "Ignora i campi bloccati (carica sempre il supporto, anche se i campi sono bloccati)" 28 | } 29 | -------------------------------------------------------------------------------- /Contents/Strings/it/LC_MESSAGES/themerr-plex.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: lizardbyte\n" 4 | "Report-Msgid-Bugs-To: github.com/themerr-plex\n" 5 | "POT-Creation-Date: 2024-03-15 14:05+0000\n" 6 | "PO-Revision-Date: 2024-03-15 21:02\n" 7 | "Last-Translator: \n" 8 | "Language-Team: Italian\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Generated-By: Babel 2.9.1\n" 13 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 14 | "X-Crowdin-Project: lizardbyte\n" 15 | "X-Crowdin-Project-ID: 606145\n" 16 | "X-Crowdin-Language: it\n" 17 | "X-Crowdin-File: /[LizardByte.Themerr-plex] master/themerr-plex.po\n" 18 | "X-Crowdin-File-ID: 5784\n" 19 | "Language: it_IT\n" 20 | 21 | #: Contents/Resources/web/templates/home.html:35 22 | msgid "Games" 23 | msgstr "Giochi" 24 | 25 | #: Contents/Resources/web/templates/home.html:37 26 | msgid "Movies" 27 | msgstr "Film" 28 | 29 | #: Contents/Resources/web/templates/home.html:39 30 | msgid "Shows" 31 | msgstr "Mostra" 32 | 33 | #: Contents/Resources/web/templates/home.html:41 34 | msgid "Artists" 35 | msgstr "Artisti" 36 | 37 | #: Contents/Resources/web/templates/home.html:43 38 | msgid "Photos" 39 | msgstr "Foto" 40 | 41 | #: Contents/Resources/web/templates/home.html:45 42 | msgid "Items" 43 | msgstr "Oggetti" 44 | 45 | #: Contents/Resources/web/templates/home.html:88 46 | msgid "Collections" 47 | msgstr "Raccolte" 48 | 49 | #: Contents/Resources/web/templates/home.html:108 50 | msgid "Title" 51 | msgstr "Titolo" 52 | 53 | #: Contents/Resources/web/templates/home.html:109 54 | msgid "Type" 55 | msgstr "Tipo" 56 | 57 | #: Contents/Resources/web/templates/home.html:110 58 | msgid "Year" 59 | msgstr "Anno" 60 | 61 | #: Contents/Resources/web/templates/home.html:111 62 | msgid "Contribute" 63 | msgstr "Contribuisci" 64 | 65 | #: Contents/Resources/web/templates/home.html:112 66 | msgid "Status" 67 | msgstr "Stato" 68 | 69 | #: Contents/Resources/web/templates/home.html:128 70 | msgid "Add" 71 | msgstr "Aggiungi" 72 | 73 | #: Contents/Resources/web/templates/home.html:131 74 | msgid "Edit" 75 | msgstr "Modifica" 76 | 77 | #: Contents/Resources/web/templates/home.html:147 78 | msgid "No known ID" 79 | msgstr "ID sconosciuto" 80 | 81 | #: Contents/Resources/web/templates/home.html:152 82 | msgid "Plex provided" 83 | msgstr "Plex fornito" 84 | 85 | #: Contents/Resources/web/templates/home.html:154 86 | msgid "User provided" 87 | msgstr "Utente fornito" 88 | 89 | #: Contents/Resources/web/templates/home.html:156 90 | msgid "Themerr provided" 91 | msgstr "Themerr fornito" 92 | 93 | #: Contents/Resources/web/templates/home.html:158 94 | msgid "provided" 95 | msgstr "fornito" 96 | 97 | #: Contents/Resources/web/templates/home.html:160 98 | msgid "Unknown provider" 99 | msgstr "Provider sconosciuto" 100 | 101 | #: Contents/Resources/web/templates/home.html:162 102 | msgid "Missing from ThemerrDB" 103 | msgstr "Manca da ThemerrDB" 104 | 105 | #: Contents/Resources/web/templates/home.html:164 106 | msgid "Failed to download" 107 | msgstr "Download non riuscito" 108 | 109 | #: Contents/Resources/web/templates/home.html:166 110 | msgid "Unknown status" 111 | msgstr "Stato sconosciuto" 112 | 113 | #: Contents/Resources/web/templates/home_db_not_cached.html:9 114 | msgid "Database is being cached, please try again soon." 115 | msgstr "Il database è in fase di caching, riprova più tardi." 116 | 117 | #: Contents/Resources/web/templates/navbar.html:23 118 | msgid "Donate" 119 | msgstr "Dona" 120 | 121 | #: Contents/Resources/web/templates/navbar.html:28 122 | msgid "GitHub Sponsors" 123 | msgstr "Sponsor GitHub" 124 | 125 | #: Contents/Resources/web/templates/navbar.html:44 126 | msgid "Support" 127 | msgstr "Supporto" 128 | 129 | #: Contents/Resources/web/templates/navbar.html:49 130 | msgid "Docs" 131 | msgstr "Documentazione" 132 | 133 | #: Contents/Resources/web/templates/navbar.html:53 134 | msgid "Support Center" 135 | msgstr "Centro assistenza" 136 | 137 | #: Contents/Resources/web/templates/navbar.html:59 138 | msgid "Home" 139 | msgstr "Pagina iniziale" 140 | 141 | -------------------------------------------------------------------------------- /Contents/Strings/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "bool_plex_movie_support": "Flex Movie エージェントのサポート (更新された Flex Movie エージェントにテーマを追加)", 3 | "bool_plex_series_support": "プレックス シリーズ エージェントのサポート (更新された プレックス シリーズ エージェントにテーマを追加)", 4 | "bool_overwrite_plex_provided_themes": "プレックスが提供するテーマを上書き", 5 | "bool_prefer_mp4a_codec": "MP4A AAC コーデックを優先 (Apple デバイスとの互換性を向上)", 6 | "bool_remove_unused_theme_songs": "未使用のテーマ曲を削除します(プレックスのメタデータディレクトリの空き容量を確保します)", 7 | "bool_remove_unused_art": "未使用のアートを削除します(コレクションに適用され、プレックスのメタデータディレクトリのスペースを解放します)", 8 | "bool_remove_unused_posters": "未使用のポスターを削除します(コレクションに適用され、プレックスのメタデータディレクトリのスペースを解放します)", 9 | "bool_auto_update_items": "アイテムを自動的に更新します (ThemerrDB で変更または以前に見つからなかったアイテムのみ)", 10 | "bool_auto_update_movie_themes": "自動更新中にムービーテーマを更新", 11 | "bool_auto_update_tv_themes": "自動更新中にテレビ番組テーマを更新する", 12 | "bool_auto_update_collection_themes": "自動更新中にコレクションテーマを更新", 13 | "bool_update_collection_metadata_plex_movie": "プレックスムービーエージェントのコレクションメタデータを更新(アップデートポスター、アート、概要)", 14 | "bool_update_collection_metadata_legacy": "レガシーエージェントのコレクションメタデータを更新(投稿、アート、概要を更新)", 15 | "int_update_themes_interval": "自動更新タスクの間隔 (分) (分: 15)", 16 | "int_update_database_cache_interval": "データベースキャッシュの更新タスクの間隔(分:15分)", 17 | "int_plexapi_plexapi_timeout": "PlexAPI タイムアウト(秒数:1)", 18 | "int_plexapi_upload_retries_max": "最大再試行回数, 整数 (min: 0)", 19 | "int_plexapi_upload_threads": "マルチプロセッシングスレッド, integer (min: 1)", 20 | "str_youtube_cookies": "YouTubeクッキー(JSON形式)", 21 | "enum_webapp_locale": "Web UI Locale", 22 | "str_webapp_http_host": "Web UI ホスト アドレス (プレックス メディア サーバーの再起動が必要)", 23 | "int_webapp_http_port": "Web UI ポート(プレックス メディア サーバーの再起動が必要)", 24 | "bool_webapp_log_werkzeug_messages": "すべてのWebサーバーメッセージをログに記録します(プレックス メディアサーバーの再起動が必要です)", 25 | "bool_migrate_locked_themes": "v0.3.0以前にThemerrを使用していた場合は、これをTrueに設定してください。", 26 | "bool_migrate_locked_collection_fields": "収集メタデータをv0.3.0から移行する(v0.3.0より前にThemerrを使用した場合は、これをTrueに設定してください)", 27 | "bool_ignore_locked_fields": "ロックされたフィールドを無視 (フィールドがロックされていても常にメディアをアップロード)" 28 | } 29 | -------------------------------------------------------------------------------- /Contents/Strings/ja/LC_MESSAGES/themerr-plex.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: lizardbyte\n" 4 | "Report-Msgid-Bugs-To: github.com/themerr-plex\n" 5 | "POT-Creation-Date: 2024-03-15 14:05+0000\n" 6 | "PO-Revision-Date: 2024-04-05 17:47\n" 7 | "Last-Translator: \n" 8 | "Language-Team: Japanese\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Generated-By: Babel 2.9.1\n" 13 | "Plural-Forms: nplurals=1; plural=0;\n" 14 | "X-Crowdin-Project: lizardbyte\n" 15 | "X-Crowdin-Project-ID: 606145\n" 16 | "X-Crowdin-Language: ja\n" 17 | "X-Crowdin-File: /[LizardByte.Themerr-plex] master/themerr-plex.po\n" 18 | "X-Crowdin-File-ID: 5784\n" 19 | "Language: ja_JP\n" 20 | 21 | #: Contents/Resources/web/templates/home.html:35 22 | msgid "Games" 23 | msgstr "ゲーム" 24 | 25 | #: Contents/Resources/web/templates/home.html:37 26 | msgid "Movies" 27 | msgstr "映画" 28 | 29 | #: Contents/Resources/web/templates/home.html:39 30 | msgid "Shows" 31 | msgstr "表示" 32 | 33 | #: Contents/Resources/web/templates/home.html:41 34 | msgid "Artists" 35 | msgstr "アーティスト" 36 | 37 | #: Contents/Resources/web/templates/home.html:43 38 | msgid "Photos" 39 | msgstr "写真" 40 | 41 | #: Contents/Resources/web/templates/home.html:45 42 | msgid "Items" 43 | msgstr "アイテム" 44 | 45 | #: Contents/Resources/web/templates/home.html:88 46 | msgid "Collections" 47 | msgstr "コレクション" 48 | 49 | #: Contents/Resources/web/templates/home.html:108 50 | msgid "Title" 51 | msgstr "タイトル" 52 | 53 | #: Contents/Resources/web/templates/home.html:109 54 | msgid "Type" 55 | msgstr "タイプ" 56 | 57 | #: Contents/Resources/web/templates/home.html:110 58 | msgid "Year" 59 | msgstr "年" 60 | 61 | #: Contents/Resources/web/templates/home.html:111 62 | msgid "Contribute" 63 | msgstr "貢献" 64 | 65 | #: Contents/Resources/web/templates/home.html:112 66 | msgid "Status" 67 | msgstr "ステータス" 68 | 69 | #: Contents/Resources/web/templates/home.html:128 70 | msgid "Add" 71 | msgstr "追加" 72 | 73 | #: Contents/Resources/web/templates/home.html:131 74 | msgid "Edit" 75 | msgstr "編集" 76 | 77 | #: Contents/Resources/web/templates/home.html:147 78 | msgid "No known ID" 79 | msgstr "既知のIDがありません" 80 | 81 | #: Contents/Resources/web/templates/home.html:152 82 | msgid "Plex provided" 83 | msgstr "プレックスが提供されました" 84 | 85 | #: Contents/Resources/web/templates/home.html:154 86 | msgid "User provided" 87 | msgstr "ユーザーが提供されました" 88 | 89 | #: Contents/Resources/web/templates/home.html:156 90 | msgid "Themerr provided" 91 | msgstr "Themerr provided" 92 | 93 | #: Contents/Resources/web/templates/home.html:158 94 | msgid "provided" 95 | msgstr "提供された" 96 | 97 | #: Contents/Resources/web/templates/home.html:160 98 | msgid "Unknown provider" 99 | msgstr "不明なプロバイダー" 100 | 101 | #: Contents/Resources/web/templates/home.html:162 102 | msgid "Missing from ThemerrDB" 103 | msgstr "ThemerrDB から欠落しています" 104 | 105 | #: Contents/Resources/web/templates/home.html:164 106 | msgid "Failed to download" 107 | msgstr "ダウンロードに失敗しました" 108 | 109 | #: Contents/Resources/web/templates/home.html:166 110 | msgid "Unknown status" 111 | msgstr "不明な状態" 112 | 113 | #: Contents/Resources/web/templates/home_db_not_cached.html:9 114 | msgid "Database is being cached, please try again soon." 115 | msgstr "データベースをキャッシュしています。しばらくしてからもう一度お試しください。" 116 | 117 | #: Contents/Resources/web/templates/navbar.html:23 118 | msgid "Donate" 119 | msgstr "寄付" 120 | 121 | #: Contents/Resources/web/templates/navbar.html:28 122 | msgid "GitHub Sponsors" 123 | msgstr "GitHub Sponsors" 124 | 125 | #: Contents/Resources/web/templates/navbar.html:44 126 | msgid "Support" 127 | msgstr "サポート" 128 | 129 | #: Contents/Resources/web/templates/navbar.html:49 130 | msgid "Docs" 131 | msgstr "ドキュメント" 132 | 133 | #: Contents/Resources/web/templates/navbar.html:53 134 | msgid "Support Center" 135 | msgstr "サポートセンター" 136 | 137 | #: Contents/Resources/web/templates/navbar.html:59 138 | msgid "Home" 139 | msgstr "ホーム" 140 | 141 | -------------------------------------------------------------------------------- /Contents/Strings/pt.json: -------------------------------------------------------------------------------- 1 | { 2 | "bool_plex_movie_support": "Suporte ao agente de cinema Plex (Adicionar temas ao agente Plex atualizado)", 3 | "bool_plex_series_support": "Suporte de agentes de Séries Plex (Adicionar temas ao agente de séries Plex atualizado)", 4 | "bool_overwrite_plex_provided_themes": "Sobrescrever temas Plex fornecidos", 5 | "bool_prefer_mp4a_codec": "Preferir MP4A AAC Codec (Melhora a compatibilidade com dispositivos Apple)", 6 | "bool_remove_unused_theme_songs": "Remover músicas temáticas não utilizadas (libera espaço em seu diretório de metadados Plex)", 7 | "bool_remove_unused_art": "Remover arte não utilizada (aplica-se a coleções, libera espaço no seu diretório de metadados Plex)", 8 | "bool_remove_unused_posters": "Remover pôsteres não utilizados (aplica-se a coleções, libera espaço no seu diretório de metadados Plex)", 9 | "bool_auto_update_items": "Atualizar automaticamente itens (apenas itens alterados ou anteriormente ausentes no ThemerrDB)", 10 | "bool_auto_update_movie_themes": "Atualizar temas de filmes durante a atualização automática", 11 | "bool_auto_update_tv_themes": "Atualizar temas de tv durante a atualização automática", 12 | "bool_auto_update_collection_themes": "Atualizar temas da coleção durante atualização automática", 13 | "bool_update_collection_metadata_plex_movie": "Atualizar metadados de coleção para o agente Plex de filme (cartaz de atualizações, arte e resumo)", 14 | "bool_update_collection_metadata_legacy": "Atualizar metadados de coleção para agentes legados (Cartaz, arte e resumo)", 15 | "int_update_themes_interval": "Intervalo para atualização automática da tarefa, em minutos (min: 15)", 16 | "int_update_database_cache_interval": "Intervalo para a tarefa de atualização do cache do banco de dados em minutos (min: 15)", 17 | "int_plexapi_plexapi_timeout": "Tempo limite da PlexAPI, em segundos (min: 1)", 18 | "int_plexapi_upload_retries_max": "Recuperação máxima, inteiro (min: 0)", 19 | "int_plexapi_upload_threads": "Multiprocessamento de Threads, inteiro (min: 1)", 20 | "str_youtube_cookies": "Cookies do YouTube (formato JSON)", 21 | "enum_webapp_locale": "Web UI Locale", 22 | "str_webapp_http_host": "Endereço de Host da Web (requer reinicialização Plex Media Server)", 23 | "int_webapp_http_port": "Porta da Web UI (requer Plex Media Server reiniciar)", 24 | "bool_webapp_log_werkzeug_messages": "Registrar todas as mensagens do servidor web (requer Plex Media Server reiniciar)", 25 | "bool_migrate_locked_themes": "Migre temas de < v0.3.0 (Se você usou o Themerr antes da v0.3.0, defina isso como Verdade)", 26 | "bool_migrate_locked_collection_fields": "Migrar metadados da coleção de < v0.3.0 (Se você usou Themerr antes da v0.3.0, defina isto como Verdade)", 27 | "bool_ignore_locked_fields": "Ignorar campos bloqueados (Sempre enviar mídia, mesmo que os campos estejam bloqueados)" 28 | } 29 | -------------------------------------------------------------------------------- /Contents/Strings/pt/LC_MESSAGES/themerr-plex.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: lizardbyte\n" 4 | "Report-Msgid-Bugs-To: github.com/themerr-plex\n" 5 | "POT-Creation-Date: 2024-03-15 14:05+0000\n" 6 | "PO-Revision-Date: 2024-04-18 23:07\n" 7 | "Last-Translator: \n" 8 | "Language-Team: Portuguese\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Generated-By: Babel 2.9.1\n" 13 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 14 | "X-Crowdin-Project: lizardbyte\n" 15 | "X-Crowdin-Project-ID: 606145\n" 16 | "X-Crowdin-Language: pt-PT\n" 17 | "X-Crowdin-File: /[LizardByte.Themerr-plex] master/themerr-plex.po\n" 18 | "X-Crowdin-File-ID: 5784\n" 19 | "Language: pt_PT\n" 20 | 21 | #: Contents/Resources/web/templates/home.html:35 22 | msgid "Games" 23 | msgstr "Partidas" 24 | 25 | #: Contents/Resources/web/templates/home.html:37 26 | msgid "Movies" 27 | msgstr "Filmes" 28 | 29 | #: Contents/Resources/web/templates/home.html:39 30 | msgid "Shows" 31 | msgstr "Séries" 32 | 33 | #: Contents/Resources/web/templates/home.html:41 34 | msgid "Artists" 35 | msgstr "artistas" 36 | 37 | #: Contents/Resources/web/templates/home.html:43 38 | msgid "Photos" 39 | msgstr "Fotos" 40 | 41 | #: Contents/Resources/web/templates/home.html:45 42 | msgid "Items" 43 | msgstr "itens" 44 | 45 | #: Contents/Resources/web/templates/home.html:88 46 | msgid "Collections" 47 | msgstr "Coleções" 48 | 49 | #: Contents/Resources/web/templates/home.html:108 50 | msgid "Title" 51 | msgstr "Título" 52 | 53 | #: Contents/Resources/web/templates/home.html:109 54 | msgid "Type" 55 | msgstr "tipo" 56 | 57 | #: Contents/Resources/web/templates/home.html:110 58 | msgid "Year" 59 | msgstr "ano" 60 | 61 | #: Contents/Resources/web/templates/home.html:111 62 | msgid "Contribute" 63 | msgstr "Contribuir" 64 | 65 | #: Contents/Resources/web/templates/home.html:112 66 | msgid "Status" 67 | msgstr "SItuação" 68 | 69 | #: Contents/Resources/web/templates/home.html:128 70 | msgid "Add" 71 | msgstr "Adicionar" 72 | 73 | #: Contents/Resources/web/templates/home.html:131 74 | msgid "Edit" 75 | msgstr "Alterar" 76 | 77 | #: Contents/Resources/web/templates/home.html:147 78 | msgid "No known ID" 79 | msgstr "Nenhum ID conhecido" 80 | 81 | #: Contents/Resources/web/templates/home.html:152 82 | msgid "Plex provided" 83 | msgstr "Plex fornecido" 84 | 85 | #: Contents/Resources/web/templates/home.html:154 86 | msgid "User provided" 87 | msgstr "Usuário fornecido" 88 | 89 | #: Contents/Resources/web/templates/home.html:156 90 | msgid "Themerr provided" 91 | msgstr "Themerr fornecido" 92 | 93 | #: Contents/Resources/web/templates/home.html:158 94 | msgid "provided" 95 | msgstr "fornecido" 96 | 97 | #: Contents/Resources/web/templates/home.html:160 98 | msgid "Unknown provider" 99 | msgstr "Provedor desconhecido" 100 | 101 | #: Contents/Resources/web/templates/home.html:162 102 | msgid "Missing from ThemerrDB" 103 | msgstr "Faltando do ThemerrDB" 104 | 105 | #: Contents/Resources/web/templates/home.html:164 106 | msgid "Failed to download" 107 | msgstr "Falha ao baixar" 108 | 109 | #: Contents/Resources/web/templates/home.html:166 110 | msgid "Unknown status" 111 | msgstr "Status desconhecido" 112 | 113 | #: Contents/Resources/web/templates/home_db_not_cached.html:9 114 | msgid "Database is being cached, please try again soon." 115 | msgstr "Banco de dados está sendo armazenado em cache, por favor, tente novamente em breve." 116 | 117 | #: Contents/Resources/web/templates/navbar.html:23 118 | msgid "Donate" 119 | msgstr "Doar" 120 | 121 | #: Contents/Resources/web/templates/navbar.html:28 122 | msgid "GitHub Sponsors" 123 | msgstr "GitHub Sponsors" 124 | 125 | #: Contents/Resources/web/templates/navbar.html:44 126 | msgid "Support" 127 | msgstr "SUPORTE" 128 | 129 | #: Contents/Resources/web/templates/navbar.html:49 130 | msgid "Docs" 131 | msgstr "Documentação" 132 | 133 | #: Contents/Resources/web/templates/navbar.html:53 134 | msgid "Support Center" 135 | msgstr "Centro de Suporte" 136 | 137 | #: Contents/Resources/web/templates/navbar.html:59 138 | msgid "Home" 139 | msgstr "Residencial" 140 | 141 | -------------------------------------------------------------------------------- /Contents/Strings/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "bool_plex_movie_support": "Поддержка Plex Movie agent (Добавить темы в обновленный Plex Movie агент)", 3 | "bool_plex_series_support": "Поддержка Plex Series agent (добавить темы в обновленный Plex Series Agent)", 4 | "bool_overwrite_plex_provided_themes": "Перезаписать предоставленные темы Plex", 5 | "bool_prefer_mp4a_codec": "Предпочитайте MP4A AAC кодек (улучшает совместимость с устройствами Apple)", 6 | "bool_remove_unused_theme_songs": "Удалить неиспользуемые песни темы (освобождает место в папке метаданных Plex)", 7 | "bool_remove_unused_art": "Удалить неиспользованный арт (применяется к коллекциям, освобождает место в папке метаданных Plex)", 8 | "bool_remove_unused_posters": "Удаление неиспользуемых постеров (применяется к коллекциям, освобождает место в папке метаданных Plex)", 9 | "bool_auto_update_items": "Автоматически обновлять элементы (только измененные или ранее отсутствующие в ThemerrDB)", 10 | "bool_auto_update_movie_themes": "Обновить темы фильмов при автоматическом обновлении", 11 | "bool_auto_update_tv_themes": "Обновление tv показывать темы при автоматическом обновлении", 12 | "bool_auto_update_collection_themes": "Обновление тем коллекции при автоматическом обновлении", 13 | "bool_update_collection_metadata_plex_movie": "Обновить собираемые метаданные для Plex Movie агента (плакат обновлений, арт и сводка)", 14 | "bool_update_collection_metadata_legacy": "Обновление сбора метаданных для старых агентов (плакат обновлений, искусство и резюме)", 15 | "int_update_themes_interval": "Интервал для задачи автоматического обновления, в минутах (мин: 15)", 16 | "int_update_database_cache_interval": "Интервал обновления базы данных в минутах (мин: 15)", 17 | "int_plexapi_plexapi_timeout": "Таймаут PlexAPI в секундах (мин: 1)", 18 | "int_plexapi_upload_retries_max": "Максимум повторов, целое число (мин: 0)", 19 | "int_plexapi_upload_threads": "Многопроцессорные потоки, целое число (мин: 1)", 20 | "str_youtube_cookies": "Cookies YouTube (формат JSON)", 21 | "enum_webapp_locale": "Web UI Locale", 22 | "str_webapp_http_host": "Адрес Web UI хоста (требуется перезапуск Plex Media Server)", 23 | "int_webapp_http_port": "WebUI порт (требуется перезапуск Plex Media Server)", 24 | "bool_webapp_log_werkzeug_messages": "Журнал всех сообщений веб-сервера (требуется перезапуск Plex Media Server)", 25 | "bool_migrate_locked_themes": "Миграция тем из < v0.3.0 (Если вы использовали Themerr до v0.3.0, установите это значение Истинный)", 26 | "bool_migrate_locked_collection_fields": "Мигрировать метаданные коллекции от < v0.3.0 (Если вы использовали Themerr до v0.3.0, установите это значение true)", 27 | "bool_ignore_locked_fields": "Игнорировать заблокированные поля (всегда загружать медиа, даже если поля заблокированы)" 28 | } 29 | -------------------------------------------------------------------------------- /Contents/Strings/ru/LC_MESSAGES/themerr-plex.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: lizardbyte\n" 4 | "Report-Msgid-Bugs-To: github.com/themerr-plex\n" 5 | "POT-Creation-Date: 2024-03-15 14:05+0000\n" 6 | "PO-Revision-Date: 2024-03-15 21:02\n" 7 | "Last-Translator: \n" 8 | "Language-Team: Russian\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Generated-By: Babel 2.9.1\n" 13 | "Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n" 14 | "X-Crowdin-Project: lizardbyte\n" 15 | "X-Crowdin-Project-ID: 606145\n" 16 | "X-Crowdin-Language: ru\n" 17 | "X-Crowdin-File: /[LizardByte.Themerr-plex] master/themerr-plex.po\n" 18 | "X-Crowdin-File-ID: 5784\n" 19 | "Language: ru_RU\n" 20 | 21 | #: Contents/Resources/web/templates/home.html:35 22 | msgid "Games" 23 | msgstr "Игры" 24 | 25 | #: Contents/Resources/web/templates/home.html:37 26 | msgid "Movies" 27 | msgstr "Фильмы" 28 | 29 | #: Contents/Resources/web/templates/home.html:39 30 | msgid "Shows" 31 | msgstr "Сериалы" 32 | 33 | #: Contents/Resources/web/templates/home.html:41 34 | msgid "Artists" 35 | msgstr "Художники" 36 | 37 | #: Contents/Resources/web/templates/home.html:43 38 | msgid "Photos" 39 | msgstr "Фото" 40 | 41 | #: Contents/Resources/web/templates/home.html:45 42 | msgid "Items" 43 | msgstr "Предметы" 44 | 45 | #: Contents/Resources/web/templates/home.html:88 46 | msgid "Collections" 47 | msgstr "Коллекции" 48 | 49 | #: Contents/Resources/web/templates/home.html:108 50 | msgid "Title" 51 | msgstr "Заголовок" 52 | 53 | #: Contents/Resources/web/templates/home.html:109 54 | msgid "Type" 55 | msgstr "Тип" 56 | 57 | #: Contents/Resources/web/templates/home.html:110 58 | msgid "Year" 59 | msgstr "Год" 60 | 61 | #: Contents/Resources/web/templates/home.html:111 62 | msgid "Contribute" 63 | msgstr "Внести вклад" 64 | 65 | #: Contents/Resources/web/templates/home.html:112 66 | msgid "Status" 67 | msgstr "Статус" 68 | 69 | #: Contents/Resources/web/templates/home.html:128 70 | msgid "Add" 71 | msgstr "Добавить" 72 | 73 | #: Contents/Resources/web/templates/home.html:131 74 | msgid "Edit" 75 | msgstr "Редактировать" 76 | 77 | #: Contents/Resources/web/templates/home.html:147 78 | msgid "No known ID" 79 | msgstr "Идентификация неизвестна" 80 | 81 | #: Contents/Resources/web/templates/home.html:152 82 | msgid "Plex provided" 83 | msgstr "Plex предоставлен" 84 | 85 | #: Contents/Resources/web/templates/home.html:154 86 | msgid "User provided" 87 | msgstr "Пользователь предоставлен" 88 | 89 | #: Contents/Resources/web/templates/home.html:156 90 | msgid "Themerr provided" 91 | msgstr "Themerr предоставлен" 92 | 93 | #: Contents/Resources/web/templates/home.html:158 94 | msgid "provided" 95 | msgstr "предоставлено" 96 | 97 | #: Contents/Resources/web/templates/home.html:160 98 | msgid "Unknown provider" 99 | msgstr "Неизвестный провайдер" 100 | 101 | #: Contents/Resources/web/templates/home.html:162 102 | msgid "Missing from ThemerrDB" 103 | msgstr "Отсутствует в ThemerrDB" 104 | 105 | #: Contents/Resources/web/templates/home.html:164 106 | msgid "Failed to download" 107 | msgstr "Не удалось загрузить" 108 | 109 | #: Contents/Resources/web/templates/home.html:166 110 | msgid "Unknown status" 111 | msgstr "Неизвестный статус" 112 | 113 | #: Contents/Resources/web/templates/home_db_not_cached.html:9 114 | msgid "Database is being cached, please try again soon." 115 | msgstr "База данных кэшируется, повторите попытку в ближайшее время." 116 | 117 | #: Contents/Resources/web/templates/navbar.html:23 118 | msgid "Donate" 119 | msgstr "Пожертвовать" 120 | 121 | #: Contents/Resources/web/templates/navbar.html:28 122 | msgid "GitHub Sponsors" 123 | msgstr "Спонсоры на GitHub" 124 | 125 | #: Contents/Resources/web/templates/navbar.html:44 126 | msgid "Support" 127 | msgstr "Поддержка" 128 | 129 | #: Contents/Resources/web/templates/navbar.html:49 130 | msgid "Docs" 131 | msgstr "Документация" 132 | 133 | #: Contents/Resources/web/templates/navbar.html:53 134 | msgid "Support Center" 135 | msgstr "Центр поддержки" 136 | 137 | #: Contents/Resources/web/templates/navbar.html:59 138 | msgid "Home" 139 | msgstr "Главная" 140 | 141 | -------------------------------------------------------------------------------- /Contents/Strings/sv.json: -------------------------------------------------------------------------------- 1 | { 2 | "bool_plex_movie_support": "Stöd för Plex Movie agent (Lägg till teman till den uppdaterade Plex Movie agenten)", 3 | "bool_plex_series_support": "Plex Series agentstöd (Lägg till teman till den uppdaterade Plex Series agenten)", 4 | "bool_overwrite_plex_provided_themes": "Skriv över Plex angivna teman", 5 | "bool_prefer_mp4a_codec": "Föredrar MP4A AAC Codec (Förbättrar kompatibiliteten med Apple-enheter)", 6 | "bool_remove_unused_theme_songs": "Ta bort oanvända temalåtar (frigör utrymme i din Plex metadatakatalog)", 7 | "bool_remove_unused_art": "Ta bort oanvänd konst (gäller samlingar, frigör utrymme i din Plex metadatakatalog)", 8 | "bool_remove_unused_posters": "Ta bort oanvända affischer (gäller för samlingar, frigör utrymme i din Plex metadatakata)", 9 | "bool_auto_update_items": "Uppdatera objekt automatiskt (endast objekt som ändrats eller tidigare saknats i ThemerrDB)", 10 | "bool_auto_update_movie_themes": "Uppdatera filmteman under automatisk uppdatering", 11 | "bool_auto_update_tv_themes": "Uppdatera TV-serie-teman under automatisk uppdatering", 12 | "bool_auto_update_collection_themes": "Uppdatera samlingsteman vid automatisk uppdatering", 13 | "bool_update_collection_metadata_plex_movie": "Uppdatera samlingsmetadata för Plex Movie agent (Uppdaterar affisch, konst och sammanfattning)", 14 | "bool_update_collection_metadata_legacy": "Uppdatera samling metadata för äldre agenter (Uppdaterar affisch, konst och sammanfattning)", 15 | "int_update_themes_interval": "Intervall för automatisk uppdatering, i minuter (min: 15)", 16 | "int_update_database_cache_interval": "Intervall för uppdateringsuppgift för databascachen, i minuter (min: 15)", 17 | "int_plexapi_plexapi_timeout": "PlexAPI Timeout, i sekunder (min: 1)", 18 | "int_plexapi_upload_retries_max": "Max Försök igen, heltal (min: 0)", 19 | "int_plexapi_upload_threads": "Multiprocessortrådar, heltal (min: 1)", 20 | "str_youtube_cookies": "YouTube Cookies (JSON-format)", 21 | "enum_webapp_locale": "Web UI Locale", 22 | "str_webapp_http_host": "Webb UI värdadress (kräver omstart av Plex Media Server)", 23 | "int_webapp_http_port": "Web UI Port (kräver omstart av Plex Media Server)", 24 | "bool_webapp_log_werkzeug_messages": "Logga alla webbservermeddelanden (kräver omstart av Plex Media Server)", 25 | "bool_migrate_locked_themes": "Migrera teman från < v0.3.0 (Om du använde Themerr innan v0.3.0, sätt detta till True)", 26 | "bool_migrate_locked_collection_fields": "Migrera insamlingsmetadata från < v0.3.0 (Om du använde Themerr innan v0.3.0, sätt detta till True)", 27 | "bool_ignore_locked_fields": "Ignorera låsta fält (ladda alltid upp media, även om fält är låsta)" 28 | } 29 | -------------------------------------------------------------------------------- /Contents/Strings/sv/LC_MESSAGES/themerr-plex.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: lizardbyte\n" 4 | "Report-Msgid-Bugs-To: github.com/themerr-plex\n" 5 | "POT-Creation-Date: 2024-03-15 14:05+0000\n" 6 | "PO-Revision-Date: 2024-03-15 21:02\n" 7 | "Last-Translator: \n" 8 | "Language-Team: Swedish\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Generated-By: Babel 2.9.1\n" 13 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 14 | "X-Crowdin-Project: lizardbyte\n" 15 | "X-Crowdin-Project-ID: 606145\n" 16 | "X-Crowdin-Language: sv-SE\n" 17 | "X-Crowdin-File: /[LizardByte.Themerr-plex] master/themerr-plex.po\n" 18 | "X-Crowdin-File-ID: 5784\n" 19 | "Language: sv_SE\n" 20 | 21 | #: Contents/Resources/web/templates/home.html:35 22 | msgid "Games" 23 | msgstr "Spel" 24 | 25 | #: Contents/Resources/web/templates/home.html:37 26 | msgid "Movies" 27 | msgstr "Filmer" 28 | 29 | #: Contents/Resources/web/templates/home.html:39 30 | msgid "Shows" 31 | msgstr "Serier" 32 | 33 | #: Contents/Resources/web/templates/home.html:41 34 | msgid "Artists" 35 | msgstr "Artister" 36 | 37 | #: Contents/Resources/web/templates/home.html:43 38 | msgid "Photos" 39 | msgstr "Foton" 40 | 41 | #: Contents/Resources/web/templates/home.html:45 42 | msgid "Items" 43 | msgstr "Objekt" 44 | 45 | #: Contents/Resources/web/templates/home.html:88 46 | msgid "Collections" 47 | msgstr "Samlingar" 48 | 49 | #: Contents/Resources/web/templates/home.html:108 50 | msgid "Title" 51 | msgstr "Titel" 52 | 53 | #: Contents/Resources/web/templates/home.html:109 54 | msgid "Type" 55 | msgstr "Typ" 56 | 57 | #: Contents/Resources/web/templates/home.html:110 58 | msgid "Year" 59 | msgstr "Årtal" 60 | 61 | #: Contents/Resources/web/templates/home.html:111 62 | msgid "Contribute" 63 | msgstr "Bidra" 64 | 65 | #: Contents/Resources/web/templates/home.html:112 66 | msgid "Status" 67 | msgstr "Status" 68 | 69 | #: Contents/Resources/web/templates/home.html:128 70 | msgid "Add" 71 | msgstr "Lägg till" 72 | 73 | #: Contents/Resources/web/templates/home.html:131 74 | msgid "Edit" 75 | msgstr "Redigera" 76 | 77 | #: Contents/Resources/web/templates/home.html:147 78 | msgid "No known ID" 79 | msgstr "Inget känt ID" 80 | 81 | #: Contents/Resources/web/templates/home.html:152 82 | msgid "Plex provided" 83 | msgstr "Plex tillhandahålls" 84 | 85 | #: Contents/Resources/web/templates/home.html:154 86 | msgid "User provided" 87 | msgstr "Användarbidrag" 88 | 89 | #: Contents/Resources/web/templates/home.html:156 90 | msgid "Themerr provided" 91 | msgstr "Themerrs bidrag" 92 | 93 | #: Contents/Resources/web/templates/home.html:158 94 | msgid "provided" 95 | msgstr "förutsatt" 96 | 97 | #: Contents/Resources/web/templates/home.html:160 98 | msgid "Unknown provider" 99 | msgstr "Andra bidrag" 100 | 101 | #: Contents/Resources/web/templates/home.html:162 102 | msgid "Missing from ThemerrDB" 103 | msgstr "Saknas från ThemerrDB" 104 | 105 | #: Contents/Resources/web/templates/home.html:164 106 | msgid "Failed to download" 107 | msgstr "Misslyckades med att ladda ner" 108 | 109 | #: Contents/Resources/web/templates/home.html:166 110 | msgid "Unknown status" 111 | msgstr "Status okänd" 112 | 113 | #: Contents/Resources/web/templates/home_db_not_cached.html:9 114 | msgid "Database is being cached, please try again soon." 115 | msgstr "Databasen håller på att cachas, var god försök senare." 116 | 117 | #: Contents/Resources/web/templates/navbar.html:23 118 | msgid "Donate" 119 | msgstr "Donera" 120 | 121 | #: Contents/Resources/web/templates/navbar.html:28 122 | msgid "GitHub Sponsors" 123 | msgstr "GitHub-sponsorer" 124 | 125 | #: Contents/Resources/web/templates/navbar.html:44 126 | msgid "Support" 127 | msgstr "Support" 128 | 129 | #: Contents/Resources/web/templates/navbar.html:49 130 | msgid "Docs" 131 | msgstr "Dokumentation" 132 | 133 | #: Contents/Resources/web/templates/navbar.html:53 134 | msgid "Support Center" 135 | msgstr "Online support" 136 | 137 | #: Contents/Resources/web/templates/navbar.html:59 138 | msgid "Home" 139 | msgstr "Hem" 140 | 141 | -------------------------------------------------------------------------------- /Contents/Strings/themerr-plex.po: -------------------------------------------------------------------------------- 1 | # Translations template for Themerr-plex. 2 | # Copyright (C) 2024 Themerr-plex 3 | # This file is distributed under the same license as the Themerr-plex 4 | # project. 5 | # FIRST AUTHOR , 2024. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Themerr-plex v0\n" 11 | "Report-Msgid-Bugs-To: github.com/themerr-plex\n" 12 | "POT-Creation-Date: 2024-03-15 14:05+0000\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.9.1\n" 20 | 21 | #: Contents/Resources/web/templates/home.html:35 22 | msgid "Games" 23 | msgstr "" 24 | 25 | #: Contents/Resources/web/templates/home.html:37 26 | msgid "Movies" 27 | msgstr "" 28 | 29 | #: Contents/Resources/web/templates/home.html:39 30 | msgid "Shows" 31 | msgstr "" 32 | 33 | #: Contents/Resources/web/templates/home.html:41 34 | msgid "Artists" 35 | msgstr "" 36 | 37 | #: Contents/Resources/web/templates/home.html:43 38 | msgid "Photos" 39 | msgstr "" 40 | 41 | #: Contents/Resources/web/templates/home.html:45 42 | msgid "Items" 43 | msgstr "" 44 | 45 | #: Contents/Resources/web/templates/home.html:88 46 | msgid "Collections" 47 | msgstr "" 48 | 49 | #: Contents/Resources/web/templates/home.html:108 50 | msgid "Title" 51 | msgstr "" 52 | 53 | #: Contents/Resources/web/templates/home.html:109 54 | msgid "Type" 55 | msgstr "" 56 | 57 | #: Contents/Resources/web/templates/home.html:110 58 | msgid "Year" 59 | msgstr "" 60 | 61 | #: Contents/Resources/web/templates/home.html:111 62 | msgid "Contribute" 63 | msgstr "" 64 | 65 | #: Contents/Resources/web/templates/home.html:112 66 | msgid "Status" 67 | msgstr "" 68 | 69 | #: Contents/Resources/web/templates/home.html:128 70 | msgid "Add" 71 | msgstr "" 72 | 73 | #: Contents/Resources/web/templates/home.html:131 74 | msgid "Edit" 75 | msgstr "" 76 | 77 | #: Contents/Resources/web/templates/home.html:147 78 | msgid "No known ID" 79 | msgstr "" 80 | 81 | #: Contents/Resources/web/templates/home.html:152 82 | msgid "Plex provided" 83 | msgstr "" 84 | 85 | #: Contents/Resources/web/templates/home.html:154 86 | msgid "User provided" 87 | msgstr "" 88 | 89 | #: Contents/Resources/web/templates/home.html:156 90 | msgid "Themerr provided" 91 | msgstr "" 92 | 93 | #: Contents/Resources/web/templates/home.html:158 94 | msgid "provided" 95 | msgstr "" 96 | 97 | #: Contents/Resources/web/templates/home.html:160 98 | msgid "Unknown provider" 99 | msgstr "" 100 | 101 | #: Contents/Resources/web/templates/home.html:162 102 | msgid "Missing from ThemerrDB" 103 | msgstr "" 104 | 105 | #: Contents/Resources/web/templates/home.html:164 106 | msgid "Failed to download" 107 | msgstr "" 108 | 109 | #: Contents/Resources/web/templates/home.html:166 110 | msgid "Unknown status" 111 | msgstr "" 112 | 113 | #: Contents/Resources/web/templates/home_db_not_cached.html:9 114 | msgid "Database is being cached, please try again soon." 115 | msgstr "" 116 | 117 | #: Contents/Resources/web/templates/navbar.html:23 118 | msgid "Donate" 119 | msgstr "" 120 | 121 | #: Contents/Resources/web/templates/navbar.html:28 122 | msgid "GitHub Sponsors" 123 | msgstr "" 124 | 125 | #: Contents/Resources/web/templates/navbar.html:44 126 | msgid "Support" 127 | msgstr "" 128 | 129 | #: Contents/Resources/web/templates/navbar.html:49 130 | msgid "Docs" 131 | msgstr "" 132 | 133 | #: Contents/Resources/web/templates/navbar.html:53 134 | msgid "Support Center" 135 | msgstr "" 136 | 137 | #: Contents/Resources/web/templates/navbar.html:59 138 | msgid "Home" 139 | msgstr "" 140 | 141 | -------------------------------------------------------------------------------- /Contents/Strings/tr.json: -------------------------------------------------------------------------------- 1 | { 2 | "bool_plex_movie_support": "Plex Movie aracı desteği (Güncellenen Plex Movie aracısına temalar ekleyin)", 3 | "bool_plex_series_support": "Plex Serisi aracı desteği (Güncellenmiş Plex Serisi aracısına temalar ekleyin)", 4 | "bool_overwrite_plex_provided_themes": "Plex tarafından sağlanan temaların üzerine yazma", 5 | "bool_prefer_mp4a_codec": "MP4A AAC Codec'i tercih edin (Apple cihazlarıyla uyumluluğu artırır)", 6 | "bool_remove_unused_theme_songs": "Kullanılmayan tema şarkılarını kaldırın (Plex metadata dizininizde yer açar)", 7 | "bool_remove_unused_art": "Kullanılmayan resimleri kaldırma (koleksiyonlar için geçerlidir, Plex metadata dizininizde yer açar)", 8 | "bool_remove_unused_posters": "Kullanılmayan posterleri kaldırın (koleksiyonlar için geçerlidir, Plex metadata dizininizde yer açar)", 9 | "bool_auto_update_items": "Öğeleri otomatik olarak güncelle (yalnızca ThemerrDB'de değiştirilen veya daha önce eksik olan öğeler)", 10 | "bool_auto_update_movie_themes": "Otomatik güncelleme sırasında film temalarını güncelleme", 11 | "bool_auto_update_tv_themes": "Otomatik güncelleme sırasında dizi temalarını güncelleme", 12 | "bool_auto_update_collection_themes": "Otomatik güncelleme sırasında koleksiyon temalarını güncelleme", 13 | "bool_update_collection_metadata_plex_movie": "Plex Movie aracısı için koleksiyon meta verilerini güncelleme (Posteri, resmi ve özeti günceller)", 14 | "bool_update_collection_metadata_legacy": "Eski ajanlar için koleksiyon meta verilerini güncelleyin (Poster, sanat ve özeti günceller)", 15 | "int_update_themes_interval": "Otomatik güncelleme görevi için aralık, dakika cinsinden (min: 15)", 16 | "int_update_database_cache_interval": "Veritabanı önbelleği güncelleme görevi için dakika cinsinden aralık (min: 15)", 17 | "int_plexapi_plexapi_timeout": "PlexAPI Zaman Aşımı, saniye cinsinden (min: 1)", 18 | "int_plexapi_upload_retries_max": "Maksimum Yeniden Deneme, tamsayı (min: 0)", 19 | "int_plexapi_upload_threads": "Çoklu İşlem İş Parçacığı, tamsayı (min: 1)", 20 | "str_youtube_cookies": "YouTube Çerezleri (JSON biçimi)", 21 | "enum_webapp_locale": "Web Arayüzü Yerel Ayarı", 22 | "str_webapp_http_host": "Web UI Ana Bilgisayar Adresi (Plex Media Server'ın yeniden başlatılmasını gerektirir)", 23 | "int_webapp_http_port": "Web UI Bağlantı Noktası (Plex Media Server'ın yeniden başlatılmasını gerektirir)", 24 | "bool_webapp_log_werkzeug_messages": "Tüm web sunucusu mesajlarını günlüğe kaydetme (Plex Media Server'ın yeniden başlatılmasını gerektirir)", 25 | "bool_migrate_locked_themes": "Migrate themes from < v0.3.0 (Themerr'i v0.3.0'dan önce kullandıysanız, bunu True olarak ayarlayın)", 26 | "bool_migrate_locked_collection_fields": "Koleksiyon meta verilerini < v0.3.0'dan taşı (Themerr'i v0.3.0'dan önce kullandıysanız, bunu True olarak ayarlayın)", 27 | "bool_ignore_locked_fields": "Kilitli alanları yoksay (Alanlar kilitli olsa bile her zaman medya yükleyin)" 28 | } 29 | -------------------------------------------------------------------------------- /Contents/Strings/tr/LC_MESSAGES/themerr-plex.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: lizardbyte\n" 4 | "Report-Msgid-Bugs-To: github.com/themerr-plex\n" 5 | "POT-Creation-Date: 2024-03-15 14:05+0000\n" 6 | "PO-Revision-Date: 2024-06-16 19:21\n" 7 | "Last-Translator: \n" 8 | "Language-Team: Turkish\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Generated-By: Babel 2.9.1\n" 13 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 14 | "X-Crowdin-Project: lizardbyte\n" 15 | "X-Crowdin-Project-ID: 606145\n" 16 | "X-Crowdin-Language: tr\n" 17 | "X-Crowdin-File: /[LizardByte.Themerr-plex] master/themerr-plex.po\n" 18 | "X-Crowdin-File-ID: 5784\n" 19 | "Language: tr_TR\n" 20 | 21 | #: Contents/Resources/web/templates/home.html:35 22 | msgid "Games" 23 | msgstr "Oyunlar" 24 | 25 | #: Contents/Resources/web/templates/home.html:37 26 | msgid "Movies" 27 | msgstr "Filmler" 28 | 29 | #: Contents/Resources/web/templates/home.html:39 30 | msgid "Shows" 31 | msgstr "Gösteriler" 32 | 33 | #: Contents/Resources/web/templates/home.html:41 34 | msgid "Artists" 35 | msgstr "Sanatçılar" 36 | 37 | #: Contents/Resources/web/templates/home.html:43 38 | msgid "Photos" 39 | msgstr "Fotoğraflar" 40 | 41 | #: Contents/Resources/web/templates/home.html:45 42 | msgid "Items" 43 | msgstr "Eşyalar" 44 | 45 | #: Contents/Resources/web/templates/home.html:88 46 | msgid "Collections" 47 | msgstr "Koleksiyonlar" 48 | 49 | #: Contents/Resources/web/templates/home.html:108 50 | msgid "Title" 51 | msgstr "Başlık" 52 | 53 | #: Contents/Resources/web/templates/home.html:109 54 | msgid "Type" 55 | msgstr "Tip" 56 | 57 | #: Contents/Resources/web/templates/home.html:110 58 | msgid "Year" 59 | msgstr "Yıl" 60 | 61 | #: Contents/Resources/web/templates/home.html:111 62 | msgid "Contribute" 63 | msgstr "Katkıda bulunun" 64 | 65 | #: Contents/Resources/web/templates/home.html:112 66 | msgid "Status" 67 | msgstr "Durum" 68 | 69 | #: Contents/Resources/web/templates/home.html:128 70 | msgid "Add" 71 | msgstr "Ekle" 72 | 73 | #: Contents/Resources/web/templates/home.html:131 74 | msgid "Edit" 75 | msgstr "Düzenle" 76 | 77 | #: Contents/Resources/web/templates/home.html:147 78 | msgid "No known ID" 79 | msgstr "Bilinen kimlik yok" 80 | 81 | #: Contents/Resources/web/templates/home.html:152 82 | msgid "Plex provided" 83 | msgstr "Plex sağladı" 84 | 85 | #: Contents/Resources/web/templates/home.html:154 86 | msgid "User provided" 87 | msgstr "Kullanıcı tarafından sağlanan" 88 | 89 | #: Contents/Resources/web/templates/home.html:156 90 | msgid "Themerr provided" 91 | msgstr "Themerr şunları sağladı" 92 | 93 | #: Contents/Resources/web/templates/home.html:158 94 | msgid "provided" 95 | msgstr "sağlanan" 96 | 97 | #: Contents/Resources/web/templates/home.html:160 98 | msgid "Unknown provider" 99 | msgstr "Bilinmeyen sağlayıcı" 100 | 101 | #: Contents/Resources/web/templates/home.html:162 102 | msgid "Missing from ThemerrDB" 103 | msgstr "ThemerrDB'den eksik" 104 | 105 | #: Contents/Resources/web/templates/home.html:164 106 | msgid "Failed to download" 107 | msgstr "İndirilemedi" 108 | 109 | #: Contents/Resources/web/templates/home.html:166 110 | msgid "Unknown status" 111 | msgstr "Bilinmeyen durum" 112 | 113 | #: Contents/Resources/web/templates/home_db_not_cached.html:9 114 | msgid "Database is being cached, please try again soon." 115 | msgstr "Veritabanı önbelleğe alınıyor, lütfen yakında tekrar deneyin." 116 | 117 | #: Contents/Resources/web/templates/navbar.html:23 118 | msgid "Donate" 119 | msgstr "Bağış" 120 | 121 | #: Contents/Resources/web/templates/navbar.html:28 122 | msgid "GitHub Sponsors" 123 | msgstr "GitHub Sponsorları" 124 | 125 | #: Contents/Resources/web/templates/navbar.html:44 126 | msgid "Support" 127 | msgstr "Destek" 128 | 129 | #: Contents/Resources/web/templates/navbar.html:49 130 | msgid "Docs" 131 | msgstr "Dokümanlar" 132 | 133 | #: Contents/Resources/web/templates/navbar.html:53 134 | msgid "Support Center" 135 | msgstr "Destek Merkezi" 136 | 137 | #: Contents/Resources/web/templates/navbar.html:59 138 | msgid "Home" 139 | msgstr "Ana Sayfa" 140 | 141 | -------------------------------------------------------------------------------- /Contents/Strings/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "bool_plex_movie_support": "启用 Plex Movie 代理支持(为最新的 Plex Movie 代理添加主题音乐)", 3 | "bool_plex_series_support": "启用 Plex Series 代理支持(为最新的 Plex Series 代理添加主题音乐)", 4 | "bool_overwrite_plex_provided_themes": "覆盖 Plex 提供的主题音乐", 5 | "bool_prefer_mp4a_codec": "优先使用 MP4A AAC 编解码器(提高与苹果设备的兼容性)", 6 | "bool_remove_unused_theme_songs": "移除未使用的主题音乐(为 Plex 元数据目录释放存储空间)", 7 | "bool_remove_unused_art": "移除未使用的背景(适用于合集,为 Plex 元数据目录释放存储空间)", 8 | "bool_remove_unused_posters": "移除未使用的海报(适用于合集,为 Plex 元数据目录释放存储空间)", 9 | "bool_auto_update_items": "自动更新项目(仅更新在 ThemerrDB 中已更改或之前不存在的项目)", 10 | "bool_auto_update_movie_themes": "在自动更新期间更新电影主题音乐", 11 | "bool_auto_update_tv_themes": "在自动更新期间更新电视节目主题音乐", 12 | "bool_auto_update_collection_themes": "在自动更新期间更新合集主题音乐", 13 | "bool_update_collection_metadata_plex_movie": "更新 Plex Movie 代理的合集元数据(更新海报、背景和简介)", 14 | "bool_update_collection_metadata_legacy": "更新 Legacy 代理的合集元数据(更新海报、背景和简介)", 15 | "int_update_themes_interval": "自动更新任务的间隔时间,以分钟为单位(最小值:15)", 16 | "int_update_database_cache_interval": "数据库缓存更新任务的间隔时间,以分钟为单位(最小值:15)", 17 | "int_plexapi_plexapi_timeout": "PlexAPI 超时时间,以秒为单位(最小值:1)", 18 | "int_plexapi_upload_retries_max": "最大重试次数,整数(最小值:0)", 19 | "int_plexapi_upload_threads": "并行处理线程数,整数(最小值:1)", 20 | "str_youtube_cookies": "YouTube Cookies(JSON 格式)", 21 | "enum_webapp_locale": "Web UI 语言", 22 | "str_webapp_http_host": "Web UI 主机地址(需要重新启动 Plex 媒体服务器)", 23 | "int_webapp_http_port": "Web UI 端口(需要重新启动 Plex 媒体服务器)", 24 | "bool_webapp_log_werkzeug_messages": "记录所有 Web 服务器消息(需要重新启动 Plex 媒体服务器)", 25 | "bool_migrate_locked_themes": "从 < v0.3.0 迁移主题音乐(如果你使用过 Themerr v0.3.0 之前的版本,请启用)", 26 | "bool_migrate_locked_collection_fields": "从 < v0.3.0 迁移合集元数据(如果你使用过 Themerr v0.3.0 之前的版本,请启用)", 27 | "bool_ignore_locked_fields": "忽略锁定的字段(即使字段已被锁定,也总是上传媒体)" 28 | } 29 | -------------------------------------------------------------------------------- /Contents/Strings/zh/LC_MESSAGES/themerr-plex.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: lizardbyte\n" 4 | "Report-Msgid-Bugs-To: github.com/themerr-plex\n" 5 | "POT-Creation-Date: 2024-03-15 14:05+0000\n" 6 | "PO-Revision-Date: 2024-03-16 18:25\n" 7 | "Last-Translator: \n" 8 | "Language-Team: Chinese Simplified\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Generated-By: Babel 2.9.1\n" 13 | "Plural-Forms: nplurals=1; plural=0;\n" 14 | "X-Crowdin-Project: lizardbyte\n" 15 | "X-Crowdin-Project-ID: 606145\n" 16 | "X-Crowdin-Language: zh-CN\n" 17 | "X-Crowdin-File: /[LizardByte.Themerr-plex] master/themerr-plex.po\n" 18 | "X-Crowdin-File-ID: 5784\n" 19 | "Language: zh_CN\n" 20 | 21 | #: Contents/Resources/web/templates/home.html:35 22 | msgid "Games" 23 | msgstr "游戏" 24 | 25 | #: Contents/Resources/web/templates/home.html:37 26 | msgid "Movies" 27 | msgstr "电影" 28 | 29 | #: Contents/Resources/web/templates/home.html:39 30 | msgid "Shows" 31 | msgstr "电视" 32 | 33 | #: Contents/Resources/web/templates/home.html:41 34 | msgid "Artists" 35 | msgstr "艺人" 36 | 37 | #: Contents/Resources/web/templates/home.html:43 38 | msgid "Photos" 39 | msgstr "照片" 40 | 41 | #: Contents/Resources/web/templates/home.html:45 42 | msgid "Items" 43 | msgstr "项目" 44 | 45 | #: Contents/Resources/web/templates/home.html:88 46 | msgid "Collections" 47 | msgstr "合集" 48 | 49 | #: Contents/Resources/web/templates/home.html:108 50 | msgid "Title" 51 | msgstr "标题" 52 | 53 | #: Contents/Resources/web/templates/home.html:109 54 | msgid "Type" 55 | msgstr "类型" 56 | 57 | #: Contents/Resources/web/templates/home.html:110 58 | msgid "Year" 59 | msgstr "年份" 60 | 61 | #: Contents/Resources/web/templates/home.html:111 62 | msgid "Contribute" 63 | msgstr "贡献" 64 | 65 | #: Contents/Resources/web/templates/home.html:112 66 | msgid "Status" 67 | msgstr "状态" 68 | 69 | #: Contents/Resources/web/templates/home.html:128 70 | msgid "Add" 71 | msgstr "添加" 72 | 73 | #: Contents/Resources/web/templates/home.html:131 74 | msgid "Edit" 75 | msgstr "编辑" 76 | 77 | #: Contents/Resources/web/templates/home.html:147 78 | msgid "No known ID" 79 | msgstr "未知ID" 80 | 81 | #: Contents/Resources/web/templates/home.html:152 82 | msgid "Plex provided" 83 | msgstr "Plex提供" 84 | 85 | #: Contents/Resources/web/templates/home.html:154 86 | msgid "User provided" 87 | msgstr "用户提供" 88 | 89 | #: Contents/Resources/web/templates/home.html:156 90 | msgid "Themerr provided" 91 | msgstr "Themerr提供" 92 | 93 | #: Contents/Resources/web/templates/home.html:158 94 | msgid "provided" 95 | msgstr "提供" 96 | 97 | #: Contents/Resources/web/templates/home.html:160 98 | msgid "Unknown provider" 99 | msgstr "未知提供者" 100 | 101 | #: Contents/Resources/web/templates/home.html:162 102 | msgid "Missing from ThemerrDB" 103 | msgstr "不在ThemerrDB中" 104 | 105 | #: Contents/Resources/web/templates/home.html:164 106 | msgid "Failed to download" 107 | msgstr "下载失败" 108 | 109 | #: Contents/Resources/web/templates/home.html:166 110 | msgid "Unknown status" 111 | msgstr "未知状态" 112 | 113 | #: Contents/Resources/web/templates/home_db_not_cached.html:9 114 | msgid "Database is being cached, please try again soon." 115 | msgstr "数据库正在缓存,请稍后再试。" 116 | 117 | #: Contents/Resources/web/templates/navbar.html:23 118 | msgid "Donate" 119 | msgstr "捐赠" 120 | 121 | #: Contents/Resources/web/templates/navbar.html:28 122 | msgid "GitHub Sponsors" 123 | msgstr "GitHub Sponsors" 124 | 125 | #: Contents/Resources/web/templates/navbar.html:44 126 | msgid "Support" 127 | msgstr "支持" 128 | 129 | #: Contents/Resources/web/templates/navbar.html:49 130 | msgid "Docs" 131 | msgstr "文档" 132 | 133 | #: Contents/Resources/web/templates/navbar.html:53 134 | msgid "Support Center" 135 | msgstr "支持中心" 136 | 137 | #: Contents/Resources/web/templates/navbar.html:59 138 | msgid "Home" 139 | msgstr "主页" 140 | 141 | -------------------------------------------------------------------------------- /DOCKER_README.md: -------------------------------------------------------------------------------- 1 | ### lizardbyte/themerr-plex 2 | 3 | This is a [docker-mod](https://linuxserver.github.io/docker-mods/) for 4 | [plex](https://hub.docker.com/r/linuxserver/plex) which adds [Themerr-plex](https://github.com/LizardByte/Themerr-plex) 5 | to plex as a plugin, to be downloaded/updated during container start. 6 | 7 | This image extends the plex image, and is not intended to be created as a separate container. 8 | 9 | ### Installation 10 | 11 | In plex docker arguments, set an environment variable `DOCKER_MODS=lizardbyte/themerr-plex:latest` or 12 | `DOCKER_MODS=ghcr.io/lizardbyte/themerr-plex:latest` 13 | 14 | If adding multiple mods, enter them in an array separated by `|`, such as 15 | `DOCKER_MODS=lizardbyte/themerr-plex:latest|linuxserver/mods:other-plex-mod` 16 | 17 | ### Supported Architectures 18 | 19 | Specifying `lizardbyte/themerr-plex:latest` or `ghcr.io/lizardbyte/themerr-plex:latest` should retrieve the correct 20 | image for your architecture. 21 | 22 | The architectures supported by this image are: 23 | 24 | | Architecture | Available | 25 | |:------------:|:---------:| 26 | | x86-64 | ✅ | 27 | | arm64 | ✅ | 28 | | armhf | ✅ | 29 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1.4 2 | # artifacts: false 3 | # platforms: linux/amd64,linux/arm64/v8,linux/arm/v7 4 | FROM ubuntu:22.04 AS buildstage 5 | 6 | # build args 7 | ARG BUILD_VERSION 8 | ARG COMMIT 9 | ARG GITHUB_SHA=$COMMIT 10 | # note: BUILD_VERSION may be blank, COMMIT is also available 11 | # note: build_plist.py uses BUILD_VERSION and GITHUB_SHA 12 | 13 | SHELL ["/bin/bash", "-o", "pipefail", "-c"] 14 | # install dependencies 15 | RUN <<_DEPS 16 | #!/bin/bash 17 | set -e 18 | apt-get update -y 19 | apt-get install -y --no-install-recommends \ 20 | npm=8.5.* \ 21 | patch \ 22 | python2=2.7.18* \ 23 | python-pip=20.3.4* 24 | apt-get clean 25 | rm -rf /var/lib/apt/lists/* 26 | _DEPS 27 | 28 | # create build dir and copy GitHub repo there 29 | COPY --link . /build 30 | 31 | # set build dir 32 | WORKDIR /build 33 | 34 | # update pip 35 | RUN <<_PIP 36 | #!/bin/bash 37 | set -e 38 | python2 -m pip --no-python-version-warning --disable-pip-version-check install --no-cache-dir --upgrade \ 39 | pip setuptools requests 40 | # requests required to install python-plexapi 41 | # dev requirements not necessary for docker image, significantly speeds up build since lxml doesn't need to build 42 | _PIP 43 | 44 | # build plugin 45 | RUN <<_BUILD 46 | #!/bin/bash 47 | set -e 48 | python2 -m pip --no-python-version-warning --disable-pip-version-check install --no-cache-dir --upgrade \ 49 | -r requirements-build.txt 50 | python2 -m pip --no-python-version-warning --disable-pip-version-check install --no-cache-dir --upgrade \ 51 | --target=./Contents/Libraries/Shared -r requirements.txt --no-warn-script-location 52 | python2 ./scripts/_locale.py --compile 53 | python2 ./scripts/build_plist.py 54 | _BUILD 55 | 56 | ## patch youtube-dl, cannot use git apply because we don't pass in any git files 57 | #WORKDIR /build/Contents/Libraries/Shared 58 | #RUN <<_PATCH 59 | ##!/bin/bash 60 | #set -e 61 | #patch_dir=/build/patches 62 | #patch -p1 < "${patch_dir}/youtube_dl-compat.patch" 63 | #_PATCH 64 | 65 | WORKDIR /build 66 | 67 | # setup npm and dependencies 68 | RUN <<_NPM 69 | #!/bin/bash 70 | set -e 71 | npm install 72 | mv ./node_modules ./Contents/Resources/web 73 | _NPM 74 | 75 | # clean 76 | RUN <<_CLEAN 77 | #!/bin/bash 78 | set -e 79 | rm -rf ./patches/ 80 | rm -rf ./scripts/ 81 | # list contents 82 | ls -a 83 | _CLEAN 84 | 85 | FROM scratch AS deploy 86 | 87 | # variables 88 | ARG PLUGIN_NAME="Themerr-plex.bundle" 89 | ARG PLUGIN_DIR="/config/Library/Application Support/Plex Media Server/Plug-ins" 90 | 91 | # add files from buildstage 92 | # trailing slash on build directory copies the contents of the directory, instead of the directory itself 93 | COPY --link --from=buildstage /build/ $PLUGIN_DIR/$PLUGIN_NAME 94 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/LizardByte/Themerr-plex/blob/master/README.rst 2 | 3 | Overview 4 | ======== 5 | LizardByte has the full documentation hosted on `Read the Docs `__. 6 | 7 | About 8 | ----- 9 | Themerr-plex is a metadata agent plug-in for Plex Media Player. The plug-in adds theme music to your movies 10 | and tv shows. 11 | 12 | This plugin contributes to the following metadata agents. 13 | 14 | - Plex Movie - `tv.plex.agents.movie` 15 | - Plex Series - `tv.plex.agents.series` 16 | - Plex Movie (Legacy) - `com.plexapp.agents.imdb` 17 | - The Movie Database - `com.plexapp.agents.themoviedb` 18 | - TheTVDB - `com.plexapp.agents.thetvdb` 19 | - `RetroArcher `__ - `dev.lizardbyte.retroarcher-plex` 20 | 21 | Integrations 22 | ------------ 23 | 24 | .. image:: https://img.shields.io/github/actions/workflow/status/lizardbyte/themerr-plex/CI.yml.svg?branch=master&label=CI%20build&logo=github&style=for-the-badge 25 | :alt: GitHub Workflow Status (CI) 26 | :target: https://github.com/LizardByte/Themerr-plex/actions/workflows/CI.yml?query=branch%3Amaster 27 | 28 | .. image:: https://img.shields.io/readthedocs/themerr-plex?label=Docs&style=for-the-badge&logo=readthedocs 29 | :alt: Read the Docs 30 | :target: http://themerr-plex.readthedocs.io/ 31 | 32 | .. image:: https://img.shields.io/codecov/c/gh/LizardByte/Themerr-plex?token=1LYYVYWY9D&style=for-the-badge&logo=codecov&label=codecov 33 | :alt: Codecov 34 | :target: https://codecov.io/gh/LizardByte/Themerr-plex 35 | 36 | Downloads 37 | --------- 38 | 39 | .. image:: https://img.shields.io/github/downloads/lizardbyte/themerr-plex/total?style=for-the-badge&logo=github 40 | :alt: GitHub Releases 41 | :target: https://github.com/LizardByte/Themerr-plex/releases/latest 42 | 43 | .. image:: https://img.shields.io/docker/pulls/lizardbyte/themerr-plex?style=for-the-badge&logo=docker 44 | :alt: Docker 45 | :target: https://hub.docker.com/r/lizardbyte/themerr-plex 46 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | --- 2 | codecov: 3 | branch: master 4 | notify: 5 | after_n_builds: 3 6 | 7 | coverage: 8 | status: 9 | project: 10 | default: 11 | target: auto 12 | threshold: 10% 13 | 14 | comment: 15 | layout: "diff, flags, files" 16 | behavior: default 17 | require_changes: false # if true: only post the comment if coverage changes 18 | -------------------------------------------------------------------------------- /crowdin.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "base_path": "." 3 | "base_url": "https://api.crowdin.com" # optional (for Crowdin Enterprise only) 4 | "preserve_hierarchy": true # false will flatten tree on crowdin, but doesn't work with dest option 5 | "pull_request_labels": [ 6 | "crowdin", 7 | "l10n" 8 | ] 9 | 10 | "files": [ 11 | { 12 | "source": "/Contents/Strings/*.po", 13 | "dest": "/%original_file_name%", 14 | "translation": "/Contents/Strings/%two_letters_code%/LC_MESSAGES/%original_file_name%", 15 | "languages_mapping": { 16 | "two_letters_code": { 17 | # map non-two letter codes here, left side is crowdin designation, right side is babel designation 18 | "en-GB": "en_GB", 19 | "en-US": "en_US" 20 | } 21 | }, 22 | "update_option": "update_as_unapproved" 23 | }, 24 | { 25 | "source": "/Contents/Strings/en.json", 26 | "dest": "/themerr-plex.json", 27 | "translation": "/Contents/Strings/%two_letters_code%.%file_extension%", 28 | "languages_mapping": { 29 | "two_letters_code": { 30 | # map non-two letter codes here, left side is crowdin designation, right side is plex designation 31 | "en-GB": "en-gb", 32 | "en-US": "en-us" 33 | } 34 | }, 35 | "update_option": "update_as_unapproved" 36 | } 37 | ] 38 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= -W --keep-going 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | set "SPHINXOPTS=-W --keep-going" 13 | 14 | %SPHINXBUILD% >NUL 2>NUL 15 | if errorlevel 9009 ( 16 | echo. 17 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 18 | echo.installed, then set the SPHINXBUILD environment variable to point 19 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 20 | echo.may add the Sphinx directory to PATH. 21 | echo. 22 | echo.If you don't have Sphinx installed, grab it from 23 | echo.https://www.sphinx-doc.org/ 24 | exit /b 1 25 | ) 26 | 27 | if "%1" == "" goto help 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% || exit /b %ERRORLEVEL% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% || exit /b %ERRORLEVEL% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/source/about/changelog.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | .. only:: epub 5 | 6 | You can view the changelog in the 7 | `online version `__. 8 | 9 | .. only:: html 10 | 11 | .. raw:: html 12 | 13 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /docs/source/about/docker.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/LizardByte/Themerr-plex/blob/master/DOCKER_README.md 2 | 3 | Docker 4 | ------ 5 | 6 | .. mdinclude:: ../../../DOCKER_README.md 7 | -------------------------------------------------------------------------------- /docs/source/about/installation.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/LizardByte/Themerr-plex/blob/master/docs/source/about/installation.rst 2 | 3 | Installation 4 | ============ 5 | The recommended method for running Themerr-plex is to use the `bundle`_ in the `latest release`_. 6 | 7 | Bundle 8 | ------ 9 | The bundle is cross platform, meaning Linux, macOS, and Windows are supported. 10 | 11 | #. Download the ``themerr-plex.bundle.zip`` from the `latest release`_ 12 | #. Extract the contents to your Plex Media Server Plugins directory. 13 | 14 | .. Tip:: See 15 | `How do I find the Plug-Ins folder `__ 16 | for information specific to your Plex server install. 17 | 18 | Docker 19 | ------ 20 | Docker images are available on `Dockerhub`_ and `ghcr.io`_. 21 | 22 | See :ref:`Docker ` for additional information. 23 | 24 | Source 25 | ------ 26 | .. Caution:: Installing from source is not recommended most users. 27 | 28 | #. Follow the steps in :ref:`Build `. 29 | #. Move the compiled ``themerr-plex.bundle`` to your Plex Media Server Plugins directory. 30 | 31 | .. _latest release: https://github.com/LizardByte/Themerr-plex/releases/latest 32 | .. _Dockerhub: https://hub.docker.com/repository/docker/lizardbyte/themerr-plex 33 | .. _ghcr.io: https://github.com/orgs/LizardByte/packages?repo_name=themerr-plex 34 | -------------------------------------------------------------------------------- /docs/source/about/overview.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../../../README.rst -------------------------------------------------------------------------------- /docs/source/about/troubleshooting.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/LizardByte/Themerr-plex/blob/master/docs/source/about/troubleshooting.rst 2 | 3 | Troubleshooting 4 | =============== 5 | 6 | Rate Limiting / Videos Not Downloading 7 | -------------------------------------- 8 | 9 | By default, YouTube-DL will perform queries to YouTube anonymously. As a result, YouTube may rate limit the 10 | requests, or sometimes simply block the content (e.g. for age-restricted content, but not only). 11 | 12 | A workaround is to login in a web browser, and then export your YouTube cookies with a tool such as `Get cookies.txt 13 | locally `__. Note 14 | that Themerr currently only supports Chromium's JSON export format. In the exporter you use, if prompted, you need to 15 | use the "JSON" or "Chrome" format. 16 | 17 | You can then paste that value in the "YouTube Cookies" field in the plugin preferences page. On the next media update 18 | or scheduled run, the cookies will be used and hopefully videos will start downloading again. 19 | 20 | Plugin Logs 21 | ----------- 22 | 23 | See `Plugin Log Files `__ for the plugin 24 | log directory. 25 | 26 | Plex uses rolling logs. There will be six log files available. The newest log file will be named 27 | ``dev.lizardbyte.themerr-plex.log``. There will be additional log files with the same name, appended with a `1-5`. 28 | 29 | It is best to replicate the issue you are experiencing, then review the latest log file. The information in the log 30 | file may seem cryptic. If so it would be best to reach out for `support `__. 31 | 32 | .. Attention:: Before uploading logs, it would be wise to review the data in the log file. Plex does not filter 33 | the masked settings (e.g. credentials) out of the log file. 34 | 35 | Plex Media Server Logs 36 | ---------------------- 37 | 38 | If you have a more severe problem, you may need to troubleshoot an issue beyond the plugin itself. See 39 | `Plex Media Server Logs `__ 40 | for more information. 41 | -------------------------------------------------------------------------------- /docs/source/code_docs/general_helper.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/LizardByte/Themerr-plex/blob/master/Contents/Code/general_helper.py 2 | 3 | .. include:: ../global.rst 4 | 5 | :modname:`general_helper` 6 | -------------------------- 7 | .. automodule:: Code.general_helper 8 | :members: 9 | :show-inheritance: 10 | -------------------------------------------------------------------------------- /docs/source/code_docs/lizardbyte_db_helper.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/LizardByte/Themerr-plex/blob/master/Contents/Code/lizardbyte_db_helper.py 2 | 3 | .. include:: ../global.rst 4 | 5 | :modname:`lizardbyte_db_helper` 6 | ------------------------------- 7 | .. automodule:: Code.lizardbyte_db_helper 8 | :members: 9 | :show-inheritance: 10 | -------------------------------------------------------------------------------- /docs/source/code_docs/main.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/LizardByte/Themerr-plex/blob/master/Contents/Code/__init__.py 2 | 3 | .. include:: ../global.rst 4 | 5 | :modname:`__init__` 6 | ------------------------ 7 | .. automodule:: Code 8 | :members: 9 | :show-inheritance: 10 | -------------------------------------------------------------------------------- /docs/source/code_docs/migration_helper.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/LizardByte/Themerr-plex/blob/master/Contents/Code/migration_helper.py 2 | 3 | .. include:: ../global.rst 4 | 5 | :modname:`migration_helper` 6 | --------------------------- 7 | .. automodule:: Code.migration_helper 8 | :members: 9 | :inherited-members: 10 | :private-members: 11 | :show-inheritance: 12 | :undoc-members: 13 | -------------------------------------------------------------------------------- /docs/source/code_docs/plex_api_helper.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/LizardByte/Themerr-plex/blob/master/Contents/Code/plex_api_helper.py 2 | 3 | .. include:: ../global.rst 4 | 5 | :modname:`plex_api_helper` 6 | -------------------------- 7 | .. automodule:: Code.plex_api_helper 8 | :members: 9 | :show-inheritance: 10 | -------------------------------------------------------------------------------- /docs/source/code_docs/scheduled_tasks.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/LizardByte/Themerr-plex/blob/master/Contents/Code/scheduled_tasks.py 2 | 3 | .. include:: ../global.rst 4 | 5 | :modname:`scheduled_tasks` 6 | ---------------------------- 7 | .. automodule:: Code.scheduled_tasks 8 | :members: 9 | :show-inheritance: 10 | -------------------------------------------------------------------------------- /docs/source/code_docs/themerr_db_helper.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/LizardByte/Themerr-plex/blob/master/Contents/Code/themerr_db_helper.py 2 | 3 | .. include:: ../global.rst 4 | 5 | :modname:`themerr_db_helper` 6 | ---------------------------- 7 | .. automodule:: Code.themerr_db_helper 8 | :members: 9 | :show-inheritance: 10 | -------------------------------------------------------------------------------- /docs/source/code_docs/tmdb_helper.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/LizardByte/Themerr-plex/blob/master/Contents/Code/tmdb_helper.py 2 | 3 | .. include:: ../global.rst 4 | 5 | :modname:`tmdb_helper` 6 | ---------------------------- 7 | .. automodule:: Code.tmdb_helper 8 | :members: 9 | :show-inheritance: 10 | -------------------------------------------------------------------------------- /docs/source/code_docs/webapp.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/LizardByte/Themerr-plex/blob/master/Contents/Code/webapp.py 2 | 3 | .. include:: ../global.rst 4 | 5 | :modname:`webapp` 6 | ---------------------------- 7 | .. automodule:: Code.webapp 8 | :members: 9 | :show-inheritance: 10 | -------------------------------------------------------------------------------- /docs/source/code_docs/youtube_dl_helper.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/LizardByte/Themerr-plex/blob/master/Contents/Code/youtube_dl_helper.py 2 | 3 | .. include:: ../global.rst 4 | 5 | :modname:`youtube_dl_helper` 6 | ---------------------------- 7 | .. automodule:: Code.youtube_dl_helper 8 | :members: 9 | :show-inheritance: 10 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # standard imports 8 | from datetime import datetime 9 | import os 10 | import sys 11 | 12 | 13 | # -- Path setup -------------------------------------------------------------- 14 | 15 | # If extensions (or modules to document with autodoc) are in another directory, 16 | # add these directories to sys.path here. If the directory is relative to the 17 | # documentation root, use os.path.abspath to make it absolute, like shown here. 18 | 19 | script_dir = os.path.dirname(os.path.abspath(__file__)) # the directory of this file 20 | source_dir = os.path.dirname(script_dir) # the source folder directory 21 | root_dir = os.path.dirname(source_dir) # the root folder directory 22 | 23 | 24 | paths = [ 25 | os.path.join(root_dir, 'Contents', 'Libraries', 'Shared'), # location of plugin dependencies 26 | os.path.join(root_dir, 'Contents'), # location of "Code" module, aka the Plugin 27 | ] 28 | 29 | for directory in paths: 30 | sys.path.insert(0, directory) 31 | 32 | # -- Project information ----------------------------------------------------- 33 | project = 'Themerr-plex' 34 | project_copyright = '%s, %s' % (datetime.now().year, project) 35 | epub_copyright = project_copyright 36 | author = 'ReenigneArcher' 37 | 38 | # The full version, including alpha/beta/rc tags 39 | # https://docs.readthedocs.io/en/stable/reference/environment-variables.html#envvar-READTHEDOCS_VERSION 40 | version = os.getenv('READTHEDOCS_VERSION', 'dirty') 41 | 42 | 43 | # -- General configuration --------------------------------------------------- 44 | 45 | # Add any Sphinx extension module names here, as strings. They can be 46 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 47 | # ones. 48 | extensions = [ 49 | 'm2r2', # enable markdown files 50 | 'numpydoc', # this automatically loads `sphinx.ext.autosummary` as well 51 | 'sphinx.ext.autodoc', # autodocument modules 52 | 'sphinx.ext.autosectionlabel', 53 | 'sphinx.ext.intersphinx', # link to other projects' documentation 54 | 'sphinx.ext.todo', # enable to-do sections 55 | 'sphinx.ext.viewcode', # add links to view source code 56 | ] 57 | 58 | # Add any paths that contain templates here, relative to this directory. 59 | # templates_path = ['_templates'] 60 | 61 | # List of patterns, relative to source directory, that match files and 62 | # directories to ignore when looking for source files. 63 | # This pattern also affects html_static_path and html_extra_path. 64 | exclude_patterns = ['toc.rst'] 65 | 66 | # Extensions to include. 67 | source_suffix = ['.rst', '.md'] 68 | 69 | # Change default contents file 70 | master_doc = 'index' 71 | 72 | # -- Options for HTML output ------------------------------------------------- 73 | 74 | # images 75 | html_favicon = os.path.join(root_dir, 'Contents', 'Resources', 'web', 'images', 'favicon.ico') 76 | html_logo = os.path.join(root_dir, 'Contents', 'Resources', 'icon-default.png') 77 | 78 | # Add any paths that contain custom static files (such as style sheets) here, 79 | # relative to this directory. They are copied after the builtin static files, 80 | # so a file named "default.css" will overwrite the builtin "default.css". 81 | # html_static_path = ['_static'] 82 | 83 | # These paths are either relative to html_static_path 84 | # or fully qualified paths (eg. https://...) 85 | # html_css_files = [ 86 | # 'css/custom.css', 87 | # ] 88 | # html_js_files = [ 89 | # 'js/custom.js', 90 | # ] 91 | 92 | # The theme to use for HTML and HTML Help pages. See the documentation for 93 | # a list of builtin themes. 94 | html_theme = 'sphinx_rtd_theme' 95 | 96 | html_theme_options = { 97 | 'analytics_id': 'G-SSW90X5YZX', # Provided by Google in your dashboard 98 | 'analytics_anonymize_ip': False, 99 | 'logo_only': False, 100 | 'display_version': True, 101 | 'prev_next_buttons_location': 'bottom', 102 | 'style_external_links': True, 103 | 'vcs_pageview_mode': 'blob', 104 | 'style_nav_header_background': '#151515', 105 | # Toc options 106 | 'collapse_navigation': True, 107 | 'sticky_navigation': True, 108 | 'navigation_depth': 4, 109 | 'includehidden': True, 110 | 'titles_only': False, 111 | } 112 | 113 | # extension config options 114 | autodoc_preserve_defaults = True # Do not evaluate default arguments of functions 115 | autosectionlabel_prefix_document = True # Make sure the target is unique 116 | todo_include_todos = True 117 | 118 | # numpydoc config 119 | numpydoc_validation_checks = {'all', 'SA01'} # Report warnings for all checks *except* for SA01 120 | 121 | # disable epub mimetype warnings 122 | # https://github.com/readthedocs/readthedocs.org/blob/eadf6ac6dc6abc760a91e1cb147cc3c5f37d1ea8/docs/conf.py#L235-L236 123 | suppress_warnings = ["epub.unknown_project_files"] 124 | 125 | python_version = '{}.{}'.format(sys.version_info.major, sys.version_info.minor) 126 | 127 | intersphinx_mapping = { 128 | 'python': ('https://docs.python.org/{}/'.format(python_version), None), 129 | 'plexapi': ('https://docs.lizardbyte.dev/projects/python-plexapi-backport/en/latest/', None), 130 | } 131 | 132 | numpydoc_show_class_members = True 133 | numpydoc_show_inherited_class_members = False 134 | numpydoc_xref_param_type = True 135 | -------------------------------------------------------------------------------- /docs/source/contributing/build.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/LizardByte/Themerr-plex/blob/master/docs/source/contributing/build.rst 2 | 3 | Build 4 | ===== 5 | Compiling Themerr-plex is fairly simple; however it is recommended to use Python 2.7 since the Plex framework is using 6 | Python 2.7. 7 | 8 | Clone 9 | ----- 10 | Ensure `git `__ is installed and run the following: 11 | 12 | .. code-block:: bash 13 | 14 | git clone --recurse-submodules https://github.com/lizardbyte/themerr-plex.git themerr-plex.bundle 15 | cd ./themerr-plex.bundle 16 | 17 | Setup venv 18 | ---------- 19 | It is recommended to setup and activate a `venv`_. 20 | 21 | .. Apply Patches 22 | .. ------------- 23 | .. Patch YouTube-DL 24 | .. .. code-block:: bash 25 | .. 26 | .. pushd ./third-party/youtube-dl 27 | .. git apply -v ../../patches/youtube_dl-compat.patch 28 | .. popd 29 | 30 | Install Requirements 31 | -------------------- 32 | Install Requirements 33 | .. code-block:: bash 34 | 35 | python -m pip install --upgrade --target=./Contents/Libraries/Shared -r requirements.txt --no-warn-script-location 36 | 37 | Development Requirements 38 | .. code-block:: bash 39 | 40 | python -m pip install -r requirements-dev.txt 41 | 42 | Compile Translations 43 | -------------------- 44 | .. code-block:: bash 45 | 46 | python ./scripts/_locale.py --compile 47 | 48 | Build Plist 49 | ----------- 50 | .. code-block:: bash 51 | 52 | python ./scripts/build_plist.py 53 | 54 | npm dependencies 55 | ---------------- 56 | Install nodejs and npm. Downloads available `here `__. 57 | 58 | Install npm dependencies. 59 | .. code-block:: bash 60 | 61 | npm install 62 | 63 | Move modules directory. 64 | Linux/macOS 65 | .. code-block:: bash 66 | 67 | mv ./node_modules ./Contents/Resources/web 68 | 69 | Windows 70 | .. code-block:: batch 71 | 72 | move .\node_modules .\Contents\Resources\web 73 | 74 | Remote Build 75 | ------------ 76 | It may be beneficial to build remotely in some cases. This will enable easier building on different operating systems. 77 | 78 | #. Fork the project 79 | #. Activate workflows 80 | #. Trigger the `CI` workflow manually 81 | #. Download the artifacts from the workflow run summary 82 | 83 | .. _venv: https://docs.python.org/3/library/venv.html 84 | -------------------------------------------------------------------------------- /docs/source/contributing/contributing.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/LizardByte/Themerr-plex/blob/master/docs/source/contributing/contributing.rst 2 | 3 | Contributing 4 | ============ 5 | 6 | Read our contribution guide in our organization level 7 | `docs `__. 8 | -------------------------------------------------------------------------------- /docs/source/contributing/database.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/LizardByte/Themerr-plex/blob/master/docs/source/contributing/database.rst 2 | 3 | Database 4 | ======== 5 | 6 | The database of themes is held in our `ThemerrDB `__ repository. To contribute 7 | to the database, follow the documentation there. 8 | -------------------------------------------------------------------------------- /docs/source/contributing/testing.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/LizardByte/Themerr-plex/blob/master/docs/source/contributing/testing.rst 2 | 3 | Testing 4 | ======= 5 | 6 | Flake8 7 | ------ 8 | Themerr-plex uses `Flake8 `__ for enforcing consistent code styling. Flake8 is included 9 | in the ``requirements-dev.txt``. 10 | 11 | The config file for flake8 is ``.flake8``. This is already included in the root of the repo and should not be modified. 12 | 13 | Test with Flake8 14 | .. code-block:: bash 15 | 16 | python -m flake8 17 | 18 | Sphinx 19 | ------ 20 | Themerr-plex uses `Sphinx `__ for documentation building. Sphinx is included 21 | in the ``requirements-dev.txt``. 22 | 23 | Themerr-plex follows `numpydoc `__ styling and formatting in 24 | docstrings. This will be tested when building the docs. `numpydoc` is included in the ``requirements-dev.txt``. 25 | 26 | The config file for Sphinx is ``docs/source/conf.py``. This is already included in the root of the repo and should not 27 | be modified. 28 | 29 | Test with Sphinx 30 | .. code-block:: bash 31 | 32 | cd docs 33 | make html 34 | 35 | Alternatively 36 | 37 | .. code-block:: bash 38 | 39 | cd docs 40 | sphinx-build -b html source build 41 | 42 | Lint with rstcheck 43 | .. code-block:: bash 44 | 45 | rstcheck -r . 46 | 47 | pytest 48 | ------ 49 | Themerr-plex uses `pytest `__ for unit testing. pytest is included in the 50 | ``requirements-dev.txt``. 51 | 52 | No config is required for pytest. 53 | 54 | .. attention:: 55 | A locally installed Plex server is required to run some of the tests. The server must be running locally so that the 56 | plugin logs can be parsed for exceptions. It is not recommended to run the tests against a production server. 57 | 58 | A script is provided that allows you to prepare the Plex server for testing. Use the help argument to see the options. 59 | 60 | Bootstrap the Plex server for testing 61 | .. code-block:: bash 62 | 63 | python scripts/plex-bootstraptest.py --help 64 | 65 | Test with pytest 66 | .. code-block:: bash 67 | 68 | python -m pytest 69 | 70 | .. tip:: 71 | Due to the complexity of setting up the environment for testing, it is recommended to run the tests in GitHub 72 | Actions. This will ensure that the tests are run in a clean environment and will not be affected by any local 73 | changes. 74 | -------------------------------------------------------------------------------- /docs/source/global.rst: -------------------------------------------------------------------------------- 1 | .. role:: modname 2 | :class: modname 3 | 4 | .. role:: title 5 | :class: title 6 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/LizardByte/Themerr-plex/blob/master/docs/source/index.rst 2 | 3 | Table of Contents 4 | ================= 5 | .. include:: toc.rst 6 | -------------------------------------------------------------------------------- /docs/source/toc.rst: -------------------------------------------------------------------------------- 1 | .. toctree:: 2 | :maxdepth: 2 3 | :caption: About 4 | 5 | about/overview 6 | about/installation 7 | about/docker 8 | about/usage 9 | about/troubleshooting 10 | about/changelog 11 | 12 | .. toctree:: 13 | :maxdepth: 2 14 | :caption: Contributing 15 | 16 | contributing/contributing 17 | contributing/database 18 | contributing/build 19 | contributing/testing 20 | 21 | .. toctree:: 22 | :maxdepth: 0 23 | :caption: Plugin Code 24 | :titlesonly: 25 | 26 | code_docs/main 27 | code_docs/general_helper 28 | code_docs/lizardbyte_db_helper 29 | code_docs/migration_helper 30 | code_docs/plex_api_helper 31 | code_docs/scheduled_tasks 32 | code_docs/themerr_db_helper 33 | code_docs/tmdb_helper 34 | code_docs/webapp 35 | code_docs/youtube_dl_helper 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@fontsource/open-sans": "5.1.0", 4 | "@fortawesome/fontawesome-free": "6.6.0", 5 | "bootstrap": "5.3.3", 6 | "jquery": "3.7.1" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /requirements-build.txt: -------------------------------------------------------------------------------- 1 | Babel==2.9.1 2 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | # development environment requirements, these should not be distributed 2 | flake8==3.9.2;python_version<"3" 3 | m2r2==0.3.2;python_version<"3" 4 | numpydoc==0.9.2;python_version<"3" 5 | plexhints==2024.809.14117 # type hinting library for plex development 6 | plexapi-backport[alert]==4.15.10 7 | pytest==4.6.11;python_version<"3" 8 | pytest-cov==2.12.1;python_version<"3" 9 | rstcheck==3.5.0;python_version<"3" 10 | Sphinx==1.8.6;python_version<"3" 11 | sphinx-rtd-theme==1.2.0;python_version<"3" 12 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # these requirements must support python 2.7 2 | # it is doubtful that Plex will ever update to Python 3+ 3 | flask==1.1.4;python_version<"3" 4 | flask-babel==1.0.0;python_version<"3" 5 | future==0.18.3 6 | plexapi-backport[alert]==4.15.10 # custom python-plexapi supporting python 2.7 7 | polib==1.2.0;python_version<"3" 8 | requests==2.27.1;python_version<"3" # 2.27 is last version supporting Python 2.7 9 | schedule==0.6.0;python_version<"3" 10 | six==1.16.0;python_version<"3" 11 | typing==3.10.0.0 12 | werkzeug==1.0.1;python_version<"3" 13 | 14 | # youtube_dl is not capable or willing to create a new release so have to install from git 15 | # youtube_dl==2021.12.17 16 | ./third-party/youtube-dl 17 | 18 | # required for websocket to pass tests 19 | pysocks==1.7.1;python_version<"3" 20 | win-inet-pton==1.1.0;python_version<"3" and platform_system=="Windows" 21 | -------------------------------------------------------------------------------- /scripts/_locale.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | .. 4 | _locale.py 5 | 6 | Functions related to building, initializing, updating, and compiling localization translations. 7 | """ 8 | # standard imports 9 | import argparse 10 | import os 11 | import subprocess 12 | 13 | project_name = 'Themerr-plex' 14 | 15 | script_dir = os.path.dirname(os.path.abspath(__file__)) 16 | root_dir = os.path.dirname(script_dir) 17 | locale_dir = os.path.join(root_dir, 'Contents', 'Strings') 18 | 19 | # target locales 20 | target_locales = [ 21 | 'de', # German 22 | 'en', # English 23 | 'en_GB', # English (United Kingdom) 24 | 'en_US', # English (United States) 25 | 'es', # Spanish 26 | 'fr', # French 27 | 'it', # Italian 28 | 'ja', # Japanese 29 | 'pt', # Portuguese 30 | 'ru', # Russian 31 | 'sv', # Swedish 32 | 'tr', # Turkish 33 | 'zh', # Chinese Simplified 34 | ] 35 | 36 | 37 | def babel_extract(): 38 | """Executes `pybabel extract` in subprocess.""" 39 | commands = [ 40 | 'pybabel', 41 | 'extract', 42 | '-F', os.path.join(script_dir, 'babel.cfg'), 43 | '-o', os.path.join(locale_dir, '%s.po' % project_name.lower()), 44 | '--sort-by-file', 45 | '--msgid-bugs-address=github.com/%s' % project_name.lower(), 46 | '--copyright-holder=%s' % project_name, 47 | '--project=%s' % project_name, 48 | '--version=v0', 49 | '--add-comments=NOTE', 50 | './Contents/Resources/web' 51 | ] 52 | 53 | print(commands) 54 | subprocess.check_output(args=commands, cwd=root_dir) 55 | 56 | 57 | def babel_init(locale_code): 58 | # type: (str) -> None 59 | """Executes `pybabel init` in subprocess. 60 | 61 | :param locale_code: str - locale code 62 | """ 63 | commands = [ 64 | 'pybabel', 65 | 'init', 66 | '-i', os.path.join(locale_dir, '%s.po' % project_name.lower()), 67 | '-d', locale_dir, 68 | '-D', project_name.lower(), 69 | '-l', locale_code 70 | ] 71 | 72 | print(commands) 73 | subprocess.check_output(args=commands, cwd=root_dir) 74 | 75 | 76 | def babel_update(): 77 | """Executes `pybabel update` in subprocess.""" 78 | commands = [ 79 | 'pybabel', 80 | 'update', 81 | '-i', os.path.join(locale_dir, '%s.po' % project_name.lower()), 82 | '-d', locale_dir, 83 | '-D', project_name.lower(), 84 | '--update-header-comment' 85 | ] 86 | 87 | print(commands) 88 | subprocess.check_output(args=commands, cwd=root_dir) 89 | 90 | 91 | def babel_compile(): 92 | """Executes `pybabel compile` in subprocess.""" 93 | commands = [ 94 | 'pybabel', 95 | 'compile', 96 | '-d', locale_dir, 97 | '-D', project_name.lower() 98 | ] 99 | 100 | print(commands) 101 | subprocess.check_output(args=commands, cwd=root_dir) 102 | 103 | 104 | if __name__ == '__main__': 105 | # Set up and gather command line arguments 106 | parser = argparse.ArgumentParser( 107 | description='Script helps update locale translations. Translations must be done manually.') 108 | 109 | parser.add_argument('--extract', action='store_true', help='Extract messages from python files and templates.') 110 | parser.add_argument('--init', action='store_true', help='Initialize any new locales specified in target locales.') 111 | parser.add_argument('--update', action='store_true', help='Update existing locales.') 112 | parser.add_argument('--compile', action='store_true', help='Compile translated locales.') 113 | 114 | args = parser.parse_args() 115 | 116 | if args.extract: 117 | babel_extract() 118 | 119 | if args.init: 120 | for locale_id in target_locales: 121 | if not os.path.isdir(os.path.join(locale_dir, locale_id)): 122 | babel_init(locale_code=locale_id) 123 | 124 | if args.update: 125 | babel_update() 126 | 127 | if args.compile: 128 | babel_compile() 129 | -------------------------------------------------------------------------------- /scripts/babel.cfg: -------------------------------------------------------------------------------- 1 | [python: **.py] 2 | [jinja2: **/templates/**.html] 3 | -------------------------------------------------------------------------------- /scripts/build_plist.py: -------------------------------------------------------------------------------- 1 | import os 2 | import plistlib 3 | 4 | version = os.getenv('BUILD_VERSION', None) 5 | print('version: %s' % version) 6 | 7 | commit = os.getenv('GITHUB_SHA', 'development build') 8 | print('commit: %s' % commit) 9 | 10 | if not version: 11 | checked = '' 12 | if commit != 'development build': 13 | version = commit[0:7] 14 | print('using commit as version: %s' % version) 15 | else: 16 | version = commit 17 | print('unknown version: %s' % version) 18 | else: 19 | checked = '' 20 | 21 | info_file = os.path.join('Contents', 'Info.plist') 22 | 23 | pl = dict( 24 | CFBundleIdentifier='dev.lizardbyte.themerr-plex', 25 | PlexAgentAttributionText=""" 26 | 28 | 31 | 36 | 37 |
38 | Themerr-plex
39 |
40 |
41 | A plugin by LizardByte that adds theme songs to 42 | movies. 43 |
44 |
45 | 46 | 47 | 48 | 49 | 51 | 52 |
Version: %s%s| Releases
53 |
54 | 55 | 56 | 57 | 59 | 60 |
Reference:| Docs
61 | ]]> 62 | """ % (checked, version), 63 | CFBundleDevelopmentRegion='English', 64 | CFBundleExecutable='', 65 | CFBundlePackageType='AAPL', 66 | CFBundleSignature='hook', 67 | PlexFrameworkVersion='2', 68 | PlexClientPlatforms='', 69 | PlexClientPlatformExclusions='', 70 | PlexPluginClass='Resource', 71 | PlexPluginCodePolicy='Elevated', 72 | PlexPluginConsoleLogging='0', 73 | PlexPluginDebug='1', 74 | PlexPluginMode='Daemon', 75 | PlexPluginRegions=[''], 76 | PlexBundleVersion=version, 77 | PlexShortBundleVersion=version, 78 | ) 79 | 80 | # PlexPluginMode: 81 | # This one does nothing with a value of "Always On", a value of "daemon" keeps the plugin alive in the background. 82 | 83 | # PlexClientPlatforms and PlexClientPlatformExclusions: 84 | # Any Clients support or not supported by the plugin. 85 | # Possible values are * for all platforms, MacOSX, Windows, Linux, Roku, Android, iOS, Safari, Firefox, Chrome, LGTV, \ 86 | # Samsung, PlexConnect and Plex Home Theater 87 | 88 | # PlexPluginRegions: 89 | # Possible string values are the proper ISO two-letter code for the country. 90 | # A full list of values are available at http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 91 | 92 | # PlexPluginDebug: 93 | # Possible values are 0 and 1. Setting it to "1" rather than "0" turns on debug logging 94 | 95 | # PlexPluginCodePolicy: 96 | # This allows channels to access some python methods which are otherwise blocked, as well as import external code \ 97 | # libraries, and interact with the PMS HTTP API 98 | 99 | # PlexPluginClass: 100 | # This key is used to show that the plugin is an agent. possible values are 'Agent' and 'Resource' 101 | 102 | # PlexPluginConsoleLogging: 103 | # This is used to send plugin log statements directly to stout when running PMS from the command line. \ 104 | # Rarely used anymore 105 | 106 | plist_string = plistlib.writePlistToString(pl).replace('<', '<').replace('>', '>') 107 | 108 | with open(info_file, 'wb') as fp: 109 | fp.write(plist_string) 110 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Themerr-plex/6580224a7f39c2c943993585853e2f0547dd30a8/tests/__init__.py -------------------------------------------------------------------------------- /tests/ci/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Themerr-plex/6580224a7f39c2c943993585853e2f0547dd30a8/tests/ci/__init__.py -------------------------------------------------------------------------------- /tests/ci/test_docs.py: -------------------------------------------------------------------------------- 1 | # standard imports 2 | import os 3 | import platform 4 | import shutil 5 | import subprocess 6 | 7 | # lib imports 8 | import pytest 9 | 10 | 11 | doc_matrix = [ 12 | ('html', os.path.join('html', 'index.html')), 13 | ('epub', os.path.join('epub', 'Themerr-plex.epub')), 14 | ] 15 | 16 | 17 | @pytest.mark.parametrize('doc_type, file_name', doc_matrix) 18 | def test_make_docs(doc_type, file_name): 19 | """Test building sphinx docs""" 20 | # remove existing build directory 21 | build_dir = os.path.join(os.getcwd(), 'docs', 'build') 22 | if os.path.isdir(build_dir): 23 | shutil.rmtree(path=build_dir) 24 | 25 | print('Building {} docs'.format(doc_type)) 26 | result = subprocess.check_call( 27 | args=['make', doc_type], 28 | cwd=os.path.join(os.getcwd(), 'docs'), 29 | shell=True if platform.system() == 'Windows' else False, 30 | ) 31 | assert result == 0, 'Failed to build {} docs'.format(doc_type) 32 | 33 | # ensure docs built 34 | assert os.path.isfile(os.path.join(build_dir, file_name)), '{} docs not built'.format(doc_type) 35 | 36 | 37 | @pytest.mark.parametrize('doc_type, file_name', doc_matrix) 38 | def test_dummy_file(doc_type, file_name): 39 | """Test building sphinx docs with known warnings""" 40 | # create a dummy rst file 41 | dummy_file = os.path.join(os.getcwd(), 'docs', 'source', 'dummy.rst') 42 | 43 | # write test to dummy file, creating the file if it doesn't exist 44 | with open(dummy_file, 'w+') as f: 45 | f.write('Dummy file\n') 46 | f.write('==========\n') 47 | 48 | # ensure CalledProcessError is raised 49 | with pytest.raises(subprocess.CalledProcessError): 50 | test_make_docs(doc_type=doc_type, file_name=file_name) 51 | 52 | # remove the dummy rst file 53 | os.remove(dummy_file) 54 | 55 | 56 | def test_rstcheck(): 57 | """Test rstcheck""" 58 | # get list of all the rst files in the project (skip venv and Contents/Libraries) 59 | rst_files = [] 60 | for root, dirs, files in os.walk(os.getcwd()): 61 | for f in files: 62 | if f.lower().endswith('.rst') and 'venv' not in root and 'Contents/Libraries' not in root: 63 | rst_files.append(os.path.join(root, f)) 64 | 65 | assert rst_files, 'No rst files found' 66 | 67 | # run rstcheck on all the rst files 68 | for rst_file in rst_files: 69 | print('Checking {}'.format(rst_file)) 70 | result = subprocess.check_call(['rstcheck', rst_file]) 71 | assert result == 0, 'rstcheck failed on {}'.format(rst_file) 72 | -------------------------------------------------------------------------------- /tests/data/video_stub.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Themerr-plex/6580224a7f39c2c943993585853e2f0547dd30a8/tests/data/video_stub.mp4 -------------------------------------------------------------------------------- /tests/functional/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Themerr-plex/6580224a7f39c2c943993585853e2f0547dd30a8/tests/functional/__init__.py -------------------------------------------------------------------------------- /tests/functional/test_plex_plugin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # standard imports 4 | import os 5 | 6 | 7 | def _check_themes(items): 8 | # ensure all items have themes 9 | for item in items: 10 | print(item.title) 11 | assert item.theme, "No theme found for {}".format(item.title) 12 | 13 | 14 | def test_plugin_logs(plugin_logs): 15 | print('plugin_logs: {}'.format(plugin_logs)) 16 | assert plugin_logs, "No plugin logs found" 17 | 18 | 19 | def test_plugin_log_file(plugin_log_file): 20 | assert os.path.isfile(plugin_log_file), "Plugin log file not found" 21 | 22 | 23 | def test_plugin_log_file_exceptions(plugin_log_file): 24 | # get all the lines in the plugin log file 25 | with open(plugin_log_file, 'r') as f: 26 | lines = f.readlines() 27 | 28 | critical_exceptions = [] 29 | for line in lines: 30 | if ') : CRITICAL (' in line: 31 | critical_exceptions.append(line) 32 | 33 | assert len(critical_exceptions) <= 1, "Too many exceptions logged to plugin log file" 34 | 35 | for exception in critical_exceptions: 36 | # every plugin will have this exception 37 | assert exception.endswith('Exception getting hosted resource hashes (most recent call last):\n'), ( 38 | "Unexpected exception: {}".format(exception)) 39 | 40 | 41 | def test_items(section): 42 | items = section.all() 43 | _check_themes(items=items) 44 | -------------------------------------------------------------------------------- /tests/functional/test_webapp.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # standard imports 4 | import os 5 | 6 | # lib imports 7 | import pytest 8 | 9 | # local imports 10 | from Code import webapp 11 | 12 | 13 | @pytest.fixture(scope='function') 14 | def remove_themerr_db_cache_file(): 15 | _backup_file_name = "{}.bak".format(webapp.database_cache_file) 16 | 17 | # rename the file, so it is not found 18 | os.rename(webapp.database_cache_file, _backup_file_name) 19 | yield 20 | 21 | # rename the file back 22 | os.rename(_backup_file_name, webapp.database_cache_file) 23 | 24 | 25 | def test_home(test_client): 26 | """ 27 | WHEN the '/' page is requested (GET) 28 | THEN check that the response is valid 29 | 30 | Repeat for '/home' 31 | """ 32 | try: 33 | response = test_client.get('/') 34 | except AttributeError: 35 | pytest.skip("cannot access Plex token/server") 36 | else: 37 | assert response.status_code == 200 38 | 39 | response = test_client.get('/home') 40 | assert response.status_code == 200 41 | 42 | assert 'id="section_' in response.data.decode('utf-8') 43 | 44 | 45 | def test_home_without_cache(remove_themerr_db_cache_file, test_client): 46 | """ 47 | WHEN the '/' page is requested (GET) 48 | THEN check that the response is valid 49 | """ 50 | try: 51 | response = test_client.get('/') 52 | except AttributeError: 53 | pytest.skip("cannot access Plex token/server") 54 | else: 55 | assert response.status_code == 200 56 | 57 | assert 'Database is being cached' in response.data.decode('utf-8') 58 | 59 | 60 | def test_image(test_client): 61 | """ 62 | WHEN the '/favicon.ico' file is requested (GET) 63 | THEN check that the response is valid 64 | THEN check the content type is 'image/vnd.microsoft.icon' 65 | """ 66 | response = test_client.get('favicon.ico') 67 | assert response.status_code == 200 68 | assert response.content_type == 'image/vnd.microsoft.icon' 69 | 70 | 71 | def test_status(test_client): 72 | """ 73 | WHEN the '/status' page is requested (GET) 74 | THEN check that the response is valid 75 | """ 76 | response = test_client.get('/status') 77 | assert response.status_code == 200 78 | assert response.content_type == 'application/json' 79 | -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LizardByte/Themerr-plex/6580224a7f39c2c943993585853e2f0547dd30a8/tests/unit/__init__.py -------------------------------------------------------------------------------- /tests/unit/test_lizardbyte_db_helper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # lib imports 4 | import pytest 5 | 6 | # local imports 7 | from Code import lizardbyte_db_helper 8 | 9 | 10 | @pytest.mark.parametrize('search_query, collection_type, expected_type, expected_id', [ 11 | ('James Bond', 'game_collections', 'game_collections', 326), 12 | ('James Bond', 'game_franchises', 'game_franchises', 37), 13 | ('James Bond', None, 'game_collections', 326), 14 | ]) 15 | def test_get_igdb_id_from_collection(search_query, collection_type, expected_type, expected_id): 16 | igdb_id = lizardbyte_db_helper.get_igdb_id_from_collection( 17 | search_query=search_query, 18 | collection_type=collection_type 19 | ) 20 | assert igdb_id == (expected_id, expected_type) 21 | 22 | 23 | def test_get_igdb_id_from_collection_invalid(): 24 | test = lizardbyte_db_helper.get_igdb_id_from_collection(search_query='Not a real collection') 25 | assert test is None 26 | 27 | invalid_collection_type = lizardbyte_db_helper.get_igdb_id_from_collection( 28 | search_query='James Bond', 29 | collection_type='invalid', 30 | ) 31 | assert invalid_collection_type is None 32 | -------------------------------------------------------------------------------- /tests/unit/test_migration_helper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # standard imports 4 | import os 5 | 6 | # lib imports 7 | import pytest 8 | 9 | # local imports 10 | from Code import migration_helper 11 | from Code import plex_api_helper 12 | 13 | migration_helper_object = migration_helper.MigrationHelper() 14 | 15 | 16 | @pytest.fixture(scope='function') 17 | def migration_helper_fixture(): 18 | return migration_helper_object 19 | 20 | 21 | @pytest.fixture(scope='function') 22 | def migration_status_file(migration_helper_fixture): 23 | migration_status_file = migration_helper_fixture.migration_status_file 24 | 25 | # delete the migration status file if it exists 26 | if os.path.isfile(migration_status_file): 27 | os.remove(migration_status_file) 28 | 29 | # yield the migration status file 30 | yield 31 | 32 | # delete the migration status file if it exists 33 | if os.path.isfile(migration_status_file): 34 | os.remove(migration_status_file) 35 | 36 | 37 | @pytest.mark.parametrize('key, raise_exception, expected_return, expected_raise', [ 38 | (migration_helper_object.LOCKED_THEMES, False, True, None), 39 | (migration_helper_object.LOCKED_THEMES, True, True, None), 40 | (migration_helper_object.LOCKED_COLLECTION_FIELDS, False, True, None), 41 | (migration_helper_object.LOCKED_COLLECTION_FIELDS, True, True, None), 42 | ('invalid', False, False, None), 43 | ('invalid', True, False, AttributeError), 44 | ]) 45 | def test_validate_migration_key(migration_helper_fixture, key, raise_exception, expected_return, expected_raise): 46 | if expected_raise is not None: 47 | with pytest.raises(expected_raise): 48 | migration_helper_fixture._validate_migration_key(key=key, raise_exception=raise_exception) 49 | else: 50 | validated = migration_helper_fixture._validate_migration_key(key=key, raise_exception=raise_exception) 51 | assert validated == expected_return, 'Expected {} but got {}'.format(expected_return, validated) 52 | 53 | 54 | @pytest.mark.parametrize('key, expected', [ 55 | (migration_helper_object.LOCKED_THEMES, None), 56 | (migration_helper_object.LOCKED_COLLECTION_FIELDS, None), 57 | pytest.param('invalid', None, marks=pytest.mark.xfail(raises=AttributeError)), 58 | ]) 59 | def test_get_migration_status(migration_helper_fixture, migration_status_file, key, expected): 60 | migration_status = migration_helper_fixture.get_migration_status(key=key) 61 | assert migration_status == expected, 'Expected {} but got {}'.format(expected, migration_status) 62 | 63 | 64 | @pytest.mark.parametrize('key', [ 65 | migration_helper_object.LOCKED_THEMES, 66 | migration_helper_object.LOCKED_COLLECTION_FIELDS, 67 | pytest.param('invalid', marks=pytest.mark.xfail(raises=AttributeError)), 68 | ]) 69 | def test_set_migration_status(migration_helper_fixture, migration_status_file, key): 70 | # perform the test twice, to load an existing migration file 71 | for _ in range(2): 72 | migration_helper_fixture.set_migration_status(key=key) 73 | migration_status = migration_helper_fixture.get_migration_status(key=key) 74 | assert migration_status is True, 'Migration status was not set to True' 75 | 76 | 77 | @pytest.mark.parametrize('key', [ 78 | migration_helper_object.LOCKED_THEMES, 79 | migration_helper_object.LOCKED_COLLECTION_FIELDS, 80 | ]) 81 | def test_perform_migration(migration_helper_fixture, migration_status_file, key): 82 | # perform the migration twice, should return early on the second run 83 | for _ in range(2): 84 | migration_helper_fixture.perform_migration(key=key) 85 | migration_status = migration_helper_fixture.get_migration_status(key=key) 86 | assert migration_status is True, 'Migration status was not set to True' 87 | 88 | 89 | def test_migrate_locked_themes(section): 90 | field = 'theme' 91 | 92 | # lock all is not working 93 | # section.lockAllField(field=field, libtype='movie') 94 | # section.reload() 95 | 96 | for item in section.all(): 97 | plex_api_helper.change_lock_status(item=item, field=field, lock=True) 98 | assert item.isLocked(field=field) is True, '{} for movie is not locked'.format(field) 99 | 100 | migration_helper_object.migrate_locked_themes() 101 | section.reload() 102 | 103 | for item in section.all(): 104 | assert item.isLocked(field=field) is False, '{} for movie is still locked'.format(field) 105 | 106 | 107 | @pytest.mark.parametrize('field', [ 108 | 'art', 109 | 'summary', 110 | 'thumb', 111 | ]) 112 | def test_migrate_locked_collection_fields(field, section): 113 | # lock all is not working, so lock manually 114 | for item in section.collections(): 115 | plex_api_helper.change_lock_status(item=item, field=field, lock=True) 116 | assert item.isLocked(field=field) is True, '{} for collection is not locked'.format(field) 117 | 118 | migration_helper_object.migrate_locked_collection_fields() 119 | section.reload() 120 | 121 | for item in section.collections(): 122 | assert item.isLocked(field=field) is False, '{} for collection is still locked'.format(field) 123 | -------------------------------------------------------------------------------- /tests/unit/test_plex_api_helper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # lib imports 4 | import pytest 5 | 6 | # local imports 7 | from Code import plex_api_helper 8 | 9 | 10 | def test_all_themes_unlocked(section): 11 | field = 'theme' 12 | for item in section.all(): 13 | assert not item.isLocked(field=field) 14 | 15 | 16 | @pytest.mark.parametrize('lock', [ 17 | False, # verify the function pre-checks are working 18 | True, # verify changing the lock status to True works 19 | False, # verify changing the lock status to False works 20 | ]) 21 | def test_change_lock_status(section, lock): 22 | field = 'theme' 23 | for item in section.all(): 24 | change_status = plex_api_helper.change_lock_status(item, field=field, lock=lock) 25 | assert change_status, 'change_lock_status did not return True' 26 | assert item.isLocked(field=field) == lock, 'Failed to change lock status to {}'.format(lock) 27 | -------------------------------------------------------------------------------- /tests/unit/test_scheduled_tasks.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # standard imports 4 | import time 5 | 6 | # local imports 7 | from Code import scheduled_tasks 8 | 9 | 10 | def test_run_threaded(): 11 | def hello_world(): 12 | time.sleep(10) 13 | return 'Hello, world!' 14 | 15 | test_thread = scheduled_tasks.run_threaded(target=hello_world, daemon=True) 16 | assert test_thread.is_alive() 17 | 18 | test_thread.join() 19 | assert not test_thread.is_alive() 20 | 21 | 22 | def test_schedule_loop(): 23 | test_thread = scheduled_tasks.run_threaded(target=scheduled_tasks.schedule_loop, daemon=True) 24 | assert test_thread.is_alive() 25 | 26 | 27 | def test_setup_scheduling(): 28 | scheduled_tasks.setup_scheduling() 29 | assert scheduled_tasks.schedule.jobs 30 | -------------------------------------------------------------------------------- /tests/unit/test_themerr_db_helper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # local imports 4 | from Code import plex_api_helper 5 | from Code import themerr_db_helper 6 | 7 | 8 | def test_update_cache(empty_themerr_db_cache): 9 | themerr_db_helper.update_cache() 10 | assert themerr_db_helper.last_cache_update > 0, 'Cache update did not complete' 11 | 12 | assert "movies" in themerr_db_helper.database_cache, 'Cache does not contain movies' 13 | assert "movie_collections" in themerr_db_helper.database_cache, 'Cache does not contain movie_collections' 14 | assert "games" in themerr_db_helper.database_cache, 'Cache does not contain games' 15 | assert "game_collections" in themerr_db_helper.database_cache, 'Cache does not contain game_collections' 16 | assert "game_franchises" in themerr_db_helper.database_cache, 'Cache does not contain game_franchises' 17 | assert "tv_shows" in themerr_db_helper.database_cache, 'Cache does not contain tv_shows' 18 | 19 | 20 | def test_item_exists(empty_themerr_db_cache, section): 21 | for item in section.all(): 22 | database_info = plex_api_helper.get_database_info(item=item) 23 | 24 | database_type = database_info[0] 25 | database = database_info[1] 26 | database_id = database_info[3] 27 | 28 | assert themerr_db_helper.item_exists(database_type=database_type, database=database, id=database_id), \ 29 | '{} {} {} does not exist in ThemerrDB'.format(database, database_type, database_id) 30 | 31 | 32 | def test_item_exists_with_invalid_database(): 33 | # movie is not valid... the correct type is movies 34 | assert not themerr_db_helper.item_exists(database_type='movie', database='invalid', id='invalid'), \ 35 | 'Invalid database should not exist in ThemerrDB' 36 | -------------------------------------------------------------------------------- /tests/unit/test_tmdb_helper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # lib imports 4 | import plexhints 5 | import pytest 6 | 7 | # local imports 8 | from Code import tmdb_helper 9 | 10 | 11 | @pytest.mark.parametrize('tmdb_test_id, database, item_type', [ 12 | ('tt1254207', 'imdb', 'movie'), 13 | ('268592', 'tvdb', 'tv'), 14 | ]) 15 | def test_get_tmdb_id_from_external_id(tmdb_test_id, database, item_type): 16 | print(plexhints.CONTENTS) 17 | print(plexhints.ELEVATED_POLICY) 18 | 19 | tmdb_id = tmdb_helper.get_tmdb_id_from_external_id(external_id=tmdb_test_id, database=database, item_type=item_type) 20 | assert tmdb_id, "No tmdb_id found for {}".format(tmdb_test_id) 21 | assert isinstance(tmdb_id, int), "tmdb_id is not an int: {}".format(tmdb_id) 22 | 23 | 24 | @pytest.mark.parametrize('tmdb_test_id, database, item_type', [ 25 | ('invalid', 'imdb', 'movie'), 26 | ('tt1254207', 'invalid', 'movie'), 27 | ('invalid', 'imdb', 'game'), 28 | ]) 29 | def test_get_tmdb_id_from_external_id_invalid(tmdb_test_id, database, item_type): 30 | test = tmdb_helper.get_tmdb_id_from_external_id(external_id=tmdb_test_id, database=database, item_type=item_type) 31 | assert test is None, "tmdb_id found for invalid imdb_id: {}".format(test) 32 | 33 | 34 | @pytest.mark.parametrize('tmdb_test_collection', [ 35 | 'James Bond', 36 | 'James Bond Collection', 37 | ]) 38 | def test_get_tmdb_id_from_collection(tmdb_test_collection): 39 | tmdb_id = tmdb_helper.get_tmdb_id_from_collection(search_query=tmdb_test_collection) 40 | assert tmdb_id, "No tmdb_id found for {}".format(tmdb_test_collection) 41 | assert isinstance(tmdb_id, int), "tmdb_id is not an int: {}".format(tmdb_id) 42 | 43 | 44 | def test_get_tmdb_id_from_collection_invalid(): 45 | test = tmdb_helper.get_tmdb_id_from_collection(search_query='Not a real collection') 46 | assert test is None, "tmdb_id found for invalid collection: {}".format(test) 47 | -------------------------------------------------------------------------------- /tests/unit/test_webapp.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # standard imports 4 | import json 5 | import os 6 | 7 | # local imports 8 | from Code import webapp 9 | 10 | 11 | def test_cache_data(): 12 | webapp.cache_data() 13 | assert os.path.isfile(webapp.database_cache_file), "Database cache file not found" 14 | 15 | with open(webapp.database_cache_file, 'r') as f: 16 | data = json.load(f) 17 | 18 | assert data, "Database cache file is empty" 19 | -------------------------------------------------------------------------------- /tests/unit/test_youtube_dl_helper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # lib imports 4 | import pytest 5 | 6 | # local imports 7 | from Code import youtube_dl_helper 8 | 9 | 10 | @pytest.mark.parametrize('url', [ 11 | 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', 12 | 'https://www.youtube.com/watch?v=Wb8j8Ojd4YQ&list=PLMYr5_xSeuXAbhxYHz86hA1eCDugoxXY0&pp=iAQB', # playlist test 13 | ]) 14 | def test_process_youtube(url): 15 | # test valid urls 16 | audio_url = youtube_dl_helper.process_youtube(url=url) 17 | assert audio_url is not None 18 | assert audio_url.startswith('https://') 19 | 20 | 21 | @pytest.mark.parametrize('url', [ 22 | 'https://www.youtube.com/watch?v=notavideoid', 23 | 'https://blahblahblah', 24 | ]) 25 | def test_process_youtube_invalid(url): 26 | # test invalid urls 27 | audio_url = youtube_dl_helper.process_youtube(url=url) 28 | assert audio_url is None 29 | --------------------------------------------------------------------------------