mirror of
https://github.com/meisnate12/Plex-Meta-Manager
synced 2024-11-24 21:43:07 +00:00
[5] Switched from retrying to tenacity for http request retries (#2105)
This commit is contained in:
parent
dcf0435a96
commit
1ff0f8492f
17 changed files with 138 additions and 81 deletions
13
.github/workflows/increment-build.yml
vendored
13
.github/workflows/increment-build.yml
vendored
|
@ -15,6 +15,7 @@ jobs:
|
|||
commit-msg: ${{ steps.update-version.outputs.commit-msg }}
|
||||
commit-hash: ${{ steps.update-version.outputs.commit-hash }}
|
||||
commit-short: ${{ steps.update-version.outputs.commit-short }}
|
||||
pr-tag: ${{ steps.update-version.outputs.pr-tag }}
|
||||
steps:
|
||||
|
||||
- name: Create App Token
|
||||
|
@ -34,6 +35,16 @@ jobs:
|
|||
- name: Update VERSION File
|
||||
id: update-version
|
||||
run: |
|
||||
branch_name=${{ github.event.pull_request.head.ref }}
|
||||
repo_name=${{ github.event.pull_request.head.repo.full_name }}
|
||||
base_name="${repo_name%/*}"
|
||||
if [[ "${branch_name}" =~ ^(master|develop|nightly)$ ]]; then
|
||||
pr_tag="${base_name}"
|
||||
else
|
||||
pr_tag="${branch_name}"
|
||||
fi
|
||||
echo "pr-tag=${pr_tag}" >> $GITHUB_OUTPUT
|
||||
|
||||
value=$(cat VERSION)
|
||||
old_msg=$(git log -1 HEAD --pretty=format:%s)
|
||||
version="${value%-build*}"
|
||||
|
@ -192,4 +203,4 @@ jobs:
|
|||
curl -i -X DELETE \
|
||||
-H "Accept: application/json" \
|
||||
-H "Authorization: JWT $HUB_TOKEN" \
|
||||
https://hub.docker.com/v2/repositories/kometateam/kometa/tags/${{ github.head_ref }}/
|
||||
https://hub.docker.com/v2/repositories/kometateam/kometa/tags/${{ needs.increment-build.outputs.pr-tag }}/
|
38
.github/workflows/validate-pull.yml
vendored
38
.github/workflows/validate-pull.yml
vendored
|
@ -32,10 +32,12 @@ jobs:
|
|||
docker-build-pull:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ validate-pull ]
|
||||
if: contains(github.event.pull_request.labels.*.name, 'docker') || contains(github.event.pull_request.labels.*.name, 'testers')
|
||||
if: contains(github.event.pull_request.labels.*.name, 'docker') || contains(github.event.pull_request.labels.*.name, 'tester')
|
||||
outputs:
|
||||
commit-msg: ${{ steps.update-version.outputs.commit-msg }}
|
||||
version: ${{ steps.update-version.outputs.version }}
|
||||
tag-name: ${{ steps.update-version.outputs.tag-name }}
|
||||
extra-text: ${{ steps.update-version.outputs.extra-text }}
|
||||
steps:
|
||||
|
||||
- name: Create App Token
|
||||
|
@ -54,6 +56,23 @@ jobs:
|
|||
- name: Update VERSION File
|
||||
id: update-version
|
||||
run: |
|
||||
branch_name=${{ github.event.pull_request.head.ref }}
|
||||
repo_name=${{ github.event.pull_request.head.repo.full_name }}
|
||||
base_name="${repo_name%/*}"
|
||||
if [[ "${branch_name}" =~ ^(master|develop|nightly)$ ]]; then
|
||||
tag_name="${base_name}"
|
||||
else
|
||||
tag_name="${branch_name}"
|
||||
fi
|
||||
echo "tag-name=${tag_name}" >> $GITHUB_OUTPUT
|
||||
|
||||
if [[ "${base_name}" == "Kometa-Team" ]]; then
|
||||
extra=""
|
||||
else
|
||||
extra=" from the ${{ github.event.pull_request.head.repo.full_name }} repo"
|
||||
fi
|
||||
echo "extra-text=${extra}" >> $GITHUB_OUTPUT
|
||||
|
||||
value=$(cat VERSION)
|
||||
old_msg=$(git log -1 HEAD --pretty=format:%s)
|
||||
echo "commit-msg=${old_msg}" >> $GITHUB_OUTPUT
|
||||
|
@ -80,7 +99,7 @@ jobs:
|
|||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "GitHub Action"
|
||||
git add VERSION
|
||||
git commit -m "Part: ${part_value}"
|
||||
git commit -m "${tag_name} Part: ${part_value}"
|
||||
git push
|
||||
|
||||
- name: Login to Docker Hub
|
||||
|
@ -105,10 +124,10 @@ jobs:
|
|||
context: ./
|
||||
file: ./Dockerfile
|
||||
build-args: |
|
||||
"BRANCH_NAME=${{ github.event.pull_request.head.ref }}"
|
||||
"BRANCH_NAME=${{ steps.update-version.outputs.tag-name }}"
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: kometateam/kometa:${{ github.event.pull_request.head.ref }}
|
||||
tags: kometateam/kometa:${{ steps.update-version.outputs.tag-name }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
|
@ -117,7 +136,7 @@ jobs:
|
|||
if: success()
|
||||
with:
|
||||
webhook_id_token: ${{ secrets.BUILD_WEBHOOK }}
|
||||
title: "${{ vars.REPO_NAME }} ${{ github.event.pull_request.head.ref }}: ${{ vars.TEXT_SUCCESS }}"
|
||||
title: "${{ vars.REPO_NAME }} ${{ steps.update-version.outputs.tag-name }}: ${{ vars.TEXT_SUCCESS }}"
|
||||
url: https://github.com/Kometa-Team/${{ vars.REPO_NAME }}/actions/runs/${{ github.run_id }}
|
||||
color: ${{ vars.COLOR_SUCCESS }}
|
||||
username: ${{ vars.BOT_NAME }}
|
||||
|
@ -131,7 +150,7 @@ jobs:
|
|||
with:
|
||||
webhook_id_token: ${{ secrets.BUILD_WEBHOOK }}
|
||||
message: ${{ vars.BUILD_FAILURE_ROLE }}
|
||||
title: "${{ vars.REPO_NAME }} ${{ github.event.pull_request.head.ref }}: ${{ vars.TEXT_FAILURE }}"
|
||||
title: "${{ vars.REPO_NAME }} ${{ steps.update-version.outputs.tag-name }}: ${{ vars.TEXT_FAILURE }}"
|
||||
url: https://github.com/Kometa-Team/${{ vars.REPO_NAME }}/actions/runs/${{ github.run_id }}
|
||||
color: ${{ vars.COLOR_FAILURE }}
|
||||
username: ${{ vars.BOT_NAME }}
|
||||
|
@ -141,6 +160,7 @@ jobs:
|
|||
|
||||
notify-testers:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ docker-build-pull ]
|
||||
if: github.event.action == 'labeled' && github.event.label.name == 'tester'
|
||||
steps:
|
||||
|
||||
|
@ -158,9 +178,9 @@ jobs:
|
|||
webhook_id_token: ${{ secrets.TESTERS_WEBHOOK }}
|
||||
message: "The Kometa team are requesting <@&917323027438510110> to assist with testing an upcoming feature/bug fix.
|
||||
|
||||
* For Local Git pull and checkout the `${{ github.event.pull_request.head.ref }}` branch
|
||||
* For Local Git pull and checkout the `${{ github.event.pull_request.head.ref }}` branch${{ needs.docker-build-pull.outputs.extra-text }}
|
||||
|
||||
* For Docker use the `kometateam/kometa:${{ github.event.pull_request.head.ref }}` image to do your testing
|
||||
* For Docker use the `kometateam/kometa:${{ needs.docker-build-pull.outputs.tag-name }}` image to do your testing
|
||||
|
||||
Please report back either here or on the original GitHub Pull Request"
|
||||
title: ${{ github.event.pull_request.title }}
|
||||
|
@ -182,7 +202,7 @@ jobs:
|
|||
uses: Kometa-Team/discord-notifications@master
|
||||
with:
|
||||
webhook_id_token: ${{ secrets.TESTERS_WEBHOOK }}
|
||||
message: "New Commit Pushed to `${{ github.event.pull_request.head.ref }}`: ${{ needs.docker-build-pull.outputs.version }}"
|
||||
message: "New Commit Pushed to `${{ needs.docker-build-pull.outputs.tag-name }}`: ${{ needs.docker-build-pull.outputs.version }}"
|
||||
description: ${{ needs.docker-build-pull.outputs.commit-msg }}
|
||||
url: https://github.com/Kometa-Team/${{ vars.REPO_NAME }}/pull/${{ github.event.number }}
|
||||
color: ${{ vars.COLOR_SUCCESS }}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# Requirements Update (requirements will need to be reinstalled)
|
||||
Added tenacity requirement at 8.3.0
|
||||
|
||||
# Removed Features
|
||||
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
2.0.2-build4
|
||||
2.0.2-build5
|
||||
|
|
|
@ -61,6 +61,8 @@ These are the developers and creators of the technologies that are required to m
|
|||
| [meisnate12](https://github.com/meisnate12) | Creator of [ArrAPI](https://github.com/Kometa-Team/ArrAPI) and [TMDbAPIs](https://github.com/Kometa-Team/TMDbAPIs) | [Click Here](https://github.com/sponsors/meisnate12) |
|
||||
| [dbader](https://github.com/dbader) | Creator of [schedule](https://github.com/dbader/schedule) | :fontawesome-solid-circle-xmark:{ .red } |
|
||||
| [rholder](https://github.com/rholder) | Creator of [retrying](https://github.com/rholder/retrying) | :fontawesome-solid-circle-xmark:{ .red } |
|
||||
| [jd](https://github.com/jd) | Creator of [tenacity](https://github.com/jd/tenacity) | :fontawesome-solid-circle-xmark:{ .red } |
|
||||
|
||||
|
||||
## Other Acknowledgements
|
||||
|
||||
|
|
|
@ -374,10 +374,10 @@ Collecting PlexAPI==4.7.0
|
|||
Collecting tmdbv3api==1.7.6
|
||||
Downloading tmdbv3api-1.7.6-py2.py3-none-any.whl (17 kB)
|
||||
...
|
||||
Installing collected packages: urllib3, idna, charset-normalizer, certifi, six, ruamel.yaml.clib, requests, tmdbv3api, schedule, ruamel.yaml, retrying, PlexAPI, pillow, pathvalidate, lxml, arrapi
|
||||
Running setup.py install for retrying ... done
|
||||
Installing collected packages: urllib3, idna, charset-normalizer, certifi, six, ruamel.yaml.clib, requests, tmdbv3api, tenacity, ruamel.yaml, tenacity, PlexAPI, pillow, pathvalidate, lxml, arrapi
|
||||
Running setup.py install for tenacity ... done
|
||||
Running setup.py install for arrapi ... done
|
||||
Successfully installed PlexAPI-4.7.0 arrapi-1.1.3 certifi-2021.10.8 charset-normalizer-2.0.7 idna-3.3 lxml-4.6.3 pathvalidate-2.4.1 pillow-8.3.2 requests-2.26.0 retrying-1.3.3 ruamel.yaml-0.17.10 ruamel.yaml.clib-0.2.6 schedule-1.1.0 six-1.16.0 tmdbv3api-1.7.6 urllib3-1.26.7
|
||||
Successfully installed PlexAPI-4.7.0 arrapi-1.1.3 certifi-2021.10.8 charset-normalizer-2.0.7 idna-3.3 lxml-4.6.3 pathvalidate-2.4.1 pillow-8.3.2 requests-2.26.0 tenacity-8.3.0 ruamel.yaml-0.17.10 ruamel.yaml.clib-0.2.6 tenacity-8.3.0 six-1.16.0 tmdbv3api-1.7.6 urllib3-1.26.7
|
||||
WARNING: You are using pip version 21.1.3; however, version 21.3 is available.
|
||||
You should consider upgrading via the '/Users/mroche/Kometa/kometa-venv/bin/python -m pip install --upgrade pip' command.
|
||||
```
|
||||
|
|
|
@ -31,7 +31,7 @@ system_versions = {
|
|||
"python-dotenv": dotenv_version.__version__,
|
||||
"python-dateutil": dateutil.__version__, # noqa
|
||||
"requests": requests.__version__,
|
||||
"retrying": None,
|
||||
"tenacity": None,
|
||||
"ruamel.yaml": ruamel.yaml.__version__,
|
||||
"schedule": None,
|
||||
"setuptools": setuptools.__version__,
|
||||
|
|
|
@ -2001,12 +2001,13 @@ class MetadataFile(DataFile):
|
|||
episodes[f"{available.month}-{available.day}"] = episode
|
||||
for episode_id, episode_dict in season_dict[season_methods["episodes"]].items():
|
||||
updated = False
|
||||
title_name = f"Episode: {episode_id} in Season: {season_id} of {mapping_name}"
|
||||
logger.info("")
|
||||
logger.info(f"Updating episode {episode_id} in {season_id} of {mapping_name}...")
|
||||
logger.info(f"Updating {title_name}...")
|
||||
if episode_id in episodes:
|
||||
episode = episodes[episode_id]
|
||||
else:
|
||||
logger.error(f"{self.type_str} Error: Episode {episode_id} in Season {season_id} not found")
|
||||
logger.error(f"{self.type_str} Error: {title_name} not found")
|
||||
continue
|
||||
episode_methods = {em.lower(): em for em in episode_dict}
|
||||
add_edit("title", episode, episode_dict, episode_methods)
|
||||
|
@ -2020,7 +2021,7 @@ class MetadataFile(DataFile):
|
|||
for tag_edit in ["director", "writer", "label"]:
|
||||
if self.edit_tags(tag_edit, episode, episode_dict, episode_methods):
|
||||
updated = True
|
||||
finish_edit(episode, f"Episode: {episode_id} in Season: {season_id}")
|
||||
finish_edit(episode, title_name)
|
||||
episode_style_data = None
|
||||
if season_style_data and "episodes" in season_style_data and season_style_data["episodes"] and episode_id in season_style_data["episodes"]:
|
||||
episode_style_data = season_style_data["episodes"][episode_id]
|
||||
|
@ -2030,7 +2031,7 @@ class MetadataFile(DataFile):
|
|||
style_data=episode_style_data)
|
||||
if ups:
|
||||
updated = True
|
||||
logger.info(f"Episode {episode_id} in Season {season_id} of {mapping_name} Metadata Update {'Complete' if updated else 'Not Needed'}")
|
||||
logger.info(f"{title_name} Metadata Update {'Complete' if updated else 'Not Needed'}")
|
||||
|
||||
if "episodes" in methods and update_episodes and self.library.is_show:
|
||||
if not meta[methods["episodes"]]:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from json import JSONDecodeError
|
||||
from modules import util
|
||||
from modules.util import Failed
|
||||
from retrying import retry
|
||||
from tenacity import retry, stop_after_attempt, wait_fixed, retry_if_not_exception_type
|
||||
|
||||
logger = util.logger
|
||||
|
||||
|
@ -14,23 +14,20 @@ class Notifiarr:
|
|||
self.apikey = params["apikey"]
|
||||
self.header = {"X-API-Key": self.apikey}
|
||||
logger.secret(self.apikey)
|
||||
try:
|
||||
self.request(path="user", params={"fetch": "settings"})
|
||||
except JSONDecodeError:
|
||||
raise Failed("Notifiarr Error: Invalid JSON response received")
|
||||
self._request(path="user", params={"fetch": "settings"})
|
||||
|
||||
def notification(self, json):
|
||||
return self.request(json=json)
|
||||
return self._request(json=json)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
def request(self, json=None, path="notification", params=None):
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type(Failed))
|
||||
def _request(self, json=None, path="notification", params=None):
|
||||
response = self.requests.get(f"{base_url}{path}/pmm/", json=json, headers=self.header, params=params)
|
||||
try:
|
||||
response_json = response.json()
|
||||
except JSONDecodeError as e:
|
||||
logger.error(response.content)
|
||||
logger.debug(e)
|
||||
raise e
|
||||
logger.debug(f"Content: {response.content}")
|
||||
logger.error(e)
|
||||
raise Failed("Notifiarr Error: Invalid JSON response received")
|
||||
if response.status_code >= 400 or ("result" in response_json and response_json["result"] == "error"):
|
||||
logger.debug(f"Response: {response_json}")
|
||||
raise Failed(f"({response.status_code} [{response.reason}]) {response_json}")
|
||||
|
|
|
@ -13,6 +13,7 @@ class OMDbObj:
|
|||
self._data = data
|
||||
if data["Response"] == "False":
|
||||
raise Failed(f"OMDb Error: {data['Error']} IMDb ID: {imdb_id}")
|
||||
|
||||
def _parse(key, is_int=False, is_float=False, is_date=False, replace=None):
|
||||
try:
|
||||
value = str(data[key]).replace(replace, '') if replace else data[key]
|
||||
|
@ -26,6 +27,7 @@ class OMDbObj:
|
|||
return value
|
||||
except (ValueError, TypeError, KeyError):
|
||||
return None
|
||||
|
||||
self.title = _parse("Title")
|
||||
self.year = _parse("Year", is_int=True)
|
||||
self.released = _parse("Released", is_date=True)
|
||||
|
|
|
@ -15,7 +15,7 @@ from plexapi.playlist import Playlist
|
|||
from plexapi.server import PlexServer
|
||||
from plexapi.video import Movie, Show, Season, Episode
|
||||
from requests.exceptions import ConnectionError, ConnectTimeout
|
||||
from retrying import retry
|
||||
from tenacity import retry, stop_after_attempt, wait_fixed, retry_if_not_exception_type
|
||||
from xml.etree.ElementTree import ParseError
|
||||
|
||||
logger = util.logger
|
||||
|
@ -560,11 +560,11 @@ class Plex(Library):
|
|||
return []
|
||||
return self.fetchItems(args)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type((BadRequest, NotFound, Unauthorized)))
|
||||
def search(self, title=None, sort=None, maxresults=None, libtype=None, **kwargs):
|
||||
return self.Plex.search(title=title, sort=sort, maxresults=maxresults, libtype=libtype, **kwargs)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type((BadRequest, NotFound, Unauthorized)))
|
||||
def exact_search(self, title, libtype=None, year=None):
|
||||
terms = {"title=": title}
|
||||
if year:
|
||||
|
@ -585,11 +585,11 @@ class Plex(Library):
|
|||
logger.trace(e)
|
||||
raise Failed(f"Plex Error: Item {item} not found")
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type((BadRequest, NotFound, Unauthorized)))
|
||||
def fetchItem(self, data):
|
||||
return self.PlexServer.fetchItem(data)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type((BadRequest, NotFound, Unauthorized)))
|
||||
def fetchItems(self, uri_args):
|
||||
return self.Plex.fetchItems(f"/library/sections/{self.Plex.key}/all{'' if uri_args is None else uri_args}")
|
||||
|
||||
|
@ -633,11 +633,11 @@ class Plex(Library):
|
|||
elif filepath:
|
||||
self.PlexServer.query(key, method=self.PlexServer._session.post, data=open(filepath, 'rb').read())
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type((BadRequest, NotFound, Unauthorized)))
|
||||
def create_playlist(self, name, items):
|
||||
return self.PlexServer.createPlaylist(name, items=items)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type((BadRequest, NotFound, Unauthorized)))
|
||||
def moveItem(self, obj, item, after):
|
||||
try:
|
||||
obj.moveItem(item, after=after)
|
||||
|
@ -645,7 +645,7 @@ class Plex(Library):
|
|||
logger.error(e)
|
||||
raise Failed("Move Failed")
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type((BadRequest, NotFound, Unauthorized)))
|
||||
def query(self, method):
|
||||
return method()
|
||||
|
||||
|
@ -656,30 +656,30 @@ class Plex(Library):
|
|||
logger.stacktrace()
|
||||
raise Failed(f"Plex Error: Failed to delete {obj.title}")
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type((BadRequest, NotFound, Unauthorized)))
|
||||
def query_data(self, method, data):
|
||||
return method(data)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type((BadRequest, NotFound, Unauthorized)))
|
||||
def tag_edit(self, item, attribute, data, locked=True, remove=False):
|
||||
return item.editTags(attribute, data, locked=locked, remove=remove)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type(Failed))
|
||||
def query_collection(self, item, collection, locked=True, add=True):
|
||||
if add:
|
||||
item.addCollection(collection, locked=locked)
|
||||
else:
|
||||
item.removeCollection(collection, locked=locked)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type((BadRequest, NotFound, Unauthorized)))
|
||||
def collection_mode_query(self, collection, data):
|
||||
collection.modeUpdate(mode=data)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type((BadRequest, NotFound, Unauthorized)))
|
||||
def collection_order_query(self, collection, data):
|
||||
collection.sortUpdate(sort=data)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type((BadRequest, NotFound, Unauthorized)))
|
||||
def item_labels(self, item):
|
||||
try:
|
||||
return item.labels
|
||||
|
@ -766,7 +766,7 @@ class Plex(Library):
|
|||
item_list.append(item)
|
||||
return item_list
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type((BadRequest, NotFound, Unauthorized)))
|
||||
def reload(self, item, force=False):
|
||||
is_full = False
|
||||
if not force and item.ratingKey in self.cached_items:
|
||||
|
@ -780,14 +780,14 @@ class Plex(Library):
|
|||
raise Failed(f"Item Failed to Load: {e}")
|
||||
return item
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type((BadRequest, NotFound, Unauthorized)))
|
||||
def edit_query(self, item, edits, advanced=False):
|
||||
if advanced:
|
||||
item.editAdvanced(**edits)
|
||||
else:
|
||||
item.edit(**edits)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type((BadRequest, NotFound, Unauthorized)))
|
||||
def _upload_image(self, item, image):
|
||||
try:
|
||||
if image.is_url and "theposterdb.com" in image.location:
|
||||
|
@ -810,21 +810,21 @@ class Plex(Library):
|
|||
item.refresh()
|
||||
raise Failed(e)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type((BadRequest, NotFound, Unauthorized)))
|
||||
def upload_poster(self, item, image, url=False):
|
||||
if url:
|
||||
item.uploadPoster(url=image)
|
||||
else:
|
||||
item.uploadPoster(filepath=image)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type((BadRequest, NotFound, Unauthorized)))
|
||||
def upload_background(self, item, image, url=False):
|
||||
if url:
|
||||
item.uploadArt(url=image)
|
||||
else:
|
||||
item.uploadArt(filepath=image)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type(Failed))
|
||||
def get_actor_id(self, name):
|
||||
results = self.Plex.hubSearch(name)
|
||||
for result in results:
|
||||
|
@ -851,7 +851,7 @@ class Plex(Library):
|
|||
logger.debug(f"Search Attribute: {final_search}")
|
||||
raise Failed(f"Plex Error: plex_search attribute: {search_name} not supported")
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type((BadRequest, NotFound, Unauthorized)))
|
||||
def get_tags(self, tag):
|
||||
if isinstance(tag, str):
|
||||
match = re.match(r'(?:([a-zA-Z]*)\.)?([a-zA-Z]+)', tag)
|
||||
|
@ -872,7 +872,7 @@ class Plex(Library):
|
|||
items = [i for i in self.Plex.findItems(self.Plex._server.query(tag.key[:-7]), FilterChoice) if i.key not in keys]
|
||||
return items
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type((BadRequest, NotFound, Unauthorized)))
|
||||
def _query(self, key, post=False, put=False):
|
||||
if post: method = self.Plex._server._session.post
|
||||
elif put: method = self.Plex._server._session.put
|
||||
|
|
|
@ -4,7 +4,7 @@ from modules import util
|
|||
from modules.poster import ImageData
|
||||
from modules.util import Failed
|
||||
from requests.exceptions import ConnectionError
|
||||
from retrying import retry
|
||||
from tenacity import retry, stop_after_attempt, wait_fixed
|
||||
from urllib import parse
|
||||
|
||||
logger = util.logger
|
||||
|
@ -149,7 +149,7 @@ class Requests:
|
|||
logger.error(str(response.content))
|
||||
raise
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10))
|
||||
def get(self, url, json=None, headers=None, params=None, header=None, language=None):
|
||||
return self.session.get(url, json=json, headers=get_header(headers, header, language), params=params)
|
||||
|
||||
|
@ -167,7 +167,7 @@ class Requests:
|
|||
logger.error(str(response.content))
|
||||
raise
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10))
|
||||
def post(self, url, data=None, json=None, headers=None, header=None, language=None):
|
||||
return self.session.post(url, data=data, json=json, headers=get_header(headers, header, language))
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import re
|
||||
from modules import util
|
||||
from modules.util import Failed
|
||||
from retrying import retry
|
||||
from tenacity import retry, stop_after_attempt, wait_fixed, retry_if_not_exception_type
|
||||
from tmdbapis import TMDbAPIs, TMDbException, NotFound, Movie
|
||||
|
||||
logger = util.logger
|
||||
|
@ -128,7 +128,7 @@ class TMDbMovie(TMDBObj):
|
|||
if self._tmdb.cache and not ignore_cache:
|
||||
self._tmdb.cache.update_tmdb_movie(expired, self, self._tmdb.expiration)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type(Failed))
|
||||
def load_movie(self):
|
||||
try:
|
||||
return self._tmdb.TMDb.movie(self.tmdb_id, partial="external_ids,keywords")
|
||||
|
@ -165,7 +165,7 @@ class TMDbShow(TMDBObj):
|
|||
if self._tmdb.cache and not ignore_cache:
|
||||
self._tmdb.cache.update_tmdb_show(expired, self, self._tmdb.expiration)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type(Failed))
|
||||
def load_show(self):
|
||||
try:
|
||||
return self._tmdb.TMDb.tv_show(self.tmdb_id, partial="external_ids,keywords")
|
||||
|
@ -201,7 +201,7 @@ class TMDbEpisode:
|
|||
if self._tmdb.cache and not ignore_cache:
|
||||
self._tmdb.cache.update_tmdb_episode(expired, self, self._tmdb.expiration)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type(Failed))
|
||||
def load_episode(self):
|
||||
try:
|
||||
return self._tmdb.TMDb.tv_episode(self.tmdb_id, self.season_number, self.episode_number)
|
||||
|
@ -235,7 +235,7 @@ class TMDb:
|
|||
raise Failed(f"TMDb Error: No {convert_to.upper().replace('B_', 'b ')} found for TMDb ID {tmdb_id}")
|
||||
return check_id
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type(Failed))
|
||||
def convert_tvdb_to(self, tvdb_id):
|
||||
try:
|
||||
results = self.TMDb.find_by_id(tvdb_id=tvdb_id)
|
||||
|
@ -245,7 +245,7 @@ class TMDb:
|
|||
pass
|
||||
raise Failed(f"TMDb Error: No TMDb ID found for TVDb ID {tvdb_id}")
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type(Failed))
|
||||
def convert_imdb_to(self, imdb_id):
|
||||
try:
|
||||
results = self.TMDb.find_by_id(imdb_id=imdb_id)
|
||||
|
@ -274,7 +274,7 @@ class TMDb:
|
|||
def get_show(self, tmdb_id, ignore_cache=False):
|
||||
return TMDbShow(self, tmdb_id, ignore_cache=ignore_cache)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type(Failed))
|
||||
def get_season(self, tmdb_id, season_number, partial=None):
|
||||
try: return self.TMDb.tv_season(tmdb_id, season_number, partial=partial)
|
||||
except NotFound as e: raise Failed(f"TMDb Error: No Season found for TMDb ID {tmdb_id} Season {season_number}: {e}")
|
||||
|
@ -282,41 +282,41 @@ class TMDb:
|
|||
def get_episode(self, tmdb_id, season_number, episode_number, ignore_cache=False):
|
||||
return TMDbEpisode(self, tmdb_id, season_number, episode_number, ignore_cache=ignore_cache)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type(Failed))
|
||||
def get_collection(self, tmdb_id, partial=None):
|
||||
try: return self.TMDb.collection(tmdb_id, partial=partial)
|
||||
except NotFound as e: raise Failed(f"TMDb Error: No Collection found for TMDb ID {tmdb_id}: {e}")
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type(Failed))
|
||||
def get_person(self, tmdb_id, partial=None):
|
||||
try: return self.TMDb.person(tmdb_id, partial=partial)
|
||||
except NotFound as e: raise Failed(f"TMDb Error: No Person found for TMDb ID {tmdb_id}: {e}")
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type(Failed))
|
||||
def _company(self, tmdb_id, partial=None):
|
||||
try: return self.TMDb.company(tmdb_id, partial=partial)
|
||||
except NotFound as e: raise Failed(f"TMDb Error: No Company found for TMDb ID {tmdb_id}: {e}")
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type(Failed))
|
||||
def _network(self, tmdb_id, partial=None):
|
||||
try: return self.TMDb.network(tmdb_id, partial=partial)
|
||||
except NotFound as e: raise Failed(f"TMDb Error: No Network found for TMDb ID {tmdb_id}: {e}")
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type(Failed))
|
||||
def _keyword(self, tmdb_id):
|
||||
try: return self.TMDb.keyword(tmdb_id)
|
||||
except NotFound as e: raise Failed(f"TMDb Error: No Keyword found for TMDb ID {tmdb_id}: {e}")
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type(Failed))
|
||||
def get_list(self, tmdb_id):
|
||||
try: return self.TMDb.list(tmdb_id)
|
||||
except NotFound as e: raise Failed(f"TMDb Error: No List found for TMDb ID {tmdb_id}: {e}")
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type(Failed))
|
||||
def get_popular_people(self, limit):
|
||||
return {str(p.id): p.name for p in self.TMDb.popular_people().get_results(limit)}
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type(Failed))
|
||||
def search_people(self, name):
|
||||
try: return self.TMDb.people_search(name)
|
||||
except NotFound: raise Failed(f"TMDb Error: Actor {name} Not Found")
|
||||
|
@ -342,7 +342,7 @@ class TMDb:
|
|||
elif tmdb_type == "List": self.get_list(tmdb_id)
|
||||
return tmdb_id
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type(Failed))
|
||||
def get_items(self, method, data, region, is_movie, result_type):
|
||||
if method == "tmdb_popular":
|
||||
results = self.TMDb.popular_movies(region=region) if is_movie else self.TMDb.popular_tv()
|
||||
|
|
|
@ -2,7 +2,7 @@ import time, webbrowser
|
|||
from modules import util
|
||||
from modules.request import urlparse
|
||||
from modules.util import Failed, TimeoutExpired
|
||||
from retrying import retry
|
||||
from tenacity import retry, stop_after_attempt, wait_fixed, retry_if_not_exception_type
|
||||
|
||||
logger = util.logger
|
||||
|
||||
|
@ -198,7 +198,7 @@ class Trakt:
|
|||
return True
|
||||
return False
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type(Failed))
|
||||
def _request(self, url, params=None, json_data=None):
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
|
|
|
@ -5,7 +5,7 @@ from lxml.etree import ParserError
|
|||
from modules import util
|
||||
from modules.util import Failed
|
||||
from requests.exceptions import MissingSchema
|
||||
from retrying import retry
|
||||
from tenacity import retry, stop_after_attempt, wait_fixed, retry_if_not_exception_type
|
||||
|
||||
logger = util.logger
|
||||
|
||||
|
@ -115,7 +115,7 @@ class TVDb:
|
|||
tvdb_id, _, _ = self.get_id_from_url(tvdb_url, is_movie=is_movie)
|
||||
return TVDbObj(self, tvdb_id, is_movie=is_movie)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
|
||||
@retry(stop=stop_after_attempt(6), wait=wait_fixed(10), retry=retry_if_not_exception_type(Failed))
|
||||
def get_request(self, tvdb_url):
|
||||
response = self.requests.get(tvdb_url, language=self.language)
|
||||
if response.status_code >= 400:
|
||||
|
|
|
@ -4,8 +4,10 @@ from modules.logs import MyLogger
|
|||
from num2words import num2words
|
||||
from pathvalidate import is_valid_filename, sanitize_filename
|
||||
from plexapi.audio import Album, Track
|
||||
from plexapi.exceptions import BadRequest, NotFound, Unauthorized
|
||||
from plexapi.video import Season, Episode, Movie
|
||||
from requests.exceptions import HTTPError
|
||||
from tenacity import retry_if_exception
|
||||
from tenacity.wait import wait_base
|
||||
|
||||
try:
|
||||
import msvcrt
|
||||
|
@ -43,11 +45,32 @@ class NotScheduled(Exception):
|
|||
class NotScheduledRange(NotScheduled):
|
||||
pass
|
||||
|
||||
def retry_if_not_failed(exception):
|
||||
return not isinstance(exception, Failed)
|
||||
|
||||
def retry_if_not_plex(exception):
|
||||
return not isinstance(exception, (BadRequest, NotFound, Unauthorized, Failed))
|
||||
class retry_if_http_429_error(retry_if_exception):
|
||||
|
||||
def __init__(self):
|
||||
def is_http_429_error(exception: BaseException) -> bool:
|
||||
return isinstance(exception, HTTPError) and exception.response.status_code == 429
|
||||
|
||||
super().__init__(predicate=is_http_429_error)
|
||||
|
||||
|
||||
class wait_for_retry_after_header(wait_base):
|
||||
def __init__(self, fallback):
|
||||
self.fallback = fallback
|
||||
|
||||
def __call__(self, retry_state):
|
||||
exc = retry_state.outcome.exception()
|
||||
if isinstance(exc, HTTPError):
|
||||
retry_after = exc.response.headers.get("Retry-After", None)
|
||||
try:
|
||||
if retry_after is not None:
|
||||
return int(retry_after)
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
|
||||
return self.fallback(retry_state)
|
||||
|
||||
|
||||
days_alias = {
|
||||
"monday": 0, "mon": 0, "m": 0,
|
||||
|
|
|
@ -9,7 +9,7 @@ psutil==5.9.8
|
|||
python-dotenv==1.0.1
|
||||
python-dateutil==2.9.0.post0
|
||||
requests==2.32.3
|
||||
retrying==1.3.4
|
||||
tenacity==8.3.0
|
||||
ruamel.yaml==0.18.6
|
||||
schedule==1.2.2
|
||||
setuptools==70.0.0
|
||||
|
|
Loading…
Reference in a new issue