mirror of
https://github.com/LukeHagar/plexpy.git
synced 2025-12-06 12:47:44 +00:00
Merge pull request #1 from LukeHagar/liblab-codegen-1698374747238
liblab SDK update
This commit is contained in:
12
.devcontainer/devcontainer.json
Normal file
12
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||||
|
// README at: https://github.com/devcontainers/templates/tree/main/src/python
|
||||||
|
{
|
||||||
|
"name": "Python SDK",
|
||||||
|
"image": "mcr.microsoft.com/devcontainers/python:1-3.11-bullseye",
|
||||||
|
"postCreateCommand": "pip3 install --user -r requirements.txt && cd examples && sh install.sh",
|
||||||
|
"customizations": {
|
||||||
|
"codespaces":{
|
||||||
|
"openFiles": ["examples/sample.py", "examples/README.md"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
.env.example
Normal file
1
.env.example
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PLEXSDK_API_KEY=
|
||||||
160
.gitignore
vendored
Normal file
160
.gitignore
vendored
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
|
||||||
|
# pdm
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||||
|
#pdm.lock
|
||||||
|
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||||
|
# in version control.
|
||||||
|
# https://pdm.fming.dev/#use-with-ide
|
||||||
|
.pdm.toml
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
||||||
19
LICENSE
Normal file
19
LICENSE
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2023
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
||||||
|
OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
33
examples/README.md
Normal file
33
examples/README.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# plexpy-example
|
||||||
|
A basic example of how to use the plexpy package.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
If `plexpy` is published to pypi:
|
||||||
|
```sh
|
||||||
|
pip install plexpy==0.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
In the event `plexpy` is not published to pypi, you can install it locally by running the following command in the example folder:
|
||||||
|
```sh
|
||||||
|
. ./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This will create and start a virtual environment and install the package locally in it.
|
||||||
|
|
||||||
|
To close the virtual environment run:
|
||||||
|
```sh
|
||||||
|
deactivate
|
||||||
|
```
|
||||||
|
|
||||||
|
To re-activate the virtual environment once it is installed run:
|
||||||
|
```sh
|
||||||
|
source .venv/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
To run the example, run the following command in the examples folder:
|
||||||
|
```sh
|
||||||
|
python sample.py
|
||||||
|
```
|
||||||
5
examples/install.sh
Normal file
5
examples/install.sh
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
python -m venv .venv
|
||||||
|
source .venv/bin/activate
|
||||||
|
pip install build
|
||||||
|
python -m build --outdir dist ../
|
||||||
|
pip install dist/plexpy-0.0.1-py3-none-any.whl
|
||||||
5
examples/install_py3.sh
Normal file
5
examples/install_py3.sh
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
python3 -m venv .venv
|
||||||
|
source .venv/bin/activate
|
||||||
|
pip3 install build
|
||||||
|
python3 -m build --outdir dist ../
|
||||||
|
pip3 install dist/plexpy-0.0.1-py3-none-any.whl
|
||||||
10
examples/sample.py
Normal file
10
examples/sample.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from os import getenv
|
||||||
|
from pprint import pprint
|
||||||
|
from plexsdk import PlexSDK
|
||||||
|
|
||||||
|
sdk = PlexSDK()
|
||||||
|
sdk.set_api_key(getenv("PLEXSDK_API_KEY"))
|
||||||
|
|
||||||
|
results = sdk.server.get_server_capabilities()
|
||||||
|
|
||||||
|
pprint(vars(results))
|
||||||
3
install.sh
Normal file
3
install.sh
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pip3 install -r requirements.txt
|
||||||
|
pip3 install -e src/plexsdk/
|
||||||
|
python3 -m unittest discover -p "test*.py"
|
||||||
21
pyproject.toml
Normal file
21
pyproject.toml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=61.0"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "plexpy"
|
||||||
|
version = "0.0.1"
|
||||||
|
license = { text = "MIT" }
|
||||||
|
authors = [
|
||||||
|
{ name="Luke Hagar", email="lukeslakemail@gmail.com" }
|
||||||
|
]
|
||||||
|
description = """An Open API Spec for interacting with Plex.tv and Plex Servers"""
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.7"
|
||||||
|
dependencies = [
|
||||||
|
"requests",
|
||||||
|
"http-exceptions",
|
||||||
|
"pytest",
|
||||||
|
"responses"
|
||||||
|
]
|
||||||
|
|
||||||
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
requests
|
||||||
|
http-exceptions
|
||||||
|
pytest
|
||||||
|
responses
|
||||||
2003
src/plexsdk/README.md
Normal file
2003
src/plexsdk/README.md
Normal file
File diff suppressed because it is too large
Load Diff
2
src/plexsdk/__init__.py
Normal file
2
src/plexsdk/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from .sdk import PlexSDK
|
||||||
|
from .net.environment import Environment
|
||||||
0
src/plexsdk/hooks/__init__.py
Normal file
0
src/plexsdk/hooks/__init__.py
Normal file
1
src/plexsdk/hooks/hook.py
Normal file
1
src/plexsdk/hooks/hook.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
7
src/plexsdk/models/Download.py
Normal file
7
src/plexsdk/models/Download.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class Download(Enum):
|
||||||
|
= "0"
|
||||||
|
V1 = "1"
|
||||||
|
def list():
|
||||||
|
return list(map(lambda x: x.value, Download._member_map_.values()))
|
||||||
7
src/plexsdk/models/Force.py
Normal file
7
src/plexsdk/models/Force.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class Force(Enum):
|
||||||
|
= "0"
|
||||||
|
V1 = "1"
|
||||||
|
def list():
|
||||||
|
return list(map(lambda x: x.value, Force._member_map_.values()))
|
||||||
32
src/plexsdk/models/GetAvailableClientsResponse.py
Normal file
32
src/plexsdk/models/GetAvailableClientsResponse.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
from .base import BaseModel
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainer(BaseModel):
|
||||||
|
def __init__(self, size: float = None, Server: list = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainer
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
size: float
|
||||||
|
Server: list of dict
|
||||||
|
"""
|
||||||
|
if size is not None:
|
||||||
|
self.size = size
|
||||||
|
if Server is not None:
|
||||||
|
self.Server = Server
|
||||||
|
|
||||||
|
|
||||||
|
class GetAvailableClientsResponseItem(BaseModel):
|
||||||
|
def __init__(self, MediaContainer: MediaContainer = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize GetAvailableClientsResponseItem
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
MediaContainer: MediaContainer
|
||||||
|
"""
|
||||||
|
if MediaContainer is not None:
|
||||||
|
self.MediaContainer = MediaContainer
|
||||||
|
|
||||||
|
|
||||||
|
GetAvailableClientsResponse = List[GetAvailableClientsResponseItem]
|
||||||
62
src/plexsdk/models/GetButlerTasksResponse.py
Normal file
62
src/plexsdk/models/GetButlerTasksResponse.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
from .base import BaseModel
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
class ButlerTasksButlerTask(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
name: str = None,
|
||||||
|
interval: float = None,
|
||||||
|
scheduleRandomized: bool = None,
|
||||||
|
enabled: bool = None,
|
||||||
|
title: str = None,
|
||||||
|
description: str = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize ButlerTasksButlerTask
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
name: str
|
||||||
|
interval: float
|
||||||
|
scheduleRandomized: bool
|
||||||
|
enabled: bool
|
||||||
|
title: str
|
||||||
|
description: str
|
||||||
|
"""
|
||||||
|
if name is not None:
|
||||||
|
self.name = name
|
||||||
|
if interval is not None:
|
||||||
|
self.interval = interval
|
||||||
|
if scheduleRandomized is not None:
|
||||||
|
self.scheduleRandomized = scheduleRandomized
|
||||||
|
if enabled is not None:
|
||||||
|
self.enabled = enabled
|
||||||
|
if title is not None:
|
||||||
|
self.title = title
|
||||||
|
if description is not None:
|
||||||
|
self.description = description
|
||||||
|
|
||||||
|
|
||||||
|
class ButlerTasks(BaseModel):
|
||||||
|
def __init__(self, ButlerTask: List[ButlerTasksButlerTask] = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize ButlerTasks
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
ButlerTask: list of ButlerTasksButlerTask
|
||||||
|
"""
|
||||||
|
if ButlerTask is not None:
|
||||||
|
self.ButlerTask = ButlerTask
|
||||||
|
|
||||||
|
|
||||||
|
class GetButlerTasksResponse(BaseModel):
|
||||||
|
def __init__(self, ButlerTasks: ButlerTasks = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize GetButlerTasksResponse
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
ButlerTasks: ButlerTasks
|
||||||
|
"""
|
||||||
|
if ButlerTasks is not None:
|
||||||
|
self.ButlerTasks = ButlerTasks
|
||||||
70
src/plexsdk/models/GetDevicesResponse.py
Normal file
70
src/plexsdk/models/GetDevicesResponse.py
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
from .base import BaseModel
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerDevice(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
id: float = None,
|
||||||
|
name: str = None,
|
||||||
|
platform: str = None,
|
||||||
|
clientIdentifier: str = None,
|
||||||
|
createdAt: float = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerDevice
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
id: float
|
||||||
|
name: str
|
||||||
|
platform: str
|
||||||
|
clientIdentifier: str
|
||||||
|
createdAt: float
|
||||||
|
"""
|
||||||
|
if id is not None:
|
||||||
|
self.id = id
|
||||||
|
if name is not None:
|
||||||
|
self.name = name
|
||||||
|
if platform is not None:
|
||||||
|
self.platform = platform
|
||||||
|
if clientIdentifier is not None:
|
||||||
|
self.clientIdentifier = clientIdentifier
|
||||||
|
if createdAt is not None:
|
||||||
|
self.createdAt = createdAt
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainer(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
size: float = None,
|
||||||
|
identifier: str = None,
|
||||||
|
Device: List[MediaContainerDevice] = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainer
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
size: float
|
||||||
|
identifier: str
|
||||||
|
Device: list of MediaContainerDevice
|
||||||
|
"""
|
||||||
|
if size is not None:
|
||||||
|
self.size = size
|
||||||
|
if identifier is not None:
|
||||||
|
self.identifier = identifier
|
||||||
|
if Device is not None:
|
||||||
|
self.Device = Device
|
||||||
|
|
||||||
|
|
||||||
|
class GetDevicesResponse(BaseModel):
|
||||||
|
def __init__(self, MediaContainer: MediaContainer = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize GetDevicesResponse
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
MediaContainer: MediaContainer
|
||||||
|
"""
|
||||||
|
if MediaContainer is not None:
|
||||||
|
self.MediaContainer = MediaContainer
|
||||||
73
src/plexsdk/models/GetMyPlexAccountResponse.py
Normal file
73
src/plexsdk/models/GetMyPlexAccountResponse.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
from .base import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class MyPlex(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
authToken: str = None,
|
||||||
|
username: str = None,
|
||||||
|
mappingState: str = None,
|
||||||
|
mappingError: str = None,
|
||||||
|
signInState: str = None,
|
||||||
|
publicAddress: str = None,
|
||||||
|
publicPort: float = None,
|
||||||
|
privateAddress: str = None,
|
||||||
|
privatePort: float = None,
|
||||||
|
subscriptionFeatures: str = None,
|
||||||
|
subscriptionActive: bool = None,
|
||||||
|
subscriptionState: str = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MyPlex
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
authToken: str
|
||||||
|
username: str
|
||||||
|
mappingState: str
|
||||||
|
mappingError: str
|
||||||
|
signInState: str
|
||||||
|
publicAddress: str
|
||||||
|
publicPort: float
|
||||||
|
privateAddress: str
|
||||||
|
privatePort: float
|
||||||
|
subscriptionFeatures: str
|
||||||
|
subscriptionActive: bool
|
||||||
|
subscriptionState: str
|
||||||
|
"""
|
||||||
|
if authToken is not None:
|
||||||
|
self.authToken = authToken
|
||||||
|
if username is not None:
|
||||||
|
self.username = username
|
||||||
|
if mappingState is not None:
|
||||||
|
self.mappingState = mappingState
|
||||||
|
if mappingError is not None:
|
||||||
|
self.mappingError = mappingError
|
||||||
|
if signInState is not None:
|
||||||
|
self.signInState = signInState
|
||||||
|
if publicAddress is not None:
|
||||||
|
self.publicAddress = publicAddress
|
||||||
|
if publicPort is not None:
|
||||||
|
self.publicPort = publicPort
|
||||||
|
if privateAddress is not None:
|
||||||
|
self.privateAddress = privateAddress
|
||||||
|
if privatePort is not None:
|
||||||
|
self.privatePort = privatePort
|
||||||
|
if subscriptionFeatures is not None:
|
||||||
|
self.subscriptionFeatures = subscriptionFeatures
|
||||||
|
if subscriptionActive is not None:
|
||||||
|
self.subscriptionActive = subscriptionActive
|
||||||
|
if subscriptionState is not None:
|
||||||
|
self.subscriptionState = subscriptionState
|
||||||
|
|
||||||
|
|
||||||
|
class GetMyPlexAccountResponse(BaseModel):
|
||||||
|
def __init__(self, MyPlex: MyPlex = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize GetMyPlexAccountResponse
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
MyPlex: MyPlex
|
||||||
|
"""
|
||||||
|
if MyPlex is not None:
|
||||||
|
self.MyPlex = MyPlex
|
||||||
446
src/plexsdk/models/GetOnDeckResponse.py
Normal file
446
src/plexsdk/models/GetOnDeckResponse.py
Normal file
@@ -0,0 +1,446 @@
|
|||||||
|
from .base import BaseModel
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadataMediaPartStream(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
id: float = None,
|
||||||
|
streamType: float = None,
|
||||||
|
default: bool = None,
|
||||||
|
codec: str = None,
|
||||||
|
index: float = None,
|
||||||
|
bitrate: float = None,
|
||||||
|
language: str = None,
|
||||||
|
languageTag: str = None,
|
||||||
|
languageCode: str = None,
|
||||||
|
bitDepth: float = None,
|
||||||
|
chromaLocation: str = None,
|
||||||
|
chromaSubsampling: str = None,
|
||||||
|
codedHeight: float = None,
|
||||||
|
codedWidth: float = None,
|
||||||
|
colorRange: str = None,
|
||||||
|
frameRate: float = None,
|
||||||
|
height: float = None,
|
||||||
|
level: float = None,
|
||||||
|
profile: str = None,
|
||||||
|
refFrames: float = None,
|
||||||
|
width: float = None,
|
||||||
|
displayTitle: str = None,
|
||||||
|
extendedDisplayTitle: str = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadataMediaPartStream
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
id: float
|
||||||
|
streamType: float
|
||||||
|
default: bool
|
||||||
|
codec: str
|
||||||
|
index: float
|
||||||
|
bitrate: float
|
||||||
|
language: str
|
||||||
|
languageTag: str
|
||||||
|
languageCode: str
|
||||||
|
bitDepth: float
|
||||||
|
chromaLocation: str
|
||||||
|
chromaSubsampling: str
|
||||||
|
codedHeight: float
|
||||||
|
codedWidth: float
|
||||||
|
colorRange: str
|
||||||
|
frameRate: float
|
||||||
|
height: float
|
||||||
|
level: float
|
||||||
|
profile: str
|
||||||
|
refFrames: float
|
||||||
|
width: float
|
||||||
|
displayTitle: str
|
||||||
|
extendedDisplayTitle: str
|
||||||
|
"""
|
||||||
|
if id is not None:
|
||||||
|
self.id = id
|
||||||
|
if streamType is not None:
|
||||||
|
self.streamType = streamType
|
||||||
|
if default is not None:
|
||||||
|
self.default = default
|
||||||
|
if codec is not None:
|
||||||
|
self.codec = codec
|
||||||
|
if index is not None:
|
||||||
|
self.index = index
|
||||||
|
if bitrate is not None:
|
||||||
|
self.bitrate = bitrate
|
||||||
|
if language is not None:
|
||||||
|
self.language = language
|
||||||
|
if languageTag is not None:
|
||||||
|
self.languageTag = languageTag
|
||||||
|
if languageCode is not None:
|
||||||
|
self.languageCode = languageCode
|
||||||
|
if bitDepth is not None:
|
||||||
|
self.bitDepth = bitDepth
|
||||||
|
if chromaLocation is not None:
|
||||||
|
self.chromaLocation = chromaLocation
|
||||||
|
if chromaSubsampling is not None:
|
||||||
|
self.chromaSubsampling = chromaSubsampling
|
||||||
|
if codedHeight is not None:
|
||||||
|
self.codedHeight = codedHeight
|
||||||
|
if codedWidth is not None:
|
||||||
|
self.codedWidth = codedWidth
|
||||||
|
if colorRange is not None:
|
||||||
|
self.colorRange = colorRange
|
||||||
|
if frameRate is not None:
|
||||||
|
self.frameRate = frameRate
|
||||||
|
if height is not None:
|
||||||
|
self.height = height
|
||||||
|
if level is not None:
|
||||||
|
self.level = level
|
||||||
|
if profile is not None:
|
||||||
|
self.profile = profile
|
||||||
|
if refFrames is not None:
|
||||||
|
self.refFrames = refFrames
|
||||||
|
if width is not None:
|
||||||
|
self.width = width
|
||||||
|
if displayTitle is not None:
|
||||||
|
self.displayTitle = displayTitle
|
||||||
|
if extendedDisplayTitle is not None:
|
||||||
|
self.extendedDisplayTitle = extendedDisplayTitle
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadataMediaPart(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
id: float = None,
|
||||||
|
key: str = None,
|
||||||
|
duration: float = None,
|
||||||
|
file: str = None,
|
||||||
|
size: float = None,
|
||||||
|
audioProfile: str = None,
|
||||||
|
container: str = None,
|
||||||
|
videoProfile: str = None,
|
||||||
|
Stream: List[MediaContainerMetadataMediaPartStream] = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadataMediaPart
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
id: float
|
||||||
|
key: str
|
||||||
|
duration: float
|
||||||
|
file: str
|
||||||
|
size: float
|
||||||
|
audioProfile: str
|
||||||
|
container: str
|
||||||
|
videoProfile: str
|
||||||
|
Stream: list of MediaContainerMetadataMediaPartStream
|
||||||
|
"""
|
||||||
|
if id is not None:
|
||||||
|
self.id = id
|
||||||
|
if key is not None:
|
||||||
|
self.key = key
|
||||||
|
if duration is not None:
|
||||||
|
self.duration = duration
|
||||||
|
if file is not None:
|
||||||
|
self.file = file
|
||||||
|
if size is not None:
|
||||||
|
self.size = size
|
||||||
|
if audioProfile is not None:
|
||||||
|
self.audioProfile = audioProfile
|
||||||
|
if container is not None:
|
||||||
|
self.container = container
|
||||||
|
if videoProfile is not None:
|
||||||
|
self.videoProfile = videoProfile
|
||||||
|
if Stream is not None:
|
||||||
|
self.Stream = Stream
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadataMedia(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
id: float = None,
|
||||||
|
duration: float = None,
|
||||||
|
bitrate: float = None,
|
||||||
|
width: float = None,
|
||||||
|
height: float = None,
|
||||||
|
aspectRatio: float = None,
|
||||||
|
audioChannels: float = None,
|
||||||
|
audioCodec: str = None,
|
||||||
|
videoCodec: str = None,
|
||||||
|
videoResolution: str = None,
|
||||||
|
container: str = None,
|
||||||
|
videoFrameRate: str = None,
|
||||||
|
audioProfile: str = None,
|
||||||
|
videoProfile: str = None,
|
||||||
|
Part: List[MediaContainerMetadataMediaPart] = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadataMedia
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
id: float
|
||||||
|
duration: float
|
||||||
|
bitrate: float
|
||||||
|
width: float
|
||||||
|
height: float
|
||||||
|
aspectRatio: float
|
||||||
|
audioChannels: float
|
||||||
|
audioCodec: str
|
||||||
|
videoCodec: str
|
||||||
|
videoResolution: str
|
||||||
|
container: str
|
||||||
|
videoFrameRate: str
|
||||||
|
audioProfile: str
|
||||||
|
videoProfile: str
|
||||||
|
Part: list of MediaContainerMetadataMediaPart
|
||||||
|
"""
|
||||||
|
if id is not None:
|
||||||
|
self.id = id
|
||||||
|
if duration is not None:
|
||||||
|
self.duration = duration
|
||||||
|
if bitrate is not None:
|
||||||
|
self.bitrate = bitrate
|
||||||
|
if width is not None:
|
||||||
|
self.width = width
|
||||||
|
if height is not None:
|
||||||
|
self.height = height
|
||||||
|
if aspectRatio is not None:
|
||||||
|
self.aspectRatio = aspectRatio
|
||||||
|
if audioChannels is not None:
|
||||||
|
self.audioChannels = audioChannels
|
||||||
|
if audioCodec is not None:
|
||||||
|
self.audioCodec = audioCodec
|
||||||
|
if videoCodec is not None:
|
||||||
|
self.videoCodec = videoCodec
|
||||||
|
if videoResolution is not None:
|
||||||
|
self.videoResolution = videoResolution
|
||||||
|
if container is not None:
|
||||||
|
self.container = container
|
||||||
|
if videoFrameRate is not None:
|
||||||
|
self.videoFrameRate = videoFrameRate
|
||||||
|
if audioProfile is not None:
|
||||||
|
self.audioProfile = audioProfile
|
||||||
|
if videoProfile is not None:
|
||||||
|
self.videoProfile = videoProfile
|
||||||
|
if Part is not None:
|
||||||
|
self.Part = Part
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadataGuid(BaseModel):
|
||||||
|
def __init__(self, id: str = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadataGuid
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
id: str
|
||||||
|
"""
|
||||||
|
if id is not None:
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadata(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
allowSync: bool = None,
|
||||||
|
librarySectionID: float = None,
|
||||||
|
librarySectionTitle: str = None,
|
||||||
|
librarySectionUUID: str = None,
|
||||||
|
ratingKey: float = None,
|
||||||
|
key: str = None,
|
||||||
|
parentRatingKey: float = None,
|
||||||
|
grandparentRatingKey: float = None,
|
||||||
|
guid: str = None,
|
||||||
|
parentGuid: str = None,
|
||||||
|
grandparentGuid: str = None,
|
||||||
|
title: str = None,
|
||||||
|
grandparentKey: str = None,
|
||||||
|
parentKey: str = None,
|
||||||
|
librarySectionKey: str = None,
|
||||||
|
grandparentTitle: str = None,
|
||||||
|
parentTitle: str = None,
|
||||||
|
contentRating: str = None,
|
||||||
|
summary: str = None,
|
||||||
|
index: float = None,
|
||||||
|
parentIndex: float = None,
|
||||||
|
lastViewedAt: float = None,
|
||||||
|
year: float = None,
|
||||||
|
thumb: str = None,
|
||||||
|
art: str = None,
|
||||||
|
parentThumb: str = None,
|
||||||
|
grandparentThumb: str = None,
|
||||||
|
grandparentArt: str = None,
|
||||||
|
grandparentTheme: str = None,
|
||||||
|
duration: float = None,
|
||||||
|
originallyAvailableAt: str = None,
|
||||||
|
addedAt: float = None,
|
||||||
|
updatedAt: float = None,
|
||||||
|
Media: List[MediaContainerMetadataMedia] = None,
|
||||||
|
Guid: List[MediaContainerMetadataGuid] = None,
|
||||||
|
type_: str = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadata
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
allowSync: bool
|
||||||
|
librarySectionID: float
|
||||||
|
librarySectionTitle: str
|
||||||
|
librarySectionUUID: str
|
||||||
|
ratingKey: float
|
||||||
|
key: str
|
||||||
|
parentRatingKey: float
|
||||||
|
grandparentRatingKey: float
|
||||||
|
guid: str
|
||||||
|
parentGuid: str
|
||||||
|
grandparentGuid: str
|
||||||
|
title: str
|
||||||
|
grandparentKey: str
|
||||||
|
parentKey: str
|
||||||
|
librarySectionKey: str
|
||||||
|
grandparentTitle: str
|
||||||
|
parentTitle: str
|
||||||
|
contentRating: str
|
||||||
|
summary: str
|
||||||
|
index: float
|
||||||
|
parentIndex: float
|
||||||
|
lastViewedAt: float
|
||||||
|
year: float
|
||||||
|
thumb: str
|
||||||
|
art: str
|
||||||
|
parentThumb: str
|
||||||
|
grandparentThumb: str
|
||||||
|
grandparentArt: str
|
||||||
|
grandparentTheme: str
|
||||||
|
duration: float
|
||||||
|
originallyAvailableAt: str
|
||||||
|
addedAt: float
|
||||||
|
updatedAt: float
|
||||||
|
Media: list of MediaContainerMetadataMedia
|
||||||
|
Guid: list of MediaContainerMetadataGuid
|
||||||
|
type_: str
|
||||||
|
"""
|
||||||
|
if allowSync is not None:
|
||||||
|
self.allowSync = allowSync
|
||||||
|
if librarySectionID is not None:
|
||||||
|
self.librarySectionID = librarySectionID
|
||||||
|
if librarySectionTitle is not None:
|
||||||
|
self.librarySectionTitle = librarySectionTitle
|
||||||
|
if librarySectionUUID is not None:
|
||||||
|
self.librarySectionUUID = librarySectionUUID
|
||||||
|
if ratingKey is not None:
|
||||||
|
self.ratingKey = ratingKey
|
||||||
|
if key is not None:
|
||||||
|
self.key = key
|
||||||
|
if parentRatingKey is not None:
|
||||||
|
self.parentRatingKey = parentRatingKey
|
||||||
|
if grandparentRatingKey is not None:
|
||||||
|
self.grandparentRatingKey = grandparentRatingKey
|
||||||
|
if guid is not None:
|
||||||
|
self.guid = guid
|
||||||
|
if parentGuid is not None:
|
||||||
|
self.parentGuid = parentGuid
|
||||||
|
if grandparentGuid is not None:
|
||||||
|
self.grandparentGuid = grandparentGuid
|
||||||
|
if title is not None:
|
||||||
|
self.title = title
|
||||||
|
if grandparentKey is not None:
|
||||||
|
self.grandparentKey = grandparentKey
|
||||||
|
if parentKey is not None:
|
||||||
|
self.parentKey = parentKey
|
||||||
|
if librarySectionKey is not None:
|
||||||
|
self.librarySectionKey = librarySectionKey
|
||||||
|
if grandparentTitle is not None:
|
||||||
|
self.grandparentTitle = grandparentTitle
|
||||||
|
if parentTitle is not None:
|
||||||
|
self.parentTitle = parentTitle
|
||||||
|
if contentRating is not None:
|
||||||
|
self.contentRating = contentRating
|
||||||
|
if summary is not None:
|
||||||
|
self.summary = summary
|
||||||
|
if index is not None:
|
||||||
|
self.index = index
|
||||||
|
if parentIndex is not None:
|
||||||
|
self.parentIndex = parentIndex
|
||||||
|
if lastViewedAt is not None:
|
||||||
|
self.lastViewedAt = lastViewedAt
|
||||||
|
if year is not None:
|
||||||
|
self.year = year
|
||||||
|
if thumb is not None:
|
||||||
|
self.thumb = thumb
|
||||||
|
if art is not None:
|
||||||
|
self.art = art
|
||||||
|
if parentThumb is not None:
|
||||||
|
self.parentThumb = parentThumb
|
||||||
|
if grandparentThumb is not None:
|
||||||
|
self.grandparentThumb = grandparentThumb
|
||||||
|
if grandparentArt is not None:
|
||||||
|
self.grandparentArt = grandparentArt
|
||||||
|
if grandparentTheme is not None:
|
||||||
|
self.grandparentTheme = grandparentTheme
|
||||||
|
if duration is not None:
|
||||||
|
self.duration = duration
|
||||||
|
if originallyAvailableAt is not None:
|
||||||
|
self.originallyAvailableAt = originallyAvailableAt
|
||||||
|
if addedAt is not None:
|
||||||
|
self.addedAt = addedAt
|
||||||
|
if updatedAt is not None:
|
||||||
|
self.updatedAt = updatedAt
|
||||||
|
if Media is not None:
|
||||||
|
self.Media = Media
|
||||||
|
if Guid is not None:
|
||||||
|
self.Guid = Guid
|
||||||
|
if type_ is not None:
|
||||||
|
self.type_ = type_
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainer(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
size: float = None,
|
||||||
|
allowSync: bool = None,
|
||||||
|
identifier: str = None,
|
||||||
|
mediaTagPrefix: str = None,
|
||||||
|
mediaTagVersion: float = None,
|
||||||
|
mixedParents: bool = None,
|
||||||
|
Metadata: List[MediaContainerMetadata] = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainer
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
size: float
|
||||||
|
allowSync: bool
|
||||||
|
identifier: str
|
||||||
|
mediaTagPrefix: str
|
||||||
|
mediaTagVersion: float
|
||||||
|
mixedParents: bool
|
||||||
|
Metadata: list of MediaContainerMetadata
|
||||||
|
"""
|
||||||
|
if size is not None:
|
||||||
|
self.size = size
|
||||||
|
if allowSync is not None:
|
||||||
|
self.allowSync = allowSync
|
||||||
|
if identifier is not None:
|
||||||
|
self.identifier = identifier
|
||||||
|
if mediaTagPrefix is not None:
|
||||||
|
self.mediaTagPrefix = mediaTagPrefix
|
||||||
|
if mediaTagVersion is not None:
|
||||||
|
self.mediaTagVersion = mediaTagVersion
|
||||||
|
if mixedParents is not None:
|
||||||
|
self.mixedParents = mixedParents
|
||||||
|
if Metadata is not None:
|
||||||
|
self.Metadata = Metadata
|
||||||
|
|
||||||
|
|
||||||
|
class GetOnDeckResponse(BaseModel):
|
||||||
|
def __init__(self, MediaContainer: MediaContainer = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize GetOnDeckResponse
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
MediaContainer: MediaContainer
|
||||||
|
"""
|
||||||
|
if MediaContainer is not None:
|
||||||
|
self.MediaContainer = MediaContainer
|
||||||
382
src/plexsdk/models/GetRecentlyAddedResponse.py
Normal file
382
src/plexsdk/models/GetRecentlyAddedResponse.py
Normal file
@@ -0,0 +1,382 @@
|
|||||||
|
from .base import BaseModel
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadataMediaPart(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
id: float = None,
|
||||||
|
key: str = None,
|
||||||
|
duration: float = None,
|
||||||
|
file: str = None,
|
||||||
|
size: float = None,
|
||||||
|
container: str = None,
|
||||||
|
has64bitOffsets: bool = None,
|
||||||
|
hasThumbnail: float = None,
|
||||||
|
optimizedForStreaming: bool = None,
|
||||||
|
videoProfile: str = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadataMediaPart
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
id: float
|
||||||
|
key: str
|
||||||
|
duration: float
|
||||||
|
file: str
|
||||||
|
size: float
|
||||||
|
container: str
|
||||||
|
has64bitOffsets: bool
|
||||||
|
hasThumbnail: float
|
||||||
|
optimizedForStreaming: bool
|
||||||
|
videoProfile: str
|
||||||
|
"""
|
||||||
|
if id is not None:
|
||||||
|
self.id = id
|
||||||
|
if key is not None:
|
||||||
|
self.key = key
|
||||||
|
if duration is not None:
|
||||||
|
self.duration = duration
|
||||||
|
if file is not None:
|
||||||
|
self.file = file
|
||||||
|
if size is not None:
|
||||||
|
self.size = size
|
||||||
|
if container is not None:
|
||||||
|
self.container = container
|
||||||
|
if has64bitOffsets is not None:
|
||||||
|
self.has64bitOffsets = has64bitOffsets
|
||||||
|
if hasThumbnail is not None:
|
||||||
|
self.hasThumbnail = hasThumbnail
|
||||||
|
if optimizedForStreaming is not None:
|
||||||
|
self.optimizedForStreaming = optimizedForStreaming
|
||||||
|
if videoProfile is not None:
|
||||||
|
self.videoProfile = videoProfile
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadataMedia(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
id: float = None,
|
||||||
|
duration: float = None,
|
||||||
|
bitrate: float = None,
|
||||||
|
width: float = None,
|
||||||
|
height: float = None,
|
||||||
|
aspectRatio: float = None,
|
||||||
|
audioChannels: float = None,
|
||||||
|
audioCodec: str = None,
|
||||||
|
videoCodec: str = None,
|
||||||
|
videoResolution: float = None,
|
||||||
|
container: str = None,
|
||||||
|
videoFrameRate: str = None,
|
||||||
|
optimizedForStreaming: float = None,
|
||||||
|
has64bitOffsets: bool = None,
|
||||||
|
videoProfile: str = None,
|
||||||
|
Part: List[MediaContainerMetadataMediaPart] = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadataMedia
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
id: float
|
||||||
|
duration: float
|
||||||
|
bitrate: float
|
||||||
|
width: float
|
||||||
|
height: float
|
||||||
|
aspectRatio: float
|
||||||
|
audioChannels: float
|
||||||
|
audioCodec: str
|
||||||
|
videoCodec: str
|
||||||
|
videoResolution: float
|
||||||
|
container: str
|
||||||
|
videoFrameRate: str
|
||||||
|
optimizedForStreaming: float
|
||||||
|
has64bitOffsets: bool
|
||||||
|
videoProfile: str
|
||||||
|
Part: list of MediaContainerMetadataMediaPart
|
||||||
|
"""
|
||||||
|
if id is not None:
|
||||||
|
self.id = id
|
||||||
|
if duration is not None:
|
||||||
|
self.duration = duration
|
||||||
|
if bitrate is not None:
|
||||||
|
self.bitrate = bitrate
|
||||||
|
if width is not None:
|
||||||
|
self.width = width
|
||||||
|
if height is not None:
|
||||||
|
self.height = height
|
||||||
|
if aspectRatio is not None:
|
||||||
|
self.aspectRatio = aspectRatio
|
||||||
|
if audioChannels is not None:
|
||||||
|
self.audioChannels = audioChannels
|
||||||
|
if audioCodec is not None:
|
||||||
|
self.audioCodec = audioCodec
|
||||||
|
if videoCodec is not None:
|
||||||
|
self.videoCodec = videoCodec
|
||||||
|
if videoResolution is not None:
|
||||||
|
self.videoResolution = videoResolution
|
||||||
|
if container is not None:
|
||||||
|
self.container = container
|
||||||
|
if videoFrameRate is not None:
|
||||||
|
self.videoFrameRate = videoFrameRate
|
||||||
|
if optimizedForStreaming is not None:
|
||||||
|
self.optimizedForStreaming = optimizedForStreaming
|
||||||
|
if has64bitOffsets is not None:
|
||||||
|
self.has64bitOffsets = has64bitOffsets
|
||||||
|
if videoProfile is not None:
|
||||||
|
self.videoProfile = videoProfile
|
||||||
|
if Part is not None:
|
||||||
|
self.Part = Part
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadataGenre(BaseModel):
|
||||||
|
def __init__(self, tag: str = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadataGenre
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
tag: str
|
||||||
|
"""
|
||||||
|
if tag is not None:
|
||||||
|
self.tag = tag
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadataDirector(BaseModel):
|
||||||
|
def __init__(self, tag: str = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadataDirector
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
tag: str
|
||||||
|
"""
|
||||||
|
if tag is not None:
|
||||||
|
self.tag = tag
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadataWriter(BaseModel):
|
||||||
|
def __init__(self, tag: str = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadataWriter
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
tag: str
|
||||||
|
"""
|
||||||
|
if tag is not None:
|
||||||
|
self.tag = tag
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadataCountry(BaseModel):
|
||||||
|
def __init__(self, tag: str = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadataCountry
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
tag: str
|
||||||
|
"""
|
||||||
|
if tag is not None:
|
||||||
|
self.tag = tag
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadataRole(BaseModel):
|
||||||
|
def __init__(self, tag: str = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadataRole
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
tag: str
|
||||||
|
"""
|
||||||
|
if tag is not None:
|
||||||
|
self.tag = tag
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadata(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
allowSync: bool = None,
|
||||||
|
librarySectionID: float = None,
|
||||||
|
librarySectionTitle: str = None,
|
||||||
|
librarySectionUUID: str = None,
|
||||||
|
ratingKey: float = None,
|
||||||
|
key: str = None,
|
||||||
|
guid: str = None,
|
||||||
|
studio: str = None,
|
||||||
|
title: str = None,
|
||||||
|
contentRating: str = None,
|
||||||
|
summary: str = None,
|
||||||
|
rating: float = None,
|
||||||
|
audienceRating: float = None,
|
||||||
|
year: float = None,
|
||||||
|
tagline: str = None,
|
||||||
|
thumb: str = None,
|
||||||
|
art: str = None,
|
||||||
|
duration: float = None,
|
||||||
|
originallyAvailableAt: str = None,
|
||||||
|
addedAt: float = None,
|
||||||
|
updatedAt: float = None,
|
||||||
|
audienceRatingImage: str = None,
|
||||||
|
chapterSource: str = None,
|
||||||
|
primaryExtraKey: str = None,
|
||||||
|
ratingImage: str = None,
|
||||||
|
Media: List[MediaContainerMetadataMedia] = None,
|
||||||
|
Genre: List[MediaContainerMetadataGenre] = None,
|
||||||
|
Director: List[MediaContainerMetadataDirector] = None,
|
||||||
|
Writer: List[MediaContainerMetadataWriter] = None,
|
||||||
|
Country: List[MediaContainerMetadataCountry] = None,
|
||||||
|
Role: List[MediaContainerMetadataRole] = None,
|
||||||
|
type_: str = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadata
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
allowSync: bool
|
||||||
|
librarySectionID: float
|
||||||
|
librarySectionTitle: str
|
||||||
|
librarySectionUUID: str
|
||||||
|
ratingKey: float
|
||||||
|
key: str
|
||||||
|
guid: str
|
||||||
|
studio: str
|
||||||
|
title: str
|
||||||
|
contentRating: str
|
||||||
|
summary: str
|
||||||
|
rating: float
|
||||||
|
audienceRating: float
|
||||||
|
year: float
|
||||||
|
tagline: str
|
||||||
|
thumb: str
|
||||||
|
art: str
|
||||||
|
duration: float
|
||||||
|
originallyAvailableAt: str
|
||||||
|
addedAt: float
|
||||||
|
updatedAt: float
|
||||||
|
audienceRatingImage: str
|
||||||
|
chapterSource: str
|
||||||
|
primaryExtraKey: str
|
||||||
|
ratingImage: str
|
||||||
|
Media: list of MediaContainerMetadataMedia
|
||||||
|
Genre: list of MediaContainerMetadataGenre
|
||||||
|
Director: list of MediaContainerMetadataDirector
|
||||||
|
Writer: list of MediaContainerMetadataWriter
|
||||||
|
Country: list of MediaContainerMetadataCountry
|
||||||
|
Role: list of MediaContainerMetadataRole
|
||||||
|
type_: str
|
||||||
|
"""
|
||||||
|
if allowSync is not None:
|
||||||
|
self.allowSync = allowSync
|
||||||
|
if librarySectionID is not None:
|
||||||
|
self.librarySectionID = librarySectionID
|
||||||
|
if librarySectionTitle is not None:
|
||||||
|
self.librarySectionTitle = librarySectionTitle
|
||||||
|
if librarySectionUUID is not None:
|
||||||
|
self.librarySectionUUID = librarySectionUUID
|
||||||
|
if ratingKey is not None:
|
||||||
|
self.ratingKey = ratingKey
|
||||||
|
if key is not None:
|
||||||
|
self.key = key
|
||||||
|
if guid is not None:
|
||||||
|
self.guid = guid
|
||||||
|
if studio is not None:
|
||||||
|
self.studio = studio
|
||||||
|
if title is not None:
|
||||||
|
self.title = title
|
||||||
|
if contentRating is not None:
|
||||||
|
self.contentRating = contentRating
|
||||||
|
if summary is not None:
|
||||||
|
self.summary = summary
|
||||||
|
if rating is not None:
|
||||||
|
self.rating = rating
|
||||||
|
if audienceRating is not None:
|
||||||
|
self.audienceRating = audienceRating
|
||||||
|
if year is not None:
|
||||||
|
self.year = year
|
||||||
|
if tagline is not None:
|
||||||
|
self.tagline = tagline
|
||||||
|
if thumb is not None:
|
||||||
|
self.thumb = thumb
|
||||||
|
if art is not None:
|
||||||
|
self.art = art
|
||||||
|
if duration is not None:
|
||||||
|
self.duration = duration
|
||||||
|
if originallyAvailableAt is not None:
|
||||||
|
self.originallyAvailableAt = originallyAvailableAt
|
||||||
|
if addedAt is not None:
|
||||||
|
self.addedAt = addedAt
|
||||||
|
if updatedAt is not None:
|
||||||
|
self.updatedAt = updatedAt
|
||||||
|
if audienceRatingImage is not None:
|
||||||
|
self.audienceRatingImage = audienceRatingImage
|
||||||
|
if chapterSource is not None:
|
||||||
|
self.chapterSource = chapterSource
|
||||||
|
if primaryExtraKey is not None:
|
||||||
|
self.primaryExtraKey = primaryExtraKey
|
||||||
|
if ratingImage is not None:
|
||||||
|
self.ratingImage = ratingImage
|
||||||
|
if Media is not None:
|
||||||
|
self.Media = Media
|
||||||
|
if Genre is not None:
|
||||||
|
self.Genre = Genre
|
||||||
|
if Director is not None:
|
||||||
|
self.Director = Director
|
||||||
|
if Writer is not None:
|
||||||
|
self.Writer = Writer
|
||||||
|
if Country is not None:
|
||||||
|
self.Country = Country
|
||||||
|
if Role is not None:
|
||||||
|
self.Role = Role
|
||||||
|
if type_ is not None:
|
||||||
|
self.type_ = type_
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainer(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
size: float = None,
|
||||||
|
allowSync: bool = None,
|
||||||
|
identifier: str = None,
|
||||||
|
mediaTagPrefix: str = None,
|
||||||
|
mediaTagVersion: float = None,
|
||||||
|
mixedParents: bool = None,
|
||||||
|
Metadata: List[MediaContainerMetadata] = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainer
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
size: float
|
||||||
|
allowSync: bool
|
||||||
|
identifier: str
|
||||||
|
mediaTagPrefix: str
|
||||||
|
mediaTagVersion: float
|
||||||
|
mixedParents: bool
|
||||||
|
Metadata: list of MediaContainerMetadata
|
||||||
|
"""
|
||||||
|
if size is not None:
|
||||||
|
self.size = size
|
||||||
|
if allowSync is not None:
|
||||||
|
self.allowSync = allowSync
|
||||||
|
if identifier is not None:
|
||||||
|
self.identifier = identifier
|
||||||
|
if mediaTagPrefix is not None:
|
||||||
|
self.mediaTagPrefix = mediaTagPrefix
|
||||||
|
if mediaTagVersion is not None:
|
||||||
|
self.mediaTagVersion = mediaTagVersion
|
||||||
|
if mixedParents is not None:
|
||||||
|
self.mixedParents = mixedParents
|
||||||
|
if Metadata is not None:
|
||||||
|
self.Metadata = Metadata
|
||||||
|
|
||||||
|
|
||||||
|
class GetRecentlyAddedResponse(BaseModel):
|
||||||
|
def __init__(self, MediaContainer: MediaContainer = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize GetRecentlyAddedResponse
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
MediaContainer: MediaContainer
|
||||||
|
"""
|
||||||
|
if MediaContainer is not None:
|
||||||
|
self.MediaContainer = MediaContainer
|
||||||
392
src/plexsdk/models/GetSearchResultsResponse.py
Normal file
392
src/plexsdk/models/GetSearchResultsResponse.py
Normal file
@@ -0,0 +1,392 @@
|
|||||||
|
from .base import BaseModel
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadataMediaPart(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
id: float = None,
|
||||||
|
key: str = None,
|
||||||
|
duration: float = None,
|
||||||
|
file: str = None,
|
||||||
|
size: float = None,
|
||||||
|
audioProfile: str = None,
|
||||||
|
container: str = None,
|
||||||
|
videoProfile: str = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadataMediaPart
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
id: float
|
||||||
|
key: str
|
||||||
|
duration: float
|
||||||
|
file: str
|
||||||
|
size: float
|
||||||
|
audioProfile: str
|
||||||
|
container: str
|
||||||
|
videoProfile: str
|
||||||
|
"""
|
||||||
|
if id is not None:
|
||||||
|
self.id = id
|
||||||
|
if key is not None:
|
||||||
|
self.key = key
|
||||||
|
if duration is not None:
|
||||||
|
self.duration = duration
|
||||||
|
if file is not None:
|
||||||
|
self.file = file
|
||||||
|
if size is not None:
|
||||||
|
self.size = size
|
||||||
|
if audioProfile is not None:
|
||||||
|
self.audioProfile = audioProfile
|
||||||
|
if container is not None:
|
||||||
|
self.container = container
|
||||||
|
if videoProfile is not None:
|
||||||
|
self.videoProfile = videoProfile
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadataMedia(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
id: float = None,
|
||||||
|
duration: float = None,
|
||||||
|
bitrate: float = None,
|
||||||
|
width: float = None,
|
||||||
|
height: float = None,
|
||||||
|
aspectRatio: float = None,
|
||||||
|
audioChannels: float = None,
|
||||||
|
audioCodec: str = None,
|
||||||
|
videoCodec: str = None,
|
||||||
|
videoResolution: float = None,
|
||||||
|
container: str = None,
|
||||||
|
videoFrameRate: str = None,
|
||||||
|
audioProfile: str = None,
|
||||||
|
videoProfile: str = None,
|
||||||
|
Part: List[MediaContainerMetadataMediaPart] = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadataMedia
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
id: float
|
||||||
|
duration: float
|
||||||
|
bitrate: float
|
||||||
|
width: float
|
||||||
|
height: float
|
||||||
|
aspectRatio: float
|
||||||
|
audioChannels: float
|
||||||
|
audioCodec: str
|
||||||
|
videoCodec: str
|
||||||
|
videoResolution: float
|
||||||
|
container: str
|
||||||
|
videoFrameRate: str
|
||||||
|
audioProfile: str
|
||||||
|
videoProfile: str
|
||||||
|
Part: list of MediaContainerMetadataMediaPart
|
||||||
|
"""
|
||||||
|
if id is not None:
|
||||||
|
self.id = id
|
||||||
|
if duration is not None:
|
||||||
|
self.duration = duration
|
||||||
|
if bitrate is not None:
|
||||||
|
self.bitrate = bitrate
|
||||||
|
if width is not None:
|
||||||
|
self.width = width
|
||||||
|
if height is not None:
|
||||||
|
self.height = height
|
||||||
|
if aspectRatio is not None:
|
||||||
|
self.aspectRatio = aspectRatio
|
||||||
|
if audioChannels is not None:
|
||||||
|
self.audioChannels = audioChannels
|
||||||
|
if audioCodec is not None:
|
||||||
|
self.audioCodec = audioCodec
|
||||||
|
if videoCodec is not None:
|
||||||
|
self.videoCodec = videoCodec
|
||||||
|
if videoResolution is not None:
|
||||||
|
self.videoResolution = videoResolution
|
||||||
|
if container is not None:
|
||||||
|
self.container = container
|
||||||
|
if videoFrameRate is not None:
|
||||||
|
self.videoFrameRate = videoFrameRate
|
||||||
|
if audioProfile is not None:
|
||||||
|
self.audioProfile = audioProfile
|
||||||
|
if videoProfile is not None:
|
||||||
|
self.videoProfile = videoProfile
|
||||||
|
if Part is not None:
|
||||||
|
self.Part = Part
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadataGenre(BaseModel):
|
||||||
|
def __init__(self, tag: str = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadataGenre
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
tag: str
|
||||||
|
"""
|
||||||
|
if tag is not None:
|
||||||
|
self.tag = tag
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadataDirector(BaseModel):
|
||||||
|
def __init__(self, tag: str = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadataDirector
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
tag: str
|
||||||
|
"""
|
||||||
|
if tag is not None:
|
||||||
|
self.tag = tag
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadataWriter(BaseModel):
|
||||||
|
def __init__(self, tag: str = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadataWriter
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
tag: str
|
||||||
|
"""
|
||||||
|
if tag is not None:
|
||||||
|
self.tag = tag
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadataCountry(BaseModel):
|
||||||
|
def __init__(self, tag: str = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadataCountry
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
tag: str
|
||||||
|
"""
|
||||||
|
if tag is not None:
|
||||||
|
self.tag = tag
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadataRole(BaseModel):
|
||||||
|
def __init__(self, tag: str = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadataRole
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
tag: str
|
||||||
|
"""
|
||||||
|
if tag is not None:
|
||||||
|
self.tag = tag
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerMetadata(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
allowSync: bool = None,
|
||||||
|
librarySectionID: float = None,
|
||||||
|
librarySectionTitle: str = None,
|
||||||
|
librarySectionUUID: str = None,
|
||||||
|
personal: bool = None,
|
||||||
|
sourceTitle: str = None,
|
||||||
|
ratingKey: float = None,
|
||||||
|
key: str = None,
|
||||||
|
guid: str = None,
|
||||||
|
studio: str = None,
|
||||||
|
title: str = None,
|
||||||
|
contentRating: str = None,
|
||||||
|
summary: str = None,
|
||||||
|
rating: float = None,
|
||||||
|
audienceRating: float = None,
|
||||||
|
year: float = None,
|
||||||
|
tagline: str = None,
|
||||||
|
thumb: str = None,
|
||||||
|
art: str = None,
|
||||||
|
duration: float = None,
|
||||||
|
originallyAvailableAt: str = None,
|
||||||
|
addedAt: float = None,
|
||||||
|
updatedAt: float = None,
|
||||||
|
audienceRatingImage: str = None,
|
||||||
|
chapterSource: str = None,
|
||||||
|
primaryExtraKey: str = None,
|
||||||
|
ratingImage: str = None,
|
||||||
|
Media: List[MediaContainerMetadataMedia] = None,
|
||||||
|
Genre: List[MediaContainerMetadataGenre] = None,
|
||||||
|
Director: List[MediaContainerMetadataDirector] = None,
|
||||||
|
Writer: List[MediaContainerMetadataWriter] = None,
|
||||||
|
Country: List[MediaContainerMetadataCountry] = None,
|
||||||
|
Role: List[MediaContainerMetadataRole] = None,
|
||||||
|
type_: str = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerMetadata
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
allowSync: bool
|
||||||
|
librarySectionID: float
|
||||||
|
librarySectionTitle: str
|
||||||
|
librarySectionUUID: str
|
||||||
|
personal: bool
|
||||||
|
sourceTitle: str
|
||||||
|
ratingKey: float
|
||||||
|
key: str
|
||||||
|
guid: str
|
||||||
|
studio: str
|
||||||
|
title: str
|
||||||
|
contentRating: str
|
||||||
|
summary: str
|
||||||
|
rating: float
|
||||||
|
audienceRating: float
|
||||||
|
year: float
|
||||||
|
tagline: str
|
||||||
|
thumb: str
|
||||||
|
art: str
|
||||||
|
duration: float
|
||||||
|
originallyAvailableAt: str
|
||||||
|
addedAt: float
|
||||||
|
updatedAt: float
|
||||||
|
audienceRatingImage: str
|
||||||
|
chapterSource: str
|
||||||
|
primaryExtraKey: str
|
||||||
|
ratingImage: str
|
||||||
|
Media: list of MediaContainerMetadataMedia
|
||||||
|
Genre: list of MediaContainerMetadataGenre
|
||||||
|
Director: list of MediaContainerMetadataDirector
|
||||||
|
Writer: list of MediaContainerMetadataWriter
|
||||||
|
Country: list of MediaContainerMetadataCountry
|
||||||
|
Role: list of MediaContainerMetadataRole
|
||||||
|
type_: str
|
||||||
|
"""
|
||||||
|
if allowSync is not None:
|
||||||
|
self.allowSync = allowSync
|
||||||
|
if librarySectionID is not None:
|
||||||
|
self.librarySectionID = librarySectionID
|
||||||
|
if librarySectionTitle is not None:
|
||||||
|
self.librarySectionTitle = librarySectionTitle
|
||||||
|
if librarySectionUUID is not None:
|
||||||
|
self.librarySectionUUID = librarySectionUUID
|
||||||
|
if personal is not None:
|
||||||
|
self.personal = personal
|
||||||
|
if sourceTitle is not None:
|
||||||
|
self.sourceTitle = sourceTitle
|
||||||
|
if ratingKey is not None:
|
||||||
|
self.ratingKey = ratingKey
|
||||||
|
if key is not None:
|
||||||
|
self.key = key
|
||||||
|
if guid is not None:
|
||||||
|
self.guid = guid
|
||||||
|
if studio is not None:
|
||||||
|
self.studio = studio
|
||||||
|
if title is not None:
|
||||||
|
self.title = title
|
||||||
|
if contentRating is not None:
|
||||||
|
self.contentRating = contentRating
|
||||||
|
if summary is not None:
|
||||||
|
self.summary = summary
|
||||||
|
if rating is not None:
|
||||||
|
self.rating = rating
|
||||||
|
if audienceRating is not None:
|
||||||
|
self.audienceRating = audienceRating
|
||||||
|
if year is not None:
|
||||||
|
self.year = year
|
||||||
|
if tagline is not None:
|
||||||
|
self.tagline = tagline
|
||||||
|
if thumb is not None:
|
||||||
|
self.thumb = thumb
|
||||||
|
if art is not None:
|
||||||
|
self.art = art
|
||||||
|
if duration is not None:
|
||||||
|
self.duration = duration
|
||||||
|
if originallyAvailableAt is not None:
|
||||||
|
self.originallyAvailableAt = originallyAvailableAt
|
||||||
|
if addedAt is not None:
|
||||||
|
self.addedAt = addedAt
|
||||||
|
if updatedAt is not None:
|
||||||
|
self.updatedAt = updatedAt
|
||||||
|
if audienceRatingImage is not None:
|
||||||
|
self.audienceRatingImage = audienceRatingImage
|
||||||
|
if chapterSource is not None:
|
||||||
|
self.chapterSource = chapterSource
|
||||||
|
if primaryExtraKey is not None:
|
||||||
|
self.primaryExtraKey = primaryExtraKey
|
||||||
|
if ratingImage is not None:
|
||||||
|
self.ratingImage = ratingImage
|
||||||
|
if Media is not None:
|
||||||
|
self.Media = Media
|
||||||
|
if Genre is not None:
|
||||||
|
self.Genre = Genre
|
||||||
|
if Director is not None:
|
||||||
|
self.Director = Director
|
||||||
|
if Writer is not None:
|
||||||
|
self.Writer = Writer
|
||||||
|
if Country is not None:
|
||||||
|
self.Country = Country
|
||||||
|
if Role is not None:
|
||||||
|
self.Role = Role
|
||||||
|
if type_ is not None:
|
||||||
|
self.type_ = type_
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerProvider(BaseModel):
|
||||||
|
def __init__(self, key: str = None, title: str = None, type_: str = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerProvider
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
key: str
|
||||||
|
title: str
|
||||||
|
type_: str
|
||||||
|
"""
|
||||||
|
if key is not None:
|
||||||
|
self.key = key
|
||||||
|
if title is not None:
|
||||||
|
self.title = title
|
||||||
|
if type_ is not None:
|
||||||
|
self.type_ = type_
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainer(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
size: float = None,
|
||||||
|
identifier: str = None,
|
||||||
|
mediaTagPrefix: str = None,
|
||||||
|
mediaTagVersion: float = None,
|
||||||
|
Metadata: List[MediaContainerMetadata] = None,
|
||||||
|
Provider: List[MediaContainerProvider] = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainer
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
size: float
|
||||||
|
identifier: str
|
||||||
|
mediaTagPrefix: str
|
||||||
|
mediaTagVersion: float
|
||||||
|
Metadata: list of MediaContainerMetadata
|
||||||
|
Provider: list of MediaContainerProvider
|
||||||
|
"""
|
||||||
|
if size is not None:
|
||||||
|
self.size = size
|
||||||
|
if identifier is not None:
|
||||||
|
self.identifier = identifier
|
||||||
|
if mediaTagPrefix is not None:
|
||||||
|
self.mediaTagPrefix = mediaTagPrefix
|
||||||
|
if mediaTagVersion is not None:
|
||||||
|
self.mediaTagVersion = mediaTagVersion
|
||||||
|
if Metadata is not None:
|
||||||
|
self.Metadata = Metadata
|
||||||
|
if Provider is not None:
|
||||||
|
self.Provider = Provider
|
||||||
|
|
||||||
|
|
||||||
|
class GetSearchResultsResponse(BaseModel):
|
||||||
|
def __init__(self, MediaContainer: MediaContainer = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize GetSearchResultsResponse
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
MediaContainer: MediaContainer
|
||||||
|
"""
|
||||||
|
if MediaContainer is not None:
|
||||||
|
self.MediaContainer = MediaContainer
|
||||||
90
src/plexsdk/models/GetServerActivitiesResponse.py
Normal file
90
src/plexsdk/models/GetServerActivitiesResponse.py
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
from .base import BaseModel
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
class Context(BaseModel):
|
||||||
|
def __init__(self, librarySectionID: str = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize Context
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
librarySectionID: str
|
||||||
|
"""
|
||||||
|
if librarySectionID is not None:
|
||||||
|
self.librarySectionID = librarySectionID
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerActivity(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
uuid: str = None,
|
||||||
|
cancellable: bool = None,
|
||||||
|
userID: float = None,
|
||||||
|
title: str = None,
|
||||||
|
subtitle: str = None,
|
||||||
|
progress: float = None,
|
||||||
|
Context: Context = None,
|
||||||
|
type_: str = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerActivity
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
uuid: str
|
||||||
|
cancellable: bool
|
||||||
|
userID: float
|
||||||
|
title: str
|
||||||
|
subtitle: str
|
||||||
|
progress: float
|
||||||
|
Context: Context
|
||||||
|
type_: str
|
||||||
|
"""
|
||||||
|
if uuid is not None:
|
||||||
|
self.uuid = uuid
|
||||||
|
if cancellable is not None:
|
||||||
|
self.cancellable = cancellable
|
||||||
|
if userID is not None:
|
||||||
|
self.userID = userID
|
||||||
|
if title is not None:
|
||||||
|
self.title = title
|
||||||
|
if subtitle is not None:
|
||||||
|
self.subtitle = subtitle
|
||||||
|
if progress is not None:
|
||||||
|
self.progress = progress
|
||||||
|
if Context is not None:
|
||||||
|
self.Context = Context
|
||||||
|
if type_ is not None:
|
||||||
|
self.type_ = type_
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainer(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
size: float = None,
|
||||||
|
Activity: List[MediaContainerActivity] = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainer
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
size: float
|
||||||
|
Activity: list of MediaContainerActivity
|
||||||
|
"""
|
||||||
|
if size is not None:
|
||||||
|
self.size = size
|
||||||
|
if Activity is not None:
|
||||||
|
self.Activity = Activity
|
||||||
|
|
||||||
|
|
||||||
|
class GetServerActivitiesResponse(BaseModel):
|
||||||
|
def __init__(self, MediaContainer: MediaContainer = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize GetServerActivitiesResponse
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
MediaContainer: MediaContainer
|
||||||
|
"""
|
||||||
|
if MediaContainer is not None:
|
||||||
|
self.MediaContainer = MediaContainer
|
||||||
250
src/plexsdk/models/GetServerCapabilitiesResponse.py
Normal file
250
src/plexsdk/models/GetServerCapabilitiesResponse.py
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
from .base import BaseModel
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerDirectory(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self, count: float = None, key: str = None, title: str = None, **kwargs
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerDirectory
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
count: float
|
||||||
|
key: str
|
||||||
|
title: str
|
||||||
|
"""
|
||||||
|
if count is not None:
|
||||||
|
self.count = count
|
||||||
|
if key is not None:
|
||||||
|
self.key = key
|
||||||
|
if title is not None:
|
||||||
|
self.title = title
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainer(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
size: float = None,
|
||||||
|
allowCameraUpload: bool = None,
|
||||||
|
allowChannelAccess: bool = None,
|
||||||
|
allowMediaDeletion: bool = None,
|
||||||
|
allowSharing: bool = None,
|
||||||
|
allowSync: bool = None,
|
||||||
|
allowTuners: bool = None,
|
||||||
|
backgroundProcessing: bool = None,
|
||||||
|
certificate: bool = None,
|
||||||
|
companionProxy: bool = None,
|
||||||
|
countryCode: str = None,
|
||||||
|
diagnostics: str = None,
|
||||||
|
eventStream: bool = None,
|
||||||
|
friendlyName: str = None,
|
||||||
|
hubSearch: bool = None,
|
||||||
|
itemClusters: bool = None,
|
||||||
|
livetv: float = None,
|
||||||
|
machineIdentifier: str = None,
|
||||||
|
mediaProviders: bool = None,
|
||||||
|
multiuser: bool = None,
|
||||||
|
musicAnalysis: float = None,
|
||||||
|
myPlex: bool = None,
|
||||||
|
myPlexMappingState: str = None,
|
||||||
|
myPlexSigninState: str = None,
|
||||||
|
myPlexSubscription: bool = None,
|
||||||
|
myPlexUsername: str = None,
|
||||||
|
offlineTranscode: float = None,
|
||||||
|
ownerFeatures: str = None,
|
||||||
|
photoAutoTag: bool = None,
|
||||||
|
platform: str = None,
|
||||||
|
platformVersion: str = None,
|
||||||
|
pluginHost: bool = None,
|
||||||
|
pushNotifications: bool = None,
|
||||||
|
readOnlyLibraries: bool = None,
|
||||||
|
streamingBrainABRVersion: float = None,
|
||||||
|
streamingBrainVersion: float = None,
|
||||||
|
sync: bool = None,
|
||||||
|
transcoderActiveVideoSessions: float = None,
|
||||||
|
transcoderAudio: bool = None,
|
||||||
|
transcoderLyrics: bool = None,
|
||||||
|
transcoderPhoto: bool = None,
|
||||||
|
transcoderSubtitles: bool = None,
|
||||||
|
transcoderVideo: bool = None,
|
||||||
|
transcoderVideoBitrates: str = None,
|
||||||
|
transcoderVideoQualities: str = None,
|
||||||
|
transcoderVideoResolutions: str = None,
|
||||||
|
updatedAt: float = None,
|
||||||
|
updater: bool = None,
|
||||||
|
version: str = None,
|
||||||
|
voiceSearch: bool = None,
|
||||||
|
Directory: List[MediaContainerDirectory] = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainer
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
size: float
|
||||||
|
allowCameraUpload: bool
|
||||||
|
allowChannelAccess: bool
|
||||||
|
allowMediaDeletion: bool
|
||||||
|
allowSharing: bool
|
||||||
|
allowSync: bool
|
||||||
|
allowTuners: bool
|
||||||
|
backgroundProcessing: bool
|
||||||
|
certificate: bool
|
||||||
|
companionProxy: bool
|
||||||
|
countryCode: str
|
||||||
|
diagnostics: str
|
||||||
|
eventStream: bool
|
||||||
|
friendlyName: str
|
||||||
|
hubSearch: bool
|
||||||
|
itemClusters: bool
|
||||||
|
livetv: float
|
||||||
|
machineIdentifier: str
|
||||||
|
mediaProviders: bool
|
||||||
|
multiuser: bool
|
||||||
|
musicAnalysis: float
|
||||||
|
myPlex: bool
|
||||||
|
myPlexMappingState: str
|
||||||
|
myPlexSigninState: str
|
||||||
|
myPlexSubscription: bool
|
||||||
|
myPlexUsername: str
|
||||||
|
offlineTranscode: float
|
||||||
|
ownerFeatures: str
|
||||||
|
photoAutoTag: bool
|
||||||
|
platform: str
|
||||||
|
platformVersion: str
|
||||||
|
pluginHost: bool
|
||||||
|
pushNotifications: bool
|
||||||
|
readOnlyLibraries: bool
|
||||||
|
streamingBrainABRVersion: float
|
||||||
|
streamingBrainVersion: float
|
||||||
|
sync: bool
|
||||||
|
transcoderActiveVideoSessions: float
|
||||||
|
transcoderAudio: bool
|
||||||
|
transcoderLyrics: bool
|
||||||
|
transcoderPhoto: bool
|
||||||
|
transcoderSubtitles: bool
|
||||||
|
transcoderVideo: bool
|
||||||
|
transcoderVideoBitrates: str
|
||||||
|
transcoderVideoQualities: str
|
||||||
|
transcoderVideoResolutions: str
|
||||||
|
updatedAt: float
|
||||||
|
updater: bool
|
||||||
|
version: str
|
||||||
|
voiceSearch: bool
|
||||||
|
Directory: list of MediaContainerDirectory
|
||||||
|
"""
|
||||||
|
if size is not None:
|
||||||
|
self.size = size
|
||||||
|
if allowCameraUpload is not None:
|
||||||
|
self.allowCameraUpload = allowCameraUpload
|
||||||
|
if allowChannelAccess is not None:
|
||||||
|
self.allowChannelAccess = allowChannelAccess
|
||||||
|
if allowMediaDeletion is not None:
|
||||||
|
self.allowMediaDeletion = allowMediaDeletion
|
||||||
|
if allowSharing is not None:
|
||||||
|
self.allowSharing = allowSharing
|
||||||
|
if allowSync is not None:
|
||||||
|
self.allowSync = allowSync
|
||||||
|
if allowTuners is not None:
|
||||||
|
self.allowTuners = allowTuners
|
||||||
|
if backgroundProcessing is not None:
|
||||||
|
self.backgroundProcessing = backgroundProcessing
|
||||||
|
if certificate is not None:
|
||||||
|
self.certificate = certificate
|
||||||
|
if companionProxy is not None:
|
||||||
|
self.companionProxy = companionProxy
|
||||||
|
if countryCode is not None:
|
||||||
|
self.countryCode = countryCode
|
||||||
|
if diagnostics is not None:
|
||||||
|
self.diagnostics = diagnostics
|
||||||
|
if eventStream is not None:
|
||||||
|
self.eventStream = eventStream
|
||||||
|
if friendlyName is not None:
|
||||||
|
self.friendlyName = friendlyName
|
||||||
|
if hubSearch is not None:
|
||||||
|
self.hubSearch = hubSearch
|
||||||
|
if itemClusters is not None:
|
||||||
|
self.itemClusters = itemClusters
|
||||||
|
if livetv is not None:
|
||||||
|
self.livetv = livetv
|
||||||
|
if machineIdentifier is not None:
|
||||||
|
self.machineIdentifier = machineIdentifier
|
||||||
|
if mediaProviders is not None:
|
||||||
|
self.mediaProviders = mediaProviders
|
||||||
|
if multiuser is not None:
|
||||||
|
self.multiuser = multiuser
|
||||||
|
if musicAnalysis is not None:
|
||||||
|
self.musicAnalysis = musicAnalysis
|
||||||
|
if myPlex is not None:
|
||||||
|
self.myPlex = myPlex
|
||||||
|
if myPlexMappingState is not None:
|
||||||
|
self.myPlexMappingState = myPlexMappingState
|
||||||
|
if myPlexSigninState is not None:
|
||||||
|
self.myPlexSigninState = myPlexSigninState
|
||||||
|
if myPlexSubscription is not None:
|
||||||
|
self.myPlexSubscription = myPlexSubscription
|
||||||
|
if myPlexUsername is not None:
|
||||||
|
self.myPlexUsername = myPlexUsername
|
||||||
|
if offlineTranscode is not None:
|
||||||
|
self.offlineTranscode = offlineTranscode
|
||||||
|
if ownerFeatures is not None:
|
||||||
|
self.ownerFeatures = ownerFeatures
|
||||||
|
if photoAutoTag is not None:
|
||||||
|
self.photoAutoTag = photoAutoTag
|
||||||
|
if platform is not None:
|
||||||
|
self.platform = platform
|
||||||
|
if platformVersion is not None:
|
||||||
|
self.platformVersion = platformVersion
|
||||||
|
if pluginHost is not None:
|
||||||
|
self.pluginHost = pluginHost
|
||||||
|
if pushNotifications is not None:
|
||||||
|
self.pushNotifications = pushNotifications
|
||||||
|
if readOnlyLibraries is not None:
|
||||||
|
self.readOnlyLibraries = readOnlyLibraries
|
||||||
|
if streamingBrainABRVersion is not None:
|
||||||
|
self.streamingBrainABRVersion = streamingBrainABRVersion
|
||||||
|
if streamingBrainVersion is not None:
|
||||||
|
self.streamingBrainVersion = streamingBrainVersion
|
||||||
|
if sync is not None:
|
||||||
|
self.sync = sync
|
||||||
|
if transcoderActiveVideoSessions is not None:
|
||||||
|
self.transcoderActiveVideoSessions = transcoderActiveVideoSessions
|
||||||
|
if transcoderAudio is not None:
|
||||||
|
self.transcoderAudio = transcoderAudio
|
||||||
|
if transcoderLyrics is not None:
|
||||||
|
self.transcoderLyrics = transcoderLyrics
|
||||||
|
if transcoderPhoto is not None:
|
||||||
|
self.transcoderPhoto = transcoderPhoto
|
||||||
|
if transcoderSubtitles is not None:
|
||||||
|
self.transcoderSubtitles = transcoderSubtitles
|
||||||
|
if transcoderVideo is not None:
|
||||||
|
self.transcoderVideo = transcoderVideo
|
||||||
|
if transcoderVideoBitrates is not None:
|
||||||
|
self.transcoderVideoBitrates = transcoderVideoBitrates
|
||||||
|
if transcoderVideoQualities is not None:
|
||||||
|
self.transcoderVideoQualities = transcoderVideoQualities
|
||||||
|
if transcoderVideoResolutions is not None:
|
||||||
|
self.transcoderVideoResolutions = transcoderVideoResolutions
|
||||||
|
if updatedAt is not None:
|
||||||
|
self.updatedAt = updatedAt
|
||||||
|
if updater is not None:
|
||||||
|
self.updater = updater
|
||||||
|
if version is not None:
|
||||||
|
self.version = version
|
||||||
|
if voiceSearch is not None:
|
||||||
|
self.voiceSearch = voiceSearch
|
||||||
|
if Directory is not None:
|
||||||
|
self.Directory = Directory
|
||||||
|
|
||||||
|
|
||||||
|
class GetServerCapabilitiesResponse(BaseModel):
|
||||||
|
def __init__(self, MediaContainer: MediaContainer = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize GetServerCapabilitiesResponse
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
MediaContainer: MediaContainer
|
||||||
|
"""
|
||||||
|
if MediaContainer is not None:
|
||||||
|
self.MediaContainer = MediaContainer
|
||||||
41
src/plexsdk/models/GetServerIdentityResponse.py
Normal file
41
src/plexsdk/models/GetServerIdentityResponse.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
from .base import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainer(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
size: float = None,
|
||||||
|
claimed: bool = None,
|
||||||
|
machineIdentifier: str = None,
|
||||||
|
version: str = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainer
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
size: float
|
||||||
|
claimed: bool
|
||||||
|
machineIdentifier: str
|
||||||
|
version: str
|
||||||
|
"""
|
||||||
|
if size is not None:
|
||||||
|
self.size = size
|
||||||
|
if claimed is not None:
|
||||||
|
self.claimed = claimed
|
||||||
|
if machineIdentifier is not None:
|
||||||
|
self.machineIdentifier = machineIdentifier
|
||||||
|
if version is not None:
|
||||||
|
self.version = version
|
||||||
|
|
||||||
|
|
||||||
|
class GetServerIdentityResponse(BaseModel):
|
||||||
|
def __init__(self, MediaContainer: MediaContainer = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize GetServerIdentityResponse
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
MediaContainer: MediaContainer
|
||||||
|
"""
|
||||||
|
if MediaContainer is not None:
|
||||||
|
self.MediaContainer = MediaContainer
|
||||||
67
src/plexsdk/models/GetServerListResponse.py
Normal file
67
src/plexsdk/models/GetServerListResponse.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
from .base import BaseModel
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerServer(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
name: str = None,
|
||||||
|
host: str = None,
|
||||||
|
address: str = None,
|
||||||
|
port: float = None,
|
||||||
|
machineIdentifier: str = None,
|
||||||
|
version: str = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerServer
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
name: str
|
||||||
|
host: str
|
||||||
|
address: str
|
||||||
|
port: float
|
||||||
|
machineIdentifier: str
|
||||||
|
version: str
|
||||||
|
"""
|
||||||
|
if name is not None:
|
||||||
|
self.name = name
|
||||||
|
if host is not None:
|
||||||
|
self.host = host
|
||||||
|
if address is not None:
|
||||||
|
self.address = address
|
||||||
|
if port is not None:
|
||||||
|
self.port = port
|
||||||
|
if machineIdentifier is not None:
|
||||||
|
self.machineIdentifier = machineIdentifier
|
||||||
|
if version is not None:
|
||||||
|
self.version = version
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainer(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self, size: float = None, Server: List[MediaContainerServer] = None, **kwargs
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainer
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
size: float
|
||||||
|
Server: list of MediaContainerServer
|
||||||
|
"""
|
||||||
|
if size is not None:
|
||||||
|
self.size = size
|
||||||
|
if Server is not None:
|
||||||
|
self.Server = Server
|
||||||
|
|
||||||
|
|
||||||
|
class GetServerListResponse(BaseModel):
|
||||||
|
def __init__(self, MediaContainer: MediaContainer = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize GetServerListResponse
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
MediaContainer: MediaContainer
|
||||||
|
"""
|
||||||
|
if MediaContainer is not None:
|
||||||
|
self.MediaContainer = MediaContainer
|
||||||
134
src/plexsdk/models/GetTranscodeSessionsResponse.py
Normal file
134
src/plexsdk/models/GetTranscodeSessionsResponse.py
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
from .base import BaseModel
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainerTranscodeSession(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
key: str = None,
|
||||||
|
throttled: bool = None,
|
||||||
|
complete: bool = None,
|
||||||
|
progress: float = None,
|
||||||
|
size: float = None,
|
||||||
|
speed: float = None,
|
||||||
|
error: bool = None,
|
||||||
|
duration: float = None,
|
||||||
|
context: str = None,
|
||||||
|
sourceVideoCodec: str = None,
|
||||||
|
sourceAudioCodec: str = None,
|
||||||
|
videoDecision: str = None,
|
||||||
|
audioDecision: str = None,
|
||||||
|
protocol: str = None,
|
||||||
|
container: str = None,
|
||||||
|
videoCodec: str = None,
|
||||||
|
audioCodec: str = None,
|
||||||
|
audioChannels: float = None,
|
||||||
|
transcodeHwRequested: bool = None,
|
||||||
|
timeStamp: float = None,
|
||||||
|
maxOffsetAvailable: float = None,
|
||||||
|
minOffsetAvailable: float = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainerTranscodeSession
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
key: str
|
||||||
|
throttled: bool
|
||||||
|
complete: bool
|
||||||
|
progress: float
|
||||||
|
size: float
|
||||||
|
speed: float
|
||||||
|
error: bool
|
||||||
|
duration: float
|
||||||
|
context: str
|
||||||
|
sourceVideoCodec: str
|
||||||
|
sourceAudioCodec: str
|
||||||
|
videoDecision: str
|
||||||
|
audioDecision: str
|
||||||
|
protocol: str
|
||||||
|
container: str
|
||||||
|
videoCodec: str
|
||||||
|
audioCodec: str
|
||||||
|
audioChannels: float
|
||||||
|
transcodeHwRequested: bool
|
||||||
|
timeStamp: float
|
||||||
|
maxOffsetAvailable: float
|
||||||
|
minOffsetAvailable: float
|
||||||
|
"""
|
||||||
|
if key is not None:
|
||||||
|
self.key = key
|
||||||
|
if throttled is not None:
|
||||||
|
self.throttled = throttled
|
||||||
|
if complete is not None:
|
||||||
|
self.complete = complete
|
||||||
|
if progress is not None:
|
||||||
|
self.progress = progress
|
||||||
|
if size is not None:
|
||||||
|
self.size = size
|
||||||
|
if speed is not None:
|
||||||
|
self.speed = speed
|
||||||
|
if error is not None:
|
||||||
|
self.error = error
|
||||||
|
if duration is not None:
|
||||||
|
self.duration = duration
|
||||||
|
if context is not None:
|
||||||
|
self.context = context
|
||||||
|
if sourceVideoCodec is not None:
|
||||||
|
self.sourceVideoCodec = sourceVideoCodec
|
||||||
|
if sourceAudioCodec is not None:
|
||||||
|
self.sourceAudioCodec = sourceAudioCodec
|
||||||
|
if videoDecision is not None:
|
||||||
|
self.videoDecision = videoDecision
|
||||||
|
if audioDecision is not None:
|
||||||
|
self.audioDecision = audioDecision
|
||||||
|
if protocol is not None:
|
||||||
|
self.protocol = protocol
|
||||||
|
if container is not None:
|
||||||
|
self.container = container
|
||||||
|
if videoCodec is not None:
|
||||||
|
self.videoCodec = videoCodec
|
||||||
|
if audioCodec is not None:
|
||||||
|
self.audioCodec = audioCodec
|
||||||
|
if audioChannels is not None:
|
||||||
|
self.audioChannels = audioChannels
|
||||||
|
if transcodeHwRequested is not None:
|
||||||
|
self.transcodeHwRequested = transcodeHwRequested
|
||||||
|
if timeStamp is not None:
|
||||||
|
self.timeStamp = timeStamp
|
||||||
|
if maxOffsetAvailable is not None:
|
||||||
|
self.maxOffsetAvailable = maxOffsetAvailable
|
||||||
|
if minOffsetAvailable is not None:
|
||||||
|
self.minOffsetAvailable = minOffsetAvailable
|
||||||
|
|
||||||
|
|
||||||
|
class MediaContainer(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
size: float = None,
|
||||||
|
TranscodeSession: List[MediaContainerTranscodeSession] = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Initialize MediaContainer
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
size: float
|
||||||
|
TranscodeSession: list of MediaContainerTranscodeSession
|
||||||
|
"""
|
||||||
|
if size is not None:
|
||||||
|
self.size = size
|
||||||
|
if TranscodeSession is not None:
|
||||||
|
self.TranscodeSession = TranscodeSession
|
||||||
|
|
||||||
|
|
||||||
|
class GetTranscodeSessionsResponse(BaseModel):
|
||||||
|
def __init__(self, MediaContainer: MediaContainer = None, **kwargs):
|
||||||
|
"""
|
||||||
|
Initialize GetTranscodeSessionsResponse
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
MediaContainer: MediaContainer
|
||||||
|
"""
|
||||||
|
if MediaContainer is not None:
|
||||||
|
self.MediaContainer = MediaContainer
|
||||||
7
src/plexsdk/models/IncludeDetails.py
Normal file
7
src/plexsdk/models/IncludeDetails.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class IncludeDetails(Enum):
|
||||||
|
= "0"
|
||||||
|
V1 = "1"
|
||||||
|
def list():
|
||||||
|
return list(map(lambda x: x.value, IncludeDetails._member_map_.values()))
|
||||||
10
src/plexsdk/models/Level.py
Normal file
10
src/plexsdk/models/Level.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class Level(Enum):
|
||||||
|
= "0"
|
||||||
|
V1 = "1"
|
||||||
|
V2 = "2"
|
||||||
|
V3 = "3"
|
||||||
|
V4 = "4"
|
||||||
|
def list():
|
||||||
|
return list(map(lambda x: x.value, Level._member_map_.values()))
|
||||||
7
src/plexsdk/models/MinSize.py
Normal file
7
src/plexsdk/models/MinSize.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class MinSize(Enum):
|
||||||
|
= "0"
|
||||||
|
V1 = "1"
|
||||||
|
def list():
|
||||||
|
return list(map(lambda x: x.value, MinSize._member_map_.values()))
|
||||||
7
src/plexsdk/models/OnlyTransient.py
Normal file
7
src/plexsdk/models/OnlyTransient.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class OnlyTransient(Enum):
|
||||||
|
= "0"
|
||||||
|
V1 = "1"
|
||||||
|
def list():
|
||||||
|
return list(map(lambda x: x.value, OnlyTransient._member_map_.values()))
|
||||||
10
src/plexsdk/models/PlaylistType.py
Normal file
10
src/plexsdk/models/PlaylistType.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class PlaylistType(Enum):
|
||||||
|
AUDIO = "audio"
|
||||||
|
VIDEO = "video"
|
||||||
|
PHOTO = "photo"
|
||||||
|
|
||||||
|
def list():
|
||||||
|
return list(map(lambda x: x.value, PlaylistType._member_map_.values()))
|
||||||
90
src/plexsdk/models/README.md
Normal file
90
src/plexsdk/models/README.md
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
# PlexSDK Models
|
||||||
|
|
||||||
|
A list of all models.
|
||||||
|
- [Level](#level)
|
||||||
|
- [Upscale](#upscale)
|
||||||
|
- [Type](#type)
|
||||||
|
- [Smart](#smart)
|
||||||
|
- [Force](#force)
|
||||||
|
- [SecurityType](#securitytype)
|
||||||
|
- [Scope](#scope)
|
||||||
|
- [Download](#download)
|
||||||
|
- [Tonight](#tonight)
|
||||||
|
- [Skip](#skip)
|
||||||
|
- [State](#state)
|
||||||
|
- [GetServerCapabilitiesResponse](#getservercapabilitiesresponse)
|
||||||
|
- [GetServerActivitiesResponse](#getserveractivitiesresponse)
|
||||||
|
- [GetButlerTasksResponse](#getbutlertasksresponse)
|
||||||
|
- [GetAvailableClientsResponse](#getavailableclientsresponse)
|
||||||
|
- [GetDevicesResponse](#getdevicesresponse)
|
||||||
|
- [GetServerIdentityResponse](#getserveridentityresponse)
|
||||||
|
- [GetRecentlyAddedResponse](#getrecentlyaddedresponse)
|
||||||
|
- [GetOnDeckResponse](#getondeckresponse)
|
||||||
|
- [GetMyPlexAccountResponse](#getmyplexaccountresponse)
|
||||||
|
- [GetSearchResultsResponse](#getsearchresultsresponse)
|
||||||
|
- [GetServerListResponse](#getserverlistresponse)
|
||||||
|
- [GetTranscodeSessionsResponse](#gettranscodesessionsresponse)
|
||||||
|
- [TaskName](#taskname)
|
||||||
|
- [OnlyTransient](#onlytransient)
|
||||||
|
- [IncludeDetails](#includedetails)
|
||||||
|
- [MinSize](#minsize)
|
||||||
|
- [PlaylistType](#playlisttype)
|
||||||
|
|
||||||
|
## Level
|
||||||
|
|
||||||
|
## Upscale
|
||||||
|
|
||||||
|
## Type
|
||||||
|
|
||||||
|
## Smart
|
||||||
|
|
||||||
|
## Force
|
||||||
|
|
||||||
|
## SecurityType
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
## Download
|
||||||
|
|
||||||
|
## Tonight
|
||||||
|
|
||||||
|
## Skip
|
||||||
|
|
||||||
|
## State
|
||||||
|
|
||||||
|
## GetServerCapabilitiesResponse
|
||||||
|
|
||||||
|
## GetServerActivitiesResponse
|
||||||
|
|
||||||
|
## GetButlerTasksResponse
|
||||||
|
|
||||||
|
## GetAvailableClientsResponse
|
||||||
|
|
||||||
|
## GetDevicesResponse
|
||||||
|
|
||||||
|
## GetServerIdentityResponse
|
||||||
|
|
||||||
|
## GetRecentlyAddedResponse
|
||||||
|
|
||||||
|
## GetOnDeckResponse
|
||||||
|
|
||||||
|
## GetMyPlexAccountResponse
|
||||||
|
|
||||||
|
## GetSearchResultsResponse
|
||||||
|
|
||||||
|
## GetServerListResponse
|
||||||
|
|
||||||
|
## GetTranscodeSessionsResponse
|
||||||
|
|
||||||
|
## TaskName
|
||||||
|
|
||||||
|
## OnlyTransient
|
||||||
|
|
||||||
|
## IncludeDetails
|
||||||
|
|
||||||
|
## MinSize
|
||||||
|
|
||||||
|
## PlaylistType
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
8
src/plexsdk/models/Scope.py
Normal file
8
src/plexsdk/models/Scope.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class Scope(Enum):
|
||||||
|
ALL = "all"
|
||||||
|
|
||||||
|
def list():
|
||||||
|
return list(map(lambda x: x.value, Scope._member_map_.values()))
|
||||||
8
src/plexsdk/models/SecurityType.py
Normal file
8
src/plexsdk/models/SecurityType.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class SecurityType(Enum):
|
||||||
|
DELEGATION = "delegation"
|
||||||
|
|
||||||
|
def list():
|
||||||
|
return list(map(lambda x: x.value, SecurityType._member_map_.values()))
|
||||||
7
src/plexsdk/models/Skip.py
Normal file
7
src/plexsdk/models/Skip.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class Skip(Enum):
|
||||||
|
= "0"
|
||||||
|
V1 = "1"
|
||||||
|
def list():
|
||||||
|
return list(map(lambda x: x.value, Skip._member_map_.values()))
|
||||||
7
src/plexsdk/models/Smart.py
Normal file
7
src/plexsdk/models/Smart.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class Smart(Enum):
|
||||||
|
= "0"
|
||||||
|
V1 = "1"
|
||||||
|
def list():
|
||||||
|
return list(map(lambda x: x.value, Smart._member_map_.values()))
|
||||||
10
src/plexsdk/models/State.py
Normal file
10
src/plexsdk/models/State.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class State(Enum):
|
||||||
|
PLAYING = "playing"
|
||||||
|
PAUSED = "paused"
|
||||||
|
STOPPED = "stopped"
|
||||||
|
|
||||||
|
def list():
|
||||||
|
return list(map(lambda x: x.value, State._member_map_.values()))
|
||||||
21
src/plexsdk/models/TaskName.py
Normal file
21
src/plexsdk/models/TaskName.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class TaskName(Enum):
|
||||||
|
BACKUPDATABASE = "BackupDatabase"
|
||||||
|
BUILDGRACENOTECOLLECTIONS = "BuildGracenoteCollections"
|
||||||
|
CHECKFORUPDATES = "CheckForUpdates"
|
||||||
|
CLEANOLDBUNDLES = "CleanOldBundles"
|
||||||
|
CLEANOLDCACHEFILES = "CleanOldCacheFiles"
|
||||||
|
DEEPMEDIAANALYSIS = "DeepMediaAnalysis"
|
||||||
|
GENERATEAUTOTAGS = "GenerateAutoTags"
|
||||||
|
GENERATECHAPTERTHUMBS = "GenerateChapterThumbs"
|
||||||
|
GENERATEMEDIAINDEXFILES = "GenerateMediaIndexFiles"
|
||||||
|
OPTIMIZEDATABASE = "OptimizeDatabase"
|
||||||
|
REFRESHLIBRARIES = "RefreshLibraries"
|
||||||
|
REFRESHLOCALMEDIA = "RefreshLocalMedia"
|
||||||
|
REFRESHPERIODICMETADATA = "RefreshPeriodicMetadata"
|
||||||
|
UPGRADEMEDIAANALYSIS = "UpgradeMediaAnalysis"
|
||||||
|
|
||||||
|
def list():
|
||||||
|
return list(map(lambda x: x.value, TaskName._member_map_.values()))
|
||||||
7
src/plexsdk/models/Tonight.py
Normal file
7
src/plexsdk/models/Tonight.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class Tonight(Enum):
|
||||||
|
= "0"
|
||||||
|
V1 = "1"
|
||||||
|
def list():
|
||||||
|
return list(map(lambda x: x.value, Tonight._member_map_.values()))
|
||||||
10
src/plexsdk/models/Type.py
Normal file
10
src/plexsdk/models/Type.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class Type(Enum):
|
||||||
|
AUDIO = "audio"
|
||||||
|
VIDEO = "video"
|
||||||
|
PHOTO = "photo"
|
||||||
|
|
||||||
|
def list():
|
||||||
|
return list(map(lambda x: x.value, Type._member_map_.values()))
|
||||||
7
src/plexsdk/models/Upscale.py
Normal file
7
src/plexsdk/models/Upscale.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class Upscale(Enum):
|
||||||
|
= "0"
|
||||||
|
V1 = "1"
|
||||||
|
def list():
|
||||||
|
return list(map(lambda x: x.value, Upscale._member_map_.values()))
|
||||||
28
src/plexsdk/models/__init__.py
Normal file
28
src/plexsdk/models/__init__.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
from .Level import Level
|
||||||
|
from .Upscale import Upscale
|
||||||
|
from .Type import Type
|
||||||
|
from .Smart import Smart
|
||||||
|
from .Force import Force
|
||||||
|
from .SecurityType import SecurityType
|
||||||
|
from .Scope import Scope
|
||||||
|
from .Download import Download
|
||||||
|
from .Tonight import Tonight
|
||||||
|
from .Skip import Skip
|
||||||
|
from .State import State
|
||||||
|
from .GetServerCapabilitiesResponse import GetServerCapabilitiesResponse
|
||||||
|
from .GetServerActivitiesResponse import GetServerActivitiesResponse
|
||||||
|
from .GetButlerTasksResponse import GetButlerTasksResponse
|
||||||
|
from .GetAvailableClientsResponse import GetAvailableClientsResponse
|
||||||
|
from .GetDevicesResponse import GetDevicesResponse
|
||||||
|
from .GetServerIdentityResponse import GetServerIdentityResponse
|
||||||
|
from .GetRecentlyAddedResponse import GetRecentlyAddedResponse
|
||||||
|
from .GetOnDeckResponse import GetOnDeckResponse
|
||||||
|
from .GetMyPlexAccountResponse import GetMyPlexAccountResponse
|
||||||
|
from .GetSearchResultsResponse import GetSearchResultsResponse
|
||||||
|
from .GetServerListResponse import GetServerListResponse
|
||||||
|
from .GetTranscodeSessionsResponse import GetTranscodeSessionsResponse
|
||||||
|
from .TaskName import TaskName
|
||||||
|
from .OnlyTransient import OnlyTransient
|
||||||
|
from .IncludeDetails import IncludeDetails
|
||||||
|
from .MinSize import MinSize
|
||||||
|
from .PlaylistType import PlaylistType
|
||||||
60
src/plexsdk/models/base.py
Normal file
60
src/plexsdk/models/base.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import re
|
||||||
|
from typing import List, Union
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class BaseModel:
|
||||||
|
"""
|
||||||
|
A base class that all models in the SDK inherit from (expect for Enum models).
|
||||||
|
|
||||||
|
Methods
|
||||||
|
-------
|
||||||
|
_pattern_matching(cls, value: str, pattern: str, variable_name: str) -> str:
|
||||||
|
Checks if a value matches a regex pattern.
|
||||||
|
Returns the value if there's a match, otherwise throws an error.
|
||||||
|
_enum_matching(cls, value: Union[str,Enum], enum_values: List[str], variable_name: str) -> str:
|
||||||
|
Checks if a value (str or enum) matches the required enum values.
|
||||||
|
Returns the value if there's a match, otherwise throws an error.
|
||||||
|
_one_of(cls, required_array, all_array, functions_array, input_data):
|
||||||
|
Validates whether an input_data satisfies the oneOf requirements.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _pattern_matching(cls, value: str, pattern: str, variable_name: str):
|
||||||
|
if re.match(r"{}".format(pattern), value):
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Invalid value for {variable_name}: must match {pattern}")
|
||||||
|
|
||||||
|
def _enum_matching(
|
||||||
|
cls, value: Union[str, Enum], enum_values: List[str], variable_name: str
|
||||||
|
):
|
||||||
|
str_value = value.value if isinstance(value, Enum) else value
|
||||||
|
if str_value in enum_values:
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Invalid value for {variable_name}: must match one of {enum_values}"
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _one_of(cls, required_array, all_array, functions_array, input_data):
|
||||||
|
input_array = list(input_data.keys())
|
||||||
|
for model, fields in required_array.items():
|
||||||
|
input_copy = input_array.copy()
|
||||||
|
matches_required = True
|
||||||
|
for param in fields:
|
||||||
|
if param not in input_copy:
|
||||||
|
matches_required = False
|
||||||
|
break
|
||||||
|
input_copy.remove(param)
|
||||||
|
if matches_required:
|
||||||
|
matches_all = True
|
||||||
|
for input in input_copy:
|
||||||
|
if input not in all_array[model]:
|
||||||
|
matches_all = False
|
||||||
|
break
|
||||||
|
if matches_all:
|
||||||
|
return functions_array[model](input_data)
|
||||||
0
src/plexsdk/net/__init__.py
Normal file
0
src/plexsdk/net/__init__.py
Normal file
11
src/plexsdk/net/environment.py
Normal file
11
src/plexsdk/net/environment.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
"""
|
||||||
|
An enum class containing all the possible environments that
|
||||||
|
a user can switch between in this SDK.
|
||||||
|
"""
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class Environment(Enum):
|
||||||
|
"""The environments available for this SDK"""
|
||||||
|
|
||||||
|
DEFAULT = "{protocol}://{ip}:{port}"
|
||||||
280
src/plexsdk/net/http_client.py
Normal file
280
src/plexsdk/net/http_client.py
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
from time import sleep
|
||||||
|
import requests
|
||||||
|
from http_exceptions import HTTPException, client_exceptions, server_exceptions
|
||||||
|
from json import JSONDecodeError
|
||||||
|
from .http_content_types import multipart_form_data_request
|
||||||
|
from .utils import to_serialize, rename_reserved_keys, rename_to_reserved_keys
|
||||||
|
|
||||||
|
|
||||||
|
from .response import ResponseWithHeaders
|
||||||
|
|
||||||
|
|
||||||
|
class HTTPClient:
|
||||||
|
"""
|
||||||
|
Provides functionality for invoking HTTP-based API calls to web services.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_retry_codes = [500, 503, 504]
|
||||||
|
"""list[int]: A list of status codes that invoke a retry."""
|
||||||
|
|
||||||
|
_initial_delay = 150
|
||||||
|
"""int: The delay (in milliseconds) before performing a retry."""
|
||||||
|
|
||||||
|
_max_retries = 3
|
||||||
|
"""int: The maximum number of retries."""
|
||||||
|
|
||||||
|
def __init__(self, hook):
|
||||||
|
self._hook = hook
|
||||||
|
|
||||||
|
def _make_http_request(self, method, endpoint_url, headers, body_input):
|
||||||
|
"""
|
||||||
|
Places API calls according to the HTTP method and content type.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
method : str
|
||||||
|
The type of http call to perform
|
||||||
|
endpoint_url : url
|
||||||
|
The endpoint url to make the http call on
|
||||||
|
headers : dict
|
||||||
|
The http call's headers
|
||||||
|
body_input : Any
|
||||||
|
The request's body
|
||||||
|
"""
|
||||||
|
request_method = getattr(requests, method)
|
||||||
|
serialized_body = rename_to_reserved_keys(to_serialize(body_input))
|
||||||
|
if "Content-type" in headers:
|
||||||
|
data_type, subtype = headers["Content-type"].split("/")
|
||||||
|
if data_type == "multipart":
|
||||||
|
return multipart_form_data_request(
|
||||||
|
method, endpoint_url, headers, serialized_body
|
||||||
|
)
|
||||||
|
if data_type in ["text", "image"]:
|
||||||
|
return request_method(
|
||||||
|
endpoint_url, headers=headers, data=serialized_body
|
||||||
|
)
|
||||||
|
if not serialized_body or method in {"get", "delete"}:
|
||||||
|
return request_method(endpoint_url, headers=headers)
|
||||||
|
return request_method(endpoint_url, headers=headers, json=serialized_body)
|
||||||
|
|
||||||
|
def delete(self, endpoint_url: str, headers: dict, retry: bool = True):
|
||||||
|
"""
|
||||||
|
Places API DELETE request and handles errors
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
endpoint_url : str
|
||||||
|
The endpoint url to make the http call on
|
||||||
|
headers : dict
|
||||||
|
The http call's headers
|
||||||
|
retry : bool
|
||||||
|
A boolean representing wether to attempt a retry
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = self._make_http_request("delete", endpoint_url, headers, None)
|
||||||
|
if response.status_code in self._retry_codes and retry:
|
||||||
|
try_cnt = 1
|
||||||
|
while (
|
||||||
|
response.status_code in self._retry_codes
|
||||||
|
and try_cnt - 1 < self._max_retries
|
||||||
|
):
|
||||||
|
sleep(self._initial_delay ** (try_cnt - 1) / 1000)
|
||||||
|
response = self._make_http_request(
|
||||||
|
"delete", endpoint_url, headers, None
|
||||||
|
)
|
||||||
|
try_cnt += 1
|
||||||
|
|
||||||
|
return self._handle_response(response)
|
||||||
|
|
||||||
|
def get(self, endpoint_url: str, headers: dict, retry: bool = True):
|
||||||
|
"""
|
||||||
|
Places an API GET request and handles errors
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
endpoint_url : str
|
||||||
|
The endpoint url to make the http call on
|
||||||
|
headers : dict
|
||||||
|
The http call's headers
|
||||||
|
retry : bool
|
||||||
|
A boolean representing wether to attempt a retry
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = self._make_http_request("get", endpoint_url, headers, None)
|
||||||
|
if response.status_code in self._retry_codes and retry:
|
||||||
|
try_cnt = 1
|
||||||
|
while (
|
||||||
|
response.status_code in self._retry_codes
|
||||||
|
and try_cnt - 1 < self._max_retries
|
||||||
|
):
|
||||||
|
sleep(self._initial_delay ** (try_cnt - 1) / 1000)
|
||||||
|
response = self._make_http_request("get", endpoint_url, headers, None)
|
||||||
|
try_cnt += 1
|
||||||
|
|
||||||
|
return self._handle_response(response)
|
||||||
|
|
||||||
|
def patch(self, endpoint_url: str, headers: dict, body_input, retry: bool = True):
|
||||||
|
"""
|
||||||
|
Places an API PATCH request and handles errors
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
endpoint_url : str
|
||||||
|
The endpoint url to make the http call on
|
||||||
|
headers : dict
|
||||||
|
The http call's headers
|
||||||
|
body_input:
|
||||||
|
The patch request body
|
||||||
|
retry : bool
|
||||||
|
A boolean representing wether to attempt a retry
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = self._make_http_request("patch", endpoint_url, headers, body_input)
|
||||||
|
if response.status_code in self._retry_codes and retry:
|
||||||
|
try_cnt = 1
|
||||||
|
while (
|
||||||
|
response.status_code in self._retry_codes
|
||||||
|
and try_cnt - 1 < self._max_retries
|
||||||
|
):
|
||||||
|
sleep(self._initial_delay ** (try_cnt - 1) / 1000)
|
||||||
|
response = self._make_http_request(
|
||||||
|
"patch", endpoint_url, headers, body_input
|
||||||
|
)
|
||||||
|
try_cnt += 1
|
||||||
|
|
||||||
|
return self._handle_response(response)
|
||||||
|
|
||||||
|
def post(self, endpoint_url: str, headers: dict, body_input, retry: bool = True):
|
||||||
|
"""
|
||||||
|
Places an API POST request and handles errors
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
endpoint_url : str
|
||||||
|
The endpoint url to make the http call on
|
||||||
|
headers : dict
|
||||||
|
The http call's headers
|
||||||
|
body_input:
|
||||||
|
The post request body
|
||||||
|
retry : bool
|
||||||
|
A boolean representing wether to attempt a retry
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = self._make_http_request("post", endpoint_url, headers, body_input)
|
||||||
|
if response.status_code in self._retry_codes and retry:
|
||||||
|
try_cnt = 1
|
||||||
|
while (
|
||||||
|
response.status_code in self._retry_codes
|
||||||
|
and try_cnt - 1 < self._max_retries
|
||||||
|
):
|
||||||
|
sleep(self._initial_delay ** (try_cnt - 1) / 1000)
|
||||||
|
response = self._make_http_request(
|
||||||
|
"post", endpoint_url, headers, body_input
|
||||||
|
)
|
||||||
|
try_cnt += 1
|
||||||
|
|
||||||
|
return self._handle_response(response)
|
||||||
|
|
||||||
|
def put(self, endpoint_url: str, headers: dict, body_input, retry: bool = True):
|
||||||
|
"""
|
||||||
|
Places an API PUT request and handles errors
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
endpoint_url : str
|
||||||
|
The endpoint url to make the http call on
|
||||||
|
headers : dict
|
||||||
|
The http call's headers
|
||||||
|
body_input:
|
||||||
|
The put request body
|
||||||
|
retry : bool
|
||||||
|
A boolean representing whether to attempt a retry
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = self._make_http_request("put", endpoint_url, headers, body_input)
|
||||||
|
if response.status_code in self._retry_codes and retry:
|
||||||
|
try_cnt = 1
|
||||||
|
while (
|
||||||
|
response.status_code in self._retry_codes
|
||||||
|
and try_cnt - 1 < self._max_retries
|
||||||
|
):
|
||||||
|
sleep(self._initial_delay ** (try_cnt - 1) / 1000)
|
||||||
|
response = self._make_http_request(
|
||||||
|
"put", endpoint_url, headers, body_input
|
||||||
|
)
|
||||||
|
try_cnt += 1
|
||||||
|
|
||||||
|
return self._handle_response(response)
|
||||||
|
|
||||||
|
def _create_exception_message(self, response, header: str) -> str:
|
||||||
|
"""
|
||||||
|
Creates an exception message using a specific header
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
response:
|
||||||
|
Response data received from an http request
|
||||||
|
header : str
|
||||||
|
Header name for the created exception
|
||||||
|
"""
|
||||||
|
if header in response.headers:
|
||||||
|
return f"{response.text}, Headers {header}: {response.headers[header]}"
|
||||||
|
return response.text
|
||||||
|
|
||||||
|
def _handle_response(self, response: dict):
|
||||||
|
"""
|
||||||
|
Handles a response from an API call
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
response:
|
||||||
|
Response data received from an http request
|
||||||
|
"""
|
||||||
|
if response.status_code >= 200 and response.status_code < 400:
|
||||||
|
try:
|
||||||
|
return ResponseWithHeaders(
|
||||||
|
rename_reserved_keys(response.json()), response.headers
|
||||||
|
)
|
||||||
|
except JSONDecodeError:
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
self._raise_from_status(response)
|
||||||
|
|
||||||
|
def _raise_from_status(self, response) -> None:
|
||||||
|
"""
|
||||||
|
Raises exception based response status, with additional information appended if useful
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
response:
|
||||||
|
Response data received from an http request
|
||||||
|
"""
|
||||||
|
if response.status_code == 401:
|
||||||
|
raise client_exceptions.UnauthorizedException(
|
||||||
|
message=self._create_exception_message(response, "WWW-Authenticate")
|
||||||
|
)
|
||||||
|
elif response.status_code == 405:
|
||||||
|
# this indicates a bug in the spec if it allows a method that the server rejects
|
||||||
|
raise client_exceptions.MethodNotAllowedException(
|
||||||
|
message=self._create_exception_message(response, "Allow")
|
||||||
|
)
|
||||||
|
elif response.status_code == 407:
|
||||||
|
raise client_exceptions.ProxyAuthenticationRequiredException(
|
||||||
|
message=self._create_exception_message(response, "Proxy-Authenticate")
|
||||||
|
)
|
||||||
|
elif response.status_code == 413:
|
||||||
|
raise client_exceptions.PayloadTooLargeException(
|
||||||
|
message=self._create_exception_message(response, "Retry-After")
|
||||||
|
)
|
||||||
|
elif response.status_code == 429:
|
||||||
|
raise client_exceptions.TooManyRequestsException(
|
||||||
|
message=self._create_exception_message(response, "Retry-After")
|
||||||
|
)
|
||||||
|
elif response.status_code == 503:
|
||||||
|
raise server_exceptions.ServiceUnavailableException(
|
||||||
|
message=self._create_exception_message(response, "Retry-After")
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise HTTPException.from_status_code(status_code=response.status_code)(
|
||||||
|
message=response.text
|
||||||
|
)
|
||||||
42
src/plexsdk/net/http_content_types.py
Normal file
42
src/plexsdk/net/http_content_types.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
"""
|
||||||
|
Collection of API calls according to the HTTP method and content type.
|
||||||
|
|
||||||
|
Functions:
|
||||||
|
multipart_form_data_request
|
||||||
|
"""
|
||||||
|
import requests
|
||||||
|
import io
|
||||||
|
from mimetypes import guess_type
|
||||||
|
|
||||||
|
|
||||||
|
def multipart_form_data_request(method, endpoint_url, headers, body_input):
|
||||||
|
"""
|
||||||
|
Places a multipart/formdata http request.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
method : str
|
||||||
|
The type of http call to perform
|
||||||
|
endpoint_url : url
|
||||||
|
The endpoint url to make the http call on
|
||||||
|
headers : dict
|
||||||
|
The http call's headers
|
||||||
|
body_input : Any
|
||||||
|
The request's body
|
||||||
|
"""
|
||||||
|
data = {}
|
||||||
|
files = {}
|
||||||
|
request_method = getattr(requests, method)
|
||||||
|
del headers["Content-type"]
|
||||||
|
for key, value in body_input.items():
|
||||||
|
if isinstance(value, (io.TextIOWrapper, io.BufferedIOBase)):
|
||||||
|
mime_type, encoding = guess_type(value.name)
|
||||||
|
file_tuple = (
|
||||||
|
(value.name, value, mime_type) if mime_type else (value.name, value)
|
||||||
|
)
|
||||||
|
files[key] = file_tuple
|
||||||
|
else:
|
||||||
|
data[key] = value
|
||||||
|
if files:
|
||||||
|
return request_method(endpoint_url, headers=headers, files=files, data=data)
|
||||||
|
return request_method(endpoint_url, headers=headers, data=data)
|
||||||
74
src/plexsdk/net/query_serializer.py
Normal file
74
src/plexsdk/net/query_serializer.py
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
from typing import Any, Dict, List
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
explode = bool
|
||||||
|
|
||||||
|
|
||||||
|
def simple(value: Any, explode: bool) -> str:
|
||||||
|
if isinstance(value, Enum):
|
||||||
|
return str(value.value)
|
||||||
|
|
||||||
|
# Check if the value is a list
|
||||||
|
if isinstance(value, list):
|
||||||
|
return ",".join(value) if explode else "".join(value)
|
||||||
|
|
||||||
|
if isinstance(value, dict):
|
||||||
|
if explode:
|
||||||
|
# Serialize object with exploded format: "key=value,key2=value2"
|
||||||
|
return ",".join([f"{k}={v}" for k, v in value.items()])
|
||||||
|
else:
|
||||||
|
# Serialize object with non-exploded format: "key,value,key2,value2"
|
||||||
|
return ",".join(
|
||||||
|
[str(item) for sublist in value.items() for item in sublist]
|
||||||
|
)
|
||||||
|
|
||||||
|
return str(value)
|
||||||
|
|
||||||
|
|
||||||
|
def form(parameter_name: str, parameter_value: Any, explode: bool) -> str:
|
||||||
|
if isinstance(parameter_value, Enum):
|
||||||
|
return f"{parameter_name}=" + str(parameter_value.value)
|
||||||
|
|
||||||
|
if isinstance(parameter_value, list):
|
||||||
|
return (
|
||||||
|
"&".join([f"{parameter_name}={v}" for v in parameter_value])
|
||||||
|
if explode
|
||||||
|
else f"{parameter_name}=" + ",".join([str(v) for v in parameter_value])
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(parameter_value, dict):
|
||||||
|
if explode:
|
||||||
|
# Serialize object with exploded format: "key1=value1&key2=value2"
|
||||||
|
return "&".join([f"{k}={v}" for k, v in parameter_value.items()])
|
||||||
|
else:
|
||||||
|
# Serialize object with non-exploded format: "key=key1,value1,key2,value2"
|
||||||
|
return f"{parameter_name}=" + ",".join(
|
||||||
|
[str(item) for sublist in parameter_value.items() for item in sublist]
|
||||||
|
)
|
||||||
|
|
||||||
|
return f"{parameter_name}=" + str(parameter_value)
|
||||||
|
|
||||||
|
|
||||||
|
style_methods = {
|
||||||
|
"simple": simple,
|
||||||
|
"form": form,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def serialize_query(parameter_style, explode, key: str, parameter_value: Any) -> str:
|
||||||
|
method = style_methods.get(parameter_style)
|
||||||
|
return method(key, parameter_value, explode) if method else ""
|
||||||
|
|
||||||
|
|
||||||
|
def serialize_path(
|
||||||
|
parameter_style, explode: bool, parameter_value: Any, parameter_key=None
|
||||||
|
):
|
||||||
|
method = style_methods.get(parameter_style)
|
||||||
|
if not method:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# The `simple` and `label` styles do not require a `parameter_key`
|
||||||
|
if not parameter_key:
|
||||||
|
return method(parameter_value, explode)
|
||||||
|
else:
|
||||||
|
return method(parameter_key, parameter_value, explode)
|
||||||
4
src/plexsdk/net/response.py
Normal file
4
src/plexsdk/net/response.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
class ResponseWithHeaders:
|
||||||
|
def __init__(self, data, headers):
|
||||||
|
self.data = data
|
||||||
|
self.headers = headers
|
||||||
65
src/plexsdk/net/utils.py
Normal file
65
src/plexsdk/net/utils.py
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
"""
|
||||||
|
Helper functions for http calls.
|
||||||
|
|
||||||
|
Functions:
|
||||||
|
to_serialize
|
||||||
|
"""
|
||||||
|
|
||||||
|
import io
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
def to_serialize(obj):
|
||||||
|
"""
|
||||||
|
Recursively converts objects into dictionaries.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
obj:
|
||||||
|
The object to transform into a dictionary.
|
||||||
|
"""
|
||||||
|
result = {}
|
||||||
|
if not hasattr(obj, "__dict__") or isinstance(
|
||||||
|
obj, (io.TextIOWrapper, io.BufferedIOBase)
|
||||||
|
):
|
||||||
|
return obj
|
||||||
|
iter_obj = obj.__dict__.items() if hasattr(obj, "__dict__") else obj.items()
|
||||||
|
for key, value in iter_obj:
|
||||||
|
if isinstance(value, (io.TextIOWrapper, io.BufferedIOBase)):
|
||||||
|
result[key] = value
|
||||||
|
elif isinstance(value, Enum):
|
||||||
|
result[key] = value.value
|
||||||
|
elif isinstance(value, (list, set, tuple)):
|
||||||
|
for i in range(len(value)):
|
||||||
|
value[i] = to_serialize(value[i])
|
||||||
|
result[key] = value
|
||||||
|
elif hasattr(value, "__dict__"):
|
||||||
|
result[key] = to_serialize(value)
|
||||||
|
else:
|
||||||
|
result[key] = value
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
response_mapper = {"type": "type_"}
|
||||||
|
request_mapper = {"type_": "type"}
|
||||||
|
|
||||||
|
|
||||||
|
def rename_keys(data, mapper):
|
||||||
|
if isinstance(data, dict):
|
||||||
|
new_data = {}
|
||||||
|
for key, value in data.items():
|
||||||
|
new_key = mapper[key] if key in mapper else key
|
||||||
|
new_data[new_key] = rename_keys(value, mapper)
|
||||||
|
return new_data
|
||||||
|
elif isinstance(data, list):
|
||||||
|
return [rename_keys(item, mapper) for item in data]
|
||||||
|
else:
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def rename_reserved_keys(data):
|
||||||
|
return rename_keys(data, response_mapper)
|
||||||
|
|
||||||
|
|
||||||
|
def rename_to_reserved_keys(data):
|
||||||
|
return rename_keys(data, request_mapper)
|
||||||
129
src/plexsdk/sdk.py
Normal file
129
src/plexsdk/sdk.py
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
"""
|
||||||
|
Creates a PlexSDK class.
|
||||||
|
Generates the main SDK with all available queries as attributes.
|
||||||
|
|
||||||
|
Class:
|
||||||
|
PlexSDK
|
||||||
|
"""
|
||||||
|
from .net.environment import Environment
|
||||||
|
|
||||||
|
from .services.activities import Activities
|
||||||
|
from .services.butler import Butler
|
||||||
|
from .services.hubs import Hubs
|
||||||
|
from .services.library import Library
|
||||||
|
from .services.log import Log
|
||||||
|
from .services.media import Media
|
||||||
|
from .services.playlists import Playlists
|
||||||
|
from .services.search import Search
|
||||||
|
from .services.security import Security
|
||||||
|
from .services.server import Server
|
||||||
|
from .services.sessions import Sessions
|
||||||
|
from .services.updater import Updater
|
||||||
|
from .services.video import Video
|
||||||
|
|
||||||
|
|
||||||
|
class PlexSDK:
|
||||||
|
"""
|
||||||
|
A class representing the full PlexSDK SDK
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
activities : Activities
|
||||||
|
butler : Butler
|
||||||
|
hubs : Hubs
|
||||||
|
library : Library
|
||||||
|
log : Log
|
||||||
|
media : Media
|
||||||
|
playlists : Playlists
|
||||||
|
search : Search
|
||||||
|
security : Security
|
||||||
|
server : Server
|
||||||
|
sessions : Sessions
|
||||||
|
updater : Updater
|
||||||
|
video : Video
|
||||||
|
|
||||||
|
Methods
|
||||||
|
-------
|
||||||
|
set_base_url(url: str)
|
||||||
|
Sets the end URL
|
||||||
|
set_api_key(api_key, api_key_header))
|
||||||
|
Set the api key
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, api_key="", api_key_header="X-Plex-Token", environment=Environment.DEFAULT
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Initializes the PlexSDK SDK class.
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
environment: str
|
||||||
|
The environment that the SDK is accessing
|
||||||
|
api_key : str
|
||||||
|
The api key
|
||||||
|
api_key_header : str
|
||||||
|
The API key header
|
||||||
|
"""
|
||||||
|
self.activities = Activities(api_key, api_key_header)
|
||||||
|
self.butler = Butler(api_key, api_key_header)
|
||||||
|
self.hubs = Hubs(api_key, api_key_header)
|
||||||
|
self.library = Library(api_key, api_key_header)
|
||||||
|
self.log = Log(api_key, api_key_header)
|
||||||
|
self.media = Media(api_key, api_key_header)
|
||||||
|
self.playlists = Playlists(api_key, api_key_header)
|
||||||
|
self.search = Search(api_key, api_key_header)
|
||||||
|
self.security = Security(api_key, api_key_header)
|
||||||
|
self.server = Server(api_key, api_key_header)
|
||||||
|
self.sessions = Sessions(api_key, api_key_header)
|
||||||
|
self.updater = Updater(api_key, api_key_header)
|
||||||
|
self.video = Video(api_key, api_key_header)
|
||||||
|
|
||||||
|
self.set_base_url(environment.value)
|
||||||
|
|
||||||
|
def set_base_url(self, url: str) -> None:
|
||||||
|
"""
|
||||||
|
Sets the end URL
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
url:
|
||||||
|
The end URL
|
||||||
|
"""
|
||||||
|
self.activities.set_base_url(url)
|
||||||
|
self.butler.set_base_url(url)
|
||||||
|
self.hubs.set_base_url(url)
|
||||||
|
self.library.set_base_url(url)
|
||||||
|
self.log.set_base_url(url)
|
||||||
|
self.media.set_base_url(url)
|
||||||
|
self.playlists.set_base_url(url)
|
||||||
|
self.search.set_base_url(url)
|
||||||
|
self.security.set_base_url(url)
|
||||||
|
self.server.set_base_url(url)
|
||||||
|
self.sessions.set_base_url(url)
|
||||||
|
self.updater.set_base_url(url)
|
||||||
|
self.video.set_base_url(url)
|
||||||
|
|
||||||
|
def set_api_key(self, api_key: str, api_key_header: str = None) -> None:
|
||||||
|
"""
|
||||||
|
Sets api key and api key header name
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
api_key: string
|
||||||
|
API Key value
|
||||||
|
api_key_header: string
|
||||||
|
Name of API Key
|
||||||
|
"""
|
||||||
|
self.activities.set_api_key(api_key, api_key_header)
|
||||||
|
self.butler.set_api_key(api_key, api_key_header)
|
||||||
|
self.hubs.set_api_key(api_key, api_key_header)
|
||||||
|
self.library.set_api_key(api_key, api_key_header)
|
||||||
|
self.log.set_api_key(api_key, api_key_header)
|
||||||
|
self.media.set_api_key(api_key, api_key_header)
|
||||||
|
self.playlists.set_api_key(api_key, api_key_header)
|
||||||
|
self.search.set_api_key(api_key, api_key_header)
|
||||||
|
self.security.set_api_key(api_key, api_key_header)
|
||||||
|
self.server.set_api_key(api_key, api_key_header)
|
||||||
|
self.sessions.set_api_key(api_key, api_key_header)
|
||||||
|
self.updater.set_api_key(api_key, api_key_header)
|
||||||
|
self.video.set_api_key(api_key, api_key_header)
|
||||||
1896
src/plexsdk/services/README.md
Normal file
1896
src/plexsdk/services/README.md
Normal file
File diff suppressed because it is too large
Load Diff
0
src/plexsdk/services/__init__.py
Normal file
0
src/plexsdk/services/__init__.py
Normal file
53
src/plexsdk/services/activities.py
Normal file
53
src/plexsdk/services/activities.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
from urllib.parse import quote
|
||||||
|
from ..net import query_serializer
|
||||||
|
from .base import BaseService
|
||||||
|
from ..models.GetServerActivitiesResponse import (
|
||||||
|
GetServerActivitiesResponse as GetServerActivitiesResponseModel,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Activities(BaseService):
|
||||||
|
def get_server_activities(self) -> GetServerActivitiesResponseModel:
|
||||||
|
"""
|
||||||
|
Get Server Activities
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/activities"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
if res and isinstance(res, dict):
|
||||||
|
return GetServerActivitiesResponseModel(**res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def cancel_server_activities(self, activity_uuid: str):
|
||||||
|
"""
|
||||||
|
Cancel Server Activities
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
activity_uuid: str
|
||||||
|
The UUID of the activity to cancel.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/activities/{activity_uuid}"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not activity_uuid:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter activity_uuid is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
url_endpoint = url_endpoint.replace(
|
||||||
|
"{activity_uuid}",
|
||||||
|
quote(
|
||||||
|
str(
|
||||||
|
query_serializer.serialize_path(
|
||||||
|
"simple", False, activity_uuid, None
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.delete(final_url, headers, True)
|
||||||
|
return res
|
||||||
106
src/plexsdk/services/base.py
Normal file
106
src/plexsdk/services/base.py
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
"""
|
||||||
|
Creates a BaseService class.
|
||||||
|
Performs API calls,sets authentication tokens and handles http exceptions.
|
||||||
|
|
||||||
|
Class:
|
||||||
|
BaseService
|
||||||
|
"""
|
||||||
|
from typing import List, Union
|
||||||
|
from enum import Enum
|
||||||
|
import re
|
||||||
|
from ..net.http_client import HTTPClient
|
||||||
|
|
||||||
|
|
||||||
|
class BaseService:
|
||||||
|
"""
|
||||||
|
A class to represent a base serivce
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
_url_prefix : str
|
||||||
|
The base URL
|
||||||
|
|
||||||
|
Methods
|
||||||
|
-------
|
||||||
|
|
||||||
|
set_api_key(api_key: str, api_key_header: str = None) -> None:
|
||||||
|
Sets api key and api key header name
|
||||||
|
def _add_required_headers(headers: dict):
|
||||||
|
Request authorization headers
|
||||||
|
def set_base_url(url: str):
|
||||||
|
Sets the base url
|
||||||
|
"""
|
||||||
|
|
||||||
|
_url_prefix = "{protocol}://{ip}:{port}"
|
||||||
|
|
||||||
|
_http = HTTPClient(None)
|
||||||
|
|
||||||
|
def __init__(self, api_key: str = "", api_key_header: str = "X-Plex-Token") -> None:
|
||||||
|
"""
|
||||||
|
Initialize client
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
api_key : str
|
||||||
|
The API Key access token
|
||||||
|
api_key_header : str
|
||||||
|
The API Key header name
|
||||||
|
"""
|
||||||
|
self._api_key = api_key
|
||||||
|
self._api_key_header = api_key_header
|
||||||
|
|
||||||
|
def _pattern_matching(cls, value: str, pattern: str, variable_name: str):
|
||||||
|
if re.match(r"{}".format(pattern), value):
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Invalid value for {variable_name}: must match {pattern}")
|
||||||
|
|
||||||
|
def _enum_matching(
|
||||||
|
cls, value: Union[str, Enum], enum_values: List[str], variable_name: str
|
||||||
|
):
|
||||||
|
str_value = value.value if isinstance(value, Enum) else value
|
||||||
|
if str_value in enum_values:
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Invalid value for {variable_name}: must match one of {enum_values}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def set_base_url(self, url: str) -> None:
|
||||||
|
"""
|
||||||
|
Sets the base URL
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
url:
|
||||||
|
The base URL
|
||||||
|
"""
|
||||||
|
self._url_prefix = url
|
||||||
|
|
||||||
|
def set_api_key(self, api_key: str, api_key_header: str = None) -> None:
|
||||||
|
"""
|
||||||
|
Sets api key and api key header name
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
api_key: string
|
||||||
|
API Key value
|
||||||
|
api_key_header: string
|
||||||
|
Name of API Key
|
||||||
|
"""
|
||||||
|
self._api_key = api_key
|
||||||
|
if api_key_header is not None:
|
||||||
|
self._api_key_header = api_key_header
|
||||||
|
|
||||||
|
def _add_required_headers(self, headers: dict):
|
||||||
|
"""
|
||||||
|
Request authorization headers
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
headers: dict
|
||||||
|
Headers dict to add auth headers to
|
||||||
|
"""
|
||||||
|
headers["User-Agent"] = "liblab/0.1.25 PlexSDK/0.0.1 python/2.7"
|
||||||
|
headers[f"{self._api_key_header}"] = f"{self._api_key}"
|
||||||
|
return headers
|
||||||
116
src/plexsdk/services/butler.py
Normal file
116
src/plexsdk/services/butler.py
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
from urllib.parse import quote
|
||||||
|
from ..net import query_serializer
|
||||||
|
from .base import BaseService
|
||||||
|
from ..models.GetButlerTasksResponse import (
|
||||||
|
GetButlerTasksResponse as GetButlerTasksResponseModel,
|
||||||
|
)
|
||||||
|
from ..models.TaskName import TaskName as TaskNameModel
|
||||||
|
|
||||||
|
|
||||||
|
class Butler(BaseService):
|
||||||
|
def get_butler_tasks(self) -> GetButlerTasksResponseModel:
|
||||||
|
"""
|
||||||
|
Get Butler tasks
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/butler"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
if res and isinstance(res, dict):
|
||||||
|
return GetButlerTasksResponseModel(**res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def start_all_tasks(self):
|
||||||
|
"""
|
||||||
|
Start all Butler tasks
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/butler"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.post(final_url, headers, {}, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def stop_all_tasks(self):
|
||||||
|
"""
|
||||||
|
Stop all Butler tasks
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/butler"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.delete(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def start_task(self, task_name: TaskNameModel):
|
||||||
|
"""
|
||||||
|
Start a single Butler task
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
task_name: TaskName
|
||||||
|
the name of the task to be started.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/butler/{task_name}"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not task_name:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter task_name is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
validated_task_name = self._enum_matching(
|
||||||
|
task_name, TaskNameModel.list(), "task_name"
|
||||||
|
)
|
||||||
|
url_endpoint = url_endpoint.replace(
|
||||||
|
"{task_name}",
|
||||||
|
quote(
|
||||||
|
str(
|
||||||
|
query_serializer.serialize_path(
|
||||||
|
"simple", False, validated_task_name, None
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.post(final_url, headers, {}, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def stop_task(self, task_name: TaskNameModel):
|
||||||
|
"""
|
||||||
|
Stop a single Butler task
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
task_name: TaskName
|
||||||
|
The name of the task to be started.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/butler/{task_name}"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not task_name:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter task_name is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
validated_task_name = self._enum_matching(
|
||||||
|
task_name, TaskNameModel.list(), "task_name"
|
||||||
|
)
|
||||||
|
url_endpoint = url_endpoint.replace(
|
||||||
|
"{task_name}",
|
||||||
|
quote(
|
||||||
|
str(
|
||||||
|
query_serializer.serialize_path(
|
||||||
|
"simple", False, validated_task_name, None
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.delete(final_url, headers, True)
|
||||||
|
return res
|
||||||
93
src/plexsdk/services/hubs.py
Normal file
93
src/plexsdk/services/hubs.py
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
from urllib.parse import quote
|
||||||
|
from ..net import query_serializer
|
||||||
|
from .base import BaseService
|
||||||
|
from ..models.OnlyTransient import OnlyTransient as OnlyTransientModel
|
||||||
|
|
||||||
|
|
||||||
|
class Hubs(BaseService):
|
||||||
|
def get_global_hubs(
|
||||||
|
self, count: float = None, only_transient: OnlyTransientModel = None
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Get Global Hubs
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
count: float
|
||||||
|
The number of items to return with each hub.
|
||||||
|
only_transient: OnlyTransient
|
||||||
|
Only return hubs which are "transient", meaning those which are prone to changing after media playback or addition (e.g. On Deck, or Recently Added).
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/hubs"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if count:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "count", count)
|
||||||
|
)
|
||||||
|
if only_transient:
|
||||||
|
validated_only_transient = self._enum_matching(
|
||||||
|
only_transient, OnlyTransientModel.list(), "only_transient"
|
||||||
|
)
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query(
|
||||||
|
"form", False, "onlyTransient", validated_only_transient
|
||||||
|
)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
if len(query_params) > 0:
|
||||||
|
final_url += "?" + "&".join(query_params)
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_library_hubs(
|
||||||
|
self,
|
||||||
|
section_id: float,
|
||||||
|
count: float = None,
|
||||||
|
only_transient: OnlyTransientModel = None,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Get library specific hubs
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
section_id: float
|
||||||
|
the Id of the library to query
|
||||||
|
count: float
|
||||||
|
The number of items to return with each hub.
|
||||||
|
only_transient: OnlyTransient
|
||||||
|
Only return hubs which are "transient", meaning those which are prone to changing after media playback or addition (e.g. On Deck, or Recently Added).
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/hubs/sections/{section_id}"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not section_id:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter section_id is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
url_endpoint = url_endpoint.replace(
|
||||||
|
"{section_id}",
|
||||||
|
quote(
|
||||||
|
str(query_serializer.serialize_path("simple", False, section_id, None))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if count:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "count", count)
|
||||||
|
)
|
||||||
|
if only_transient:
|
||||||
|
validated_only_transient = self._enum_matching(
|
||||||
|
only_transient, OnlyTransientModel.list(), "only_transient"
|
||||||
|
)
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query(
|
||||||
|
"form", False, "onlyTransient", validated_only_transient
|
||||||
|
)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
if len(query_params) > 0:
|
||||||
|
final_url += "?" + "&".join(query_params)
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
354
src/plexsdk/services/library.py
Normal file
354
src/plexsdk/services/library.py
Normal file
@@ -0,0 +1,354 @@
|
|||||||
|
from urllib.parse import quote
|
||||||
|
from ..net import query_serializer
|
||||||
|
from .base import BaseService
|
||||||
|
from ..models.GetRecentlyAddedResponse import (
|
||||||
|
GetRecentlyAddedResponse as GetRecentlyAddedResponseModel,
|
||||||
|
)
|
||||||
|
from ..models.IncludeDetails import IncludeDetails as IncludeDetailsModel
|
||||||
|
from ..models.GetOnDeckResponse import GetOnDeckResponse as GetOnDeckResponseModel
|
||||||
|
|
||||||
|
|
||||||
|
class Library(BaseService):
|
||||||
|
def get_file_hash(self, url: str, type_: float = None):
|
||||||
|
"""
|
||||||
|
Get Hash Value
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
url: str
|
||||||
|
This is the path to the local file, must be prefixed by `file://`
|
||||||
|
type: float
|
||||||
|
Item type
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/library/hashes"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not url:
|
||||||
|
raise ValueError("Parameter url is required, cannot be empty or blank.")
|
||||||
|
query_params.append(query_serializer.serialize_query("form", False, "url", url))
|
||||||
|
if type_:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "type_", type_)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint + "?" + "&".join(query_params)
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_recently_added(self) -> GetRecentlyAddedResponseModel:
|
||||||
|
"""
|
||||||
|
Get Recently Added
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/library/recentlyAdded"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
if res and isinstance(res, dict):
|
||||||
|
return GetRecentlyAddedResponseModel(**res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_libraries(self):
|
||||||
|
"""
|
||||||
|
Get All Libraries
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/library/sections"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_library(
|
||||||
|
self, section_id: float, include_details: IncludeDetailsModel = None
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Get Library Details
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
section_id: float
|
||||||
|
the Id of the library to query
|
||||||
|
include_details: IncludeDetails
|
||||||
|
Whether or not to include details for a section (types, filters, and sorts).
|
||||||
|
Only exists for backwards compatibility, media providers other than the server libraries have it on always.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/library/sections/{section_id}"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not section_id:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter section_id is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
url_endpoint = url_endpoint.replace(
|
||||||
|
"{section_id}",
|
||||||
|
quote(
|
||||||
|
str(query_serializer.serialize_path("simple", False, section_id, None))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if include_details:
|
||||||
|
validated_include_details = self._enum_matching(
|
||||||
|
include_details, IncludeDetailsModel.list(), "include_details"
|
||||||
|
)
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query(
|
||||||
|
"form", False, "includeDetails", validated_include_details
|
||||||
|
)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
if len(query_params) > 0:
|
||||||
|
final_url += "?" + "&".join(query_params)
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def delete_library(self, section_id: float):
|
||||||
|
"""
|
||||||
|
Delete Library Section
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
section_id: float
|
||||||
|
the Id of the library to query
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/library/sections/{section_id}"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not section_id:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter section_id is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
url_endpoint = url_endpoint.replace(
|
||||||
|
"{section_id}",
|
||||||
|
quote(
|
||||||
|
str(query_serializer.serialize_path("simple", False, section_id, None))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.delete(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_library_items(
|
||||||
|
self, section_id: float, type_: float = None, filter: str = None
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Get Library Items
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
section_id: float
|
||||||
|
the Id of the library to query
|
||||||
|
type: float
|
||||||
|
item type
|
||||||
|
filter: str
|
||||||
|
the filter parameter
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/library/sections/{section_id}/all"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not section_id:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter section_id is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
url_endpoint = url_endpoint.replace(
|
||||||
|
"{section_id}",
|
||||||
|
quote(
|
||||||
|
str(query_serializer.serialize_path("simple", False, section_id, None))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if type_:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "type_", type_)
|
||||||
|
)
|
||||||
|
if filter:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "filter", filter)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
if len(query_params) > 0:
|
||||||
|
final_url += "?" + "&".join(query_params)
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def refresh_library(self, section_id: float):
|
||||||
|
"""
|
||||||
|
Refresh Library
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
section_id: float
|
||||||
|
the Id of the library to refresh
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/library/sections/{section_id}/refresh"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not section_id:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter section_id is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
url_endpoint = url_endpoint.replace(
|
||||||
|
"{section_id}",
|
||||||
|
quote(
|
||||||
|
str(query_serializer.serialize_path("simple", False, section_id, None))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_latest_library_items(
|
||||||
|
self, type_: float, section_id: float, filter: str = None
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Get Latest Library Items
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
section_id: float
|
||||||
|
the Id of the library to query
|
||||||
|
type: float
|
||||||
|
item type
|
||||||
|
filter: str
|
||||||
|
the filter parameter
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/library/sections/{section_id}/latest"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not section_id:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter section_id is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
url_endpoint = url_endpoint.replace(
|
||||||
|
"{section_id}",
|
||||||
|
quote(
|
||||||
|
str(query_serializer.serialize_path("simple", False, section_id, None))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if not type_:
|
||||||
|
raise ValueError("Parameter type_ is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "type_", type_)
|
||||||
|
)
|
||||||
|
if filter:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "filter", filter)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint + "?" + "&".join(query_params)
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_common_library_items(
|
||||||
|
self, type_: float, section_id: float, filter: str = None
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Get Common Library Items
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
section_id: float
|
||||||
|
the Id of the library to query
|
||||||
|
type: float
|
||||||
|
item type
|
||||||
|
filter: str
|
||||||
|
the filter parameter
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/library/sections/{section_id}/common"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not section_id:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter section_id is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
url_endpoint = url_endpoint.replace(
|
||||||
|
"{section_id}",
|
||||||
|
quote(
|
||||||
|
str(query_serializer.serialize_path("simple", False, section_id, None))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if not type_:
|
||||||
|
raise ValueError("Parameter type_ is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "type_", type_)
|
||||||
|
)
|
||||||
|
if filter:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "filter", filter)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint + "?" + "&".join(query_params)
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_metadata(self, rating_key: float):
|
||||||
|
"""
|
||||||
|
Get Items Metadata
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
rating_key: float
|
||||||
|
the id of the library item to return the children of.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/library/metadata/{rating_key}"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not rating_key:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter rating_key is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
url_endpoint = url_endpoint.replace(
|
||||||
|
"{rating_key}",
|
||||||
|
quote(
|
||||||
|
str(query_serializer.serialize_path("simple", False, rating_key, None))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_metadata_children(self, rating_key: float):
|
||||||
|
"""
|
||||||
|
Get Items Children
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
rating_key: float
|
||||||
|
the id of the library item to return the children of.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/library/metadata/{rating_key}/children"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not rating_key:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter rating_key is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
url_endpoint = url_endpoint.replace(
|
||||||
|
"{rating_key}",
|
||||||
|
quote(
|
||||||
|
str(query_serializer.serialize_path("simple", False, rating_key, None))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_on_deck(self) -> GetOnDeckResponseModel:
|
||||||
|
"""
|
||||||
|
Get On Deck
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/library/onDeck"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
if res and isinstance(res, dict):
|
||||||
|
return GetOnDeckResponseModel(**res)
|
||||||
|
return res
|
||||||
75
src/plexsdk/services/log.py
Normal file
75
src/plexsdk/services/log.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
from urllib.parse import quote
|
||||||
|
from ..net import query_serializer
|
||||||
|
from .base import BaseService
|
||||||
|
from ..models.Level import Level as LevelModel
|
||||||
|
|
||||||
|
|
||||||
|
class Log(BaseService):
|
||||||
|
def log_line(self, source: str, message: str, level: LevelModel):
|
||||||
|
"""
|
||||||
|
Logging a single line message.
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
level: Level
|
||||||
|
An integer log level to write to the PMS log with.
|
||||||
|
0: Error
|
||||||
|
1: Warning
|
||||||
|
2: Info
|
||||||
|
3: Debug
|
||||||
|
4: Verbose
|
||||||
|
|
||||||
|
message: str
|
||||||
|
The text of the message to write to the log.
|
||||||
|
source: str
|
||||||
|
a string indicating the source of the message.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/log"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not level:
|
||||||
|
raise ValueError("Parameter level is required, cannot be empty or blank.")
|
||||||
|
validated_level = self._enum_matching(level, LevelModel.list(), "level")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "level", validated_level)
|
||||||
|
)
|
||||||
|
if not message:
|
||||||
|
raise ValueError("Parameter message is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "message", message)
|
||||||
|
)
|
||||||
|
if not source:
|
||||||
|
raise ValueError("Parameter source is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "source", source)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint + "?" + "&".join(query_params)
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def log_multi_line(self):
|
||||||
|
"""
|
||||||
|
Logging a multi-line message
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/log"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.post(final_url, headers, {}, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def enable_paper_trail(self):
|
||||||
|
"""
|
||||||
|
Enabling Papertrail
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/log/networked"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
79
src/plexsdk/services/media.py
Normal file
79
src/plexsdk/services/media.py
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
from urllib.parse import quote
|
||||||
|
from ..net import query_serializer
|
||||||
|
from .base import BaseService
|
||||||
|
|
||||||
|
|
||||||
|
class Media(BaseService):
|
||||||
|
def mark_played(self, key: float):
|
||||||
|
"""
|
||||||
|
Mark Media Played
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
key: float
|
||||||
|
The media key to mark as played
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/:/scrobble"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not key:
|
||||||
|
raise ValueError("Parameter key is required, cannot be empty or blank.")
|
||||||
|
query_params.append(query_serializer.serialize_query("form", False, "key", key))
|
||||||
|
final_url = self._url_prefix + url_endpoint + "?" + "&".join(query_params)
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def mark_unplayed(self, key: float):
|
||||||
|
"""
|
||||||
|
Mark Media Unplayed
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
key: float
|
||||||
|
The media key to mark as Unplayed
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/:/unscrobble"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not key:
|
||||||
|
raise ValueError("Parameter key is required, cannot be empty or blank.")
|
||||||
|
query_params.append(query_serializer.serialize_query("form", False, "key", key))
|
||||||
|
final_url = self._url_prefix + url_endpoint + "?" + "&".join(query_params)
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def update_play_progress(self, state: str, time: float, key: str):
|
||||||
|
"""
|
||||||
|
Update Media Play Progress
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
key: str
|
||||||
|
the media key
|
||||||
|
time: float
|
||||||
|
The time, in milliseconds, used to set the media playback progress.
|
||||||
|
state: str
|
||||||
|
The playback state of the media item.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/:/progress"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not key:
|
||||||
|
raise ValueError("Parameter key is required, cannot be empty or blank.")
|
||||||
|
query_params.append(query_serializer.serialize_query("form", False, "key", key))
|
||||||
|
if not time:
|
||||||
|
raise ValueError("Parameter time is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "time", time)
|
||||||
|
)
|
||||||
|
if not state:
|
||||||
|
raise ValueError("Parameter state is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "state", state)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint + "?" + "&".join(query_params)
|
||||||
|
res = self._http.post(final_url, headers, {}, True)
|
||||||
|
return res
|
||||||
326
src/plexsdk/services/playlists.py
Normal file
326
src/plexsdk/services/playlists.py
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
from urllib.parse import quote
|
||||||
|
from ..net import query_serializer
|
||||||
|
from .base import BaseService
|
||||||
|
from ..models.Type import Type as TypeModel
|
||||||
|
from ..models.Smart import Smart as SmartModel
|
||||||
|
from ..models.PlaylistType import PlaylistType as PlaylistTypeModel
|
||||||
|
from ..models.Force import Force as ForceModel
|
||||||
|
|
||||||
|
|
||||||
|
class Playlists(BaseService):
|
||||||
|
def create_playlist(
|
||||||
|
self,
|
||||||
|
smart: SmartModel,
|
||||||
|
type_: TypeModel,
|
||||||
|
title: str,
|
||||||
|
uri: str = None,
|
||||||
|
play_queue_id: float = None,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Create a Playlist
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
title: str
|
||||||
|
name of the playlist
|
||||||
|
type: Type
|
||||||
|
type of playlist to create
|
||||||
|
smart: Smart
|
||||||
|
whether the playlist is smart or not
|
||||||
|
uri: str
|
||||||
|
the content URI for the playlist
|
||||||
|
play_queue_id: float
|
||||||
|
the play queue to copy to a playlist
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/playlists"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not title:
|
||||||
|
raise ValueError("Parameter title is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "title", title)
|
||||||
|
)
|
||||||
|
if not type_:
|
||||||
|
raise ValueError("Parameter type_ is required, cannot be empty or blank.")
|
||||||
|
validated_type_ = self._enum_matching(type_, TypeModel.list(), "type_")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "type_", validated_type_)
|
||||||
|
)
|
||||||
|
if not smart:
|
||||||
|
raise ValueError("Parameter smart is required, cannot be empty or blank.")
|
||||||
|
validated_smart = self._enum_matching(smart, SmartModel.list(), "smart")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "smart", validated_smart)
|
||||||
|
)
|
||||||
|
if uri:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "uri", uri)
|
||||||
|
)
|
||||||
|
if play_queue_id:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query(
|
||||||
|
"form", False, "playQueueID", play_queue_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint + "?" + "&".join(query_params)
|
||||||
|
res = self._http.post(final_url, headers, {}, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_playlists(
|
||||||
|
self, playlist_type: PlaylistTypeModel = None, smart: SmartModel = None
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Get All Playlists
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
playlist_type: PlaylistType
|
||||||
|
limit to a type of playlist.
|
||||||
|
smart: Smart
|
||||||
|
type of playlists to return (default is all).
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/playlists/all"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if playlist_type:
|
||||||
|
validated_playlist_type = self._enum_matching(
|
||||||
|
playlist_type, PlaylistTypeModel.list(), "playlist_type"
|
||||||
|
)
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query(
|
||||||
|
"form", False, "playlistType", validated_playlist_type
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if smart:
|
||||||
|
validated_smart = self._enum_matching(smart, SmartModel.list(), "smart")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query(
|
||||||
|
"form", False, "smart", validated_smart
|
||||||
|
)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
if len(query_params) > 0:
|
||||||
|
final_url += "?" + "&".join(query_params)
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_playlist(self, playlist_id: float):
|
||||||
|
"""
|
||||||
|
Retrieve Playlist
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
playlist_id: float
|
||||||
|
the ID of the playlist
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/playlists/{playlist_id}"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not playlist_id:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter playlist_id is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
url_endpoint = url_endpoint.replace(
|
||||||
|
"{playlist_id}",
|
||||||
|
quote(
|
||||||
|
str(query_serializer.serialize_path("simple", False, playlist_id, None))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def update_playlist(self, playlist_id: float):
|
||||||
|
"""
|
||||||
|
Update a Playlist
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
playlist_id: float
|
||||||
|
the ID of the playlist
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/playlists/{playlist_id}"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not playlist_id:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter playlist_id is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
url_endpoint = url_endpoint.replace(
|
||||||
|
"{playlist_id}",
|
||||||
|
quote(
|
||||||
|
str(query_serializer.serialize_path("simple", False, playlist_id, None))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.put(final_url, headers, {}, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def delete_playlist(self, playlist_id: float):
|
||||||
|
"""
|
||||||
|
Deletes a Playlist
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
playlist_id: float
|
||||||
|
the ID of the playlist
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/playlists/{playlist_id}"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not playlist_id:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter playlist_id is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
url_endpoint = url_endpoint.replace(
|
||||||
|
"{playlist_id}",
|
||||||
|
quote(
|
||||||
|
str(query_serializer.serialize_path("simple", False, playlist_id, None))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.delete(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_playlist_contents(self, type_: float, playlist_id: float):
|
||||||
|
"""
|
||||||
|
Retrieve Playlist Contents
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
playlist_id: float
|
||||||
|
the ID of the playlist
|
||||||
|
type: float
|
||||||
|
the metadata type of the item to return
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/playlists/{playlist_id}/items"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not playlist_id:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter playlist_id is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
url_endpoint = url_endpoint.replace(
|
||||||
|
"{playlist_id}",
|
||||||
|
quote(
|
||||||
|
str(query_serializer.serialize_path("simple", False, playlist_id, None))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if not type_:
|
||||||
|
raise ValueError("Parameter type_ is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "type_", type_)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint + "?" + "&".join(query_params)
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def add_playlist_contents(self, play_queue_id: float, uri: str, playlist_id: float):
|
||||||
|
"""
|
||||||
|
Adding to a Playlist
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
playlist_id: float
|
||||||
|
the ID of the playlist
|
||||||
|
uri: str
|
||||||
|
the content URI for the playlist
|
||||||
|
play_queue_id: float
|
||||||
|
the play queue to add to a playlist
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/playlists/{playlist_id}/items"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not playlist_id:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter playlist_id is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
url_endpoint = url_endpoint.replace(
|
||||||
|
"{playlist_id}",
|
||||||
|
quote(
|
||||||
|
str(query_serializer.serialize_path("simple", False, playlist_id, None))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if not uri:
|
||||||
|
raise ValueError("Parameter uri is required, cannot be empty or blank.")
|
||||||
|
query_params.append(query_serializer.serialize_query("form", False, "uri", uri))
|
||||||
|
if not play_queue_id:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter play_queue_id is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query(
|
||||||
|
"form", False, "playQueueID", play_queue_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint + "?" + "&".join(query_params)
|
||||||
|
res = self._http.put(final_url, headers, {}, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def clear_playlist_contents(self, playlist_id: float):
|
||||||
|
"""
|
||||||
|
Delete Playlist Contents
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
playlist_id: float
|
||||||
|
the ID of the playlist
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/playlists/{playlist_id}/items"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not playlist_id:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter playlist_id is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
url_endpoint = url_endpoint.replace(
|
||||||
|
"{playlist_id}",
|
||||||
|
quote(
|
||||||
|
str(query_serializer.serialize_path("simple", False, playlist_id, None))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.delete(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def upload_playlist(self, force: ForceModel, path: str):
|
||||||
|
"""
|
||||||
|
Upload Playlist
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
path: str
|
||||||
|
absolute path to a directory on the server where m3u files are stored, or the absolute path to a playlist file on the server.
|
||||||
|
If the `path` argument is a directory, that path will be scanned for playlist files to be processed.
|
||||||
|
Each file in that directory creates a separate playlist, with a name based on the filename of the file that created it.
|
||||||
|
The GUID of each playlist is based on the filename.
|
||||||
|
If the `path` argument is a file, that file will be used to create a new playlist, with the name based on the filename of the file that created it.
|
||||||
|
The GUID of each playlist is based on the filename.
|
||||||
|
|
||||||
|
force: Force
|
||||||
|
force overwriting of duplicate playlists. By default, a playlist file uploaded with the same path will overwrite the existing playlist.
|
||||||
|
The `force` argument is used to disable overwriting. If the `force` argument is set to 0, a new playlist will be created suffixed with the date and time that the duplicate was uploaded.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/playlists/upload"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not path:
|
||||||
|
raise ValueError("Parameter path is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "path", path)
|
||||||
|
)
|
||||||
|
if not force:
|
||||||
|
raise ValueError("Parameter force is required, cannot be empty or blank.")
|
||||||
|
validated_force = self._enum_matching(force, ForceModel.list(), "force")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "force", validated_force)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint + "?" + "&".join(query_params)
|
||||||
|
res = self._http.post(final_url, headers, {}, True)
|
||||||
|
return res
|
||||||
102
src/plexsdk/services/search.py
Normal file
102
src/plexsdk/services/search.py
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
from urllib.parse import quote
|
||||||
|
from ..net import query_serializer
|
||||||
|
from .base import BaseService
|
||||||
|
from ..models.GetSearchResultsResponse import (
|
||||||
|
GetSearchResultsResponse as GetSearchResultsResponseModel,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Search(BaseService):
|
||||||
|
def perform_search(self, query: str, section_id: float = None, limit: float = None):
|
||||||
|
"""
|
||||||
|
Perform a search
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
query: str
|
||||||
|
The query term
|
||||||
|
section_id: float
|
||||||
|
This gives context to the search, and can result in re-ordering of search result hubs
|
||||||
|
limit: float
|
||||||
|
The number of items to return per hub
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/hubs/search"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not query:
|
||||||
|
raise ValueError("Parameter query is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "query", query)
|
||||||
|
)
|
||||||
|
if section_id:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "sectionId", section_id)
|
||||||
|
)
|
||||||
|
if limit:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "limit", limit)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint + "?" + "&".join(query_params)
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def perform_voice_search(
|
||||||
|
self, query: str, section_id: float = None, limit: float = None
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Perform a voice search
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
query: str
|
||||||
|
The query term
|
||||||
|
section_id: float
|
||||||
|
This gives context to the search, and can result in re-ordering of search result hubs
|
||||||
|
limit: float
|
||||||
|
The number of items to return per hub
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/hubs/search/voice"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not query:
|
||||||
|
raise ValueError("Parameter query is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "query", query)
|
||||||
|
)
|
||||||
|
if section_id:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "sectionId", section_id)
|
||||||
|
)
|
||||||
|
if limit:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "limit", limit)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint + "?" + "&".join(query_params)
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_search_results(self, query: str) -> GetSearchResultsResponseModel:
|
||||||
|
"""
|
||||||
|
Get Search Results
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
query: str
|
||||||
|
The search query string to use
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/search"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not query:
|
||||||
|
raise ValueError("Parameter query is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "query", query)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint + "?" + "&".join(query_params)
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
if res and isinstance(res, dict):
|
||||||
|
return GetSearchResultsResponseModel(**res)
|
||||||
|
return res
|
||||||
60
src/plexsdk/services/security.py
Normal file
60
src/plexsdk/services/security.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
from urllib.parse import quote
|
||||||
|
from ..net import query_serializer
|
||||||
|
from .base import BaseService
|
||||||
|
from ..models.SecurityType import SecurityType as SecurityTypeModel
|
||||||
|
from ..models.Scope import Scope as ScopeModel
|
||||||
|
|
||||||
|
|
||||||
|
class Security(BaseService):
|
||||||
|
def get_transient_token(self, scope: ScopeModel, type_: SecurityTypeModel):
|
||||||
|
"""
|
||||||
|
Get a Transient Token.
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
type: SecurityType
|
||||||
|
`delegation` - This is the only supported `type` parameter.
|
||||||
|
scope: Scope
|
||||||
|
`all` - This is the only supported `scope` parameter.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/security/token"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not type_:
|
||||||
|
raise ValueError("Parameter type_ is required, cannot be empty or blank.")
|
||||||
|
validated_type_ = self._enum_matching(type_, SecurityTypeModel.list(), "type_")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "type_", validated_type_)
|
||||||
|
)
|
||||||
|
if not scope:
|
||||||
|
raise ValueError("Parameter scope is required, cannot be empty or blank.")
|
||||||
|
validated_scope = self._enum_matching(scope, ScopeModel.list(), "scope")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "scope", validated_scope)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint + "?" + "&".join(query_params)
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_source_connection_information(self, source: str):
|
||||||
|
"""
|
||||||
|
Get Source Connection Information
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
source: str
|
||||||
|
The source identifier with an included prefix.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/security/resources"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not source:
|
||||||
|
raise ValueError("Parameter source is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "source", source)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint + "?" + "&".join(query_params)
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
210
src/plexsdk/services/server.py
Normal file
210
src/plexsdk/services/server.py
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
from urllib.parse import quote
|
||||||
|
from ..net import query_serializer
|
||||||
|
from .base import BaseService
|
||||||
|
from ..models.GetServerCapabilitiesResponse import (
|
||||||
|
GetServerCapabilitiesResponse as GetServerCapabilitiesResponseModel,
|
||||||
|
)
|
||||||
|
from ..models.GetAvailableClientsResponse import (
|
||||||
|
GetAvailableClientsResponse as GetAvailableClientsResponseModel,
|
||||||
|
)
|
||||||
|
from ..models.GetAvailableClientsResponse import (
|
||||||
|
GetAvailableClientsResponseItem as GetAvailableClientsResponseItemModel,
|
||||||
|
)
|
||||||
|
from ..models.GetDevicesResponse import GetDevicesResponse as GetDevicesResponseModel
|
||||||
|
from ..models.GetServerIdentityResponse import (
|
||||||
|
GetServerIdentityResponse as GetServerIdentityResponseModel,
|
||||||
|
)
|
||||||
|
from ..models.GetMyPlexAccountResponse import (
|
||||||
|
GetMyPlexAccountResponse as GetMyPlexAccountResponseModel,
|
||||||
|
)
|
||||||
|
from ..models.MinSize import MinSize as MinSizeModel
|
||||||
|
from ..models.Upscale import Upscale as UpscaleModel
|
||||||
|
from ..models.GetServerListResponse import (
|
||||||
|
GetServerListResponse as GetServerListResponseModel,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Server(BaseService):
|
||||||
|
def get_server_capabilities(self) -> GetServerCapabilitiesResponseModel:
|
||||||
|
"""
|
||||||
|
Server Capabilities
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
if res and isinstance(res, dict):
|
||||||
|
return GetServerCapabilitiesResponseModel(**res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_server_preferences(self):
|
||||||
|
"""
|
||||||
|
Get Server Preferences
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/:/prefs"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_available_clients(self) -> GetAvailableClientsResponseModel:
|
||||||
|
"""
|
||||||
|
Get Available Clients
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/clients"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
if res and isinstance(res, list):
|
||||||
|
return [GetAvailableClientsResponseItemModel(**model) for model in res]
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_devices(self) -> GetDevicesResponseModel:
|
||||||
|
"""
|
||||||
|
Get Devices
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/devices"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
if res and isinstance(res, dict):
|
||||||
|
return GetDevicesResponseModel(**res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_server_identity(self) -> GetServerIdentityResponseModel:
|
||||||
|
"""
|
||||||
|
Get Server Identity
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/identity"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
if res and isinstance(res, dict):
|
||||||
|
return GetServerIdentityResponseModel(**res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_my_plex_account(self) -> GetMyPlexAccountResponseModel:
|
||||||
|
"""
|
||||||
|
Get MyPlex Account
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/myplex/account"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
if res and isinstance(res, dict):
|
||||||
|
return GetMyPlexAccountResponseModel(**res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_resized_photo(
|
||||||
|
self,
|
||||||
|
url: str,
|
||||||
|
upscale: UpscaleModel,
|
||||||
|
min_size: MinSizeModel,
|
||||||
|
blur: float,
|
||||||
|
opacity: int,
|
||||||
|
height: float,
|
||||||
|
width: float,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Get a Resized Photo
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
width: float
|
||||||
|
The width for the resized photo
|
||||||
|
height: float
|
||||||
|
The height for the resized photo
|
||||||
|
opacity: int
|
||||||
|
The opacity for the resized photo
|
||||||
|
blur: float
|
||||||
|
The width for the resized photo
|
||||||
|
min_size: MinSize
|
||||||
|
images are always scaled proportionally. A value of '1' in minSize will make the smaller native dimension the dimension resized against.
|
||||||
|
upscale: Upscale
|
||||||
|
allow images to be resized beyond native dimensions.
|
||||||
|
url: str
|
||||||
|
path to image within Plex
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/photo/:/transcode"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not width:
|
||||||
|
raise ValueError("Parameter width is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "width", width)
|
||||||
|
)
|
||||||
|
if not height:
|
||||||
|
raise ValueError("Parameter height is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "height", height)
|
||||||
|
)
|
||||||
|
if not opacity:
|
||||||
|
raise ValueError("Parameter opacity is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "opacity", opacity)
|
||||||
|
)
|
||||||
|
if not blur:
|
||||||
|
raise ValueError("Parameter blur is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "blur", blur)
|
||||||
|
)
|
||||||
|
if not min_size:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter min_size is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
validated_min_size = self._enum_matching(
|
||||||
|
min_size, MinSizeModel.list(), "min_size"
|
||||||
|
)
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query(
|
||||||
|
"form", False, "minSize", validated_min_size
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not upscale:
|
||||||
|
raise ValueError("Parameter upscale is required, cannot be empty or blank.")
|
||||||
|
validated_upscale = self._enum_matching(upscale, UpscaleModel.list(), "upscale")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query(
|
||||||
|
"form", False, "upscale", validated_upscale
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not url:
|
||||||
|
raise ValueError("Parameter url is required, cannot be empty or blank.")
|
||||||
|
query_params.append(query_serializer.serialize_query("form", False, "url", url))
|
||||||
|
final_url = self._url_prefix + url_endpoint + "?" + "&".join(query_params)
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_server_list(self) -> GetServerListResponseModel:
|
||||||
|
"""
|
||||||
|
Get Server List
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/servers"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
if res and isinstance(res, dict):
|
||||||
|
return GetServerListResponseModel(**res)
|
||||||
|
return res
|
||||||
75
src/plexsdk/services/sessions.py
Normal file
75
src/plexsdk/services/sessions.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
from urllib.parse import quote
|
||||||
|
from ..net import query_serializer
|
||||||
|
from .base import BaseService
|
||||||
|
from ..models.GetTranscodeSessionsResponse import (
|
||||||
|
GetTranscodeSessionsResponse as GetTranscodeSessionsResponseModel,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Sessions(BaseService):
|
||||||
|
def get_sessions(self):
|
||||||
|
"""
|
||||||
|
Get Active Sessions
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/status/sessions"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_session_history(self):
|
||||||
|
"""
|
||||||
|
Get Session History
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/status/sessions/history/all"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_transcode_sessions(self) -> GetTranscodeSessionsResponseModel:
|
||||||
|
"""
|
||||||
|
Get Transcode Sessions
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/transcode/sessions"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
if res and isinstance(res, dict):
|
||||||
|
return GetTranscodeSessionsResponseModel(**res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def stop_transcode_session(self, session_key: str):
|
||||||
|
"""
|
||||||
|
Stop a Transcode Session
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
session_key: str
|
||||||
|
the Key of the transcode session to stop
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/transcode/sessions/{session_key}"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not session_key:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter session_key is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
url_endpoint = url_endpoint.replace(
|
||||||
|
"{session_key}",
|
||||||
|
quote(
|
||||||
|
str(query_serializer.serialize_path("simple", False, session_key, None))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.delete(final_url, headers, True)
|
||||||
|
return res
|
||||||
84
src/plexsdk/services/updater.py
Normal file
84
src/plexsdk/services/updater.py
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
from urllib.parse import quote
|
||||||
|
from ..net import query_serializer
|
||||||
|
from .base import BaseService
|
||||||
|
from ..models.Download import Download as DownloadModel
|
||||||
|
from ..models.Tonight import Tonight as TonightModel
|
||||||
|
from ..models.Skip import Skip as SkipModel
|
||||||
|
|
||||||
|
|
||||||
|
class Updater(BaseService):
|
||||||
|
def get_update_status(self):
|
||||||
|
"""
|
||||||
|
Querying status of updates
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/updater/status"
|
||||||
|
headers = {}
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def check_for_updates(self, download: DownloadModel = None):
|
||||||
|
"""
|
||||||
|
Checking for updates
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
download: Download
|
||||||
|
Indicate that you want to start download any updates found.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/updater/check"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if download:
|
||||||
|
validated_download = self._enum_matching(
|
||||||
|
download, DownloadModel.list(), "download"
|
||||||
|
)
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query(
|
||||||
|
"form", False, "download", validated_download
|
||||||
|
)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
if len(query_params) > 0:
|
||||||
|
final_url += "?" + "&".join(query_params)
|
||||||
|
res = self._http.put(final_url, headers, {}, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def apply_updates(self, tonight: TonightModel = None, skip: SkipModel = None):
|
||||||
|
"""
|
||||||
|
Apply Updates
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
tonight: Tonight
|
||||||
|
Indicate that you want the update to run during the next Butler execution. Omitting this or setting it to false indicates that the update should install
|
||||||
|
skip: Skip
|
||||||
|
Indicate that the latest version should be marked as skipped. The <Release> entry for this version will have the `state` set to `skipped`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/updater/apply"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if tonight:
|
||||||
|
validated_tonight = self._enum_matching(
|
||||||
|
tonight, TonightModel.list(), "tonight"
|
||||||
|
)
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query(
|
||||||
|
"form", False, "tonight", validated_tonight
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if skip:
|
||||||
|
validated_skip = self._enum_matching(skip, SkipModel.list(), "skip")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "skip", validated_skip)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint
|
||||||
|
if len(query_params) > 0:
|
||||||
|
final_url += "?" + "&".join(query_params)
|
||||||
|
res = self._http.put(final_url, headers, {}, True)
|
||||||
|
return res
|
||||||
266
src/plexsdk/services/video.py
Normal file
266
src/plexsdk/services/video.py
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
from urllib.parse import quote
|
||||||
|
from ..net import query_serializer
|
||||||
|
from .base import BaseService
|
||||||
|
from ..models.State import State as StateModel
|
||||||
|
|
||||||
|
|
||||||
|
class Video(BaseService):
|
||||||
|
def start_universal_transcode(
|
||||||
|
self,
|
||||||
|
protocol: str,
|
||||||
|
part_index: float,
|
||||||
|
media_index: float,
|
||||||
|
path: str,
|
||||||
|
has_mde: float,
|
||||||
|
fast_seek: float = None,
|
||||||
|
direct_play: float = None,
|
||||||
|
direct_stream: float = None,
|
||||||
|
subtitle_size: float = None,
|
||||||
|
subtites: str = None,
|
||||||
|
audio_boost: float = None,
|
||||||
|
location: str = None,
|
||||||
|
media_buffer_size: float = None,
|
||||||
|
session: str = None,
|
||||||
|
add_debug_overlay: float = None,
|
||||||
|
auto_adjust_quality: float = None,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Start Universal Transcode
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
has_mde: float
|
||||||
|
Whether the media item has MDE
|
||||||
|
path: str
|
||||||
|
The path to the media item to transcode
|
||||||
|
media_index: float
|
||||||
|
The index of the media item to transcode
|
||||||
|
part_index: float
|
||||||
|
The index of the part to transcode
|
||||||
|
protocol: str
|
||||||
|
The protocol to use for the transcode session
|
||||||
|
fast_seek: float
|
||||||
|
Whether to use fast seek or not
|
||||||
|
direct_play: float
|
||||||
|
Whether to use direct play or not
|
||||||
|
direct_stream: float
|
||||||
|
Whether to use direct stream or not
|
||||||
|
subtitle_size: float
|
||||||
|
The size of the subtitles
|
||||||
|
subtites: str
|
||||||
|
The subtitles
|
||||||
|
audio_boost: float
|
||||||
|
The audio boost
|
||||||
|
location: str
|
||||||
|
The location of the transcode session
|
||||||
|
media_buffer_size: float
|
||||||
|
The size of the media buffer
|
||||||
|
session: str
|
||||||
|
The session ID
|
||||||
|
add_debug_overlay: float
|
||||||
|
Whether to add a debug overlay or not
|
||||||
|
auto_adjust_quality: float
|
||||||
|
Whether to auto adjust quality or not
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/video/:/transcode/universal/start.mpd"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not has_mde:
|
||||||
|
raise ValueError("Parameter has_mde is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "hasMDE", has_mde)
|
||||||
|
)
|
||||||
|
if not path:
|
||||||
|
raise ValueError("Parameter path is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "path", path)
|
||||||
|
)
|
||||||
|
if not media_index:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter media_index is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "mediaIndex", media_index)
|
||||||
|
)
|
||||||
|
if not part_index:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter part_index is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "partIndex", part_index)
|
||||||
|
)
|
||||||
|
if not protocol:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter protocol is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "protocol", protocol)
|
||||||
|
)
|
||||||
|
if fast_seek:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "fastSeek", fast_seek)
|
||||||
|
)
|
||||||
|
if direct_play:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query(
|
||||||
|
"form", False, "directPlay", direct_play
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if direct_stream:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query(
|
||||||
|
"form", False, "directStream", direct_stream
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if subtitle_size:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query(
|
||||||
|
"form", False, "subtitleSize", subtitle_size
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if subtites:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "subtites", subtites)
|
||||||
|
)
|
||||||
|
if audio_boost:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query(
|
||||||
|
"form", False, "audioBoost", audio_boost
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if location:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "location", location)
|
||||||
|
)
|
||||||
|
if media_buffer_size:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query(
|
||||||
|
"form", False, "mediaBufferSize", media_buffer_size
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if session:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "session", session)
|
||||||
|
)
|
||||||
|
if add_debug_overlay:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query(
|
||||||
|
"form", False, "addDebugOverlay", add_debug_overlay
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if auto_adjust_quality:
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query(
|
||||||
|
"form", False, "autoAdjustQuality", auto_adjust_quality
|
||||||
|
)
|
||||||
|
)
|
||||||
|
final_url = self._url_prefix + url_endpoint + "?" + "&".join(query_params)
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_timeline(
|
||||||
|
self,
|
||||||
|
row: float,
|
||||||
|
play_back_time: float,
|
||||||
|
play_queue_item_id: float,
|
||||||
|
context: str,
|
||||||
|
duration: float,
|
||||||
|
time: float,
|
||||||
|
has_mde: float,
|
||||||
|
state: StateModel,
|
||||||
|
key: str,
|
||||||
|
rating_key: float,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Get the timeline for a media item
|
||||||
|
Parameters:
|
||||||
|
----------
|
||||||
|
rating_key: float
|
||||||
|
The rating key of the media item
|
||||||
|
key: str
|
||||||
|
The key of the media item to get the timeline for
|
||||||
|
state: State
|
||||||
|
The state of the media item
|
||||||
|
has_mde: float
|
||||||
|
Whether the media item has MDE
|
||||||
|
time: float
|
||||||
|
The time of the media item
|
||||||
|
duration: float
|
||||||
|
The duration of the media item
|
||||||
|
context: str
|
||||||
|
The context of the media item
|
||||||
|
play_queue_item_id: float
|
||||||
|
The play queue item ID of the media item
|
||||||
|
play_back_time: float
|
||||||
|
The playback time of the media item
|
||||||
|
row: float
|
||||||
|
The row of the media item
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_endpoint = "/:/timeline"
|
||||||
|
headers = {}
|
||||||
|
query_params = []
|
||||||
|
self._add_required_headers(headers)
|
||||||
|
if not rating_key:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter rating_key is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "ratingKey", rating_key)
|
||||||
|
)
|
||||||
|
if not key:
|
||||||
|
raise ValueError("Parameter key is required, cannot be empty or blank.")
|
||||||
|
query_params.append(query_serializer.serialize_query("form", False, "key", key))
|
||||||
|
if not state:
|
||||||
|
raise ValueError("Parameter state is required, cannot be empty or blank.")
|
||||||
|
validated_state = self._enum_matching(state, StateModel.list(), "state")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "state", validated_state)
|
||||||
|
)
|
||||||
|
if not has_mde:
|
||||||
|
raise ValueError("Parameter has_mde is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "hasMDE", has_mde)
|
||||||
|
)
|
||||||
|
if not time:
|
||||||
|
raise ValueError("Parameter time is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "time", time)
|
||||||
|
)
|
||||||
|
if not duration:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter duration is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "duration", duration)
|
||||||
|
)
|
||||||
|
if not context:
|
||||||
|
raise ValueError("Parameter context is required, cannot be empty or blank.")
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query("form", False, "context", context)
|
||||||
|
)
|
||||||
|
if not play_queue_item_id:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter play_queue_item_id is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query(
|
||||||
|
"form", False, "playQueueItemID", play_queue_item_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not play_back_time:
|
||||||
|
raise ValueError(
|
||||||
|
"Parameter play_back_time is required, cannot be empty or blank."
|
||||||
|
)
|
||||||
|
query_params.append(
|
||||||
|
query_serializer.serialize_query(
|
||||||
|
"form", False, "playBackTime", play_back_time
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not row:
|
||||||
|
raise ValueError("Parameter row is required, cannot be empty or blank.")
|
||||||
|
query_params.append(query_serializer.serialize_query("form", False, "row", row))
|
||||||
|
final_url = self._url_prefix + url_endpoint + "?" + "&".join(query_params)
|
||||||
|
res = self._http.get(final_url, headers, True)
|
||||||
|
return res
|
||||||
9
src/plexsdk/setup.py
Normal file
9
src/plexsdk/setup.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import setuptools
|
||||||
|
|
||||||
|
setuptools.setup(
|
||||||
|
name="PlexSDK",
|
||||||
|
version="0.0.1",
|
||||||
|
description="""An Open API Spec for interacting with Plex.tv and Plex Servers""",
|
||||||
|
license="MIT",
|
||||||
|
packages=setuptools.find_packages(),
|
||||||
|
)
|
||||||
0
test/__init__.py
Normal file
0
test/__init__.py
Normal file
0
test/models/__init__.py
Normal file
0
test/models/__init__.py
Normal file
14
test/models/test_base.py
Normal file
14
test/models/test_base.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import unittest
|
||||||
|
import responses
|
||||||
|
from http import HTTPStatus
|
||||||
|
from src.plexsdk.models.base import BaseModel
|
||||||
|
from http_exceptions import ClientException
|
||||||
|
|
||||||
|
|
||||||
|
class TestBaseModel(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
16
test/models/test_get_butler_tasks_response.py
Normal file
16
test/models/test_get_butler_tasks_response.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import unittest
|
||||||
|
from src.plexsdk.models.GetButlerTasksResponse import GetButlerTasksResponse
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetButlerTasksResponseModel(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
def test_get_butler_tasks_response(self):
|
||||||
|
# Create GetButlerTasksResponse class instance
|
||||||
|
test_model = GetButlerTasksResponse(ButlerTasks={"accusantium": 1})
|
||||||
|
self.assertEqual(test_model.ButlerTasks, {"accusantium": 1})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
16
test/models/test_get_devices_response.py
Normal file
16
test/models/test_get_devices_response.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import unittest
|
||||||
|
from src.plexsdk.models.GetDevicesResponse import GetDevicesResponse
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetDevicesResponseModel(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
def test_get_devices_response(self):
|
||||||
|
# Create GetDevicesResponse class instance
|
||||||
|
test_model = GetDevicesResponse(MediaContainer={"velit": 1})
|
||||||
|
self.assertEqual(test_model.MediaContainer, {"velit": 1})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
16
test/models/test_get_my_plex_account_response.py
Normal file
16
test/models/test_get_my_plex_account_response.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import unittest
|
||||||
|
from src.plexsdk.models.GetMyPlexAccountResponse import GetMyPlexAccountResponse
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetMyPlexAccountResponseModel(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
def test_get_my_plex_account_response(self):
|
||||||
|
# Create GetMyPlexAccountResponse class instance
|
||||||
|
test_model = GetMyPlexAccountResponse(MyPlex={"repellat": 8})
|
||||||
|
self.assertEqual(test_model.MyPlex, {"repellat": 8})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
16
test/models/test_get_on_deck_response.py
Normal file
16
test/models/test_get_on_deck_response.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import unittest
|
||||||
|
from src.plexsdk.models.GetOnDeckResponse import GetOnDeckResponse
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetOnDeckResponseModel(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
def test_get_on_deck_response(self):
|
||||||
|
# Create GetOnDeckResponse class instance
|
||||||
|
test_model = GetOnDeckResponse(MediaContainer={"quisquam": 1})
|
||||||
|
self.assertEqual(test_model.MediaContainer, {"quisquam": 1})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
16
test/models/test_get_recently_added_response.py
Normal file
16
test/models/test_get_recently_added_response.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import unittest
|
||||||
|
from src.plexsdk.models.GetRecentlyAddedResponse import GetRecentlyAddedResponse
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetRecentlyAddedResponseModel(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
def test_get_recently_added_response(self):
|
||||||
|
# Create GetRecentlyAddedResponse class instance
|
||||||
|
test_model = GetRecentlyAddedResponse(MediaContainer={"numquam": 1})
|
||||||
|
self.assertEqual(test_model.MediaContainer, {"numquam": 1})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
16
test/models/test_get_search_results_response.py
Normal file
16
test/models/test_get_search_results_response.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import unittest
|
||||||
|
from src.plexsdk.models.GetSearchResultsResponse import GetSearchResultsResponse
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetSearchResultsResponseModel(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
def test_get_search_results_response(self):
|
||||||
|
# Create GetSearchResultsResponse class instance
|
||||||
|
test_model = GetSearchResultsResponse(MediaContainer={"eius": 4})
|
||||||
|
self.assertEqual(test_model.MediaContainer, {"eius": 4})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
16
test/models/test_get_server_activities_response.py
Normal file
16
test/models/test_get_server_activities_response.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import unittest
|
||||||
|
from src.plexsdk.models.GetServerActivitiesResponse import GetServerActivitiesResponse
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetServerActivitiesResponseModel(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
def test_get_server_activities_response(self):
|
||||||
|
# Create GetServerActivitiesResponse class instance
|
||||||
|
test_model = GetServerActivitiesResponse(MediaContainer={"quisquam": 5})
|
||||||
|
self.assertEqual(test_model.MediaContainer, {"quisquam": 5})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
18
test/models/test_get_server_capabilities_response.py
Normal file
18
test/models/test_get_server_capabilities_response.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import unittest
|
||||||
|
from src.plexsdk.models.GetServerCapabilitiesResponse import (
|
||||||
|
GetServerCapabilitiesResponse,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetServerCapabilitiesResponseModel(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
def test_get_server_capabilities_response(self):
|
||||||
|
# Create GetServerCapabilitiesResponse class instance
|
||||||
|
test_model = GetServerCapabilitiesResponse(MediaContainer={"maiores": 3})
|
||||||
|
self.assertEqual(test_model.MediaContainer, {"maiores": 3})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
16
test/models/test_get_server_identity_response.py
Normal file
16
test/models/test_get_server_identity_response.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import unittest
|
||||||
|
from src.plexsdk.models.GetServerIdentityResponse import GetServerIdentityResponse
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetServerIdentityResponseModel(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
def test_get_server_identity_response(self):
|
||||||
|
# Create GetServerIdentityResponse class instance
|
||||||
|
test_model = GetServerIdentityResponse(MediaContainer={"eum": 1})
|
||||||
|
self.assertEqual(test_model.MediaContainer, {"eum": 1})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
16
test/models/test_get_server_list_response.py
Normal file
16
test/models/test_get_server_list_response.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import unittest
|
||||||
|
from src.plexsdk.models.GetServerListResponse import GetServerListResponse
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetServerListResponseModel(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
def test_get_server_list_response(self):
|
||||||
|
# Create GetServerListResponse class instance
|
||||||
|
test_model = GetServerListResponse(MediaContainer={"asperiores": 6})
|
||||||
|
self.assertEqual(test_model.MediaContainer, {"asperiores": 6})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
16
test/models/test_get_transcode_sessions_response.py
Normal file
16
test/models/test_get_transcode_sessions_response.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import unittest
|
||||||
|
from src.plexsdk.models.GetTranscodeSessionsResponse import GetTranscodeSessionsResponse
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetTranscodeSessionsResponseModel(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
def test_get_transcode_sessions_response(self):
|
||||||
|
# Create GetTranscodeSessionsResponse class instance
|
||||||
|
test_model = GetTranscodeSessionsResponse(MediaContainer={"eaque": 9})
|
||||||
|
self.assertEqual(test_model.MediaContainer, {"eaque": 9})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
0
test/services/__init__.py
Normal file
0
test/services/__init__.py
Normal file
67
test/services/test_activities.py
Normal file
67
test/services/test_activities.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import unittest
|
||||||
|
import responses
|
||||||
|
from src.plexsdk.net.http_client import HTTPClient
|
||||||
|
from http_exceptions import ClientException
|
||||||
|
from src.plexsdk.services.activities import Activities
|
||||||
|
|
||||||
|
|
||||||
|
class TestActivities_(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_server_activities(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/activities", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Activities("testkey")
|
||||||
|
response = test_service.get_server_activities()
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_server_activities_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/activities", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Activities("testkey")
|
||||||
|
test_service.get_server_activities()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_cancel_server_activities(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.delete(
|
||||||
|
"{protocol}://{ip}:{port}/activities/3963273161", json={}, status=200
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Activities("testkey")
|
||||||
|
response = test_service.cancel_server_activities("3963273161")
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_cancel_server_activities_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.delete(
|
||||||
|
"{protocol}://{ip}:{port}/activities/8877676477", json={}, status=202
|
||||||
|
)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Activities("testkey")
|
||||||
|
test_service.cancel_server_activities()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_cancel_server_activities_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.delete(
|
||||||
|
"{protocol}://{ip}:{port}/activities/6744354026", json={}, status=404
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Activities("testkey")
|
||||||
|
test_service.cancel_server_activities("6744354026")
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
14
test/services/test_base.py
Normal file
14
test/services/test_base.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import unittest
|
||||||
|
import responses
|
||||||
|
from http import HTTPStatus
|
||||||
|
from src.plexsdk.services.base import BaseService
|
||||||
|
from http_exceptions import ClientException
|
||||||
|
|
||||||
|
|
||||||
|
class TestBaseService(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
139
test/services/test_butler.py
Normal file
139
test/services/test_butler.py
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
import unittest
|
||||||
|
import responses
|
||||||
|
from src.plexsdk.net.http_client import HTTPClient
|
||||||
|
from http_exceptions import ClientException
|
||||||
|
from src.plexsdk.services.butler import Butler
|
||||||
|
|
||||||
|
|
||||||
|
class TestButler_(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_butler_tasks(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/butler", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Butler("testkey")
|
||||||
|
response = test_service.get_butler_tasks()
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_butler_tasks_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/butler", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Butler("testkey")
|
||||||
|
test_service.get_butler_tasks()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_start_all_tasks(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.post("{protocol}://{ip}:{port}/butler", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Butler("testkey")
|
||||||
|
response = test_service.start_all_tasks()
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_start_all_tasks_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.post("{protocol}://{ip}:{port}/butler", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Butler("testkey")
|
||||||
|
test_service.start_all_tasks()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_stop_all_tasks(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.delete("{protocol}://{ip}:{port}/butler", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Butler("testkey")
|
||||||
|
response = test_service.stop_all_tasks()
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_stop_all_tasks_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.delete("{protocol}://{ip}:{port}/butler", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Butler("testkey")
|
||||||
|
test_service.stop_all_tasks()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_start_task(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.post(
|
||||||
|
"{protocol}://{ip}:{port}/butler/BackupDatabase", json={}, status=200
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Butler("testkey")
|
||||||
|
response = test_service.start_task("BackupDatabase")
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_start_task_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.post(
|
||||||
|
"{protocol}://{ip}:{port}/butler/BackupDatabase", json={}, status=202
|
||||||
|
)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Butler("testkey")
|
||||||
|
test_service.start_task()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_start_task_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.post(
|
||||||
|
"{protocol}://{ip}:{port}/butler/BackupDatabase", json={}, status=404
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Butler("testkey")
|
||||||
|
test_service.start_task("BackupDatabase")
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_stop_task(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.delete(
|
||||||
|
"{protocol}://{ip}:{port}/butler/BackupDatabase", json={}, status=200
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Butler("testkey")
|
||||||
|
response = test_service.stop_task("BackupDatabase")
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_stop_task_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.delete(
|
||||||
|
"{protocol}://{ip}:{port}/butler/BackupDatabase", json={}, status=202
|
||||||
|
)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Butler("testkey")
|
||||||
|
test_service.stop_task()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_stop_task_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.delete(
|
||||||
|
"{protocol}://{ip}:{port}/butler/BackupDatabase", json={}, status=404
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Butler("testkey")
|
||||||
|
test_service.stop_task("BackupDatabase")
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
67
test/services/test_hubs.py
Normal file
67
test/services/test_hubs.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import unittest
|
||||||
|
import responses
|
||||||
|
from src.plexsdk.net.http_client import HTTPClient
|
||||||
|
from http_exceptions import ClientException
|
||||||
|
from src.plexsdk.services.hubs import Hubs
|
||||||
|
|
||||||
|
|
||||||
|
class TestHubs_(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_global_hubs(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/hubs", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Hubs("testkey")
|
||||||
|
response = test_service.get_global_hubs(8, 4)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_global_hubs_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/hubs", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Hubs("testkey")
|
||||||
|
test_service.get_global_hubs(8, 3)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_library_hubs(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/hubs/sections/3684333305", json={}, status=200
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Hubs("testkey")
|
||||||
|
response = test_service.get_library_hubs(3684333305, 2, 4)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_library_hubs_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/hubs/sections/2635728470", json={}, status=202
|
||||||
|
)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Hubs("testkey")
|
||||||
|
test_service.get_library_hubs()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_library_hubs_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/hubs/sections/5770327453", json={}, status=404
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Hubs("testkey")
|
||||||
|
test_service.get_library_hubs(5770327453, 9, 3)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
399
test/services/test_library.py
Normal file
399
test/services/test_library.py
Normal file
@@ -0,0 +1,399 @@
|
|||||||
|
import unittest
|
||||||
|
import responses
|
||||||
|
from src.plexsdk.net.http_client import HTTPClient
|
||||||
|
from http_exceptions import ClientException
|
||||||
|
from src.plexsdk.services.library import Library
|
||||||
|
|
||||||
|
|
||||||
|
class TestLibrary_(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_file_hash(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/library/hashes", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Library("testkey")
|
||||||
|
response = test_service.get_file_hash("asperiores", 7)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_file_hash_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/library/hashes", json={}, status=202)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.get_file_hash()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_file_hash_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/library/hashes", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.get_file_hash("cum", 4)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_recently_added(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/recentlyAdded", json={}, status=200
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Library("testkey")
|
||||||
|
response = test_service.get_recently_added()
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_recently_added_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/recentlyAdded", json={}, status=404
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.get_recently_added()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_libraries(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/library/sections", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Library("testkey")
|
||||||
|
response = test_service.get_libraries()
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_libraries_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/library/sections", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.get_libraries()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_library(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/sections/9871766595", json={}, status=200
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Library("testkey")
|
||||||
|
response = test_service.get_library(9871766595, 8)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_library_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/sections/5100915384", json={}, status=202
|
||||||
|
)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.get_library()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_library_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/sections/6801308709", json={}, status=404
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.get_library(6801308709, 3)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_delete_library(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.delete(
|
||||||
|
"{protocol}://{ip}:{port}/library/sections/3061349599", json={}, status=200
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Library("testkey")
|
||||||
|
response = test_service.delete_library(3061349599)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_delete_library_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.delete(
|
||||||
|
"{protocol}://{ip}:{port}/library/sections/6921817889", json={}, status=202
|
||||||
|
)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.delete_library()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_delete_library_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.delete(
|
||||||
|
"{protocol}://{ip}:{port}/library/sections/9683751588", json={}, status=404
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.delete_library(9683751588)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_library_items(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/sections/6582958821/all",
|
||||||
|
json={},
|
||||||
|
status=200,
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Library("testkey")
|
||||||
|
response = test_service.get_library_items(6582958821, 9, "quibusdam")
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_library_items_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/sections/9837157577/all",
|
||||||
|
json={},
|
||||||
|
status=202,
|
||||||
|
)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.get_library_items()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_library_items_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/sections/2390106765/all",
|
||||||
|
json={},
|
||||||
|
status=404,
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.get_library_items(2390106765, 1, "voluptas")
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_refresh_library(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/sections/6321566139/refresh",
|
||||||
|
json={},
|
||||||
|
status=200,
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Library("testkey")
|
||||||
|
response = test_service.refresh_library(6321566139)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_refresh_library_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/sections/1404494475/refresh",
|
||||||
|
json={},
|
||||||
|
status=202,
|
||||||
|
)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.refresh_library()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_refresh_library_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/sections/5664562376/refresh",
|
||||||
|
json={},
|
||||||
|
status=404,
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.refresh_library(5664562376)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_latest_library_items(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/sections/7193296778/latest",
|
||||||
|
json={},
|
||||||
|
status=200,
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Library("testkey")
|
||||||
|
response = test_service.get_latest_library_items(3, 7193296778, "optio")
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_latest_library_items_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/sections/6458970563/latest",
|
||||||
|
json={},
|
||||||
|
status=202,
|
||||||
|
)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.get_latest_library_items()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_latest_library_items_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/sections/1032189612/latest",
|
||||||
|
json={},
|
||||||
|
status=404,
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.get_latest_library_items(9, 1032189612, "enim")
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_common_library_items(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/sections/8358490584/common",
|
||||||
|
json={},
|
||||||
|
status=200,
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Library("testkey")
|
||||||
|
response = test_service.get_common_library_items(7, 8358490584, "sed")
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_common_library_items_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/sections/6763505882/common",
|
||||||
|
json={},
|
||||||
|
status=202,
|
||||||
|
)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.get_common_library_items()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_common_library_items_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/sections/8191586203/common",
|
||||||
|
json={},
|
||||||
|
status=404,
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.get_common_library_items(2, 8191586203, "nobis")
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_metadata(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/metadata/2", json={}, status=200
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Library("testkey")
|
||||||
|
response = test_service.get_metadata(2)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_metadata_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/metadata/3", json={}, status=202
|
||||||
|
)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.get_metadata()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_metadata_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/metadata/5", json={}, status=404
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.get_metadata(5)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_metadata_children(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/metadata/4/children", json={}, status=200
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Library("testkey")
|
||||||
|
response = test_service.get_metadata_children(4)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_metadata_children_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/metadata/2/children", json={}, status=202
|
||||||
|
)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.get_metadata_children()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_metadata_children_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/library/metadata/9/children", json={}, status=404
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.get_metadata_children(9)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_on_deck(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/library/onDeck", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Library("testkey")
|
||||||
|
response = test_service.get_on_deck()
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_on_deck_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/library/onDeck", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Library("testkey")
|
||||||
|
test_service.get_on_deck()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
80
test/services/test_log.py
Normal file
80
test/services/test_log.py
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import unittest
|
||||||
|
import responses
|
||||||
|
from src.plexsdk.net.http_client import HTTPClient
|
||||||
|
from http_exceptions import ClientException
|
||||||
|
from src.plexsdk.services.log import Log
|
||||||
|
|
||||||
|
|
||||||
|
class TestLog_(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_log_line(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/log", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Log("testkey")
|
||||||
|
response = test_service.log_line("adipisci", "reprehenderit", 1)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_log_line_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/log", json={}, status=202)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Log("testkey")
|
||||||
|
test_service.log_line()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_log_line_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/log", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Log("testkey")
|
||||||
|
test_service.log_line("eaque", "ea", 4)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_log_multi_line(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.post("{protocol}://{ip}:{port}/log", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Log("testkey")
|
||||||
|
response = test_service.log_multi_line()
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_log_multi_line_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.post("{protocol}://{ip}:{port}/log", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Log("testkey")
|
||||||
|
test_service.log_multi_line()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_enable_paper_trail(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/log/networked", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Log("testkey")
|
||||||
|
response = test_service.enable_paper_trail()
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_enable_paper_trail_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/log/networked", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Log("testkey")
|
||||||
|
test_service.enable_paper_trail()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
98
test/services/test_media.py
Normal file
98
test/services/test_media.py
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import unittest
|
||||||
|
import responses
|
||||||
|
from src.plexsdk.net.http_client import HTTPClient
|
||||||
|
from http_exceptions import ClientException
|
||||||
|
from src.plexsdk.services.media import Media
|
||||||
|
|
||||||
|
|
||||||
|
class TestMedia_(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_mark_played(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/:/scrobble", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Media("testkey")
|
||||||
|
response = test_service.mark_played(1)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_mark_played_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/:/scrobble", json={}, status=202)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Media("testkey")
|
||||||
|
test_service.mark_played()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_mark_played_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/:/scrobble", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Media("testkey")
|
||||||
|
test_service.mark_played(9)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_mark_unplayed(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/:/unscrobble", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Media("testkey")
|
||||||
|
response = test_service.mark_unplayed(9)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_mark_unplayed_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/:/unscrobble", json={}, status=202)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Media("testkey")
|
||||||
|
test_service.mark_unplayed()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_mark_unplayed_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/:/unscrobble", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Media("testkey")
|
||||||
|
test_service.mark_unplayed(7)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_update_play_progress(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.post("{protocol}://{ip}:{port}/:/progress", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Media("testkey")
|
||||||
|
response = test_service.update_play_progress("esse", 7, "nesciunt")
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_update_play_progress_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.post("{protocol}://{ip}:{port}/:/progress", json={}, status=202)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Media("testkey")
|
||||||
|
test_service.update_play_progress()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_update_play_progress_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.post("{protocol}://{ip}:{port}/:/progress", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Media("testkey")
|
||||||
|
test_service.update_play_progress("quo", 7, "dolorum")
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
297
test/services/test_playlists.py
Normal file
297
test/services/test_playlists.py
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
import unittest
|
||||||
|
import responses
|
||||||
|
from src.plexsdk.net.http_client import HTTPClient
|
||||||
|
from http_exceptions import ClientException
|
||||||
|
from src.plexsdk.services.playlists import Playlists
|
||||||
|
|
||||||
|
|
||||||
|
class TestPlaylists_(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_create_playlist(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.post("{protocol}://{ip}:{port}/playlists", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
response = test_service.create_playlist(
|
||||||
|
8, "audio", "quasi", "alias", 5922730203
|
||||||
|
)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_create_playlist_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.post("{protocol}://{ip}:{port}/playlists", json={}, status=202)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
test_service.create_playlist()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_create_playlist_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.post("{protocol}://{ip}:{port}/playlists", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
test_service.create_playlist(4, "audio", "nihil", "cumque", 5164878131)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_playlists(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/playlists/all", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
response = test_service.get_playlists("audio", 5)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_playlists_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/playlists/all", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
test_service.get_playlists("audio", 4)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_playlist(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/playlists/8961898762", json={}, status=200
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
response = test_service.get_playlist(8961898762)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_playlist_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/playlists/2350925795", json={}, status=202
|
||||||
|
)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
test_service.get_playlist()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_playlist_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/playlists/7803831874", json={}, status=404
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
test_service.get_playlist(7803831874)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_update_playlist(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.put(
|
||||||
|
"{protocol}://{ip}:{port}/playlists/4846174885", json={}, status=200
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
response = test_service.update_playlist(4846174885)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_update_playlist_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.put(
|
||||||
|
"{protocol}://{ip}:{port}/playlists/3217640966", json={}, status=202
|
||||||
|
)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
test_service.update_playlist()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_update_playlist_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.put(
|
||||||
|
"{protocol}://{ip}:{port}/playlists/2969411689", json={}, status=404
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
test_service.update_playlist(2969411689)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_delete_playlist(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.delete(
|
||||||
|
"{protocol}://{ip}:{port}/playlists/9157110662", json={}, status=200
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
response = test_service.delete_playlist(9157110662)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_delete_playlist_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.delete(
|
||||||
|
"{protocol}://{ip}:{port}/playlists/9590435699", json={}, status=202
|
||||||
|
)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
test_service.delete_playlist()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_delete_playlist_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.delete(
|
||||||
|
"{protocol}://{ip}:{port}/playlists/7937977423", json={}, status=404
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
test_service.delete_playlist(7937977423)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_playlist_contents(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/playlists/5686140716/items", json={}, status=200
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
response = test_service.get_playlist_contents(8, 5686140716)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_playlist_contents_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/playlists/3666405994/items", json={}, status=202
|
||||||
|
)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
test_service.get_playlist_contents()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_playlist_contents_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/playlists/9239392917/items", json={}, status=404
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
test_service.get_playlist_contents(4, 9239392917)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_add_playlist_contents(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.put(
|
||||||
|
"{protocol}://{ip}:{port}/playlists/2224463633/items", json={}, status=200
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
response = test_service.add_playlist_contents(
|
||||||
|
7330486290, "provident", 2224463633
|
||||||
|
)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_add_playlist_contents_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.put(
|
||||||
|
"{protocol}://{ip}:{port}/playlists/8938618789/items", json={}, status=202
|
||||||
|
)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
test_service.add_playlist_contents()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_add_playlist_contents_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.put(
|
||||||
|
"{protocol}://{ip}:{port}/playlists/7136237365/items", json={}, status=404
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
test_service.add_playlist_contents(3430294919, "aspernatur", 7136237365)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_clear_playlist_contents(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.delete(
|
||||||
|
"{protocol}://{ip}:{port}/playlists/6699998436/items", json={}, status=200
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
response = test_service.clear_playlist_contents(6699998436)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_clear_playlist_contents_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.delete(
|
||||||
|
"{protocol}://{ip}:{port}/playlists/1772875063/items", json={}, status=202
|
||||||
|
)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
test_service.clear_playlist_contents()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_clear_playlist_contents_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.delete(
|
||||||
|
"{protocol}://{ip}:{port}/playlists/3406600816/items", json={}, status=404
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
test_service.clear_playlist_contents(3406600816)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_upload_playlist(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.post("{protocol}://{ip}:{port}/playlists/upload", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
response = test_service.upload_playlist(2, "dignissimos")
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_upload_playlist_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.post("{protocol}://{ip}:{port}/playlists/upload", json={}, status=202)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
test_service.upload_playlist()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_upload_playlist_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.post("{protocol}://{ip}:{port}/playlists/upload", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Playlists("testkey")
|
||||||
|
test_service.upload_playlist(5, "fugit")
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
98
test/services/test_search.py
Normal file
98
test/services/test_search.py
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import unittest
|
||||||
|
import responses
|
||||||
|
from src.plexsdk.net.http_client import HTTPClient
|
||||||
|
from http_exceptions import ClientException
|
||||||
|
from src.plexsdk.services.search import Search
|
||||||
|
|
||||||
|
|
||||||
|
class TestSearch_(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_perform_search(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/hubs/search", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Search("testkey")
|
||||||
|
response = test_service.perform_search("incidunt", 2233364198, 9)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_perform_search_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/hubs/search", json={}, status=202)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Search("testkey")
|
||||||
|
test_service.perform_search()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_perform_search_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/hubs/search", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Search("testkey")
|
||||||
|
test_service.perform_search("molestias", 8612735357, 3)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_perform_voice_search(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/hubs/search/voice", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Search("testkey")
|
||||||
|
response = test_service.perform_voice_search("numquam", 7139321773, 2)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_perform_voice_search_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/hubs/search/voice", json={}, status=202)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Search("testkey")
|
||||||
|
test_service.perform_voice_search()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_perform_voice_search_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/hubs/search/voice", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Search("testkey")
|
||||||
|
test_service.perform_voice_search("maxime", 5682941219, 6)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_search_results(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/search", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Search("testkey")
|
||||||
|
response = test_service.get_search_results("veritatis")
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_search_results_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/search", json={}, status=202)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Search("testkey")
|
||||||
|
test_service.get_search_results()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_search_results_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/search", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Search("testkey")
|
||||||
|
test_service.get_search_results("eaque")
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
76
test/services/test_security.py
Normal file
76
test/services/test_security.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import unittest
|
||||||
|
import responses
|
||||||
|
from src.plexsdk.net.http_client import HTTPClient
|
||||||
|
from http_exceptions import ClientException
|
||||||
|
from src.plexsdk.services.security import Security
|
||||||
|
|
||||||
|
|
||||||
|
class TestSecurity_(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_transient_token(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/security/token", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Security("testkey")
|
||||||
|
response = test_service.get_transient_token("all", "delegation")
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_transient_token_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/security/token", json={}, status=202)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Security("testkey")
|
||||||
|
test_service.get_transient_token()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_transient_token_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/security/token", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Security("testkey")
|
||||||
|
test_service.get_transient_token("all", "delegation")
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_source_connection_information(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/security/resources", json={}, status=200
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Security("testkey")
|
||||||
|
response = test_service.get_source_connection_information("provident")
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_source_connection_information_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/security/resources", json={}, status=202
|
||||||
|
)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Security("testkey")
|
||||||
|
test_service.get_source_connection_information()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_source_connection_information_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/security/resources", json={}, status=404
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Security("testkey")
|
||||||
|
test_service.get_source_connection_information("molestiae")
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
175
test/services/test_server.py
Normal file
175
test/services/test_server.py
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
import unittest
|
||||||
|
import responses
|
||||||
|
from src.plexsdk.net.http_client import HTTPClient
|
||||||
|
from http_exceptions import ClientException
|
||||||
|
from src.plexsdk.services.server import Server
|
||||||
|
|
||||||
|
|
||||||
|
class TestServer_(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_server_capabilities(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Server("testkey")
|
||||||
|
response = test_service.get_server_capabilities()
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_server_capabilities_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Server("testkey")
|
||||||
|
test_service.get_server_capabilities()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_server_preferences(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/:/prefs", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Server("testkey")
|
||||||
|
response = test_service.get_server_preferences()
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_server_preferences_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/:/prefs", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Server("testkey")
|
||||||
|
test_service.get_server_preferences()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_available_clients(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/clients", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Server("testkey")
|
||||||
|
response = test_service.get_available_clients()
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_available_clients_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/clients", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Server("testkey")
|
||||||
|
test_service.get_available_clients()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_devices(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/devices", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Server("testkey")
|
||||||
|
response = test_service.get_devices()
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_devices_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/devices", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Server("testkey")
|
||||||
|
test_service.get_devices()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_server_identity(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/identity", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Server("testkey")
|
||||||
|
response = test_service.get_server_identity()
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_server_identity_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/identity", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Server("testkey")
|
||||||
|
test_service.get_server_identity()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_my_plex_account(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/myplex/account", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Server("testkey")
|
||||||
|
response = test_service.get_my_plex_account()
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_my_plex_account_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/myplex/account", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Server("testkey")
|
||||||
|
test_service.get_my_plex_account()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_resized_photo(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/photo/:/transcode", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Server("testkey")
|
||||||
|
response = test_service.get_resized_photo("saepe", 6, 6, 3, 4, 6, 6805299528)
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_resized_photo_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/photo/:/transcode", json={}, status=202)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Server("testkey")
|
||||||
|
test_service.get_resized_photo()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_resized_photo_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/photo/:/transcode", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Server("testkey")
|
||||||
|
test_service.get_resized_photo("asperiores", 3, 2, 3, 5, 4, 8443664162)
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_server_list(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/servers", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Server("testkey")
|
||||||
|
response = test_service.get_server_list()
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_server_list_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/servers", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Server("testkey")
|
||||||
|
test_service.get_server_list()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
115
test/services/test_sessions.py
Normal file
115
test/services/test_sessions.py
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
import unittest
|
||||||
|
import responses
|
||||||
|
from src.plexsdk.net.http_client import HTTPClient
|
||||||
|
from http_exceptions import ClientException
|
||||||
|
from src.plexsdk.services.sessions import Sessions
|
||||||
|
|
||||||
|
|
||||||
|
class TestSessions_(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_sessions(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/status/sessions", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Sessions("testkey")
|
||||||
|
response = test_service.get_sessions()
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_sessions_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/status/sessions", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Sessions("testkey")
|
||||||
|
test_service.get_sessions()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_session_history(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/status/sessions/history/all", json={}, status=200
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Sessions("testkey")
|
||||||
|
response = test_service.get_session_history()
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_session_history_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/status/sessions/history/all", json={}, status=404
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Sessions("testkey")
|
||||||
|
test_service.get_session_history()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_transcode_sessions(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/transcode/sessions", json={}, status=200
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Sessions("testkey")
|
||||||
|
response = test_service.get_transcode_sessions()
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_transcode_sessions_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get(
|
||||||
|
"{protocol}://{ip}:{port}/transcode/sessions", json={}, status=404
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Sessions("testkey")
|
||||||
|
test_service.get_transcode_sessions()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_stop_transcode_session(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.delete(
|
||||||
|
"{protocol}://{ip}:{port}/transcode/sessions/natus", json={}, status=200
|
||||||
|
)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Sessions("testkey")
|
||||||
|
response = test_service.stop_transcode_session("natus")
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_stop_transcode_session_required_fields_missing(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.delete(
|
||||||
|
"{protocol}://{ip}:{port}/transcode/sessions/iure", json={}, status=202
|
||||||
|
)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
test_service = Sessions("testkey")
|
||||||
|
test_service.stop_transcode_session()
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_stop_transcode_session_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.delete(
|
||||||
|
"{protocol}://{ip}:{port}/transcode/sessions/praesentium",
|
||||||
|
json={},
|
||||||
|
status=404,
|
||||||
|
)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Sessions("testkey")
|
||||||
|
test_service.stop_transcode_session("praesentium")
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
71
test/services/test_updater.py
Normal file
71
test/services/test_updater.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import unittest
|
||||||
|
import responses
|
||||||
|
from src.plexsdk.net.http_client import HTTPClient
|
||||||
|
from http_exceptions import ClientException
|
||||||
|
from src.plexsdk.services.updater import Updater
|
||||||
|
|
||||||
|
|
||||||
|
class TestUpdater_(unittest.TestCase):
|
||||||
|
def test_true(self):
|
||||||
|
self.assertTrue(True)
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_update_status(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/updater/status", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Updater("testkey")
|
||||||
|
response = test_service.get_update_status()
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_update_status_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.get("{protocol}://{ip}:{port}/updater/status", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Updater("testkey")
|
||||||
|
test_service.get_update_status()
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_check_for_updates(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.put("{protocol}://{ip}:{port}/updater/check", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Updater("testkey")
|
||||||
|
response = test_service.check_for_updates("foo")
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_check_for_updates_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.put("{protocol}://{ip}:{port}/updater/check", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Updater("testkey")
|
||||||
|
test_service.check_for_updates("foo")
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_apply_updates(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.put("{protocol}://{ip}:{port}/updater/apply", json={}, status=200)
|
||||||
|
# call the method to test
|
||||||
|
test_service = Updater("testkey")
|
||||||
|
response = test_service.apply_updates("foo", "foo")
|
||||||
|
self.assertEqual(response.data, {})
|
||||||
|
responses.reset(),
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_apply_updates_error_on_non_200(self):
|
||||||
|
# Mock the API response
|
||||||
|
responses.put("{protocol}://{ip}:{port}/updater/apply", json={}, status=404)
|
||||||
|
with self.assertRaises(ClientException):
|
||||||
|
test_service = Updater("testkey")
|
||||||
|
test_service.apply_updates("foo", "foo")
|
||||||
|
responses.reset()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user